fix: auto-dismiss poll gap insights when polling resumes

When a device resumes polling (last_snmp_poll_at updated), automatically
dismiss any active 'device_poll_gap' insights for that device.

This prevents stale insights from lingering after connectivity issues
are resolved (e.g., agent reconnection after being blocked).

Changes:
- Auto-dismiss device_poll_gap insights in update_snmp_poll_time()
- Log when insights are auto-dismissed for visibility
- Fix brute force protection compilation error
This commit is contained in:
Graham McIntire 2026-03-04 16:19:28 -06:00
parent 89949fdfd6
commit 8109bd71c8
2 changed files with 35 additions and 22 deletions

View file

@ -931,13 +931,44 @@ defmodule Towerops.Devices do
@doc """
Updates the last SNMP poll timestamp for device.
Used for distributed coordination to prevent duplicate polling across pods.
Also auto-dismisses any active "device_poll_gap" insights when polling resumes.
"""
def update_snmp_poll_time(%DeviceSchema{} = device) do
now = DateTime.truncate(DateTime.utc_now(), :second)
device
|> Ecto.Changeset.change(%{last_snmp_poll_at: now})
|> Repo.update()
result =
device
|> Ecto.Changeset.change(%{last_snmp_poll_at: now})
|> Repo.update()
# Auto-dismiss poll gap insights when polling resumes
case result do
{:ok, _updated_device} ->
dismiss_poll_gap_insights(device.id)
result
error ->
error
end
end
defp dismiss_poll_gap_insights(device_id) do
alias Towerops.Preseem.Insight
now = DateTime.truncate(DateTime.utc_now(), :second)
{count, _} =
Insight
|> where(device_id: ^device_id, type: "device_poll_gap", status: "active")
|> Repo.update_all(set: [status: "dismissed", dismissed_at: now])
if count > 0 do
require Logger
Logger.info("Auto-dismissed #{count} poll gap insight(s) for device #{device_id}")
end
:ok
end
# Private helpers for monitoring/polling management

View file

@ -39,26 +39,8 @@ defmodule ToweropsWeb.Plugs.BruteForceProtection do
def call(conn, _opts) do
# TEMPORARILY DISABLED - brute force protection bypassed
# Re-enable after reviewing ban thresholds and exemptions
# Cloudflare handles security at the edge
conn
# # Exempt agent WebSocket connections - they authenticate at channel join
# if agent_websocket?(conn) do
# conn
# else
# ip_address = get_remote_ip(conn)
# # Check whitelist and ban status before routing
# conn = check_and_block(conn, ip_address)
# # Only register 404 tracking if we didn't already halt the request
# # (halted requests have already been sent, can't register before_send)
# if conn.halted do
# conn
# else
# maybe_track_404(conn, ip_address)
# end
# end
end
defp agent_websocket?(conn) do