Commit graph

2581 commits

Author SHA1 Message Date
4bd444f147 revert: remove custom color palette, restore standard Tailwind v4 colors 2026-06-23 12:29:46 -05:00
253657280b Revert "refactor: apply custom color palette across entire codebase"
This reverts commit 5a7cdc7138.
2026-06-23 12:26:57 -05:00
5a7cdc7138 refactor: apply custom color palette across entire codebase
- Replace gray->cool-steel, blue/indigo->cerulean, red->sweet-salmon, yellow/amber->wheat
- Dark sidebar: sidebar/footer use cool-steel-800 bg, text lightened for contrast
- ~11,600 color replacements across ~99 files
- Update tests for new color class names
2026-06-23 10:58:48 -05:00
ebcd54be5b refactor: remove daisyUI, use straight Tailwind v4 with custom color palette
- Remove daisyUI plugins and theme from app.css
- Define 5 custom color palettes: sweet-salmon, desert-sand, wheat, cool-steel, cerulean
- Add @utility card, badge, btn classes to replace daisyUI components
- Replace all daisyUI classes across 16+ template files
- Update tests for new class return values
- Set light mode as default theme
2026-06-23 10:44:20 -05:00
785f077c50 feat: change default theme from system to light mode 2026-06-23 10:28:31 -05:00
b76637c14f refactor: remove icons from sidebar and mobile nav menu items 2026-06-23 10:18:40 -05:00
86ad19fc74 chore: update vendored oban_pro to 1.7.6 2026-06-23 09:57:21 -05:00
8cb8b5b027 update elixir and erlang 2026-06-23 09:43:24 -05:00
3a408a8dc1 Security hardening + performance fixes across codebase
CRITICAL:
- Membership: remove :role/:org_id/:user_id from mass-assignment cast; use explicit create_changeset/4 and role_update_changeset/2
- GraphQL member resolver: add authorize_invite/3 checking admin/owner role and role hierarchy
- REST invitations controller: add auth check for invite creation

HIGH:
- ApiToken: remove :organization_id/:user_id from cast; use explicit create_changeset/4

MEDIUM:
- Move 8 LiveView Ecto queries into context modules (Admin, Alerts, Coverages, OnCall, Snmp)
- Replace Process.put/Process.get with socket assigns for unresolved_alert_count (user_auth + layouts + 50 templates)
- Add batch get_utilization_for_interfaces/1 to eliminate N+1 capacity queries in device show
- Replace Process.sleep with Process.monitor/assert_receive or Process.send_after in 5 test files

LOW:
- Add handle_params/3 to UserResetPasswordLive, UserRegistrationLive, StatusPageLive
- Remove redundant Repo.preload calls; add preloads to list_site_devices/1
- Fix @impl annotations and credo nesting warnings
2026-06-21 17:40:50 -05:00
89ed385865 bump elixir 2026-06-16 17:20:06 -05:00
0ef5c235c8 Fix ProfileWatcherTest: verify init return shape only 2026-06-16 16:44:38 -05:00
00b0ed2f5d Fix ProfileWatcherTest: handle FileSystem not loaded in test env 2026-06-16 16:33:26 -05:00
bdf22cd411 Fix flaky deferred_discovery timeout test — use receive instead of Process.sleep for deterministic blocking 2026-06-16 16:26:11 -05:00
a4e08b2aca Major codebase cleanup: DRY schemas, remove dead config, consolidate modules
- Created Towerops.Schema base macro (95+ schemas updated to use it)
- Created Towerops.Snmp.Reading macro (7 reading schemas consolidated)
- Created Towerops.Gaiia.BaseSchema macro (4 Gaiia schemas consolidated)
- Created Towerops.SyncLog shared module (UISP + Preseem merge)
- Created Towerops.LogFilters (3 log filter modules merged into 1)
- Created Towerops.OnCall.ChangesetHelpers (9 on-call schemas simplified)
- Created API v1 ResourceController shared helpers (7 controllers)
- Extracted ConnectionHelpers.format_connection_result (2 LiveViews)
- Removed 5 dead config keys (scopes, mib_dirs, stripe_meter_id, etc.)
- Fixed 2 pre-existing broken tests
- All 13,219 tests pass, Credo clean (0 issues)
2026-06-16 15:29:22 -05:00
d1403c8069 Fix failing tests and clean up code
- Fix doctests for Accounts, Agents.Stats, Snmp to match actual behavior
- Fix dynamic_extra_test vendor post-processing tests to seed sensor data
- Fix activity_controller_test to seed devices for feed data
- Fix session_manager_test to create browser session for test
- Fix topology_test link creation for connection test
- Fix device_monitor/driver_worker tests for unique job constraints
- Fix accounts_test expired_tokens assertion (magic link token is expired)
- Fix happy_path_test and show_events_test to seed monitor data
- Fix admin user_live_test user.name -> user.email (no name field)
- Fix schema_test to seed activity data
- Fix mobile_qr_live_test to match actual template text
- Fix SnmpKit.MIB doctests and tests for enriched return values
- Fix onboarding_live, mobile_controller, mib_test weak assertions
- Remove dead code and fix credo warnings
2026-06-16 14:54:34 -05:00
13f5e8a487 fix settings doctests with undefined function refs 2026-06-16 11:13:15 -05:00
3c2e22fe0d fix broken doctests and test var refs 2026-06-16 10:52:50 -05:00
58daf45b60 fix oban pro worker warnings 2026-06-16 10:47:34 -05:00
78c013e8b7 credo fixes 2026-06-16 09:56:51 -05:00
d85d156bc5 credo fixes 2026-06-15 11:15:46 -05:00
5096ea2669 credo fixes 2026-06-14 16:38:03 -05:00
5e2a3c229e credo fixes 2026-06-14 08:56:02 -05:00
d0160f7d8a credo fixes 2026-06-14 08:27:57 -05:00
9c3319050f credo fixes 2026-06-13 18:24:06 -05:00
0abe522582 credo fixes 2026-06-13 18:20:20 -05:00
5bb55180a7 Fix all mix credo --strict and compiler warnings 2026-06-13 15:45:09 -05:00
bc1a8bd4bd credo fixes 2026-06-13 14:52:18 -05:00
454d9821ae credo fixes 2026-06-13 14:25:19 -05:00
7a3950d1ad chore: add jump_credo_checks ~> 0.4 with all 19 checks enabled
All checks set to exit_status: 0 initially to avoid blocking on
pre-existing violations. Remove exit_status overrides as each
check category is cleaned up incrementally.

Dependency added to dev/test only.
2026-06-12 13:45:59 -05:00
b96b1fabcb chore: add jump_credo_checks ~> 0.4 with all 19 checks enabled
All checks set to exit_status: 0 initially to avoid blocking on
pre-existing violations. Remove exit_status overrides as each
check category is cleaned up incrementally.
2026-06-12 13:44:31 -05:00
080e5111e4 fix: resolve all dialyzer type warnings across codebase
- Narrow @spec for NetworkInsightPrompt.build/1 and NetworkSnapshot.build/1
- Match/discard unmatched returns in monitoring.ex, alerts.ex, and
  cloud_latency_probe_worker.ex
- Remove unreachable :rate_limited pattern in mikrotik_webhook_worker.ex
  (resolve_error returns {:rate_limited, pos_integer()}, never bare atom)
- Fix gettext pluralisation in insights_live/index.ex: dngettext was
  receiving a string where it expected a non_neg_integer count
2026-06-08 16:17:10 -05:00
b5e3c1f977 fix: reduce health-check probe pressure to stop readiness-flapping downtime
Three changes targeting the root cause of production up/down alerting:

1. Skip BruteForceProtection for /health and /health/live paths
   (removes unnecessary Repo.get_by(IpBlock) DB query from every
   k8s probe, matching the existing /socket/agent exemption).

2. Relax k8s readiness probe: periodSeconds 5→10, timeoutSeconds
   2→5, failureThreshold 2→3, successThreshold 3→2 so transient
   DB/Redis blips don't cascade into full service unavailability.

3. Use the persistent Towerops.Redix connection for health checks
   instead of opening a brand-new TCP connection (handshake→AUTH→
   PING→close) on every probe. Towerops.Redix.Fake gains a
   set_ping_response/2 toggle for testing failure paths.
2026-06-08 16:08:31 -05:00
26ac82653e chore: update elixir 1.20.0 / erlang 29.0.1 / debian trixie, add libsctp1 2026-06-04 15:41:19 -05:00
a797bc7cef upgrade: re-vendor oban_pro 1.7.5, oban_web 2.12.5, oban_met 1.2.0; fix Oban unique states warnings; update deps and elixir to 1.19 2026-06-03 14:38:34 -05:00
cad00fee06 ci: install hex/rebar before deps.get to fix OTP 28 compat
The previous commit bumped CI from OTP 27.2/Elixir 1.18.1 to
OTP 28.5/Elixir 1.19.5. The broad mix cache restore-keys was
restoring a stale ~/.mix cache with hex compiled for the old
OTP, causing beam_load errors on new bs_add instructions.

Adding explicit mix local.hex --force ensures the correct hex
version for the running OTP is installed before deps.get.
2026-06-02 17:20:06 -05:00
f58312ca82 bump elixir/erlang version in ci 2026-06-02 17:06:06 -05:00
d85b42c3ee fix: replace remaining Process.sleep calls with receive/timeout patterns
Production code (lib/): all Process.sleep calls replaced with receive
after timeout - retry backoff, exponential backoff, batch delays,
poll intervals, and settle waits.

Test code: poll_until helpers across 4 agent channel test files now
use receive after instead of Process.sleep. Various standalone
Process.sleep(N) calls replaced with :sys.get_state sync barriers
or receive after. deferred_discovery test simulation sleeps reduced
500ms -> 30ms.
2026-06-02 15:27:23 -05:00
cd19e6626c fix(tests): replace Process.sleep with event-driven sync patterns
Replaced unbounded Process.sleep calls across the test suite with
event-driven alternatives: :sys.get_state sync barriers, assert_receive
with bounded timeouts, and Process.send_after + assert_receive.

Largest improvements:
- storm_detector_test: 120-150ms sleeps removed, uses assert_receive
- deferred_discovery_test: 500ms -> 50ms (timeout test simulation)
- event_logger_test: polling loop replaced with :sys.get_state barrier
- tcp_executor_test: 2000ms sleep replaced with bounded recv timeout
- rate_limit_test: sleeps replaced with send_after + assert_receive
- Various 5-50ms sleeps removed from live_view tests
- Unused require Logger removed from 2 test files
2026-06-02 15:16:53 -05:00
075e779b19 fix(snmpkit): increase timeout on charlist parse test from 1s to 30s
Test takes ~16s on this hardware to resolve/error on invalid
charlists. 1000ms timeout was causing CI pipeline failure, preventing
image build and deployment.
2026-06-02 14:51:22 -05:00
8f9dc201af feat(monitoring): alert when backhaul radios lose connected clients
Backhaul radios (ePMP, AirOS, PMP, Cambium, MikroTik, RouterOS) now
monitor their connected client count with a low threshold of 1. When
the count drops to 0 the SnmpSensorExecutor marks it CRITICAL, which
triggers an alert through the existing check → alert pipeline.

- Adds  field to sensor_def type in Vendor behaviour
- Sets  on all clients-type wireless sensors in
  backhaul-capable vendor profiles
- Adds default executor handling: any clients sensor at 0 → status 2
- 3 new tests for clients sensor behavior
2026-06-02 12:27:44 -05:00
b1cbdc6c28 fix: serve logo as WebP with PNG fallback via picture element
Converts the 120KB PNG logo to 17KB WebP (86% smaller). Both logo instances in the marketing layout use <picture> with WebP source and PNG fallback for maximum browser compatibility.

Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
2026-05-31 09:53:54 -05:00
56843b8e4e fix: add aria-label to section landmarks on home page
Adds descriptive aria-label attributes to 5 <section> elements that were missing accessible landmark labels: 'Pain points', 'Use cases', 'Pricing', 'Technical details', 'Get started'.

Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
2026-05-31 09:53:50 -05:00
1ce1738fc5 fix: set explicit robots meta, Twitter Card tags, and WebP favicon
Adds <meta name='robots'>, twitter:card/title/description meta tags, and WebP favicon reference to the root layout. Fixes title duplication (TowerOps | TowerOps -> Home | TowerOps) by passing page_title from the page controller.

Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
2026-05-31 09:53:37 -05:00
62af9991af Fix L2, L3: remove blocking Process.sleep calls
L2: Remove 2-second Process.sleep from post_startup — the try/catch
already handles transient noproc errors from the TaskSupervisor.

L3: Remove Process.sleep from delete_device — in-flight jobs already
handle the race condition via verify_polling_assignment_unchanged.
The blocking sleep was unnecessary and delayed web requests by 500ms.
2026-05-29 16:00:07 -05:00
b07d42bcba Remove incorrect H1 finding from findings.md
The :env config key IS properly set in all environments:
- config/test.exs:112 sets :test
- config/dev.exs:216 sets :dev
- config/runtime.exs:461 sets :prod

Application.get_env(:towerops, :env) works correctly at runtime.
2026-05-29 15:51:55 -05:00
4421727e61 fix: resolve all Elixir 1.20 type system warnings across project
Removes unreachable catch-all clauses, drops unused `require Logger`
lines, adds pin operators to bitstring size patterns, reorders function
heads for correct dispatch, and replaces deprecated LoggerBackends calls.
2026-05-28 14:52:30 -05:00
196c9473a4 ops(k8s): point liveness/startup at /health/live, scale to 2 replicas
Now that the image serving /health/live is live, switch liveness and startup
probes to the shallow endpoint so transient db/redis blips can't kill or block
a healthy pod; readiness stays on the deep /health. Run 2 replicas so a single
pod restart no longer means full downtime.
2026-05-20 12:22:53 -05:00
9ffa49a8f2 fix(oban): use DynamicLifeline in prod; add /health/live + split k8s probes
Prod paired the Oban Pro Smart engine + unique workers with core
Oban.Plugins.Lifeline, whose rescue has no unique_violation handling. An
orphaned executing CheckExecutorWorker job rescued back to available collides
with its already-scheduled successor on oban_jobs_unique_index (23505),
crashing the plugin every 60s and leaving ~150 orphans stuck. Switch prod to
Oban.Pro.Plugins.DynamicLifeline, which repairs the conflict via
Smart.clear_uniq_violation. Dev keeps core Lifeline (Basic engine).

Add a shallow /health/live endpoint that does not touch db/redis. k8s liveness
and startup probes now target it so a transient dependency outage can't kill or
block boot of a healthy pod; readiness keeps the deep /health to gate the load
balancer. (deployment.yaml probe/replica change pushed separately, after the
image carrying /health/live is live.)

Also drop unused POSTGRES_* keys from the secrets example.
2026-05-20 12:07:29 -05:00
d9bf4f260b fix(monitoring): stop CheckExecutorWorker duplicate job buildup with unique constraint
CheckExecutorWorker had no Oban `unique` constraint, so every insert path
created a fresh job row: self-scheduling, JobHealthCheckWorker, the check
form/API/GraphQL, and — the main amplifier — Monitoring.schedule_check/1
called unconditionally by snmp.ex discovery after create_check/1, which is
an upsert that returns {:ok, check} even when the check already exists.
Every SNMP discovery pass re-enqueued a job for every existing check, so
the check_executors queue grew to ~70k jobs for ~5.7k checks and stopped
draining.

Add `unique: [keys: [:check_id], states: [:available, :scheduled,
:retryable], period: :infinity]`. `:executing` is excluded so perform/1's
self-reschedule does not conflict with its own running job.

Also add the `checks` and `check_executors` queues to config/dev.exs —
they were missing from dev entirely (prod's runtime.exs already had them).
2026-05-14 14:01:07 -05:00
6b82205a58 fix(sync): classify unexpected 5xx statuses as transient so vendor syncs retry
SyncErrors.transient?/1 only had explicit clauses for the {:http_error, ...}
shape, but vendor API clients return {:unexpected_status, status, body}. A 502
was retried only by accident via the catch-all, and a 4xx in that shape would
have been retried 20 times. Add explicit {:unexpected_status, ...} clauses: 5xx
is transient (Oban backs off and retries), 4xx is permanent.
2026-05-14 09:27:17 -05:00