Commit graph

93 commits

Author SHA1 Message Date
e7bca6174c feat(on_call): schedules list with gantt timeline (chunk 2)
Resolver
- Resolver.resolve_range/3: returns the contiguous on-call segments for a
  schedule across [start_at, end_at). Walks layer handoffs + override
  boundaries, drops gap segments, and merges adjacent same-user segments.

UserColor module
- New Towerops.OnCall.UserColor with deterministic id -> color mapping
  (Tailwind class + matching #RRGGBB hex). Refactored ScheduleLive.Show
  to use it instead of its own per-page index palette so the same person
  gets the same color across the schedules list and detail pages.

ScheduleLive.Index
- Date navigation: Today / prev / next + 1/2/4-week range selector.
- URL-driven: ?start=YYYY-MM-DD&range=N for shareable views; falls back
  to defaults on malformed values.
- Per-row card: schedule name link, on-call-now avatar swatch, day-of-week
  header strip, gantt strip rendered as a CSS grid with one column per day,
  Today marker overlay.

Tests: 5 new resolver tests, 9 new UserColor tests, 5 new ScheduleLive
integration tests. Full suite: 10,210 / 0 failures. Format/dialyzer clean.

Also: cleaned up two stale inline comments in k8s/deployment.yaml that
disagreed with the values they sat next to (replicas/maxSurge).
2026-04-28 12:48:52 -05:00
a91c00f3cf feat(on_call): PagerDuty-style escalation policy redesign + dialyzer fix
Chunk 1 of the on-call/escalation redesign plan
(docs/plans/2026-04-28-on-call-pagerduty-style-redesign.md):

Schema
- on_call.ex: list_devices_using_policy/2 (direct + org-default inheritance)
- on_call.ex: move_escalation_rule/2 for up/down level reordering
- list_escalation_policies/1 now preloads :rules

Show page
- Levels rendered as numbered cards with up/down/delete controls
- "Notify the following users immediately and escalate after N minutes."
- REPEAT footer surfaces repeat_count inline (no longer a standalone column)
- Right-side Services sidebar lists devices using the policy
- Header line shows the handoff notifications mode (when in use by a service /
  always / never)

Edit form
- Removed standalone Repeat Count input
- Added handoff notifications mode select
- Added inline "If no one acknowledges, repeat this policy N time(s)" block

Index views (policies tab + standalone)
- Replaced Repeat Count column with a Levels (rule count) column

Other
- rate_limit.ex: bind unmatched :ets.new/2 + schedule_clean/1 returns to fix
  dialyzer :unmatched_returns warning under the project's strict flags.

All 10,191 tests pass; mix format clean; mix compile --warnings-as-errors clean;
mix dialyzer passes.
2026-04-28 12:36:41 -05:00
3f82c1c110 feat: add /sitemap.xml and update robots.txt for agent discovery 2026-04-17 13:58:19 -05:00
978a72ad41 db-performance-migrations (#234)
Reviewed-on: graham/towerops-web#234
2026-04-04 14:22:25 -05:00
cd6603873a fix: update Phoenix.PubSub.Redis configuration for 3.1.0 compatibility (#197)
The recent dependency update to phoenix_pubsub_redis 3.1.0 introduced
breaking changes in the configuration API. The new version no longer
accepts connection options at the top level - they must be nested in
:redis_opts.

Changes:
- Move Redix connection options into :redis_opts keyword list
- Keep only :node_name at top level per v3.1.0 requirements
- Maintains all resilience settings (timeouts, backoff, keepalive)

Before (invalid for v3.1.0):
  [node_name: node(), host: "...", port: 6379, timeout: 5000, ...]

After (valid for v3.1.0):
  [node_name: node(), redis_opts: [host: "...", port: 6379, timeout: 5000, ...]]

This fixes the production crash with error:
  NimbleOptions.ValidationError: unknown options [:timeout, :backoff_initial,
  :backoff_max, :exit_on_disconnection, :sync_connect]

Related: commit 0399b30d (Update Elixir dependencies)

Reviewed-on: graham/towerops-web#197
2026-03-27 17:31:25 -05:00
3f08fd42b6 fix: handle alert_changed message in DashboardLive (#189)
Fixes FunctionClauseError when DashboardLive receives {:alert_changed, org_id}
message broadcast by user_auth hooks.

The user_auth module subscribes all LiveViews to the
"organization:#{org_id}:alerts" topic and broadcasts {:alert_changed, org_id}
messages when alerts are created or resolved. DashboardLive was missing a
handle_info/2 clause to handle this message format, causing crashes in
production.

Solution:
- Added handle_info/2 clause for {:alert_changed, _org_id} message
- Uses existing debounced reload pattern to update dashboard
- Added test to verify message is handled without crashing
- Updated both technical and user-facing changelogs

All tests pass. The fix follows the same pattern used in AlertLive.Index
and DeviceLive.Show.

Reviewed-on: graham/towerops-web#189
2026-03-27 13:26:39 -05:00
a9c2f8e5e5 fix/findings-medium-priority (#188)
Reviewed-on: graham/towerops-web#188
2026-03-27 12:31:53 -05:00
0bc6407c3f feature/entity-physical-inventory (#187)
Reviewed-on: graham/towerops-web#187
2026-03-27 10:48:50 -05:00
8a58c7c238 feat: implement mempools (memory pools) feature for SNMP monitoring (#184)
Add complete memory pool monitoring using HOST-RESOURCES-MIB.
Tracks RAM/swap usage with historical data and real-time updates.

- Add Mempool and MempoolReading schemas
- Integrate discovery and polling with concurrent Task.async_stream
- Add 7 context API functions for querying mempools
- 24 new tests, all 8754 tests passing (0 failures)

Implements C1 from findings_librenms.md

Reviewed-on: graham/towerops-web#184
2026-03-26 17:07:43 -05:00
f442c59ad0 fix/ping-monitoring-without-snmp (#171)
Reviewed-on: graham/towerops-web#171
2026-03-25 16:01:05 -05:00
2b24626baa fix: correct capacity calculation and wireless sensor display (#116)
- Capacity: Sensor values (Rx/Tx Capacity) now represent total device
  capacity, not per-interface. Previous bug applied the same sensor value
  to all interfaces and summed them (e.g., 773.6 Mbps × 5 = 3.87 Gbps).

- Wireless sensors: Changed Float.round/2 to :erlang.float_to_binary/2
  to prevent scientific notation in frequency display (was showing
  "2.41e4 MHz" instead of "24100.0 MHz").

Files:
- lib/towerops_web/live/device_live/show.ex
- lib/towerops_web/live/device_live/show.html.heex
- CHANGELOG.txt
- priv/static/changelog.txt

Reviewed-on: graham/towerops-web#116
2026-03-22 15:49:39 -05:00
5a6acf0d7c feat: simplify device type to manual-only with 6 options (#109)
- Reduce device type options from 10 to 6 (server, switch, router, access_point, backhaul, other)
- Change label from "Device Role" to "Device Type"
- Disable auto-detection - device type is now required and manual-only
- Add migration to map existing device roles to new simplified set
- Update REST API to include device_role and device_role_source fields
- Automatically set device_role_source to "manual" when device_role is provided
- Update wireless detection to include "backhaul" type
- Enhance display formatting for "Access Point"

Breaking Changes:
- Auto-inference of device type has been disabled
- device_role is now a required field in forms
- GraphQL/REST API consumers will see changed device_role values after migration

Files Changed:
- lib/towerops_web/live/device_live/form.html.heex
- lib/towerops/topology.ex
- lib/towerops/snmp/discovery.ex
- lib/towerops/devices/device.ex
- lib/towerops_web/live/device_live/index.ex
- lib/towerops_web/controllers/api/v1/devices_controller.ex
- priv/repo/migrations/20260322152809_migrate_device_roles_to_simplified_set.exs
- test/towerops/topology_test.exs
- test/towerops_web/live/device_live/form_test.exs

Reviewed-on: graham/towerops-web#109
2026-03-22 11:24:53 -05:00
8dae83a592 tweaks (#85)
Reviewed-on: graham/towerops-web#85
2026-03-19 13:51:02 -05:00
997bc46cbf fix: dns and ping checks not executing in production (#71)
## Summary

- **Ping checks never scheduled**: `ensure_default_ping_check` created check records but never called `schedule_check`, so auto-created ping checks never executed
- **Skipped checks permanently orphaned**: `CheckExecutorWorker` only rescheduled checks that actually executed — skipped checks (agent-assigned, SNMP disabled) broke the scheduling chain permanently
- **No recovery mechanism**: `JobHealthCheckWorker` only recovered `DeviceMonitorWorker` jobs, not `CheckExecutorWorker` jobs — any lost check job was gone forever

## Changes

- `monitoring.ex`: Call `schedule_check` after creating ping check in `ensure_default_ping_check`
- `check_executor_worker.ex`: Reschedule skipped-but-enabled checks so conditions are re-evaluated next cycle
- `job_health_check_worker.ex`: Recover orphaned `CheckExecutorWorker` jobs for enabled checks (runs every 10 min)
- `devices.ex`: Wrap `ensure_default_ping_check` in try/rescue for deadlock resilience

## Test plan

- [x] New tests: ping check scheduling on creation, no duplicate scheduling on idempotent call
- [x] Updated tests: skipped checks now assert rescheduling instead of orphaning
- [x] New tests: health check worker recovers orphaned check executor jobs, skips disabled checks
- [x] All 177 targeted tests pass
- [x] Compile clean with warnings-as-errors
- [x] Pre-commit hooks pass (credo, format)

Reviewed-on: graham/towerops-web#71
2026-03-18 08:43:37 -05:00
28e97ff5f0 add service checks REST API, GraphQL API, and ping executor (#52)
- 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
2026-03-16 18:40:18 -05:00
a1c5d593dc
fix: network map label sizes, site layout, and missing links
- 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"
2026-03-14 18:27:54 -05:00
19abd5508a
ux: improve mobile responsiveness across all pages
- Hide less-critical table columns on small screens (IP, subs, MRR, etc.)
- Add overflow-x-auto wrappers on tables to prevent horizontal scroll
- Fix <.header> component to flex-wrap action buttons on mobile
- Make page action button labels icon-only on small screens
- Add min-w-0 on grid children to prevent viewport overflow
- Right-align device detail dt/dd values consistently
- Fix schedule timeline cards with overflow-x-auto wrappers
2026-03-12 16:49:59 -05:00
72f81c572d
add real-time GraphQL API with time-series queries and subscriptions
- add absinthe_phoenix for WebSocket subscription support
- create GraphQL WebSocket socket with API token auth at /socket/graphql
- add time-series queries: deviceSensors, sensorReadings, interfaceTraffic, checkResults
- add subscriptions: deviceStatusChanged, alertEvent, sensorReadingsUpdated
- wire subscription triggers into agent_channel and alerts contexts
- add org-scoped access control for sensors/interfaces via join queries
- add 17 tests covering queries, org isolation, empty data edge cases, and auth
- update GraphQL API docs page with time-series and subscription sections
2026-03-12 10:24:39 -05:00
3c0157d2ea
docs: update user-facing changelog with recent UI and feature improvements
Added missing entries:
- UI button styling improvements (2026-03-09)
- Backhaul capacity analysis feature (2026-03-07)
2026-03-09 15:53:37 -05:00
d811bcb503
feat: add ErrorTrackerNotifier and fix PagerDuty notification handling
- Add error_tracker_notifier for email alerts on exceptions
  * Sends alerts to graham@towerops.net
  * Configured in production only (gracefully shuts down in dev/test)
  * Includes throttling to prevent duplicate error spam

- Fix AlertNotificationWorker when PagerDuty not configured
  * Worker now handles :not_configured gracefully instead of failing
  * Prevents unnecessary retries and error noise
  * Logs as debug when PagerDuty integration isn't set up

Files changed:
- mix.exs, mix.lock
- config/runtime.exs
- lib/towerops/application.ex
- lib/towerops/workers/alert_notification_worker.ex
- CHANGELOG.txt, priv/static/changelog.txt
2026-03-09 13:14:30 -05:00
d5b0f38ed6
feat: add remaining e2e tests and fix 'too many open files' error
- Added e2e tests for auth flows (registration, login, password reset, TOTP)
- Added e2e tests for onboarding and organization creation
- Added e2e tests for team collaboration (org switching, invitations)
- Added e2e tests for activity feed
- Added e2e tests for device graphs (sensors, time ranges, interfaces)
- Added e2e tests for MikroTik backup comparison
- Added e2e tests for sites map (geographic view)
- Added e2e tests for network trace tool

- Fixed 'too many open files' error by configuring Phoenix code reloader
  to ignore deps/, _build/, node_modules/, e2e/, and other non-source directories
- This prevents the file system watcher from monitoring unnecessary files
2026-03-06 17:08:27 -06:00
a2ce5817a2
fix: use transparent background for stoplight favicon
Replace white background with transparent for better appearance
in browser tabs with different background colors.
2026-03-06 14:46:02 -06:00
e278d320ce
design: improve stoplight favicon with transparent background
Make circles more prominent with transparent background and better spacing.
2026-03-06 14:36:32 -06:00
45579813bd
design: update favicon to stoplight style with app colors
Replace standard favicon with stoplight design using red (#ef4444),
yellow (#eab308), and green (#22c55e) circles matching the app's
status indicator colors.
2026-03-06 14:33:29 -06:00
ac2547e7c9
Merge branch 'feature/dynamic-global-pricing' 2026-03-06 14:04:12 -06:00
6e2bba4e88
docs: update changelogs for dynamic pricing feature 2026-03-06 14:00:58 -06:00
6d3fc15f4a
fix: allow data URLs in CSP for dynamic favicon
The dashboard status indicator favicon (colored circle) uses canvas
to generate a data URL that's set as the favicon. This was blocked
by CSP in staging/production because default-src didn't include data:.

Added data: to default-src directive to fix favicon not updating in
browser tabs when device status changes.
2026-03-06 13:51:31 -06:00
9229e6e920
fix: remove internal details from public changelog 2026-03-06 13:03:36 -06:00
e161aaab83
docs: update changelogs for billing overrides and interface stats fix 2026-03-06 13:02:55 -06:00
8f52d87854
feat: add rate limiting to admin endpoints (100 req/min)
- Add :admin type to RateLimit plug with 100 requests per minute per IP
- Apply rate limiting to all /admin routes (LiveView and controller actions)
- Protects superuser endpoints from abuse and brute force attempts
- Aligns with existing auth (10 req/min) and API (1000 req/min) limits
2026-03-05 14:11:19 -06:00
1d928d4356
security: implement comprehensive security audit fixes
Critical Fixes:
- Remove /health/time endpoint exposing system time information
  * Prevents attackers from detecting time sync issues for TOTP attacks
  * Removed route and controller function, updated tests
- Add email confirmation check to account data export
  * GDPR export now requires confirmed email address
  * Prevents unconfirmed accounts from accessing data export
- Add path traversal validation for MIB archive uploads
  * Extract to temp directory, validate all paths, then copy if safe
  * Prevents malicious tar/zip files from writing outside target directory
  * Added validate_extracted_paths/1 helper function

High Priority Fixes:
- Add comprehensive input validation for mobile auth
  * Length limits: device_name (255), device_os (100), app_version (50), push_token (512)
  * Prevents database corruption and storage exhaustion
- Add heartbeat rate limiting to agent channel
  * Limit database updates to once per 30 seconds (max ~2/min per agent)
  * Prevents malicious agents from exhausting database connections
- Sanitize 500 error responses
  * Return generic error messages to clients
  * Log full details server-side with request_id for support
  * Prevents leaking stack traces and module names
- Add message size limits to agent channel
  * 10MB maximum for all protobuf messages (result, heartbeat, error)
  * Prevents DoS attacks via oversized payloads

Medium Priority Fixes:
- Add GraphQL query depth limits (max_depth: 10)
  * Prevents DoS from deeply nested queries
  * Complements existing complexity limits

Code Quality:
- Refactor agent channel handlers to reduce nesting depth
  * Extract message processing into separate private functions
  * Fixes Credo warnings about excessive nesting
  * Improves code readability and maintainability

Files changed:
- lib/towerops_web/controllers/health_controller.ex
- lib/towerops_web/controllers/api/account_data_controller.ex
- lib/towerops_web/controllers/api/v1/mib_controller.ex
- lib/towerops/mobile_sessions/mobile_session.ex
- lib/towerops_web/channels/agent_channel.ex
- lib/towerops_web/controllers/error_json.ex
- lib/towerops_web/router.ex
- CHANGELOG.txt
- priv/static/changelog.txt
- test/towerops_web/controllers/health_controller_test.exs
- test/towerops_web/controllers/error_json_test.exs

All 7,424 tests passing.
2026-03-05 13:08:10 -06:00
7d73193dc6
docs: update changelog for LLDP topology discovery 2026-03-05 10:54:53 -06:00
29b23fb06c
docs: update changelogs for reliability audit completion
- Added entries for organization_id alert optimization
- Added entries for cascade delete safety improvements
- Added entry for sensor state validation
- Updated user-facing changelog with performance improvements
- All 23 bugs from reliability audit now addressed
2026-03-05 09:34:57 -06:00
1d1a686634
docs: update changelog and memory with bug fix details
- Added comprehensive CHANGELOG.txt entry documenting all 16 bug fixes
- Updated priv/static/changelog.txt with user-facing improvements
- Enhanced MEMORY.md with key learnings:
  * Task.yield_many race condition patterns
  * Enum.zip data corruption prevention
  * LiveView timer memory leak fixes
  * PubSub subscription cleanup
  * Pattern match error handling
  * Health check log silencing

Also fixed:
- Added error checking for sensor reading batch inserts
2026-03-05 08:55:59 -06:00
c231cbdf4c
docs: update changelog for log filtering improvements 2026-03-05 08:23:52 -06:00
5e1f8d3ebe
docs: update changelog for TimescaleDB limit fix 2026-03-04 17:44:44 -06:00
b07c322dc9 docs: update changelogs with navigation and visual polish 2026-02-13 19:18:36 -06:00
5a6643c6aa docs: update changelogs with UX polish and activity feed improvements 2026-02-13 19:13:32 -06:00
dec6f0f271 feat: rewrite marketing landing page for WISP operators
- Pain point section: disconnected tools, slow impact assessment
- WISP-specific features: impact dashboard, subscriber trace, alert triage, config tracking
- Pricing tiers: Starter (free/10 devices), Pro (/dev/mo), Business (/dev/mo)
- Use case sections: NOC, Help Desk, Owner perspectives
- Tech details: 786 device profiles, secure agent arch, SNMP v1/v2c/v3
- Updated both changelogs
2026-02-13 19:07:04 -06:00
5fb585d4fd docs: update changelogs with all new features 2026-02-13 18:59:25 -06:00
9c266e57ff feat: wire up Trace and Activity Feed routes + nav 2026-02-13 18:50:28 -06:00
c512f30e5a feat: add device list health indicators
- Health status dot (green/yellow/red/gray) from worst-case check state
- Relative 'last seen' with color-coded staleness thresholds
- Response time badge from latest monitoring check
- Subscriber count badge from Gaiia inventory items
- Batch queries to avoid N+1: Monitoring.get_device_health_summary/1,
  Monitoring.get_device_latest_response_times/1,
  Gaiia.get_device_subscriber_counts/1
2026-02-13 18:14:29 -06:00
5a3ede1f71 feat: config change → performance correlation system (Roadmap #2)
- New config_change_events table linking backup diffs to time windows
- ConfigChanges context with record/list functions and date filters
- Auto-detect changed sections from MikroTik config diffs
- Agent channel emits change events after backup creation
- Correlator engine: 2h before / 4h after QoE metric comparison
- Creates suspect_config_change insights on significant degradation
- Config Timeline LiveView with chart data, change list, detail view
- Device page: Config Timeline tab + Recent Config Changes card
- Dashboard: Recent Config Changes section with impact indicators
- Fix pre-existing integrations_live.html.heex scope bug
2026-02-13 17:56:55 -06:00
02f837ca5b
fix: replace em-dashes with hyphens in changelog to avoid encoding issues 2026-02-13 17:48:07 -06:00
314d93f3ab
docs: update changelogs for impact dashboard and integration nav improvements 2026-02-13 16:45:45 -06:00
872bef3512
feat: add 12/24-hour time format setting with consistent timezone display
wire time_format into scope, add settings UI selector, change defaults
to 24h, and replace all user-facing Calendar.strftime calls with
centralized TimeHelpers using the user's timezone and time format.
2026-02-13 15:16:29 -06:00
a6d5563b41
docs: update changelogs for command center dashboard feature 2026-02-13 14:56:01 -06:00
ee277a457e
update changelogs for gaiia integration 2026-02-13 13:29:54 -06:00
4174987a88 feat: complete DevicePollerWorker to CheckExecutorWorker migration
Completes full migration from device-level polling to per-check polling.

Migration Changes:
- Removed DevicePollerWorker scheduling from device lifecycle
- Checks automatically scheduled when created
- Discovery creates and schedules checks atomically

New Functions:
- Monitoring.stop_device_checks/1: Cancel all check jobs for device
- Monitoring.disable_device_checks/1: Disable checks when SNMP off

Discovery Integration:
- All create_*_check functions now schedule checks after creation

JobHealthCheckWorker Updates:
- Removed DevicePollerWorker recovery logic
- Only recovers DeviceMonitorWorker jobs now

Test Updates:
- Marked DevicePollerWorkerTest as skipped (deprecated)
- Updated JobHealthCheckWorkerTest expectations

All 6953 tests passing, 0 failures

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-02-12 17:52:33 -06:00
75d64d8772
feat: add check creation from discovery and backfill tool (Phase 5)
Completes Phase 5 of the unified checks system by enabling automatic check
creation during SNMP discovery and providing a backfill tool for existing devices.

Discovery Integration:
- create_checks_from_discovery/2: Creates checks for all discovered entities
  (sensors, interfaces, processors, storage) after SNMP discovery completes
- Links each check to its source entity via source_id for data lookup
- Returns detailed results with counts per type and error tracking
- Logs creation results with total counts and any failures

Helper Functions:
- create_sensor_check/2: Creates snmp_sensor checks with config
- create_interface_check/2: Creates snmp_interface checks
- create_processor_check/2: Creates snmp_processor checks
- create_storage_check/2: Creates snmp_storage checks

All checks configured with:
- 60-second poll intervals (default)
- Auto-discovery source type
- Enabled by default
- Type-specific configuration in JSONB config field

Backfill Tool:
- mix backfill.checks: Creates checks for existing SNMP-enabled devices
- Supports --dry-run mode for preview
- Shows counts per device (sensors, interfaces, processors, storage)
- Reports success/failure counts and detailed errors
- Safely handles devices without discovery data
- Reduced nesting depth using cond and helper functions for credo compliance

This enables the unified monitoring system to work with both new discoveries
and existing devices, ensuring all SNMP data can be monitored through the
unified checks interface.

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-02-12 17:04:06 -06:00