Commit graph

2395 commits

Author SHA1 Message Date
139a42531f
Refactor credo issues: reduce complexity and nesting depth
- Refactor check_threshold_violation: extract threshold checks into separate functions (complexity 10→~3)
- Refactor load_sensor_chart_data: extract helper functions to reduce nesting
- Refactor save_equipment: extract SNMP discovery logic into helpers
- Refactor poll_sensors: extract error handling and result processing
- Refactor handle_status_change: extract equipment down/up handlers
- Refactor mount_current_scope: extract session scope building
- Refactor load_traffic_chart_data: extract aggregation and calculation helpers
- Refactor load_overall_traffic_chart_data: similar pattern to traffic chart
- Refactor load_sensor_chart_data in equipment_live/show: extract dataset builders
- Refactor discover_storage_sensors: extract storage sensor building
- Refactor perform_poll: extract device polling logic into separate functions

All 254 tests passing. Credo reports no issues.
2026-01-06 13:55:16 -06:00
02e0744c3c
Disable logging for health check endpoint
- Add log: false option to /health route to prevent K8s probe spam in logs
- Remove incorrect logger filter configuration from runtime.exs
2026-01-06 13:43:57 -06:00
e02c2d2b8f
Fix impersonation crash when session IDs are nil
Critical bug fix: fetch_current_scope_for_user and mount_current_scope
were calling Accounts.get_user(nil) when impersonating session flag was
true but superuser_id or target_user_id were nil.

This caused FunctionClauseError crashes for all logged-out users if they
had stale impersonation session data.

Changes:
- Check if both superuser_id and target_user_id exist before calling get_user
- Clear invalid impersonation state if IDs are missing
- Apply fix to both fetch_current_scope_for_user (controllers) and
  mount_current_scope (LiveViews)

This ensures graceful handling of corrupted/partial session state.
2026-01-06 13:32:17 -06:00
7df6e8b3b4
Redirect users to default org equipment page on login
Changed signed_in_path to redirect users to their default organization's
equipment page instead of the organization list.

Behavior:
- If user has organizations, redirect to first org's equipment page
- First org is determined by most recently joined (membership.inserted_at)
- If user has no organizations, redirect to /orgs to create one

This provides a better UX by landing users directly at their equipment
list instead of requiring an extra click through the org selector.
2026-01-06 13:30:07 -06:00
152c308b68
Add input validation for port numbers and SNMP testing
Security improvements to prevent potential issues:

1. Port Validation (normalize_port):
   - Validate port range is 1-65535
   - Use Integer.parse instead of String.to_integer to prevent crashes
   - Return default port 161 for any invalid input

2. SNMP Test Validation (validate_test_snmp_input):
   - Validate IP address format before allowing SNMP tests
   - Require non-empty community string
   - Prevent network scanning with invalid/missing IPs
   - Return clear error messages for validation failures

These changes ensure user input is properly validated and prevent:
- Integer overflow/underflow with ports
- Process crashes from invalid input
- Unauthorized network scanning via SNMP test feature
- SNMP requests with missing credentials
2026-01-06 13:28:30 -06:00
806b293ead
Fix impersonation to show correct user data and banner
Two issues were preventing impersonation from working correctly:

1. Templates were not passing current_scope to Layouts.authenticated,
   so the impersonation banner never displayed.

2. fetch_current_scope_for_user was looking up the user from the
   session token, which always pointed to the superuser. This caused
   the superuser to see their own organizations and equipment instead
   of the impersonated user's data.

Changes:
- Pass current_scope={@current_scope} to all Layouts.authenticated calls
- Store target_user_id in session during impersonation
- Fetch target user directly from database using target_user_id
- Update both fetch_current_scope_for_user (for controllers) and
  mount_current_scope (for LiveViews) to properly handle impersonation
- Clean up target_user_id from session when impersonation ends

Now when a superuser impersonates a user, they correctly see:
- The impersonation banner at the top with exit link
- The target user's organizations and equipment
- All data scoped to the impersonated user
2026-01-06 13:22:13 -06:00
a1e163d3ee
Add dialyzer type specs to refactored functions
Added comprehensive type specifications to all recently refactored
functions to improve type safety and enable dialyzer checking:

PollerWorker:
- detect_sensor_changes/3
- extract_thresholds/1
- detect_and_log_changes/4

EquipmentLive.Form:
- extract_snmp_config/1
- normalize_port/1
- test_snmp_connection/1

EquipmentLive.Show:
- seconds_to_weeks/1, seconds_to_days/1
- seconds_to_hours/1, seconds_to_minutes/1
- format_time_part/2

All specs use proper Elixir types and handle nil cases appropriately.
2026-01-06 13:12:55 -06:00
fb4da2df6d
Refactor handle_event SNMP test to reduce complexity
Extracted SNMP configuration extraction and connection testing
into separate helper functions, reducing cyclomatic complexity
from 11 to ~5.

- extract_snmp_config/1: Consolidates form data and params
- normalize_port/1: Handles port type conversion
- test_snmp_connection/1: Performs test and formats result

This makes the handle_event callback simpler and each concern
easier to test independently.
2026-01-06 13:11:07 -06:00
b328dfb96e
Refactor complex functions to reduce cyclomatic complexity
Refactored three highly complex functions to improve code maintainability:

1. PollerWorker.detect_sensor_changes (complexity 33 -> ~9):
   - Extracted threshold checking logic into separate functions
   - Split event creation into focused helper functions
   - Reduced nesting by using pipe operator pattern

2. PollerWorker.detect_and_log_changes (complexity 16 -> ~5):
   - Created separate functions for each interface change type
   - Extracted event building logic into dedicated functions
   - Improved readability with clear function names

3. EquipmentLive.Show.format_duration (complexity 13 -> ~5):
   - Extracted time unit conversion into separate functions
   - Simplified pluralization logic with pattern matching
   - Reduced nested conditionals

These changes make the code easier to test, maintain, and understand
while preserving all existing functionality.
2026-01-06 13:09:51 -06:00
77b41e0b24
Fix poller worker to use debug level for missing SNMP OIDs
The poller was re-logging SNMP errors from Client.get as warnings,
even though the client now logs them at debug level. Updated the
poller to also use debug level for expected errors like :no_such_object.

This completes the fix to reduce SNMP logging noise for devices
that don't support certain MIB objects.
2026-01-06 13:05:22 -06:00
20101201c6
Fix admin layout to use proper Phoenix component pattern
The admin layout was incorrectly defined as a separate .html.heex file
using @inner_content. Phoenix components should use render_slot(@inner_block).

Moved admin layout definition into layouts.ex as a proper function
component, matching the pattern used by app/1 and authenticated/1 layouts.
2026-01-06 13:04:22 -06:00
354e500405
Reduce SNMP logging noise for missing OIDs
Changed SNMP Client to use debug level for expected errors
(:no_such_object, :no_such_instance, :end_of_mib_view) and
only use warning level for actual communication errors.

This prevents log spam when polling interfaces that don't
support certain MIB objects, which is normal behavior.
2026-01-06 13:02:08 -06:00
837e2ea13f
Fix CSRF token error in impersonation banner
Replace raw HTML form with Phoenix .link helper using method="delete" to properly handle CSRF token in LiveView context. This fixes the UndefinedFunctionError when accessing /admin route.
2026-01-06 12:58:17 -06:00
a3cd43d2c2
Fix credo warnings and software design suggestions
Replace expensive length/1 comparisons with direct list comparisons or Enum.empty?/1 checks (13 instances in source code and tests). Add module aliases for Phoenix.HTML.Form and Towerops.Accounts.Scope to reduce nested module references.

Changes:
- Replace length(list) > 0 with list != [] or refute Enum.empty?(list)
- Add Phoenix.HTML.Form alias in CoreComponents
- Add Towerops.Accounts.Scope alias in ConnCase test helper

This eliminates all credo warnings and software design suggestions.
2026-01-06 12:55:22 -06:00
853d548f82
Add superuser system with user impersonation for admin support
Implement comprehensive admin interface allowing designated superusers to view all users and organizations, impersonate users for debugging, and perform administrative operations. All superuser actions are tracked in audit logs for compliance.

Features:
- Superuser authentication with dedicated admin routes at /admin
- User impersonation with session state preservation
- Admin dashboard with system statistics
- User and organization management interfaces
- Comprehensive audit logging with IP tracking
- Visual impersonation banner with exit capability
- Security controls preventing self-impersonation and superuser-to-superuser impersonation

Database:
- Add is_superuser boolean field to users table
- Create audit_logs table for tracking sensitive operations
- Set graham@mcintire.me as initial superuser
2026-01-06 12:50:10 -06:00
f29282d9dc
Fix compiler warnings in equipment monitor
- Prefix unused alert variables with underscore
- Remove unused test_env?/0 function

All warnings resolved, compilation clean with --warnings-as-errors
2026-01-05 14:37:03 -06:00
cb9c095847
Set logger level to info in development to prevent credential exposure
SNMPKit library logs complete SNMP messages at debug level, which
includes the community string (credential). Setting log level to :info
prevents these debug messages from appearing in development logs.

Production and test environments already use :info and :warning levels.
2026-01-05 14:35:09 -06:00
d5905cea73
Change storage display from chart to sensor list
- Display storage sensors as a list similar to temperature/voltage
- Show individual disk/partition usage with current values
- Link to graph view from sensor names
- More consistent with other sensor displays
2026-01-05 14:08:00 -06:00
bf301aa5fc
Enhance MikroTik discovery with comprehensive sensor support
Based on MikroTik MIB analysis, added discovery for:

Device Information:
- Board name, display name, build time

Health Sensors - Voltages:
- Core voltage, 3.3V/5V/12V supplies, input voltage

Health Sensors - Temperatures:
- Sensor chip, CPU, board, system, processor temperatures

Health Sensors - Power & Cooling:
- Power consumption (W), current draw (mA)
- Processor frequency (MHz)
- Active fan status, fan speeds (RPM)
- Power supply state, backup PSU state

POE Monitoring (per port):
- Voltage, current, power consumption
- Automatically discovers all POE-enabled ports

Optical/SFP Monitoring (per interface):
- Temperature, voltage
- TX/RX optical power levels (dBm)
- Supports fiber transceivers and SFP modules

All sensors include proper units, divisors, and descriptive names.
2026-01-05 14:06:46 -06:00
6c631cbe26
Add sensor value change tracking and threshold monitoring
- Add new event types: sensor_threshold_warning, sensor_threshold_critical, sensor_threshold_normal, sensor_value_spike, sensor_value_drop
- Implement sensor change detection in PollerWorker
- Track threshold violations (warning/critical high/low)
- Detect value spikes/drops for percentage sensors (30% change)
- Track return to normal from threshold violations
- Store threshold config in sensor metadata
- Update sensor last_value and last_checked_at on each poll
- Broadcast sensor events via PubSub for real-time logging
2026-01-05 14:01:35 -06:00
7ea79afea1
disable alert emails 2026-01-05 13:51:26 -06:00
be3a588e40
Fix boolean attribute rendering for traffic graph 2026-01-05 13:48:59 -06:00
4242044cbd
Add parameter filtering to redact SNMP community strings from logs 2026-01-05 13:48:39 -06:00
af39c7e9fd
Add raw attribute logging to debug boolean values 2026-01-05 13:47:16 -06:00
ae5b73c495
Add console logging to debug traffic graph issues 2026-01-05 13:45:37 -06:00
13ca70da40
Add safety checks for traffic graph scale calculation 2026-01-05 13:44:02 -06:00
527e28736a
Make traffic graph scale symmetric to keep zero line in middle 2026-01-05 13:43:09 -06:00
abe4cb6845
Make traffic bar chart bars connected with no gaps 2026-01-05 13:40:44 -06:00
ce7988b10d
Show traffic as mirrored bar chart with outbound above and inbound below zero axis 2026-01-05 13:40:13 -06:00
b6abe23493
Format traffic values in Mbps/Gbps and fix column layout 2026-01-05 13:38:07 -06:00
a57dfd2e26
Move traffic graph below device info and add zero axis line 2026-01-05 13:36:54 -06:00
703dfcf58c
Fix Float.round error in traffic graph calculations 2026-01-05 13:34:54 -06:00
2212bf9134
Make traffic graph header clickable and add dedicated graph page 2026-01-05 13:33:32 -06:00
3643700d73
Add overall traffic graph combining all interfaces 2026-01-05 13:32:07 -06:00
223434dd99
Make equipment list rows clickable 2026-01-05 13:29:23 -06:00
ffb2b9576f
Remove force_ssl config since Traefik handles SSL termination 2026-01-05 13:23:38 -06:00
3629522d25
Auto-select site when only one is available for new equipment 2026-01-05 13:18:09 -06:00
31ed13cf1a
Upgrade Docker to version 27 for API compatibility 2026-01-05 12:57:02 -06:00
46cfd86981
Add catch-all handler for unknown PubSub messages in PollerWorker 2026-01-05 12:49:56 -06:00
8f7ed19d34
Trigger immediate poll after SNMP discovery completes 2026-01-05 12:46:25 -06:00
62689ea939
Configure CI to use gitlabrunner 2026-01-05 12:26:19 -06:00
fb0bf14b66
event pubsub 2026-01-05 12:23:15 -06:00
13d98ce548
Fix all failing tests after UI changes
- Add EventLogger tests for PubSub event system
- Update navigation tests to match current UI patterns
- Fix alert notifier from address expectations
- Update site and equipment tests for relocated delete buttons
- Remove obsolete tests for removed UI elements (Recent Checks, monitoring widgets)
- All 254 tests now passing
2026-01-05 11:33:26 -06:00
249c7d3560
Log initial interface discovery as events
- Detect when interface speed changes from null to a value
- Detect when MAC address changes from null to a value
- Log initial discoveries with "info" severity
- Differentiate between initial discovery and actual changes
- Show "detected" message for initial values
- Show "changed from X to Y" message for actual changes
2026-01-05 11:16:31 -06:00
06a31441f2
Change temperature and voltage display to table format
- Replace grid of sensor cards with table format matching device info
- Display sensor name on left, value on right
- Make sensor names clickable links to graph detail page
- Remove generic "Sensors" section from overview
- Cleaner, more scannable layout for temperature and voltage sensors
2026-01-05 11:14:59 -06:00
ddabb3f030
Add detailed graph view with date range selection
- Create GraphLive.Show for detailed sensor graph visualization
- Add route for /equipment/:id/graph/:sensor_type endpoint
- Make all chart headers clickable with navigation to detail view
- Implement date range selector (1h, 6h, 12h, 24h, 7d, 30d)
- Fix chart rendering by destroying and recreating on data updates
- Fix duplicate data loading in LiveView event handlers
- Fix MikroTik profile typing warning for entity sensor discovery
2026-01-05 11:09:21 -06:00
05f1aec227
Add sensor charts and improve equipment detail page
- Add Chart.js integration for interactive sensor graphs
- Add processor, memory, storage, temperature, and voltage charts showing 24 hours of data
- Fix device information box to use natural height instead of stretching
- Update MikroTik profile to discover ENTITY-SENSOR-MIB sensors
- Support multiple sensor types per chart (celsius/temperature, volts/voltage)
- Use 24-hour time format in chart tooltips and x-axis labels
- Support auto-scaling y-axis for non-percentage metrics
2026-01-05 11:01:21 -06:00
4ad4e3a673
cleanup 2026-01-05 10:44:21 -06:00
cac54b46a2
Implement event logging system and fix sensor display
- Add equipment_events table and Event schema for tracking device changes
- Implement automatic change detection for interface attributes during SNMP polling
  - Detects operational status changes (up/down)
  - Detects admin status changes
  - Detects speed changes with warning severity for speed drops
  - Detects MAC address changes
- Add Logs tab to equipment detail page with event display
- Fix temperature sensor display bug (was showing 4.3C instead of 43C due to double division)
- Remove response time tracking from monitoring checks and UI
- Move Rediscover Device button to equipment edit page
2026-01-05 10:42:34 -06:00
9eca2e7069
Fix database schema mismatches preventing SNMP and monitoring data collection
This fixes critical issues where SNMP sensor readings, interface stats, and
monitoring checks were not being saved to the database due to schema mismatches.

Database schema fixes:
- Recreate snmp_sensor_readings table with binary_id primary key
- Recreate snmp_interface_stats table with binary_id primary key
- Recreate monitoring_checks table with binary_id primary key

The original migrations used default integer primary keys, but the schemas
expected binary_id (UUID). This caused Ecto to generate UUID strings that
Postgrex tried to encode as binaries, resulting in silent insert failures.

SNMP interface stats fixes:
- Fix field name mismatch in get_interface_stats (if_in_octets vs in_octets)
- Remove unnecessary tuple handling code (Client already unwraps SNMPKit tuples)
- Clean up decode_snmp_value function and improve error messages

Monitoring check fixes:
- Fix response_time type from float to integer in Poller.check_device
- Add error logging to catch and report check creation failures

These changes enable:
- SNMP sensor readings to be collected and stored (disk usage, memory, CPU, etc.)
- Interface statistics to be collected (Counter64 octets, errors, discards)
- Equipment availability metrics to update on the dashboard
2026-01-05 08:26:16 -06:00