Cloud pollers (is_cloud_poller = true, organization_id = nil) were
being blocked from polling devices because verify_device_organization
was checking if the device's organization matched the agent's
organization.
Since cloud pollers don't belong to any organization (organization_id
is nil), they should be allowed to poll devices from any organization.
Changes:
- verify_device_organization now allows access if organization_id is nil
- This fixes "Device not in agent's organization" errors for cloud pollers
Error before:
Device b7e8dd99-0b19-496d-97ae-9f2601d16464 not in agent's organization
Production was crashing with CaseClauseError when viewing device edit
form for devices using the global default cloud poller.
Added missing :global case branch to the agent source case statement
in form.html.heex line 127.
Displays: 'Using global default cloud poller: <agent_name>'
Two issues fixed:
1. Cloud pollers (organization_id = nil) were causing access denied errors
when superadmins tried to view them. Now allows access if user is
superadmin and agent is a cloud poller.
2. Ecto.NoResultsError was being raised without required :queryable option,
causing KeyError crash. Now properly raises with queryable parameter.
Error was:
KeyError: key :queryable not found in []
(ecto 3.13.5) lib/ecto/exceptions.ex:203: Ecto.NoResultsError.exception/1
(towerops) lib/towerops_web/live/agent_live/show.ex:16: mount/3
HTML checkboxes send value='on' when checked, not 'true'. Updated
parse_agent_params to accept both 'on' and 'true' values for the
is_cloud_poller field.
Added debug logging to help diagnose future form submission issues.
Added logging to identify which component is failing health checks:
- Log database and Redis status on each health check
- Log Redis errors with full error details
- This will help diagnose the production 503 errors
Fixed dialyzer warnings across multiple files by properly handling return
values from functions that are called for side effects.
Changes:
- lib/towerops/agents.ex: Explicitly match broadcast and if-expression returns
- lib/towerops/devices.ex: Match worker start function returns in if-expressions
- lib/towerops/workers/agent_latency_evaluator.ex: Match broadcast return
- lib/towerops/workers/device_monitor_worker.ex: Match perform_check and handle_status_change returns, handle schedule_next_check errors
- lib/towerops/workers/device_poller_worker.ex: Match all PubSub broadcast returns, handle schedule_next_poll errors, remove unreachable pattern
- lib/towerops/workers/stale_agent_worker.ex: Match process_stale_agents and broadcast returns
- lib/towerops_web/channels/agent_channel.ex: Match PubSub.subscribe return, improve get_addr pattern matching, remove unreachable format_ip clause
- lib/towerops_web/live/device_live/form.ex: Match enqueue_discovery return, replace compile-time @dev_env with runtime check
Pattern applied:
- PubSub broadcasts: `_ = Phoenix.PubSub.broadcast(...)`
- If-expressions with side effects: `_ = if condition do ... end`
- Critical operations: Added error logging with case statements
All 3625 tests passing. No dialyzer warnings remaining in lib/towerops or lib/towerops_web.
🤖 Generated with Claude Code
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Changes:
- Add Oban configuration to config/dev.exs (was only in runtime.exs)
- Replace Exq telemetry metrics with Oban equivalents
- Update publish_exq_stats to publish_oban_stats using Oban.Job queries
- Track queue sizes, executing jobs, and available jobs
This fixes the startup error where Oban config was not available in dev.
- Created new on_mount hook :load_default_organization that automatically loads user's first organization
- Moved sites and devices routes to root path (/ sites, /devices) instead of /orgs/:org_slug
- Updated all navigation links and route references throughout the app
- Dashboard, alerts, and agents still use org-specific routes (/orgs/:org_slug)
- Users can still switch organizations via org selector for other pages