Registers AppGate as a checkConfigOnly integration-health row and public sync route, matching the existing factory + is<Name>Configured() pattern. Committed now so Phase 13's worktree-isolated executors fork from a HEAD that includes this integration-health.ts entry, since Plan 13-02 inserts the PAX8 row immediately after it. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LHRgZqkzBHBbAbc3KHneuR
78 lines
3.7 KiB
TypeScript
78 lines
3.7 KiB
TypeScript
/**
|
|
* AppGate sync API
|
|
*
|
|
* POST /api/appgate/sync — trigger a sync (body: { syncType, triggeredBy })
|
|
* GET /api/appgate/sync — last-sync status + record counts
|
|
*
|
|
* No auth gate: webhook-style entrypoint hit by the in-process scheduler
|
|
* (same pattern as `/api/qbo/sync`). Make sure it's listed in
|
|
* `middleware.ts`'s public path array.
|
|
*/
|
|
|
|
import { NextRequest, NextResponse } from 'next/server';
|
|
import postgresClient from '@/lib/services/postgres-client';
|
|
import { getAppgateSyncService } from '@/lib/services/appgate-sync-service';
|
|
import { isAppgateConfigured } from '@/lib/services/appgate-factory';
|
|
|
|
export async function POST(request: NextRequest) {
|
|
try {
|
|
if (!isAppgateConfigured()) {
|
|
return NextResponse.json(
|
|
{ error: 'AppGate not configured — set APPGATE_URL/USERNAME/PASSWORD/DEVICE_ID env vars' },
|
|
{ status: 503 },
|
|
);
|
|
}
|
|
const body = await request.json().catch(() => ({}));
|
|
const syncType: 'sessions' | 'daily' = body.syncType === 'daily' ? 'daily' : 'sessions';
|
|
const triggeredBy = typeof body.triggeredBy === 'string' ? body.triggeredBy : 'api';
|
|
const svc = getAppgateSyncService();
|
|
if (svc.isSyncInProgress()) {
|
|
return NextResponse.json({ error: 'AppGate sync already in progress' }, { status: 409 });
|
|
}
|
|
(syncType === 'daily' ? svc.dailySync(triggeredBy) : svc.sessionsSync(triggeredBy))
|
|
.catch((e) => console.error('[AppgateSync API] sync failed:', e));
|
|
return NextResponse.json({ message: `AppGate ${syncType} sync started`, triggeredBy });
|
|
} catch (err) {
|
|
const msg = err instanceof Error ? err.message : String(err);
|
|
return NextResponse.json({ error: msg }, { status: 500 });
|
|
}
|
|
}
|
|
|
|
export async function GET() {
|
|
try {
|
|
const [sessions, devices, appliances, licenseUsers, snapshots, lastSync] = await Promise.all([
|
|
postgresClient.query<{ count: string }>(`SELECT COUNT(*)::text as count FROM appgate_active_sessions`),
|
|
postgresClient.query<{ count: string; deleted: string }>(
|
|
`SELECT COUNT(*) FILTER (WHERE is_deleted=false)::text as count,
|
|
COUNT(*) FILTER (WHERE is_deleted=true)::text as deleted
|
|
FROM appgate_devices`),
|
|
postgresClient.query<{ count: string }>(`SELECT COUNT(*) FILTER (WHERE is_deleted=false)::text as count FROM appgate_appliances`),
|
|
postgresClient.query<{ count: string }>(`SELECT COUNT(*)::text as count FROM appgate_license_users`),
|
|
postgresClient.query<{ snapshot_date: string; max_users: number; used_users: number; expiration: string | null }>(
|
|
`SELECT snapshot_date, max_users, used_users, expiration
|
|
FROM appgate_license_snapshots
|
|
ORDER BY snapshot_date DESC LIMIT 1`),
|
|
postgresClient.query<{
|
|
sync_id: string; sync_type: string; status: string; started_at: string; completed_at: string | null; last_error: string | null;
|
|
}>(
|
|
`SELECT sync_id, sync_type, status, started_at, completed_at, last_error
|
|
FROM appgate_sync_history
|
|
ORDER BY started_at DESC LIMIT 1`),
|
|
]);
|
|
return NextResponse.json({
|
|
configured: isAppgateConfigured(),
|
|
counts: {
|
|
active_sessions: parseInt(sessions.rows[0]?.count ?? '0', 10),
|
|
devices: parseInt(devices.rows[0]?.count ?? '0', 10),
|
|
devices_tombstoned: parseInt(devices.rows[0]?.deleted ?? '0', 10),
|
|
appliances: parseInt(appliances.rows[0]?.count ?? '0', 10),
|
|
license_users: parseInt(licenseUsers.rows[0]?.count ?? '0', 10),
|
|
},
|
|
latest_license: snapshots.rows[0] ?? null,
|
|
last_sync: lastSync.rows[0] ?? null,
|
|
});
|
|
} catch (err) {
|
|
const msg = err instanceof Error ? err.message : String(err);
|
|
return NextResponse.json({ error: msg }, { status: 500 });
|
|
}
|
|
}
|