77 lines
3.1 KiB
TypeScript
77 lines
3.1 KiB
TypeScript
|
|
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 });
|
||
|
|
}
|
||
|
|
}
|