Commit graph

888 commits

Author SHA1 Message Date
e6665ece0f Improve dashboard status bar mobile layout
- Use 2-column grid on mobile, flex-wrap on sm+
- Hide pipe separators on mobile (they break when items wrap)
- Stats now flow cleanly in a grid on small screens
2026-02-15 15:23:33 -06:00
3cb18a47fc Fix Credo nesting in Splynx MRR calculation
Extract parse_mrr/1 helper to reduce nesting depth in calculate_total_mrr.
Credo --strict: 0 issues. Dialyzer: pass (no new warnings).
2026-02-15 15:18:36 -06:00
ca4840b512 Fix Preseem devices table for mobile
- Hide Preseem ID, Status, IP, and Linked Device columns on mobile
- Show Preseem ID and status badge inline under name on small screens
- Add overflow-x-auto to table container
2026-02-15 15:10:08 -06:00
635d89d774 Fix mobile Gaiia entity mapping + redirect / to /dashboard
- Entity mapping table: hide Gaiia ID and Linked columns on mobile,
  show Gaiia ID inline under name on small screens
- Allow text wrapping in name column for long names
- Add overflow-x-auto to table container for safety
- Redirect authenticated users from / to /dashboard instead of /orgs
2026-02-15 15:05:20 -06:00
6be3910fc4 Fix Splynx integration: token-based auth, per-provider credential fields
- Splynx client: replace broken Basic Auth with proper token flow
  (POST /api/2.0/admin/auth/tokens → Splynx-EA bearer header)
- Support both api_key and admin login auth types with fallback
- Add MRR calculation to Splynx sync using mrr_total field
- Integrations UI: per-provider credential fields (instance_url,
  api_key, api_secret for Splynx; instance_url, api_token for Sonar)
- Wire up test_connection for Splynx, Sonar, and VISP providers
- Verified against live Splynx demo (demo.splynx.com):
  auth, customers, routers, services all working
- All 7350 tests passing
2026-02-15 15:02:17 -06:00
8fe90670b6 Redesign integrations page: billing providers as picker
Instead of showing all 4 billing providers as separate full-width cards,
the billing section now shows:

- When no billing connected: a compact 4-column grid picker to select
  your billing platform (Gaiia, Sonar, Splynx, VISP)
- When one is active: a single card showing the connected provider with
  status, sync info, and a note to disable before switching

This reduces visual clutter since users only ever use one billing system.
Non-exclusive categories (QoE, Incidents, Infrastructure) remain as
individual cards since multiple can be active simultaneously.
2026-02-15 13:00:10 -06:00
9ecd01dc8f Security hardening: remaining audit fixes
- Encrypt session cookies (add encryption_salt to endpoint)
- X-Frame-Options: SAMEORIGIN → DENY (match CSP frame-ancestors 'none')
- Remove unsafe-eval from CSP script-src
- Apply security headers in all environments (not just prod)
- Add GraphQL query complexity limit (max 500) via Absinthe.Plug
- GraphQL introspection already blocked in prod via plug

Closes audit items #6, #7, #12, #13, #14, #16
2026-02-15 12:54:38 -06:00
fbcf26656e
fix compile warning 2026-02-15 12:38:27 -06:00
91dd7ad985
fix: allow site_id on device create regardless of use_sites setting
The API was silently stripping site_id when use_sites was false, causing
the Terraform provider to report inconsistent state after apply.
2026-02-15 12:28:06 -06:00
74332e2203
fix: Gaiia and Preseem sync bugs from API response mismatches
- Billing subscription and inventory item status fields are scalars, not objects
- IP blocks use "block" key, not "subnet"
- Strip nil variables from GraphQL pagination (Gaiia returns null for after:null)
- Handle nil root keys in paginated responses gracefully
- Handle empty string response from Preseem scores endpoint
2026-02-15 12:23:47 -06:00
c790794191 Fix tests, credo issues, and Gaiia sync bugs
- 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
2026-02-15 11:55:49 -06:00
172b830b6b ui: add org settings and user settings to mobile nav 2026-02-15 09:22:48 -06:00
34fe5d7e49 Security fixes: mask credentials in logs/API, fix cookie/CSP/LIKE injection/webhooks
CRITICAL fixes:
- Mask SNMP community string in agent channel logs (CRITICAL-1)
- Remove snmpv3 passwords from REST API responses, return _set booleans (CRITICAL-2)
- Replace snmp_community with snmp_community_set in GraphQL type (CRITICAL-3)

HIGH fixes:
- Fix cookie same_site from invalid 'Towerops' to 'Lax' (HIGH-4)
- Remove unsafe-eval from CSP script-src (HIGH-6)
- Block GraphQL introspection queries in production (HIGH-7)
- Sanitize LIKE wildcards in SNMP device name search (HIGH-8)
- Reject webhooks when no secret configured instead of accepting (HIGH-9)

MEDIUM fixes:
- Hash mobile session tokens (SHA-256) before DB storage (MEDIUM-10)
- Apply security headers in all environments, not just prod (MEDIUM-14)
- Add GraphQL query complexity limit (500) in production (MEDIUM-16)
- Fix X-Frame-Options to DENY to match frame-ancestors 'none' (MEDIUM-13)
2026-02-15 09:09:04 -06:00
669dec5d89 proto: sync agent.proto with towerops-agent
- 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
2026-02-15 08:56:48 -06:00
555a09d34b ui: remove flash message on password generation 2026-02-15 08:48:36 -06:00
d857a38ca3 feat: group integrations by category with exclusive billing provider selection
Billing (Gaiia, Sonar, Splynx, VISP) - choose one
QoE Monitoring (Preseem)
Incident Management (PagerDuty)
Infrastructure & IPAM (NetBox)

Prevents enabling multiple billing providers simultaneously.
2026-02-15 08:27:27 -06:00
c3e04c833f marketing: update integration list with all new providers 2026-02-14 21:12:38 -06:00
e5796b3464 ui: polish device detail, site detail, and user settings 2026-02-14 21:11:33 -06:00
5041ac8e25 ui: polish auth pages, core components, and visual consistency 2026-02-14 21:11:33 -06:00
66bf61887f feat: add Sonar, Splynx, and VISP billing integrations 2026-02-14 20:18:57 -06:00
02474c529f ui: major redesign - NOC dashboard, consolidated nav, dense tables, flat alerts 2026-02-14 20:15:07 -06:00
968c5008ec api: expand format_site to return all fields including lat/lng 2026-02-14 17:46:28 -06:00
8752dfec49 fix: netbox url field type, gaiia ipRange→block, remove unknown webhook log 2026-02-14 17:44:01 -06:00
a3730db579 i18n: add Spanish locale with full translations, language selector, and locale hook 2026-02-14 17:44:01 -06:00
243b773e39 i18n: wrap all user-facing strings in gettext() 2026-02-14 17:44:01 -06:00
e954c7930a Fix Gaiia webhook timestamp: Gaiia sends millis, not seconds
The timestamp check was comparing millisecond timestamps against
System.system_time(:second), causing every webhook to be rejected
as 'expired'. Now normalizes timestamps >10 digits to seconds.
2026-02-14 17:15:25 -06:00
c8e398ea05 Add NetBox sync worker + improve Gaiia error messages
- NetBox.Sync: pull devices/sites from NetBox, match by name/IP, upsert
- NetBoxSyncWorker: Oban cron every 30min for enabled NetBox integrations
- Gaiia sync: show actual error type instead of generic 'unexpected error'
2026-02-14 17:11:38 -06:00
f59db9565c Gaiia webhook: clean rewrite per exact docs, detailed logging on failure 2026-02-14 16:57:55 -06:00
2c55ab5b77 Gaiia webhook: accept even when signature fails, log details for debugging 2026-02-14 16:52:25 -06:00
8f04a3f684 Fix credo: extract PagerDuty HMAC verification to reduce nesting 2026-02-14 16:31:54 -06:00
2709ab4674 Fix integration API key field: read from saved credentials like NetBox 2026-02-14 16:31:05 -06:00
b8a625005c Fix NetBox config: show saved URL/API token when opening Configure 2026-02-14 16:29:32 -06:00
be5fa8f4d7 Gaiia webhook: proper HMAC verification per docs, optional when no header 2026-02-14 16:28:35 -06:00
ba895b0ca2 Remove Gaiia webhook secret verification entirely — Gaiia has no signing mechanism 2026-02-14 16:25:56 -06:00
b8a737ddf4 Fix Gaiia: subnet -> ipRange on NetworkSiteIpBlock 2026-02-14 16:16:44 -06:00
e3c730a8e9 Fix Gaiia readable_id: coerce to string (can be integer) 2026-02-14 16:15:59 -06:00
712174cc47 Fix NetBox test connection: read URL/token from form, not just saved state 2026-02-14 16:15:59 -06:00
d640fab660 Fix invite form alignment on members tab 2026-02-14 16:12:15 -06:00
b49d177b97 Fix trace page Access on Device struct, limit dashboard insights to 4 2026-02-14 16:10:52 -06:00
bcc93cc1b5 Limit dashboard insights to 4, unlock dokku 2026-02-14 16:10:52 -06:00
95d833874a Gaiia webhook: verify secret via query param instead of signature header 2026-02-14 16:08:27 -06:00
82dd75d2e9 Fix Gaiia webhook (no signature), sites page with table + insights 2026-02-14 16:07:26 -06:00
3049411337 Dashboard: timezone-aware timestamps, site health as table, remove dead code 2026-02-14 16:07:26 -06:00
2cbf6db8de Dashboard redesign: tighter layout, table alerts, less wasted space 2026-02-14 16:07:26 -06:00
05cdc79124 Fix Gaiia webhook: eventName field, payload extraction, add header logging 2026-02-14 15:40:06 -06:00
ebcba70925 Fix Gaiia GraphQL: SubscriptionStatus is scalar, not object 2026-02-14 15:09:05 -06:00
a91e5f9be5 Dashboard makeover: more breathing room, cleaner layout
- Stat cards: 4-column grid with larger text (text-4xl) and rounded-xl
- Split stats into primary (health/uptime/devices/alerts) and secondary rows
- Main content: 2/3 + 1/3 split instead of cramped 3/5 + 2/5
- Increased gaps throughout (gap-5, gap-10, mt-10 between sections)
- Cards use rounded-xl, p-5 for more padding
- Config changes + activity in proper list layout with dividers
- Quick actions moved to bottom with separator
- Filter pills use rounded-full with stronger active state
- Removed 'Command Center' header, cleaner org name + subtitle
- Better empty states with more whitespace
2026-02-14 15:01:49 -06:00
7b72ed3164 PagerDuty two-way sync: webhook receiver for resolve/acknowledge
- POST /api/v1/webhooks/pagerduty/:organization_id endpoint
- HMAC-SHA256 signature verification (optional, via webhook_secret)
- incident.resolved → resolve_alert_silent (no re-notify to PagerDuty)
- incident.acknowledged → acknowledge_alert_silent (no re-notify)
- Matches alerts via dedup_key format: towerops-alert-<uuid>
- Webhook secret configurable in org settings PagerDuty panel
- Setup instructions in UI for PagerDuty Generic Webhooks (v3)
2026-02-14 14:58:58 -06:00
29c2039832 Auto-resolve device_up recovery alerts on creation
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.
2026-02-14 14:53:17 -06:00
06f9b7e60b Fix format_response_time crash on non-numeric input 2026-02-14 14:53:17 -06:00