Commit graph

1698 commits

Author SHA1 Message Date
89949fdfd6 fix: deduplicate Preseem sync entries in activity feed
Group sync entries by minute to show only one entry per minute instead
of multiple duplicate entries with the same timestamp. This aggregates:
- Total records synced in that minute
- Average sync duration
- Most recent status

This keeps the activity feed informative while reducing noise from
rapid syncs happening within the same minute.
2026-03-04 15:18:03 -06:00
74ad58bada fix: hide successful Preseem syncs from activity feed
Only show failed Preseem syncs in the activity feed to reduce noise.
Successful syncs happen every 10-20 minutes and clutter the dashboard
without providing useful information to users.

Users can still see all sync history (including successful syncs) in
the Preseem integration settings page if needed.
2026-03-04 15:14:06 -06:00
74173dad0b fix: temporarily disable brute force IP protection
Disable all IP blocking and 404 tracking to allow unrestricted access
while we review and tune ban thresholds and exemptions.

This is a temporary measure to prevent legitimate traffic from being
blocked. Will re-enable after proper configuration.
2026-03-04 14:05:39 -06:00
94bc755dd8
fix: add dark mode support to marketing site
Add dark: variants throughout the marketing page and layout so text,
backgrounds, cards, badges, and borders render properly when the
system or user preference is set to dark mode. Use invert + screen
blend mode on the logo PNG to eliminate its white background.
2026-03-04 13:57:12 -06:00
dce9253afc
fix: exempt agent WebSocket connections from brute force IP bans
Agent connections authenticate at the channel join level with token
validation, so they should not be subject to IP-based brute force
protection. This prevents legitimate agents from being banned when
connecting to the server.

Changes:
- Exempt /socket/agent/websocket from all IP ban checks
- Update documentation to reflect agent exemption
- Agents still authenticate securely via token during channel join
2026-03-04 13:53:13 -06:00
61586d14e2
chore: update all hex dependencies
Upgrades: phoenix 1.8.4, phoenix_live_view 1.1.26, ecto_sql 3.13.5,
bandit 1.10.3, credo 1.7.17, swoosh 1.22.1, yaml_elixir 2.12.1,
styler 1.11.0, error_tracker 0.8.0, finch 0.21.0, honeybadger 0.25.0.

Fix site_aggregation_test assertion to match actual DB default (NULL,
not 0) for account_count on skipped sites.
2026-03-04 13:30:32 -06:00
FluxCD
18c610ca50 chore: update towerops image to git.mcintire.me/graham/towerops-web:main-1772578710-f761290 [skip ci] 2026-03-03 23:05:29 +00:00
FluxCD
19dd54114a chore: update towerops image to git.mcintire.me/graham/towerops-web:main-1772578563-f761290 [skip ci] 2026-03-03 22:58:30 +00:00
f761290ed7
chore(config): add Renovate and build workflows 2026-03-03 16:54:19 -06:00
FluxCD
8882012a65 chore: update towerops image to git.mcintire.me/graham/towerops-web:main-1771432526-f0fea0d [skip ci] 2026-02-18 16:39:38 +00:00
f0fea0dd3e
fix: retry firmware fetch with exponential backoff on network errors
Increase max_attempts from 3 to 10 and add custom backoff/1 that uses
quadratic growth (attempt² × 60s) capped at 2 hours, so transient
network failures retry over ~9 hours rather than being discarded after
3 quick attempts.
2026-02-18 10:34:51 -06:00
FluxCD
cc26b05a9b chore: update towerops image to git.mcintire.me/graham/towerops-web:main-1771431639-998bdf3 [skip ci] 2026-02-18 16:26:36 +00:00
998bdf39b8
update 2026-02-18 10:19:56 -06:00
7081e5daed
fix: correct subscriber counts by filtering CGNAT IPs and excluding routers
Router devices were inflating per-site subscriber counts because:
1. CGNAT IPs (100.64.0.0/10) in router ARP tables caused false matches
2. Routers see all traffic and shouldn't have subscribers attributed
3. Site totals were computed from raw links without filtering

Changes:
- Filter CGNAT (RFC 6598) IPs from device subscriber links
- Deduplicate accounts across devices by match method priority
- Detect routers via SNMP sys_descr and zero out per-device counts
- Derive site totals from filtered per-device impacts
- Add per-device Subs/MRR columns to site device tables
- Add device link fallback for sites without IP blocks
- Fix Gaiia sync case mismatch and execution order
- Improve device list toolbar button icons and tooltips
2026-02-18 09:15:10 -06:00
076b8454de
docs: merge upstream Phoenix AGENTS.md with project-specific patterns
- Add module design/complexity guidelines, quality checks (credo, sobelow,
  coverage), security guidelines (deps.audit), updated auth section with
  live_session details, OTP guidelines, and expanded Elixir core rules
  from cheezy/kanban upstream AGENTS.md
- Move project-specific patterns into AGENTS.md: browser navigation URL
  state, JS hook memory leak prevention, LiveView form data access,
  SNMP test mocking patterns
- Trim CLAUDE.md to project architecture docs only, removing sections
  now covered in AGENTS.md
2026-02-18 09:08:33 -06:00
b765d6d6ce
fix: install hex/rebar inside cache-mounted RUN steps in Dockerfile
The --mount=type=cache,target=/root/.mix overlays shadow the hex
installation done in the prior non-cached RUN step. With a cold cache
the hex archive is absent, causing 'Could not find an SCM for dependency'
errors. Install hex and rebar inside each cache-mounted step instead.
2026-02-17 16:59:27 -06:00
68826dc89b
fix: temporarily disable Honeybadger in production 2026-02-17 16:42:37 -06:00
7668518114
fix: handle missing value attr in input component when using name= directly
When calling <.input name="..." ...> without a field or value attr, @value
was absent from assigns causing a KeyError. Added default: nil to the attr
and use __given__ to detect whether value was explicitly passed so the field
clause still reads value from the FormField correctly.
2026-02-17 16:13:46 -06:00
9d232b8368
fix: quote RELEASE_NODE to prevent word splitting
Co-Authored-By: Graham McIntire <graham@mcintire.me>
2026-02-17 13:19:15 -06:00
0f32086933
Ignore pod shutdown errors in ErrorTracker
Add ErrorTrackerIgnorer to drop port_died, write_failed, epipe, and
broken pipe errors that occur during K8s pod shutdown. Same patterns
already filtered by HoneybadgerFilter and LoggerFilters.
2026-02-17 10:59:51 -06:00
FluxCD
90c24cce49 chore: update towerops image to git.mcintire.me/graham/towerops-web:main-1771347031-2929848 [skip ci] 2026-02-17 16:54:13 +00:00
2929848f51
fix: validate NetBox URL scheme before making HTTP requests
The NetBox client crashed with "scheme is required for url" when
integration credentials had a nil or blank URL. normalize_url/1 now
validates the scheme and returns a clear error instead of letting
Finch blow up.
2026-02-17 10:50:00 -06:00
FluxCD
621afeb5c3 chore: update towerops image to git.mcintire.me/graham/towerops-web:main-1771346675-edeeacf [skip ci] 2026-02-17 16:48:12 +00:00
edeeacfefb
Add Exceptions link to admin nav bar 2026-02-17 10:44:03 -06:00
FluxCD
1c745a7e1e chore: update towerops image to git.mcintire.me/graham/towerops-web:main-1771346299-b7e2fb4 [skip ci] 2026-02-17 16:42:13 +00:00
b7e2fb497c
Add Error Tracker link to admin dashboard 2026-02-17 10:37:44 -06:00
FluxCD
f5f98ce00b chore: update towerops image to git.mcintire.me/graham/towerops-web:main-1771337653-c92dfff [skip ci] 2026-02-17 14:19:34 +00:00
c92dfff6c9
Add ErrorTracker, remove Honeybadger email notice filter
Replace the HoneybadgerNoticeFilter that emailed raw stacktraces on
every exception with ErrorTracker for self-hosted error tracking.
Honeybadger itself is retained. ErrorTracker dashboard is mounted at
/admin/errors behind the superuser auth wall.
2026-02-17 08:13:34 -06:00
FluxCD
c6cc803425 chore: update towerops image to git.mcintire.me/graham/towerops-web:main-1771336126-1712e7a [skip ci] 2026-02-17 13:52:27 +00:00
FluxCD
9897af57df chore: update towerops image to git.mcintire.me/graham/towerops-web:main-1771335850-8f850f9 [skip ci] 2026-02-17 13:48:27 +00:00
1712e7ac9d
Skip SNMP check execution in CheckExecutorWorker for agent-polled devices
CheckExecutorWorker was missing the phoenix_snmp_disabled and
should_phoenix_poll_device guards that DevicePollerWorker and
DeviceMonitorWorker already have. This caused SNMP checks to execute
through Phoenix even when devices are assigned to agents, creating
an infinite loop of failing jobs that fill the Oban queue.
2026-02-17 07:47:15 -06:00
FluxCD
327c981422 chore: update towerops image to git.mcintire.me/graham/towerops-web:main-1771333965-78aa415 [skip ci] 2026-02-17 13:16:21 +00:00
78aa415073 Skip Phoenix poller/monitor scheduling when SNMP disabled
JobCleanupTask was scheduling poller and monitor jobs for all SNMP-enabled
devices on every startup without checking phoenix_snmp_disabled. These jobs
would immediately bail out but still consumed queue capacity.

JobHealthCheckWorker had the same issue - recovering 'missing' monitor jobs
that would just no-op.

Both now check Client.phoenix_snmp_disabled() before scheduling.
2026-02-17 07:12:15 -06:00
FluxCD
ee478a4817 chore: update towerops image to git.mcintire.me/graham/towerops-web:main-1771282252-7160bf9 [skip ci] 2026-02-16 22:54:51 +00:00
7160bf95d6 Add admin endpoint to flush Oban jobs by state 2026-02-16 16:50:23 -06:00
FluxCD
0b03efe7b6 chore: update towerops image to git.mcintire.me/graham/towerops-web:main-1771281501-de986bd [skip ci] 2026-02-16 22:42:48 +00:00
de986bddf6
Prevent Oban polling/monitoring job stacking per device
When the Oban queue backs up, the 60-second uniqueness window expires
and duplicate jobs stack up per device. Switch to period: :infinity so
only one poll/monitor job exists per device at any time. Add replace
option to supersede stale scheduled jobs with updated scheduled_at.
Remove :executing from unique states so self-scheduling works while
the current job runs. Set max_attempts: 1 since retrying stale polls
is pointless.

Also fix all credo --strict issues across the codebase.
2026-02-16 16:37:48 -06:00
FluxCD
09c56e3e9b chore: update towerops image to git.mcintire.me/graham/towerops-web:main-1771279924-179f049 [skip ci] 2026-02-16 22:14:45 +00:00
179f0494e5
Fix pre-existing test failures from role enum and MikroTik changes
- invitation_test: default role is now :technician not :member
- settings_live_members_test: use "technician" role in invite form (member removed from select)
- form_test: MikroTik API section is intentionally disabled, expect refute
2026-02-16 16:11:32 -06:00
FluxCD
72e0c0bf22 chore: update towerops image to git.mcintire.me/graham/towerops-web:main-1771279533-e09b306 [skip ci] 2026-02-16 22:09:42 +00:00
e09b306f9c
Fix maintenance window form crash on mount
The form template iterated over changeset error tuples directly
(e.g. changeset.errors[:name] returns a tuple, not a list), causing
Protocol.UndefinedError for Enumerable on Tuple.

Rewrote form to use to_form/2 and <.input> components per LiveView
conventions. Errors are now handled properly by the input component.
2026-02-16 16:04:59 -06:00
FluxCD
0cc519b6bf chore: update towerops image to git.mcintire.me/graham/towerops-web:main-1771279255-d233f55 [skip ci] 2026-02-16 22:03:41 +00:00
d233f55a35 Fix: add check_executors queue to Oban config (was missing, causing 4.5k stuck jobs) 2026-02-16 16:00:24 -06:00
FluxCD
7e57ff8ad5 chore: update towerops image to git.mcintire.me/graham/towerops-web:main-1771278915-6e3db72 [skip ci] 2026-02-16 21:59:39 +00:00
6e3db720fd Collapse device page tabs into header row — single line nav 2026-02-16 15:54:42 -06:00
FluxCD
9dca0b099e chore: update towerops image to git.mcintire.me/graham/towerops-web:main-1771263863-44d9bb4 [skip ci] 2026-02-16 17:48:03 +00:00
44d9bb4c49 Merge pull request 'feature/subscriber-impact' (#6) from feature/subscriber-impact into main
Reviewed-on: graham/towerops-web#6
2026-02-16 09:43:54 -08:00
b12d46c716 Add wireless client table discovery and improve subscriber matching
Poll connected client/SM tables from APs via vendor-specific SNMP:
- Cambium ePMP: cambiumAPConnectedSTATable
- Ubiquiti airOS: ubntStaTable
- MikroTik RouterOS: mtxrWlRtabTable

Wireless client MAC matches now take highest priority for
subscriber-to-AP mapping, giving accurate per-radio subscriber counts.
2026-02-16 11:16:52 -06:00
520f7b0c54 Add subscriber-to-AP matching via ARP + Gaiia integration
Matches Gaiia subscriber accounts to TowerOps devices using:
- ARP table IP matching (high confidence)
- ARP table MAC matching (high confidence)
- Site CIDR fallback (medium confidence)

Enables subscriber impact display on alerts and device pages.
2026-02-16 11:07:20 -06:00
FluxCD
c1b816f7fe chore: update towerops image to git.mcintire.me/graham/towerops-web:main-1771260135-261bfd4 [skip ci] 2026-02-16 16:45:53 +00:00