- 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
68 lines
2.3 KiB
TypeScript
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 });
|
|
}
|
|
}
|