wulf-pulse/app/api/duo/status/route.ts
lorentz 72bdc6a241 feat: Add Duo Security card to /admin/sync overview + detail page
- Added Duo card to sync overview grid (category: 2FA/MFA, green)
- Shows accounts, users, phones, auth logs counts + bypass/disabled warning
- Created /admin/sync/duo detail page with:
  - Stat cards (accounts, users, phones, auth logs, groups, integrations)
  - Parent account summary
  - Child accounts table with user counts, matched company, sync time
  - Sync Now button with polling for completion
- Created GET /api/duo/status endpoint (counts + last sync + bypass count)
- Added duo.ico logo
2026-03-27 11:14:49 -04:00

42 lines
1.6 KiB
TypeScript

import { NextResponse } from 'next/server';
import { postgresClient } from '@/lib/services/postgres-client';
import { duoSyncService } from '@/lib/services/duo-sync-service';
export async function GET() {
try {
const [accounts, users, phones, authLogs, groups, integrations, lastSync] = await Promise.all([
postgresClient.query(`SELECT COUNT(*) as cnt FROM duo_accounts WHERE is_parent = false`),
postgresClient.query(`SELECT COUNT(*) as cnt FROM duo_users`),
postgresClient.query(`SELECT COUNT(*) as cnt FROM duo_phones`),
postgresClient.query(`SELECT COUNT(*) as cnt FROM duo_auth_logs`),
postgresClient.query(`SELECT COUNT(*) as cnt FROM duo_groups`),
postgresClient.query(`SELECT COUNT(*) as cnt FROM duo_integrations`),
postgresClient.query(`SELECT MAX(synced_at) as last_sync FROM duo_accounts`),
]);
const bypassed = await postgresClient.query(
`SELECT COUNT(*) as cnt FROM duo_users WHERE status IN ('bypass', 'disabled')`
);
return NextResponse.json({
connected: true,
syncing: duoSyncService.isSyncInProgress(),
lastSync: lastSync.rows[0]?.last_sync ?? null,
counts: {
accounts: Number(accounts.rows[0].cnt),
users: Number(users.rows[0].cnt),
phones: Number(phones.rows[0].cnt),
authLogs: Number(authLogs.rows[0].cnt),
groups: Number(groups.rows[0].cnt),
integrations: Number(integrations.rows[0].cnt),
bypassed: Number(bypassed.rows[0].cnt),
},
});
} catch (error: any) {
return NextResponse.json({
connected: false,
error: error.message,
counts: {},
});
}
}