Commit graph

386 commits

Author SHA1 Message Date
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
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
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
6689eb27b4
prevent poller fallback when assigned poller is selected 2026-02-04 16:24:19 -06:00
031880c954
fix device page 2026-02-04 16:17:07 -06:00
fc66109f19
Add default organization feature
Features:
- Users can set a default organization in org settings
- First organization created is automatically set as default
- Default org selector only shown if user has 2+ organizations
- Migration auto-sets first org as default for existing users

Database changes:
- Added is_default boolean field to organization_memberships
- Added partial index for efficient default org lookups
- Migration sets oldest org as default for each existing user

UI changes:
- Organization settings page shows default org toggle
- Green badge displayed when org is already default
- Button to set org as default if it's not current default

Backend:
- Organizations.set_default_organization/2 manages defaults
- Organizations.get_default_membership/1 retrieves default
- Auto-set first org as default in create_organization/3
2026-02-04 16:09:43 -06:00
7a1317d0d0
API: Default organization_id to authenticated org when empty
When creating devices via API, organization_id is now properly defaulted
to the authenticated organization's ID even when an empty string is sent.

This fixes Terraform provider compatibility where organization_id may be
sent as "" instead of omitted entirely.

Before: Only checked if key was missing (Map.has_key?)
After: Also handles nil and empty string values
2026-02-04 16:03:20 -06:00
8382b5df6a
make devices site optional and many test fix 2026-02-04 15:05:42 -06:00
1a054fd598
make sites optional 2026-02-04 13:05:32 -06:00
4ef4f4fbf6
handle exceptions gracefully on api endpoints 2026-02-04 12:18:14 -06:00
09f2907ede
Remove debugging logs, fix Mix.env call 2026-02-04 12:10:48 -06:00
c93144cc37
snmp v3 support 2026-02-04 12:02:38 -06:00
3583624e0d
fix: prevent nil boolean error in device edit page
Fixed production bug where visiting the edit page for a device with no
SNMP discovery data would crash with "expected boolean on left-side of
'and', got: nil" error.

The mikrotik_device?/1 function was using && operator which returns nil
when device.snmp_device is nil, instead of returning false. The template
conditional on line 455 requires a proper boolean value.

Changed from:
  device.snmp_device && (...)

To:
  not is_nil(device.snmp_device) and (...)

This ensures the function always returns a boolean (true/false) instead
of potentially returning nil.

Added regression test to verify the page renders correctly when a device
has no SNMP discovery data yet.

Fixes: https://app.honeybadger.io/projects/136860/faults/127120431
2026-02-03 15:32:51 -06:00
ecf6edaf85
update docker compose instructions 2026-02-03 13:00:51 -06:00
c198fdeaa1
add snmp port 2026-02-03 12:51:50 -06:00
88d549cd92
more tests 2026-02-03 12:03:54 -06:00
5b84844e97
doc update 2026-02-03 09:07:06 -06:00
e181c17f78
fix: resolve MikroTik sensor values and empty SNMP communities
Fixes three critical issues affecting agent-based SNMP polling:

1. MikroTik gauge sensors incorrectly showing 10x values (223°C instead of 22.3°C)
   - Root cause: get_int_value() rejected zero values, causing mtxrGaugeUnit
     to be treated as nil and bypassing divisor logic
   - Fixed: Accept zero values and apply correct divisor=10 for temp/voltage/current/power

2. Empty SNMP community strings sent to agents causing authentication failures
   - Root cause: Devices with source="device" but null community weren't falling
     back to organization/site inheritance
   - Fixed: Enhanced resolve_snmp_community() to fall back to inheritance chain
     even when source="device" but community is empty

3. Continuous MikroTik API commands sent every 60s instead of once per 24h
   - Root cause: MikroTik jobs built on every poll cycle, not just discovery
   - Fixed: Only build MikroTik jobs during discovery phase

Migrations update existing data to fix incorrect divisor values and community
source tracking. Added comprehensive debug logging to diagnose future issues.
2026-02-02 17:12:36 -06:00
aaf8c38b78
handle mikrotik ssh 2026-02-02 16:42:18 -06:00
9a60bfec58
feat: migrate user settings and agent management to gettext
Migrates all remaining user settings and agent management flash messages to gettext:

User Settings:
- session_manager.ex: mobile device removal, alert preferences, browser session revocation, revoke all sessions
- totp_manager.ex: device creation, verification, deletion, recovery code generation
- api_token_manager.ex: token creation, deletion, validation errors
- user_auth.ex: policy consent acceptance message

Agent Management:
- agent_live/edit.ex: agent update success
- agent_live/index.ex: agent/cloud poller creation, token regeneration, agent deletion, global default settings

All flash messages now use domain-specific helpers (t_auth, t_equipment) for proper internationalization.

Tests passing: 4165 tests, 0 failures
2026-02-02 13:20:54 -06:00
3633ff1626
more gettext 2026-02-02 13:10:08 -06:00
0d85e85884
feat: migrate all remaining authentication and session flash messages to gettext
Completes the flash message migration by converting all remaining user-facing strings:

**UserAuth.ex changes:**
- All Plug functions: require_sudo_mode, require_authenticated_user, require_superuser
- load_current_organization error messages (3 messages)
- All LiveView on_mount callbacks (8 auth-related messages)
- Policy consent success message

**SessionManager.ex changes:**
- Mobile device session management (2 messages)
- Alert preferences updates (3 messages including conditional)

All messages now use t_auth() for consistent translation in the auth domain.

All tests passing (100 tests).
2026-02-02 13:02:44 -06:00
005235a54a
more gettext 2026-02-02 13:00:27 -06:00
2ef154e4d7
feat: migrate core components to gettext
Migrates consent prompt component strings to gettext for internationalization:
- Updated Policies modal title
- Policy review message
- Policy acceptance checkbox labels (Privacy Policy, Terms of Service)
- Consent disclaimer text
- Accept button text

All strings now use gettext() for translation support.
2026-02-02 12:49:13 -06:00
a995e6283f
feat: migrate admin features to gettext
Migrates all admin-related flash messages to gettext for internationalization:
- Admin.UserLive.Index: user deletion messages
- Admin.OrgLive.Index: organization deletion messages
- UserAuth: impersonation start/stop messages

Adds Gettext backend and helpers import to UserAuth module to support translations.

All admin and auth tests passing (87 tests).
2026-02-02 12:45:38 -06:00
89a076fb67
feat: migrate equipment features to gettext and fix sudo mode tests
Migrates all equipment-related flash messages to gettext for internationalization:
- DeviceLive.Index: device discovery, reordering, error messages
- DeviceLive.Show: backup messages, permission errors
- DeviceLive.Form: device CRUD operations
- AlertLive.Index: alert acknowledgment messages
- SiteLive.Show: site discovery messages
- SiteLive.Form: site CRUD and SNMP/agent propagation

Fixes sudo mode test suite issues:
- Updates Accounts.sudo_mode?/2 test to check last_sudo_at instead of authenticated_at
- Adds register_and_log_in_user_with_sudo/1 helper to ConnCase
- Fixes UserAuth sudo mode tests to use session-based authentication
- Updates MyData tests to use sudo mode (now required per router config)
- Removes obsolete UserSettingsLive "without sudo mode" test
- Fixes grant_sudo_mode/1 DateTime truncation to seconds

All equipment module tests passing. Ready for Phase 3 (Admin Features).
2026-02-02 12:41:58 -06:00
f6195cd5c6
sudo redirect fix 2026-02-02 10:54:32 -06:00
949cfaf4e0
add default org 2026-02-02 10:45:37 -06:00
1ba55ce451
mikrotik api update 2026-02-02 10:35:44 -06:00
bd6dad85e1
purge more passkey and gettext updates 2026-02-02 10:20:59 -06:00
02413d5cfd
cleanup forms 2026-02-02 10:07:37 -06:00
5d5116681a
device edit refactor 2026-02-02 10:00:00 -06:00
19b635c46f
feat: migrate email templates to gettext (Phase 2 - Emails)
- Update UserNotifier to use t_email() helper for all email content
- Extract 4 email subjects and 4 email bodies to emails domain:
  * Update email instructions
  * Log in instructions
  * Confirmation instructions
  * Reset password instructions
- Replace string interpolation with Gettext bindings (%{email}, %{url})
- Auto-populate English translations (msgstr = msgid)
- All emails now ready for future internationalization

First module migration complete per design plan.
2026-02-02 09:48:30 -06:00
cfb60cd186
feat: implement gettext infrastructure (Phase 1)
- Create domain-specific .pot template files (auth, equipment, admin, emails)
- Create corresponding English .po files for all domains
- Add GettextHelpers module with domain-specific helper functions:
  * t() for default domain (common UI)
  * t_auth() for authentication flows
  * t_equipment() for equipment/device management
  * t_admin() for admin features
  * t_email() for email templates
- Import GettextHelpers in html_helpers() for automatic availability in LiveViews/components
- Add mix populate_english task to auto-fill English translations (msgstr = msgid)
- Task properly handles plural forms and multi-line strings

Phase 1 complete - infrastructure ready for module-by-module migration.
2026-02-02 09:40:32 -06:00
f938b263cd
Mikrotik backup and gettext start 2026-02-02 09:11:22 -06:00
a48cb292da
update org settings layout 2026-02-01 17:42:14 -06:00
1414f087fc
bug fix 2026-02-01 17:25:16 -06:00
1790db8d5a
update mikrotik port with/without ssl 2026-02-01 17:09:52 -06:00
23af86ba73
add mikrotik device handling 2026-02-01 16:42:39 -06:00
a66aec675e Merge branch 'gmcintire/user-first-last-name' into 'main'
Replace single name field with first_name and last_name

See merge request towerops/towerops!3
2026-02-01 15:00:24 -06:00
e46ecbfca2 Replace single name field with first_name and last_name 2026-02-01 15:00:23 -06:00