add early bail in DiscoveryWorker when snmp_enabled is false.
mark snmp_not_enabled and device_unresponsive as non-retryable
errors to prevent wasted retries.
Reviewed-on: graham/towerops-web#58
The new CI server doesn't have Docker daemon running by default,
causing buildx setup to fail with:
ERROR: failed to connect to the docker API at unix:///var/run/docker.sock
This fix:
- Adds explicit Docker daemon startup check and initialization
- Configures buildx with docker-container driver
- Adds install: true to ensure proper buildx installation
This was already fixed in build-deploy.yml (commit 54280fd7) but
was missing from production.yaml.
Reviewed-on: graham/towerops-web#57
- REST CRUD at /api/v1/checks for HTTP, TCP, DNS, ping checks
- GraphQL queries (checks, check) and mutations (create/update/delete)
- ping executor using system ping with macOS/Linux parsing
- check config validation for ping type (requires host)
- API documentation updated with checks resource section
Reviewed-on: graham/towerops-web#52
send check_jobs protobuf to agents during job dispatch cycle, handle
check_result messages back with state transitions and alert
creation/resolution. auto-assign checks to device's effective agent
token on creation, broadcast check changes via PubSub so connected
agents get immediate updates.
add edit/delete support for service checks on device show page with
form pre-population from existing check config. auto-fill TCP host
from device IP address.
includes validate_check_result/1 for protobuf validation and
list_checks_for_agent/2 for querying agent-assigned service checks.
also fixes pre-existing broken device_type_icon reference in device
index template.
Reviewed-on: graham/towerops-web#50
Extend the GraphQL API and socket to accept mobile session tokens
alongside existing API tokens. Add OrganizationScope middleware
that extracts organization_id from query args for mobile users.
Add my_organizations query for mobile app org listing.
Reviewed-on: graham/towerops-web#48
- Commented out 'View RF Links' button in network map node detail panel
- Commented out RF Link health legend (Good/Degraded/Critical) from map legend
- Backend code (topology.ex) left intact per instructions
- No RF tab found on device show page (nothing to hide there)
- All tests pass, compiles clean with --warnings-as-errors
Reviewed-on: graham/towerops-web#46
## Summary
Hide RF link-related UI elements from the network map page while keeping all backend code intact.
### Changes
- Commented out "View RF Links" button in network map node detail panel
- Commented out RF Link health legend (Good signal / Degraded / Critical) from map legend
- Backend topology.ex code untouched — `compute_rf_link_stats/1` and `enrich_edges_with_rf/2` remain
- No RF tab exists on the device show page, so no changes needed there
### Verification
- `mix compile --warnings-as-errors` — zero warnings
- `mix test` — 8490 tests pass, 0 failures
Paperclip issue: TOW-44
Reviewed-on: graham/towerops-web#45
the complete_qr_login endpoint was returning session.token (the SHA256
hash stored in the database) instead of session.raw_token (the plaintext).
this caused all subsequent API calls and WebSocket connections to fail
with 401/REFUSED since the server would hash the already-hashed token.
adds a regression test that verifies the returned token is usable for
authenticated API requests.
Reviewed-on: graham/towerops-web#42
staging was hitting postgres max_connections due to 252 concurrent oban
workers plus pool connections. OBAN_QUEUE_SCALE divides all queue sizes
(e.g. "5" = 1/5th capacity). defaults to 1, no change for production.
Reviewed-on: graham/towerops-web#41
The styfle/cancel-workflow-action is not compatible with Forgejo Actions,
causing failures with 'Cannot read properties of undefined (reading id)'.
The pr-tests.yaml workflow already has concurrency control that cancels
in-progress runs when new commits are pushed, which handles most cases.
Removing this workflow to eliminate CI errors.
Reviewed-on: graham/towerops-web#38
The cancel-workflow-action requires an access_token to query and cancel
running workflows. Without it, the action fails with 'Cannot read properties
of undefined' error when trying to access workflow information.
Reviewed-on: graham/towerops-web#36
- Changed sidebar from fixed to sticky positioning
- Wrapped sidebar and main content in flex container
- Sidebar now sticks to top along with header when scrolling
- Removed unused Ecto.Query import from gps_sync.ex
Credo improvements (17 → 0 remaining):
- Replaced all length/1 checks with empty list comparisons
- Extracted helper functions to reduce nesting depth
- Split complex functions into smaller, testable units
- Applied 'with' statements for clearer control flow
- Refactored high-complexity functions (cyclomatic complexity)
- Files improved: storm_detector, alert_notification_worker,
alert_digest_worker, gps_sync, statistics_sync, device_sync,
site_sync, site_correlation, reports_live, topology,
pagerduty/client, cn_maestro/sync
Reviewed-on: graham/towerops-web#32
The StormDetector was being added twice:
1. In the main children list (line 127)
2. In background_workers() (line 176)
This caused the supervisor to fail with 'duplicate_child_name' error.
Removed the duplicate from main children list since it's correctly
placed in background_workers() which also handles test env properly.
- StormDetector GenServer: sliding-window per org, >10 alerts/min triggers
suppression mode with single summary notification
- Site correlation: >3 devices at same site down within 120s creates
single 'Site Outage' alert instead of N individual alerts
- Notification rate limiter: max 5 per user per 15min window, excess
batched into digest via AlertDigestWorker
- PagerDuty client: retry with exponential backoff on 429, respects
Retry-After header
- Batch maintenance checks: new Maintenance.devices_in_maintenance/1
takes list of device_ids, returns MapSet (single query vs N queries)
- Migration: alert_storms, site_outages, notification_digests tables
plus site_outage_id/storm_suppressed on alerts
- DeviceMonitorWorker integrates storm detection and site correlation
before creating alerts
- Tests for StormDetector, SiteCorrelation, NotificationRateLimiter,
and batch maintenance checks
- Add 1.5s debounce to dashboard LiveView for alert events, preventing
excessive re-renders during mass outages where hundreds of alerts fire
simultaneously
- Replace global 'device:events' PubSub topic with org-scoped
'device_events:org:{id}' topics across all broadcasters (device_poller_worker,
sensor_change_detector, discovery) so LiveView processes only receive
events for their tenant
- Update EventLogger GenServer to subscribe to per-org topics dynamically,
with runtime subscription support for newly created orgs
- Add Organizations.list_organization_ids/0 for EventLogger bootstrap
- Increase default DB pool_size from 20 to 25 for production concurrent users
Co-Authored-By: Paperclip <noreply@paperclip.ing>
- Tower/device icons: role-based shapes (triangle for APs/wireless,
diamond for switches, round-rect for routers, hexagon for firewalls)
- RF link overlays: edges colored by signal health (green/yellow/red)
with bandwidth-proportional line thickness
- Enhanced hover tooltips: device-to-device name, SNR dB, bandwidth,
signal health indicator
- Wireless client count badge on AP nodes
- Filter bar: All Links | Degraded Only | Sites with Alerts | Search
- Search with zoom-to-match and highlight
- Geographic layout toggle (when sites have lat/lng)
- Enhanced detail panel: RF stats (client count, signal health, SNR),
View RF Links button for wireless devices
- Backend: compute_wireless_stats/compute_rf_link_stats enrich topology
- Site nodes include lat/lng for geographic layout
- Edge enrichment with RF signal health from SNR sensors
- Fix pre-existing compile error in notification_rate_limiter.ex
- Increase node font from 11px to 13px, compound labels to 14px
- Replace cose physics layout with deterministic grid preset layout to eliminate site group overlap
- Fix nil source_interface_id crash in topology.ex find_existing_link/1 (was using == nil instead of is_nil/1)
- Fix nil device label in device_to_node/1 fallback to IP or "Unknown"
- LIKE wildcard injection: sanitize %, _ in search queries (devices, sites, gaiia)
- Jason.decode! → Jason.decode with error handling for untrusted input
- inspect() leak: replace with generic error messages, log details server-side
- SSRF protection: URL validator blocks private IPs, localhost, non-HTTP schemes
- SSRF validation added to HTTP monitoring executor and integration credentials
- GraphQL complexity limits: always applied, not just in prod
- GraphQL introspection: also check GET query params, not just body
- Stripe webhook: explicit nil/empty checks for signature and body
- Cookie security: secure flag for session (prod), http_only+secure for remember_me
- Honeybadger API key: read from env var with fallback
- String.to_integer → Integer.parse with fallback for URL params
- String.to_atom → whitelist map for HTTP methods
- Gaiia webhook: remove secret_len and expected signature from log
- Admin API: add rate limiting pipeline
- to_atom_keys: per-key fallback instead of all-or-nothing rescue
- add MobileSocket for token-authenticated mobile WebSocket connections
- add MobileChannel with org-level and device-level real-time events
- add /mobile/socket endpoint for iOS app connections
- add "Link Mobile App" to user dropdown menu for discoverability
- improve QR code page with numbered steps and clearer instructions
- add socket/channel tests (17 total)
When alert routing is set to "builtin" (TowerOps) but no escalation
policy exists, the checklist item now reads "Create an escalation
policy" and links directly to the new escalation policy page instead
of looping back to general settings where the routing already looks
configured.
The sync schedule was shown twice on non-exclusive integration cards
(inline status row + separate box). Remove the box to match the
uniform inline display used by the billing card.
Adjust sidebar bottom section padding from py-3 to py-3.5 to align
the horizontal border with the footer border. Also remove unused
private functions from settings_live and fix credo warnings.
- Replace inline integrations tab with link to /settings/integrations
- Remove ~1500 lines of duplicated integration UI from settings
- Remove duplicated integration event handlers from settings_live
- Standalone IntegrationsLive page remains the single source of truth
- Settings page down from ~2400 to ~900 lines