import type { FastifyPluginAsync } from 'fastify' import { repos } from '@evofw/db' import { AppError } from '../plugins/error-handler.js' import type { AppConfig } from '../config.js' import { auditMutation } from '../services/audit.js' import { mapAgent } from '../services/row-mappers.js' export const statsRoutes: FastifyPluginAsync<{ config: AppConfig }> = async ( app, opts, ) => { const { config } = opts app.get<{ Params: { id: string } }>('/agents/:id/stats', async (req) => ({ items: repos.listStatsSamples(app.db, req.params.id).map((s) => ({ id: s.id, agent_id: s.agentId, packets_dropped: s.packetsDropped, packets_accepted: s.packetsAccepted, prefix_count: s.prefixCount, kernel_method: s.kernelMethod, recorded_at: s.recordedAt, })), })) app.get<{ Params: { id: string } }>( '/agents/:id/blocked-ips', async (req) => { const agent = repos.getAgent(app.db, req.params.id) if (!agent) throw new AppError('NOT_FOUND', 'Agent not found', 404) const items = repos.listIpBlockStats(app.db, agent.id) const portsByIp = repos.mapTopPortsByIp( app.db, agent.id, items.map((s) => s.ip), 5, ) return { items: items.map((s) => ({ ip: s.ip, packets: s.packets, first_seen_at: s.firstSeenAt, last_seen_at: s.lastSeenAt, ports: (portsByIp.get(s.ip) ?? []).map((p) => ({ port: p.port, protocol: p.protocol, packets: p.packets, })), })), } }, ) app.get<{ Params: { id: string } }>( '/agents/:id/blocked-ports', async (req) => { const agent = repos.getAgent(app.db, req.params.id) if (!agent) throw new AppError('NOT_FOUND', 'Agent not found', 404) return { items: repos.listPortBlockStatsAggregate(app.db, agent.id, 50).map((s) => ({ port: s.port, protocol: s.protocol, packets: s.packets, last_seen_at: s.lastSeenAt, })), } }, ) app.post<{ Params: { id: string } }>( '/agents/:id/stats/reset', async (req) => { const agent = repos.getAgent(app.db, req.params.id) if (!agent) throw new AppError('NOT_FOUND', 'Agent not found', 404) const updated = repos.updateAgent(app.db, agent.id, { lastApplyPacketsDropped: 0, lastApplyPacketsAccepted: 0, totalPacketsDropped: 0, totalPacketsAccepted: 0, }) repos.deleteStatsSamplesForAgent(app.db, agent.id) repos.deleteIpBlockStatsForAgent(app.db, agent.id) repos.deletePortBlockStatsForAgent(app.db, agent.id) auditMutation(app, config, req, { action: 'agent.stats_reset', severity: 'info', targetType: 'app_resource', targetId: agent.id, summary: `Сброшена статистика counters агента ${agent.name}`, details: { agent_id: agent.id }, }) return mapAgent(updated!) }, ) app.get('/stats/recent', async () => ({ items: repos.listRecentStats(app.db).map((s) => ({ id: s.id, agent_id: s.agentId, packets_dropped: s.packetsDropped, packets_accepted: s.packetsAccepted, prefix_count: s.prefixCount, kernel_method: s.kernelMethod, recorded_at: s.recordedAt, })), })) }