Files
EvoFirewall/apps/api/src/routes/ui-summary.ts
T
Denozordec 9fa6269174
quality / changes (push) Successful in 7s
quality / commitlint (push) Skipped
quality / openapi (push) Successful in 30s
quality / web (push) Successful in 1m3s
quality / api (push) Successful in 1m15s
quality / linux-agent-smoke (push) Failing after 27s
quality / docker-check (push) Failing after 2m28s
CD / quality (push) Failing after 5m52s
CD / publish (push) Skipped
feat: harden firewall policies telemetry and operations
Implement reliable agent apply, telemetry v2, analytics retention, guarded rollouts, enrollment, labels and webhooks. Improve ReUI frontend data safety and bounded list loading, and add Docker smoke, recovery and resource checks. Document local verification and pending native acceptance.
2026-10-26 13:10:58 +07:00

33 lines
2.3 KiB
TypeScript

import type { FastifyPluginAsync } from 'fastify'
import { hasPermission, type UiSummary } from '@evofw/shared'
/** Authentication comes from the protected API scope; each section is independent. */
export const uiSummaryRoutes: FastifyPluginAsync = async (app) => {
app.get('/ui/summary', async (req) => {
const now = new Date()
const summary: UiSummary = { updated_at: now.toISOString() }
const permissions = req.authUser?.permissions ?? []
const allowed = (section: string) => req.authUser?.isAdmin || hasPermission(permissions, `fw:${section}:read`)
const agentsAllowed = allowed('agents'), dashboardAllowed = allowed('dashboard')
let counts: { total: number; approved: number; online: number; pending: number; dropped: number; accepted: number } | undefined
if (agentsAllowed || dashboardAllowed) {
counts = app.sqlite.prepare(`SELECT count(*) AS total,
coalesce(sum(status='approved'),0) AS approved,
coalesce(sum(status='approved' AND last_seen_at >= ?),0) AS online,
coalesce(sum(status='pending'),0) AS pending,
coalesce(sum(total_packets_dropped),0) AS dropped,
coalesce(sum(total_packets_accepted),0) AS accepted FROM agents`).get(new Date(now.getTime() - 5 * 60_000).toISOString()) as typeof counts
}
let lists: { total: number; failed: number } | undefined
if (allowed('lists') || dashboardAllowed) lists = app.sqlite.prepare("SELECT count(*) AS total, coalesce(sum(last_error IS NOT NULL AND last_error != ''),0) AS failed FROM ip_lists").get() as typeof lists
if (agentsAllowed && counts) summary.agents = { approved: counts.approved, online: counts.online, pending: counts.pending, offline: counts.approved - counts.online }
if (allowed('lists') && lists) summary.lists = lists
if (dashboardAllowed && counts && lists) summary.dashboard = {
agents_total: counts.total, agents_approved: counts.approved, agents_online: counts.online, agents_pending: counts.pending,
packets_dropped: counts.dropped, packets_accepted: counts.accepted, lists_total: lists.total,
}
if (allowed('policies')) summary.policies = app.sqlite.prepare('SELECT (SELECT count(*) FROM policy_sets) AS sets_total, (SELECT count(*) FROM policy_rules) AS rules_total').get() as UiSummary['policies']
return summary
})
}