Commit graph

595 commits

Author SHA1 Message Date
7f7b722965
feat: add SNMPv3 credential logging for debugging
Log SNMPv3 credentials being sent to agents (with passwords redacted) to help diagnose authentication issues. Shows security level, username, auth/priv protocols, and whether passwords are present.
2026-02-06 13:48:35 -06:00
5f04d716ba
fix: handle device deletion during discovery
Previously, if a device was deleted while a discovery job was in progress, the wait_loop would crash with Ecto.NoResultsError when trying to check if discovery had completed.

Now uses get_device_with_details which returns nil for missing devices, and gracefully returns {:error, :device_deleted} instead of crashing.
2026-02-06 13:06:37 -06:00
6a29ee1671
fix: Split agent polling queries to isolate SNMP failures
Changed from one large WALK query with all OIDs to separate WALK queries
per table type. This prevents a failure in one unsupported table (like ARP
on a device that doesn't support it) from killing the entire polling
operation.

Before: Single WALK with neighbor + ARP + MAC + IP + HOST-RESOURCES OIDs
After: Separate WALKs for each table type

This makes polling more resilient - if a device doesn't support ARP tables
or HOST-RESOURCES-MIB, those WALKs will fail individually while neighbors,
MAC tables, and IP addresses can still succeed.

Fixes agent errors where a single SNMP receive error was killing the
entire poller thread and failing all subsequent operations with
'Poller thread died'.
2026-02-06 12:30:52 -06:00
184c2bc999
fix: Use snmp_device.id for sync_processors and sync_storage
Fixes KeyError when processing processors/storage during agent polling.
The sync_processors and sync_storage functions expect device.id to be
the snmp_device id, not the main device id.

Changed from:
  %{device_id: device.id}

To:
  %{id: device.snmp_device.id}

This matches the expected structure for these sync functions which
query where snmp_device_id == device.id.
2026-02-06 12:06:30 -06:00
c6ec9ab891
fix: Handle nil vlan_id in MAC address upsert queries
Fixes ArgumentError when upserting MAC addresses with nil vlan_id.
Ecto doesn't allow direct comparison with nil in queries - must use
is_nil/1 instead.

Changed upsert_mac_address to build dynamic query that uses is_nil(m.vlan_id)
when vlan_id is nil, or direct comparison when it has a value.

Error was:
  Comparison with nil is forbidden as it is unsafe.
  Instead write a query with is_nil/1
2026-02-06 12:00:29 -06:00
26e5d20510
fix: Add device_id to interfaces for neighbor discovery in agent polling
Fixes KeyError when processing neighbor discovery during agent polling.
NeighborDiscovery.discover_neighbors/2 requires interfaces to have a
:device_id field for building neighbor records, but interfaces from the
database only have :snmp_device_id.

Solution matches the approach in DevicePollerWorker which adds device_id
to interfaces before calling discover_neighbors.
2026-02-06 11:26:00 -06:00
f17ee2183c
fix: Use server-side timestamps for agent polling metrics
Replaces agent-supplied timestamps with server-side DateTime.utc_now()
to prevent clock drift issues between agents from causing weird graphs.

All metrics now use consistent server time regardless of polling method:
- Agent polling (via WebSocket): server time on receipt
- Phoenix polling (direct SNMP): server time on poll (already correct)

This ensures time-series data aligns correctly when graphing metrics
from devices polled by different agents with potentially different
system clocks.
2026-02-06 11:23:26 -06:00
bf4fee4846
feat: Add comprehensive agent polling for all discoverable data
Extended agent polling to collect all available SNMP data during regular
polls, not just sensors and interfaces. This ensures data stays current
and reduces reliance on periodic discovery runs.

Changes:
- Added polling queries for: neighbors (LLDP/CDP), ARP tables, MAC tables,
  IP addresses, HOST-RESOURCES processors and storage
- Process additional data using Replay adapter pattern to reuse existing
  parsing logic from discovery modules
- Made discovery helper functions public for reuse in polling context:
  save_neighbors/2, save_arp_entries/3, sync_ip_addresses/2,
  sync_processors/2, sync_storage/2
- Optimized by fetching interfaces once and reusing across operations
- Fixed compilation warnings by using proper guard syntax (list != [])

Technical details:
- Detection threshold: polls with more OIDs than sensors+interfaces
  are processed for additional data
- Background processing: spawns async process to avoid blocking metric
  recording
- Replay adapter: allows reusing discovery parsing logic without
  re-querying SNMP device
2026-02-06 11:18:36 -06:00
5f0549908e
Add IP address table walks to agent discovery queries
Fixes missing IP addresses tab on device page after agent-based discovery.

Issue:
Discovery was not collecting IP address data because the agent's
discovery job didn't include IP address table OID walks.

Root cause:
build_discovery_queries() walked interface tables, sensors, vendor MIBs,
and neighbors, but was missing the IP-MIB address tables.

Changes:
- Add new SnmpQuery to walk IP address tables during discovery:
  - 1.3.6.1.2.1.4.20 (ipAddrTable - IPv4, deprecated but widely supported)
  - 1.3.6.1.2.1.4.34 (ipAddressTable - RFC 4293, unified IPv4/IPv6)

This matches the OIDs used by Phoenix's direct discovery in
lib/towerops/snmp/profiles/base.ex discover_all_ip_addresses/1.

Result:
Agent now collects IP address data during discovery, which is then
synced to the database and displayed in the IP Addresses tab.

Tests: 5578 tests passing (1 pre-existing failure in unrelated Dahua test)
2026-02-06 11:05:59 -06:00
4db953bafa
Add dev-only INFO logging for IP address discovery
Shows IP address discovery and sync results at INFO level in dev
environment only to help diagnose discovery issues.

Changes:
- Add log_ip_discovery_results/2 helper to log discovered IPs
- Enhanced sync_ip_addresses/2 to log IPv4/IPv6 counts and removals
- Only logs at INFO level when env is :dev, uses DEBUG in prod

Example output in dev:
  Discovered IP addresses: 5 IPv4, 2 IPv6 (total: 7)
  Synced IP addresses: 5 IPv4, 2 IPv6, removed 1

This helps diagnose why IP addresses tab isn't showing on device page.
2026-02-06 11:01:10 -06:00
d03ad72aec
Increase max OID values limit from 1,000 to 50,000
Fixes discovery failures on enterprise network devices with many
interfaces and routing table entries.

Issue:
Discovery was failing with "too_many_oids - OID values map exceeds
1000 entries" on SNMPv3 devices. Enterprise routers/switches during
discovery can easily return 10k-50k OIDs from:
- 100+ interfaces with 20-30 OIDs each
- System info, routing tables, ARP tables, neighbor tables
- Various MIB objects

The original limit of 1,000 was too restrictive for real-world devices
while still being intended as DoS protection.

Changes:
- Increase @max_oid_values from 1,000 to 50,000
- Update test to generate 50,100 OIDs (exceeds new limit)
- Add comment explaining why limit is set to 50k

This maintains DoS protection (prevents multi-million OID attacks)
while accommodating realistic device discovery scenarios.

All tests passing (5578 tests, 0 failures).
2026-02-06 10:41:30 -06:00
a4dd7210d8
Improve error logging for SNMP result validation failures
Add detailed error logging to show specific validation error type and
message when SNMP results fail validation. Also handle base64 decode
failures separately.

This will help diagnose validation issues introduced by recent strict
protobuf validation changes (commit 8044a6d1).
2026-02-06 10:36:37 -06:00
0a978746e1
Fix all Credo issues
Resolves 26 Credo warnings/issues:

Code Readability (2 issues):
- Break long line in mac_address.ex (line 316 > 120 chars)
- Break long @spec in agents.ex (line 332 > 120 chars)

Refactoring Opportunities (2 issues):
- Reduce cyclomatic complexity in device_live/form.ex
  - Extract helper functions to simplify SNMPv3 credential resolution
  - Split logic into smaller, focused functions
- Reduce nesting depth in snmp_oid.ex
  - Extract nested logic into separate helper functions

Warnings (22 issues):
- Fix comparison warning in validator.ex
  - Split validate_sensor_value/1 into separate clauses for int/float
  - Add credo directive for valid NaN check (value != value)
- Replace 21 expensive length/1 checks with Enum.empty?/1
  - Change assertions from `assert length(list) >= 1` to `refute Enum.empty?(list)`
  - Change assertions from `assert length(list) > 0` to `refute Enum.empty?(list)`
  - Affected files: device_poller_worker_test.exs, mib_test.exs,
    snmp_tokenizer_test.exs, parser_test.exs, error_test.exs, compiler_test.exs

All tests passing (5578 tests, 0 failures).
Credo now reports: "found no issues"
2026-02-06 10:31:09 -06:00
ed50257d8a
fix snmp v3 test 2026-02-06 10:21:54 -06:00
4d980ed58a
Fix Dialyzer :exact_compare warnings for NaN/infinity checks
Fixes 3 Dialyzer warnings about impossible comparisons when checking for NaN
and infinity values in sensor validation.

Problem:
Dialyzer correctly identified that comparing a float with atoms (:nan,
:infinity, :neg_infinity) using == can never evaluate to true. The previous
code attempted to validate sensor values by comparing with these atoms, which
is incorrect in Erlang/Elixir.

Solution:
Use proper float comparison techniques:
- NaN detection: value != value (NaN is the only value that doesn't equal itself)
- Infinity detection: value > 1.0e308 or value < -1.0e308 (beyond max float range)

This approach correctly identifies special float values without impossible
comparisons that Dialyzer flags.

Dialyzer results:
- Before: Total errors: 509, Skipped: 506
- After: Total errors: 506, Skipped: 506
- Status: done (passed successfully), EXIT CODE: 0

All 3434 tests passing. Validation logic now correctly identifies NaN and
infinity values while satisfying Dialyzer type analysis.
2026-02-06 10:02:46 -06:00
0a0ec23fc9
Fix all compilation warnings for --warnings-as-errors
Resolves all warnings in agent_channel.ex and validator.ex to enable strict
compilation mode.

agent_channel.ex changes:
- Removed unused aliases: AgentError, AgentHeartbeat, CredentialTestResult, SnmpResult
- Removed unused typep declarations: device_id, job_id, agent_token_id
- Prefixed unused variable with underscore: binary -> _binary
- Grouped handle_in/3 clauses together (moved credential_test_result handler)
- Removed @doc from private functions (safe_base64_decode)
- Removed unused private function safe_base64_encode/1

validator.ex changes:
- Removed unused alias: MikrotikSentence
- Prefixed unused parameter with underscore in validate_counter: field -> _field
- Commented out unused module attributes: @max_oid_length, @max_port, @max_interval_seconds
- Removed redundant validate_response_time(0.0) clause (covered by general case)
- Updated validate_response_time guard to use >= 0.0 instead of pattern matching on 0.0

All tests passing (3434 tests). Compilation now succeeds with --warnings-as-errors.
2026-02-06 09:57:04 -06:00
0c77dd84e7
Add strict typespecs to all Oban worker perform/1 functions
Adds @spec annotations to all Oban worker perform/1 functions to improve
static analysis and documentation. Return types are specific to each worker's
actual behavior:

Workers returning 🆗
- BackupSummaryWorker
- BackupTimeoutWorker
- DeviceMonitorWorker
- DevicePollerWorker
- JobHealthCheckWorker
- NeighborCleanupWorker

Workers with complex returns:
- DiscoveryWorker: :ok | :discard
- FirmwareVersionFetcherWorker: :ok | {:error, term()}
- LoginHistoryCleanupWorker: {:ok, %{deleted: non_neg_integer(), anonymized_deleted: non_neg_integer()}}
- MikrotikBackupWorker: :ok | {:error, String.t()}
- SessionCleanupWorker: {:ok, %{sessions_deleted: non_neg_integer()}}

Analysis found that none of these workers have complex job parameters that
would benefit from embedded schemas - all use either no parameters or simple
device_id strings passed in job args.

StaleAgentWorker already had a typespec on find_stale_agents/0 from previous work.

All 98 worker tests passing after changes.
2026-02-06 09:50:51 -06:00
e363fbc691
Implement MacAddress and SnmpOid custom Ecto types
Continues the pattern of using custom Ecto types to create rich domain objects
and avoid primitive obsession. Adds two new custom types following the
established pattern from IPAddress.

## MacAddress Custom Type

Provides structured MAC address handling with:
- Multiple input format support (colon, hyphen, dot-separated, compact)
- Normalization to colon-separated lowercase (canonical format)
- SNMP binary format conversion (6-byte binary)
- Format conversion helpers (:colon, :hyphen, :dot, :compact)
- Comprehensive validation and error handling

### Supported Formats
- Colon: `00:11:22:33:44:55`
- Hyphen: `00-11-22-33-44-55`
- Dot (Cisco): `0011.2233.4455`
- Compact: `001122334455`
- SNMP binary: `<<0, 17, 34, 51, 68, 85>>`

All formats normalize to lowercase colon-separated format for consistency.

## SnmpOid Custom Type

Provides structured SNMP OID handling with:
- OID format validation (numeric and named)
- Normalization to dotted-decimal with leading dot
- OID resolution via SnmpKit (named → numeric)
- Helper functions (parent, child_of?, append)
- Integer list representation support

### Supported Formats
- Dotted-decimal: `.1.3.6.1.2.1.1.1.0`
- Numeric (no leading dot): `1.3.6.1.2.1.1.1.0`
- Named OID: `sysDescr.0` (with automatic resolution)
- Integer list: `[1, 3, 6, 1, 2, 1, 1, 1, 0]`

## Benefits

- **Type Safety**: Structured data with validation at cast time
- **Normalization**: Consistent storage format regardless of input
- **Rich API**: Helper functions for common operations
- **Zero Migration Cost**: No database changes needed
- **Better Errors**: Clear validation errors instead of DB constraint violations
- **Domain Modeling**: Code works with %MacAddress{} and %SnmpOid{} instead of strings

## Database Storage

Both types store as VARCHAR in the database:
- MacAddress: VARCHAR(17) - colon-separated format
- SnmpOid: VARCHAR(255) - dotted-decimal format with leading dot

## Test Coverage

Added comprehensive test suites:
- MacAddress: 47 tests covering all formats, roundtrips, edge cases
- SnmpOid: 49 tests covering OID operations, validation, helpers
- All 96 tests passing 

## Usage Example

```elixir
# Before (primitive obsession)
field :mac_address, :string
field :sensor_oid, :string

# After (rich domain types)
field :mac_address, Towerops.EctoTypes.MacAddress
field :sensor_oid, Towerops.EctoTypes.SnmpOid

# Usage
changeset
|> cast(attrs, [:mac_address, :sensor_oid])
# Validation happens automatically in cast/1
```

Files:
- lib/towerops/ecto_types/mac_address.ex (330 lines)
- lib/towerops/ecto_types/snmp_oid.ex (340 lines)
- test/towerops/ecto_types/mac_address_test.exs (380 lines)
- test/towerops/ecto_types/snmp_oid_test.exs (390 lines)
2026-02-06 09:41:54 -06:00
3fff0db784
Add comprehensive typespecs to agent-related modules
Adds @spec annotations to all public functions in agent-related modules for
better static analysis and documentation.

Changes:
- Added 17 typespecs to Towerops.Agents context module
  - All CRUD operations (create, read, update, delete, revoke)
  - Assignment management functions
  - Agent token resolution and lookup functions
  - PubSub broadcast function
- Added 9 typespecs to Towerops.Agents.Stats module
  - Agent health and metrics statistics
  - Device assignment breakdowns
  - Latency analysis and reassignment candidate detection
- Added typespec to Towerops.Workers.StaleAgentWorker
  - find_stale_agents/0 function
- Preserved existing typespecs in AgentChannel (socket types)

Benefits:
- Improved code documentation with clear function signatures
- Better static analysis via dialyzer
- Clearer API contracts for all agent management functions
- Type-safe UUID handling throughout agent system
2026-02-06 09:33:44 -06:00
20952a781f
add device count to superadmin 2026-02-06 09:32:13 -06:00
8044a6d140
Add strict protobuf validation for agent messages
Implements comprehensive validation layer on top of protobuf schema to
prevent malformed data, DoS attacks, and business logic violations.

Changes:
- Created Towerops.Agent.Validator module with strict validation for all
  agent message types (AgentHeartbeat, MetricBatch, SnmpResult, AgentError,
  CredentialTestResult, MikrotikResult)
- Added validation limits to prevent memory exhaustion and DoS attacks
  (max string lengths, collection sizes, numeric ranges)
- Added format validation for UUIDs, IP addresses, versions, hostnames
- Added enum validation for status and protocol fields
- Modified AgentChannel handlers to validate all incoming messages before
  processing
- Added comprehensive typespecs to AgentChannel for better static analysis
- Created safe_base64_decode/1 helper with error handling
- Added 32 comprehensive tests covering valid cases, invalid inputs, and
  edge cases

Security improvements:
- Prevents memory exhaustion via oversized strings and collections
- Validates all numeric fields are within expected ranges
- Checks UUID format for all ID fields
- Validates IP addresses and hostnames against RFC standards
- Rejects future timestamps and excessive values

Backward compatibility:
- Empty version and hostname strings allowed for older agents
2026-02-06 09:26:04 -06:00
5342be99db
fix device count 2026-02-05 15:04:34 -06:00
156d9a47bf
UI improvements 2026-02-05 14:57:11 -06:00
cb0cc66768
fix mistaken poller ids 2026-02-05 13:57:15 -06:00
472482c4f5
fix nil firmware comparison 2026-02-05 13:47:56 -06:00
a803ddb893
more dialyzer specs 2026-02-05 13:11:18 -06:00
6e730c6558
performance and security improvements 2026-02-05 12:42:35 -06:00
38eeb1c7b8
fix: improve agent polling and SNMP testing
Changes:
- Fix traffic graph to handle nil interface octets gracefully
- Add comprehensive tests for nil octets scenarios
- Fix agent polling query to support devices assigned directly to org (left join on sites)
- Fix test SNMP connection to inherit credentials from site/org
- Refactor agent_channel process_polling_result to reduce nesting (Credo)
- Add alias for JobCleanupTask in application.ex (Credo)

This fixes crashes when interface stats have nil octets and ensures
devices without a site assignment can be polled by agents.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-02-05 12:34:28 -06:00
987eae0408
fix: improve agent-based live polling with better logging and validation
- Added Logger.require for debug logging
- Refactored fetch_latest_agent_data to reduce nesting depth
- Added debug logging to trace agent assignment detection
- Check for nil/stale sensor readings and log appropriately
- Use actual reading timestamp instead of current time for data points

This will help diagnose why live polling shows 0 values and why
Phoenix SNMP disabled messages appear for agent-assigned devices.
2026-02-05 09:25:29 -06:00
e0592dbf3d
fix: use agent-collected data for live polling when agent assigned
Modified GraphLive live polling to check for agent assignments and use
agent-collected sensor data from the database instead of doing direct
SNMP polling. This respects the agent assignment and prevents Phoenix
from bypassing the agent for live graph updates.

Also refactored handle_params to reduce nesting depth.
2026-02-05 09:21:43 -06:00
dd8c97a45f
fix: handle devices without sites in GraphLive live polling
Devices can belong directly to organizations without sites. Fixed
perform_live_poll to check device.organization_id instead of
device.site.organization_id which caused BadMapError when site is nil.
Also removed unused Repo alias.
2026-02-05 09:16:11 -06:00
912cb8d280
fix: add missing handle_info for state_sensors_updated in GraphLive.Show
Adds handler to reload graph data when state sensors are updated via PubSub,
preventing GenServer crash when viewing graph page during sensor updates.
2026-02-05 09:14:21 -06:00
0d6caa897f
refactor: remove debug logging from SNMPv3 credential handling
Discovery is now working successfully with SNMPv3 after increasing
agent timeout to 60s. Removing the temporary debug logging that was
added to verify credentials were being properly decrypted and sent
to the agent.
2026-02-05 09:00:42 -06:00
b36da6f327
debug: log SNMPv3 credential lengths sent to agent
Added logging to show what SNMPv3 parameters are being sent from Phoenix
to the agent, including password lengths to verify they're decrypted
properly.
2026-02-05 08:35:09 -06:00
ae64ec2576
feat: add automatic polling job cleanup on deployment
Added JobCleanupTask that runs on production startup to cancel all
existing polling jobs and reschedule them with the latest worker code.

This ensures:
- No stale jobs run with old code after deployment (e.g., missing SNMPv3 credentials)
- All devices get fresh polling jobs with current logic
- Production deployments are seamless without manual intervention

The task:
- Only runs in production environment (skipped in dev/test)
- Runs asynchronously after supervisor initialization
- Cancels all DevicePollerWorker and DeviceMonitorWorker jobs
- Reschedules polling for all SNMP-enabled devices
- Is idempotent and safe to run multiple times

Removed debug logging from Client and DevicePollerWorker as SNMPv3
implementation is now verified and working correctly.
2026-02-05 08:28:27 -06:00
197d52096d
debug: add DevicePollerWorker client opts logging
Added logging to show what options are being built in DevicePollerWorker,
including whether security_name (v3) or community (v2c) credentials are
being included.
2026-02-05 08:18:35 -06:00
e1c4930bea
debug: add SNMPv3 parameter logging
Added info-level logging to show what parameters are being passed to
SnmpKit for SNMPv3 requests. This will help diagnose encoding errors.
2026-02-05 08:14:49 -06:00
d1ff581d85
fix: add SNMPv3 credentials to DevicePollerWorker
The DevicePollerWorker was only passing SNMPv2c credentials (community
string) to the SNMP client, causing KeyError when polling SNMPv3 devices.

Changes:
- Updated build_client_opts in device_poller_worker.ex to detect version "3"
- Added same SNMPv3 credential handling as discovery.ex
- Includes: security_name, security_level, auth_protocol, auth_password,
  priv_protocol, priv_password

This completes the SNMPv3 implementation - both discovery and polling now
support v3 authentication alongside the existing v2c support.
2026-02-05 08:07:11 -06:00
129bc9a8f4
fix: handle nil site in graph_live access check
Similar to previous fix - use device.organization_id directly instead of
device.site.organization_id to avoid BadMapError when device has no site.
2026-02-04 18:25:48 -06:00
95c6f53d15
fix: handle nil sys_descr in profile matching
Fixes FunctionClauseError when sys_descr is nil during device profile
matching. Added nil check before attempting String.contains? comparison.
2026-02-04 18:22:12 -06:00
ff89415ed3
feat: add SNMPv3 support to discovery and SNMP client
The discovery process was only passing SNMPv2c credentials (community
string) to the SNMP client, causing SNMPv3 devices to fail discovery
even when v3 credentials were configured.

Changes:
- Updated build_client_opts in discovery.ex to include SNMPv3 credentials
  (security_name, security_level, auth_protocol, auth_password,
   priv_protocol, priv_password) when version is "3"
- Updated build_snmp_opts in client.ex to pass v3 credentials to SnmpKit
- Added parse functions for security_level, auth_protocol, and priv_protocol
- Updated connection_opts typespec to include v3 parameters
- Fixed parameter names to match SnmpKit expectations (security_name vs sec_name)

This enables full SNMPv3 discovery on devices like MikroTik routers that
use v3 authentication, allowing them to discover the same sensors and
data that SNMPv2c would show.
2026-02-04 18:18:37 -06:00
8e0fad599e
fix: handle nil site in list_neighbors_with_equipment
Fixes BadMapError when loading device show page for devices without
an assigned site. Changed to use device.organization_id directly
instead of device.site.organization_id since site can be nil but
organization is always present.
2026-02-04 18:09:36 -06:00
c70b385db1
refactor: consolidate reorder handlers in device_live
Extract common pattern from perform_site_reorder/4 and perform_device_reorder/4
into a single perform_reorder/4 helper function.

Both reorder handlers now delegate to the shared helper, reducing duplication
and making the reload/regroup/flash pattern consistent.

- Reduces code from 40 lines to 28 lines
- Maintains identical behavior (all tests pass)
- Uses static gettext strings to satisfy compile-time requirements
2026-02-04 18:02:13 -06:00
aa0e7f3d05
help update 2026-02-04 17:56:48 -06:00
6f6627a54d
refactor: consolidate device count calculations in agent_live
Remove duplicate device counting logic by consolidating three identical
implementations into a single reusable function.

**Changes:**
- Refactor agent_live/index.ex mount/3 to use calculate_device_counts/1
- Delete duplicate calculate_cloud_poller_counts/1 function
- Replace all usages with calculate_device_counts/1

**Before:**
- Lines 31-47: Inline counting for agent_tokens and cloud_pollers
- Lines 312-318: calculate_device_counts/1
- Lines 320-326: calculate_cloud_poller_counts/1 (identical logic)

**After:**
- Single calculate_device_counts/1 function used everywhere
- ~20 lines removed

**Benefits:**
- Eliminates triple duplication of counting logic
- Single source of truth for device counting
- Easier to maintain and test

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-02-04 17:53:18 -06:00
6aab59dcf5
refactor: centralize LiveView access control checks
Extract duplicate access control logic from multiple LiveView files into
a reusable AccessControl helper module.

**Changes:**
- NEW: lib/towerops_web/live/helpers/access_control.ex
- NEW: test/towerops_web/live/helpers/access_control_test.exs
- Refactor device_live/index.ex to use AccessControl.verify_site_access/2
  and verify_device_access/2
- Refactor device_live/form.ex to use AccessControl.verify_device_access/2
- Refactor alert_live/index.ex to use AccessControl.verify_alert_access/2
- Refactor device_live/show.ex to use AccessControl.verify_device_access/2
- Remove unused Repo aliases from refactored files

**Benefits:**
- Reduces code duplication across 7+ locations
- Centralizes security-critical access checks
- Improves testability (helper module has >90% coverage)
- Consistent error handling across all LiveViews

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-02-04 17:51:05 -06:00
d16f09666f
snmp v3 ui fixes 2026-02-04 17:41:15 -06:00
dbb2412e89
tcp snmp test 2026-02-04 17:28:14 -06:00
89911bae9c
Device list bug fix 2026-02-04 16:49:17 -06:00
4f936d5549
device edit fix 2026-02-04 16:28:15 -06:00