Login flow improvements:
- Remove "Welcome back!" flash message on successful login
- Auto-login users clicking magic links without confirmation prompt
- Always use "remember me" for magic link logins
- Fix Accounts.login_user_by_magic_link/1 to handle invalid token format
Test fixes:
- Add @tag :integration to Ping tests (require actual system ping)
- Add halt() calls to UserAuth.start_impersonation error paths
Users now go directly to their intended destination after clicking a
magic link, with no interruption for "log in and stay logged in" choice.
- Updated Monitoring module to calculate hourly/daily stats on-demand from monitoring_checks table
- Removed TimescaleDB continuous aggregate dependency (disabled due to licensing)
- Enabled all previously skipped TimescaleDB tests with proper test data
- Fixed UUID encoding for SQL queries using Ecto.UUID.dump!
- Fixed Decimal handling in uptime percentage calculation
- Added test for get_uptime_percentage with nil checks
- Monitoring module now at 100% coverage
- Add MIB files from LibreNMS in priv/mibs/ for reference
- Create MibParser module to validate OIDs against official MIB definitions
- Add MIB validation tests to ensure hardcoded OIDs match MIB specs
- Refactor SNMP tests to be generic/behavior-focused instead of vendor-specific
- Remove vendor-specific test files (cisco_test, net_snmp_test)
- All 104 tests passing with automated OID validation
Enhance SNMP error logging to include:
- Target IP address
- SNMP version (v1/v2c)
- Timeout value in milliseconds
- OID being queried
Before:
[warning] SNMP GET failed for "1.3.6.1.2.1.2.2.1.10.15729368": :timeout
After:
[warning] SNMP GET failed for 192.168.1.1 (v2c, timeout: 5000ms) OID "1.3.6.1.2.1.2.2.1.10.15729368": :timeout
This makes it much easier to diagnose SNMP issues by immediately knowing which
device is timing out and what the network configuration is.
Updated all SNMP operations: GET, WALK, and GET-BULK.
Use Plug.Telemetry's dynamic log level feature to disable logging for
/health requests. This is the proper Phoenix way to exclude specific
endpoints from request logs.
- Add log_level/1 function to endpoint that returns false for /health
- Configure Plug.Telemetry to use dynamic log level
- Remove log: false from router (handled by endpoint now)
This prevents Kubernetes health probe spam in application logs.
When exiting impersonation, now redirects to the superuser's default
organization's equipment list page instead of /admin. Falls back to /orgs
if the superuser has no organizations.
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.
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.
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
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
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.
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.
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.
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.
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.
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.
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.
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
- Prefix unused alert variables with underscore
- Remove unused test_env?/0 function
All warnings resolved, compilation clean with --warnings-as-errors
- 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
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.
- 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