wulf-pulse/app/api/zabbix/hosts/route.ts
lorentz c518eefdb2 feat: Morning NOC Summary adaptive card for Teams
- Add MorningSummaryService with Zabbix aggregation and adaptive card builder
- Add webhook delivery system with Teams incoming webhooks
- Add admin UI at /admin/morning-summary for webhook/config management
- Add API routes: /send, /test, /webhooks, /webhooks/[id], /config, /history
- Register morning-summary cron job in SyncScheduler (Mon-Fri 6:30 AM)
- Add outages_only filter (Unavailable triggers only)
- Fix host resolution: use getTriggerEnabledHosts to exclude disabled hosts
- Fix resolved events: event.get value:1 scoped to window with r_eventid filter
- Remove emojis from fact rows and section headers in card
- Remove Open Zabbix button (duplicate of View Problems)
- Add migrations: morning_summary_config + morning_summaries tables
- Add outages_only column to morning_summary_config
2026-03-11 09:34:51 -04:00

68 lines
2.3 KiB
TypeScript

/**
* GET /api/zabbix/hosts — list all hosts with full detail + RMM match status
* DELETE /api/zabbix/hosts — bulk delete by hostid array
*/
import { NextRequest, NextResponse } from 'next/server';
import { ZabbixClient } from '@/lib/services/zabbix-client';
import { postgresClient } from '@/lib/services/postgres-client';
function makeZabbix() {
if (!process.env.ZABBIX_API_URL || !process.env.ZABBIX_API_TOKEN) {
throw new Error('Zabbix is not configured. Set ZABBIX_API_URL and ZABBIX_API_TOKEN.');
}
return new ZabbixClient({
apiUrl: process.env.ZABBIX_API_URL!,
apiToken: process.env.ZABBIX_API_TOKEN!,
});
}
export async function GET() {
try {
const zabbix = makeZabbix();
const hosts = await zabbix.getHosts();
// Load all known RMM site UIDs from Postgres for mismatch detection
const res = await postgresClient.query<{ rmm_site_uid: string }>(
'SELECT rmm_site_uid FROM rmm_site_mappings'
);
const knownSiteUids = new Set(res.rows.map((r) => r.rmm_site_uid));
// Annotate each host with rmmMatched flag
const annotated = hosts.map((h) => {
const rmmUidMacro = h.macros?.find((m) => m.macro === '{$RMM_SITE_UID}');
const sourceTag = h.tags?.find((t) => t.tag === 'source')?.value ?? null;
let rmmMatched: boolean | null = null;
if (sourceTag === 'datto-rmm') {
rmmMatched = rmmUidMacro ? knownSiteUids.has(rmmUidMacro.value) : false;
}
return { ...h, rmmMatched, sourceTag };
});
return NextResponse.json({ hosts: annotated });
} catch (error) {
console.error('[GET /api/zabbix/hosts]', error);
return NextResponse.json({ error: String(error) }, { status: 500 });
}
}
export async function DELETE(request: NextRequest) {
try {
const body = await request.json();
const { hostids } = body as { hostids: string[] };
if (!Array.isArray(hostids) || hostids.length === 0) {
return NextResponse.json({ error: 'hostids array is required' }, { status: 400 });
}
const zabbix = makeZabbix();
await zabbix.deleteHosts(hostids);
return NextResponse.json({ deleted: hostids.length });
} catch (error) {
console.error('[DELETE /api/zabbix/hosts]', error);
return NextResponse.json({ error: String(error) }, { status: 500 });
}
}