towerops/lib/towerops_web/live/admin/dashboard_live.ex
Graham McIntire 6fa0b791f2 fix: M3, M6, M7, M12 — open redirect, overfetch, missing constraint, admin defense-in-depth
- M3: Replace blacklist-based valid_return_path? with whitelist of known app
  route prefixes, plus URI decoding to prevent %2f encoding bypasses
- M6: Replace full-org device/link load in get_node_detail with targeted
  join query filtering by discovered node fields
- M7: Add partial unique index on (organization_id, email) for pending
  invitations (WHERE accepted_at IS NULL)
- M12: Add on_mount superuser verification to all 7 admin LiveViews for
  defense-in-depth
2026-05-12 12:46:16 -05:00

56 lines
1.4 KiB
Elixir

defmodule ToweropsWeb.Admin.DashboardLive do
@moduledoc """
Admin dashboard showing system statistics and recent audit logs.
"""
use ToweropsWeb, :live_view
alias Towerops.Admin
on_mount {ToweropsWeb.UserAuth, :require_superuser}
@impl true
def mount(_params, _session, socket) do
{:ok,
socket
|> assign(:page_title, t("Dashboard"))
|> assign(:timezone, socket.assigns.current_scope.timezone)}
end
@impl true
def handle_params(params, _url, socket) do
page =
case Integer.parse(Map.get(params, "page", "1")) do
{n, _} when n >= 1 -> n
_ -> 1
end
per_page = 10
user_count = Admin.count_users()
org_count = Admin.count_organizations()
# Fetch all recent logs (limited to last 100 for performance)
all_logs = Admin.list_audit_logs(limit: 100)
total_count = length(all_logs)
total_pages = ceil(total_count / per_page)
# Ensure page is within valid range
page = max(1, min(page, max(1, total_pages)))
# Slice logs for current page
offset = (page - 1) * per_page
recent_logs = Enum.slice(all_logs, offset, per_page)
{:noreply,
socket
|> assign(:user_count, user_count)
|> assign(:org_count, org_count)
|> assign(:recent_logs, recent_logs)
|> assign(:pagination, %{
page: page,
per_page: per_page,
total_count: total_count,
total_pages: total_pages
})}
end
end