wulf-pulse/app/api/sentinelone/coverage/route.ts

77 lines
3.1 KiB
TypeScript
Raw Normal View History

import { NextResponse } from 'next/server';
import { postgresClient } from '@/lib/services/postgres-client';
export async function GET() {
try {
const result = await postgresClient.query(`
SELECT
s.id AS s1_site_id,
s.name AS s1_site_name,
s.state,
s.sku,
s.health_status,
s.active_licenses,
m.company_id,
m.company_name,
COALESCE(a.total_agents, 0) AS total_agents,
COALESCE(a.active_agents, 0) AS active_agents,
COALESCE(a.infected_agents, 0) AS infected_agents,
COALESCE(a.outdated_agents, 0) AS outdated_agents,
COALESCE(a.decommissioned_agents, 0) AS decommissioned_agents,
a.last_seen,
COALESCE(t.total_threats, 0) AS total_threats,
COALESCE(t.active_threats, 0) AS active_threats
FROM s1_sites s
LEFT JOIN s1_company_mappings m ON m.s1_site_id = s.id
LEFT JOIN (
SELECT
site_id,
COUNT(*) AS total_agents,
COUNT(*) FILTER (WHERE is_active = true) AS active_agents,
COUNT(*) FILTER (WHERE infected = true) AS infected_agents,
COUNT(*) FILTER (WHERE is_up_to_date = false) AS outdated_agents,
COUNT(*) FILTER (WHERE is_decommissioned = true) AS decommissioned_agents,
MAX(updated_at) AS last_seen
FROM s1_agents
WHERE is_decommissioned = false
GROUP BY site_id
) a ON a.site_id = s.id
LEFT JOIN (
SELECT
site_id,
COUNT(*) AS total_threats,
COUNT(*) FILTER (WHERE mitigation_status NOT IN
('mitigated','marked_as_benign','marked_as_false_positive')) AS active_threats
FROM s1_threats
GROUP BY site_id
) t ON t.site_id = s.id
ORDER BY s.name
`);
const rows = result.rows.map((r: any) => ({
...r,
total_agents: Number(r.total_agents),
active_agents: Number(r.active_agents),
infected_agents: Number(r.infected_agents),
outdated_agents: Number(r.outdated_agents),
decommissioned_agents: Number(r.decommissioned_agents),
total_threats: Number(r.total_threats),
active_threats: Number(r.active_threats),
}));
const summary = {
totalSites: rows.length,
mappedSites: rows.filter((r: any) => r.company_id).length,
totalAgents: rows.reduce((s: number, r: any) => s + r.total_agents, 0),
infectedAgents: rows.reduce((s: number, r: any) => s + r.infected_agents, 0),
activeThreats: rows.reduce((s: number, r: any) => s + r.active_threats, 0),
outdatedAgents: rows.reduce((s: number, r: any) => s + r.outdated_agents, 0),
};
return NextResponse.json({ sites: rows, summary });
} catch (error) {
const msg = error instanceof Error ? error.message : String(error);
return NextResponse.json({ error: msg }, { status: 500 });
}
}