Commit graph

72 commits

Author SHA1 Message Date
ae16acf016
Add build timestamp to footer with shared time formatting helpers
- Add compile-time @build_timestamp to Application module
- Create ToweropsWeb.TimeHelpers module for shared time formatting
- Update footer to display 'Last deployed Xm ago · YYYY-MM-DD HH:MM:SS UTC'
- Refactor AgentLive.Helpers to delegate to shared TimeHelpers
- Consolidate duplicate time formatting logic into single module
- Timestamp updates automatically on each build/deploy
2026-01-17 11:10:50 -06:00
b53a53b199
Add comprehensive Dialyzer type specifications
- Add @type definitions to all schema modules:
  - User, UserCredential, Membership, Invitation
  - Organization, Site, AgentAssignment
  - InterfaceStat, SensorReading, Alert
- Fix all compilation warnings with stronger Elixir types:
  - Remove unused require Logger in log_filter.ex
  - Remove unused parse_float(nil) clause
  - Add pin operators (^) for variables in binary pattern matches
- Fix Dialyzer errors (25 → 0):
  - Remove unreachable pattern match clauses
  - Fix unmatched return values with _ = prefix
  - Update @spec for deliver_alert_notification/1
- Properly handle all Phoenix.PubSub.subscribe and Task.start return values
- Explicitly ignore if statement return values where needed

All files now pass mix compile --warnings-as-errors and mix dialyzer.
2026-01-17 10:52:02 -06:00
538f582fec
Update agent setup instructions with Watchtower auto-updates
- Change default agent image to gmcintire/towerops-agent:main
- Add Watchtower service to docker-compose instructions
- Configure 15-minute update check interval for development
- Add DOCKER_API_VERSION=1.44 to prevent API compatibility errors
- Remove deprecated docker-compose version field
2026-01-16 17:38:30 -06:00
eb91798729
agent improvements and agent config 2026-01-16 17:26:51 -06:00
a7ed057976
neighbor improvements 2026-01-16 16:25:49 -06:00
dcd4588304
fix neighbors 2026-01-16 16:09:15 -06:00
3539e36f12
updates 2026-01-16 13:15:59 -06:00
7068ab2466
Convert User Settings from controller to LiveView
- Create UserSettingsLive to replace UserSettingsController
- Convert email/password forms to LiveView with proper form handling
- Add mobile session management (toggle alerts, revoke devices)
- Add require_sudo_mode on_mount hook to UserAuth
- Create dedicated live_session for sudo mode routes in router
- Keep UserSettingsController for email confirmation via token only
- Add MobileSessions.get_session/1 function for test support
- Update tests to match LiveView behavior (password update redirects to login)
- Remove old controller tests for email/password updates (now in LiveView)
2026-01-15 16:29:50 -06:00
c7f02eac24
add api for mobile login 2026-01-15 15:36:14 -06:00
58433ec072
Enable Docker socket mount by default in agent setup
- Removed comment from Docker socket volume mount
- Auto-updates now work out of the box when users copy config
- Socket mount: /var/run/docker.sock:/var/run/docker.sock
2026-01-15 09:29:58 -06:00
b70ddfe166
timestamp improvement 2026-01-15 08:03:30 -06:00
723f064315
Add batch loading for sensor/interface stats to eliminate N+1 queries
Added two new functions to Snmp context:
- get_latest_sensor_readings_batch/1 - Loads latest readings for multiple sensors in one query
- get_latest_interface_stats_batch/1 - Loads latest stats for multiple interfaces in one query

Both use PostgreSQL DISTINCT ON to efficiently get only the latest record per ID.

Updated EquipmentLive.Show.load_snmp_data/1 to use batch functions:
- Equipment with 20 sensors: 20 queries → 1 query (20x reduction)
- Equipment with 10 interfaces: 10 queries → 1 query (10x reduction)
- Total improvement: 30 queries → 2 queries for typical equipment page load

This significantly improves page load performance for equipment with many sensors/interfaces.
2026-01-15 07:31:57 -06:00
96706b2cf8
Deduplicate agent status helper functions
Extracted 5 shared helper functions into ToweropsWeb.AgentLive.Helpers:
- agent_status/1 - Determines online/warning/offline/never status
- status_badge_class/1 - Returns Tailwind CSS classes for status badges
- status_dot_class/1 - Returns CSS classes for animated status dots
- format_last_seen/1 - Formats DateTime as human-readable time ago
- format_uptime/1 - Formats uptime seconds as days/hours/minutes

Removed duplicate code from:
- lib/towerops_web/live/agent_live/index.ex
- lib/towerops_web/live/agent_live/show.ex

Both modules now import the shared helpers, following DRY principle.
This reduces code duplication and makes status formatting consistent
across all agent-related pages.
2026-01-14 19:12:06 -06:00
4297556700
Implement all quick win improvements for agent UI and functionality
1. Fixed Docker Compose example to include socket mount
   - Added /var/run/docker.sock mount for auto-updates in agent modal

2. Show total equipment count with inheritance
   - Display both direct and inherited equipment counts in agents table
   - Shows breakdown: 'X total' with 'Y direct · Z inherited' details

3. Add visual status indicators with better CSS
   - Added animated pulsing dots for online agents
   - Color-coded status dots (green=online, yellow=warning, red=offline)
   - Improved status badge styling

4. Create agent detail page with metrics
   - New /agents/:id page showing comprehensive agent health
   - 4 metric cards: Status, Equipment count, Last seen, Agent info
   - Displays agent metadata (hostname, version, uptime)
   - Lists all polling targets with assignment source badges
   - Shows direct vs inherited equipment assignments
   - Clickable agent names in table link to detail page

5. Add health endpoint to Rust agent
   - Added /health HTTP endpoint on port 8080
   - Returns JSON with agent status, version, uptime, metrics pending
   - Uses lightweight tiny_http server
   - Non-blocking background thread
   - Ready for monitoring/health checks
2026-01-14 18:46:03 -06:00
8864b8d26d
Enhance agent assignment UI with clear inheritance indicators
- Add override warnings (amber) when site/equipment explicitly override defaults
- Show inherited agent names in organization and site forms
- Display equipment assignment breakdown in organization settings
- Make equipment boxes fully clickable on site page (remove View button)
- Add hover effects to equipment boxes for better UX
- Consistent icon usage across all assignment levels
2026-01-14 16:20:20 -06:00
fa591fde05
Revert manual data directory setup instructions from agent modal
The Docker agent now handles permissions automatically via the
entrypoint script, so users don't need to manually create the
data directory or run chown commands.

Reverted the UI back to the simple docker-compose example.
2026-01-14 09:36:59 -06:00
2aca9dc933
Add data directory setup instructions to agent modal
The Docker agent runs as non-root user (UID 1000) for security. When
mounting host directories, users must create the data directory with
proper permissions before starting the container.

Added clear instructions to:
1. Create the data directory: mkdir -p data
2. Set proper ownership: chown -R 1000:1000 data
3. Then use the Docker Compose configuration

This fixes the 'unable to open database file' error when starting
the agent.
2026-01-14 09:33:12 -06:00
569b5cb671
Make agent Docker image configurable and update success criteria
- Added agent_docker_image config option (defaults to towerops/agent:latest)
- Updated agent UI modal to use configurable image URL
- Can override in runtime.exs or environment config for production
- Updated AGENT_NEXT_STEPS.md success criteria with completion status
- 8/12 success criteria now complete
2026-01-14 09:16:48 -06:00
599fa2e69e
Integrate agent health statistics into agent UI
- Added Stats module to agent LiveView index
- Load organization-wide health statistics on mount
- Display agent health, equipment counts, and assignment breakdown
- Refresh stats after agent creation/revocation
- All 10 agent LiveView tests passing
2026-01-14 09:12:59 -06:00
a2d96f8e6e
Implement hierarchical agent assignment for SNMP polling
Add three-level agent assignment hierarchy (Equipment > Site > Organization)
allowing flexible agent deployment strategies. Agents now receive only equipment
assigned to them through direct assignment or inheritance from site/organization
defaults.

Key changes:
- Add agent_token_id to Sites table with migration
- Implement get_effective_agent_token/1 for hierarchical resolution
- Add list_agent_polling_targets/1 to return polling targets per agent
- Update API config endpoint to use hierarchical polling targets
- Add agent assignment UI to equipment, site, and organization forms
- Show agent source (direct/site/org/none) in equipment form
- Add equipment count column to agent list
- Update terminology from "poll from server" to "cloud polling"

Tests:
- Add 8 comprehensive tests for list_agent_polling_targets/1
- Add end-to-end test for hierarchical config endpoint
- All 775 tests passing
2026-01-14 08:38:50 -06:00
b8bd952681
agent token changes 2026-01-13 13:43:43 -06:00
aa62cc7349
Move Create Agent buttons below input field
- Changed form layout from horizontal to vertical
- Agent name input now on its own line
- Create Agent and Cancel buttons on line below with gap-3 spacing
2026-01-13 13:22:18 -06:00
cf77949527
add remote agent setup 2026-01-09 13:15:31 -06:00
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
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
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
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
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
be3a588e40
Fix boolean attribute rendering for traffic graph 2026-01-05 13:48:59 -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
3629522d25
Auto-select site when only one is available for new equipment 2026-01-05 13:18:09 -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
2e650c2be8
Add UTC timestamp tooltip to Last Check time
Shows the full UTC timestamp when hovering over the relative 'time ago'
text on the equipment detail page.

Changes:
- Wrapped time_ago display in a span with title attribute
- Title shows full datetime in UTC format
- Added cursor-help class for visual hint that tooltip is available

Users can now see both the easy-to-read relative time ('5m ago') and
the precise UTC timestamp (2026-01-04 19:30:15Z) by hovering.
2026-01-04 13:39:50 -06:00
40a329ea78
Add back navigation links to edit pages
Added back navigation links at the top of all edit/new pages to
improve UX and make it easier to navigate back to previous pages.

Changes:
- Added back link to equipment edit/new pages
  - Edit: goes back to equipment detail page
  - New: goes back to equipment list
- Added back link to site edit/new pages
  - Edit: goes back to site detail page
  - New: goes back to sites list
- Links styled with arrow icon and subtle hover effect
- Added error handling to Counter64 decoder with try/rescue

The back links appear above the page header and provide clear
navigation context for users.
2026-01-04 13:35:31 -06:00