Changed foreign key constraints from CASCADE to RESTRICT for:
- agent_tokens.organization_id
- agent_assignments.agent_token_id
**Problem:**
Deleting an organization would cascade delete all agent tokens, which
would then cascade delete all agent assignments, causing silent data
loss without any audit trail.
**Solution:**
Use RESTRICT instead of CASCADE - this prevents deletion if there are
dependent records, forcing explicit cleanup and preventing accidental
data loss.
**Impact:**
- Organizations cannot be deleted if they have agent tokens
- Agent tokens cannot be deleted if they have active assignments
- User must explicitly clean up dependencies first
This provides better data integrity and prevents accidental data loss.
Fixes Medium Bug #15 from reliability audit.
Previous version used hardcoded 'towerops_dev' which doesn't exist in production.
Now uses current_database() to get the actual database name at runtime.
This fixes the crash: ERROR 3D000 (invalid_catalog_name) database "towerops_dev" does not exist
Fixes ERROR 53400 (configuration_limit_exceeded) during large interface
sync operations in device discovery.
The sync_interfaces operation was decompressing 146,603 tuples but the
default limit was 100,000. Setting to 0 (unlimited) removes this constraint.
Error occurred in: Towerops.Snmp.Discovery.sync_interfaces/2
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.
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.
New role system:
- owner: full access + financials
- admin: full access + financials (except org deletion)
- executive: read-only + full financial visibility (MRR, revenue)
- technician: field ops (devices, sites, alerts) without financials
- member: legacy alias for technician (migrated)
- viewer: read-only, no financials
Financial data (MRR, revenue, billing) gated behind can_view_financials
assign in all LiveView templates: dashboard, alerts, trace, site show.
Includes migration to rename existing member roles to technician.
Each billing sync (Gaiia, Splynx, VISP, Sonar) already computes
subscriber counts and MRR but only stored them in sync messages.
Now persists them on the integrations table so the dashboard can
aggregate across all billing providers instead of only querying
Gaiia network sites (which were never populated).
Also fixes SnmpKit Config environment detection to use Mix.env()
as fallback when MIX_ENV env var is not set.
- Fix CLOAK_KEY placeholder in nix shell (invalid base64)
- Fix error HTML test assertion to match actual template
- Fix Gaiia sync: read 'subnet' field instead of 'block' for IP blocks
- Fix Gaiia sync: extract nested status name from subscription objects
- Fix security headers tests for environment detection
- Fix mobile auth tests to use raw_token
- Fix LiveView test assertions to match actual template content
- Fix SNMP config test expectations for test environment
- Refactor: resolve all Credo complexity/nesting issues
- Extract helper functions in NetBox sync, VISP sync, Sonar sync
- Flatten nested conditionals in settings, integrations, alerts
- Use with/validate_present pattern for connection testing
- Add transport field to SnmpDevice and SnmpConfig
- Add arch field to AgentHeartbeat (already in .pb.ex)
- Add Check, CheckResult, CheckList messages for service checks (HTTP/TCP/DNS)
- Add checks field to AgentConfig
- Add CheckResult to Metric oneof
- Sync proto source with compiled .pb.ex
device_up alerts are informational recovery events, not ongoing issues.
Previously they were created without resolved_at, inflating the
unresolved alert count. Now they're born resolved.
Includes a data migration to fix existing stale device_up alerts.
- Added last_sync_message field to integrations schema
- Gaiia sync: shows item counts on success, friendly error on failure
- Preseem sync: shows AP/device counts on success, friendly error on failure
- Error messages translated from technical errors to user-friendly text
- Message displayed on integration card next to status badge
- Exposed in REST API response
- Fix broken route paths in dashboard (/discovery, /subscribers/trace, /config-changes)
- Fix insights empty state settings link (org-scoped route)
- Add underscores to all 86400 literals across 6 files
- Alphabetize aliases in search.ex and agent_channel.ex
- Extract changelog parser helper to reduce nesting
- Extract dashboard impact calculation to reduce nesting
- Refactor agent_channel config change detection (pattern match, extract function)
- Combine double Enum.reject into single call in trace.ex
- Fix line length in trace.ex search query
- Replace length/1 > 0 with != [] in trace_live
- Run mix format on all files
- 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
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.
Transform the dashboard into a single-pane-of-glass command center that
blends operational metrics with business context from Gaiia subscriber data.
- Extend insight schema with multi-source support (snmp, gaiia, system)
- Add Oban workers for automated insight generation (device health, system, gaiia)
- New Dashboard context with health score computation and site summaries
- Rewrite dashboard LiveView with health overview, alert/insight feeds, site grid
- Add source filter pills for insights with URL state management
- Add metrics bar to site detail pages (device count, alerts, subscribers, MRR)
- Add subscriber/MRR context to alert list site links
- Add subscriber badges to device list site headers
- Real-time PubSub updates for alerts on dashboard
Computes subscriber impact when device alerts fire. Three analysis
layers: device-level (inventory item -> direct subscriber) and
site-level (network site -> all subscribers at tower). Impact data
stored as JSONB on alert records and displayed as badges on alert list.
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>
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>