From ad992f3f6d9f9080d0b0d78133dea34f09a77aa7 Mon Sep 17 00:00:00 2001 From: lorentz Date: Fri, 10 Jul 2026 19:46:34 -0400 Subject: [PATCH] feat(11-02): add fire-and-forget POST/GET /api/pax8/sync route MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - POST returns 409 if a sync is already in progress, otherwise starts Pax8SyncService.fullSync() without awaiting and returns immediately - GET reports inProgress, non-deleted row counts across the three PAX8 tables, and the last 10 sync_history rows for entity_type='pax8' - Route stays behind the session-cookie check (not added to middleware.ts's public allowlist) — matches itglue/veeam sync routes --- app/api/pax8/sync/route.ts | 54 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 54 insertions(+) create mode 100644 app/api/pax8/sync/route.ts diff --git a/app/api/pax8/sync/route.ts b/app/api/pax8/sync/route.ts new file mode 100644 index 0000000..60ae938 --- /dev/null +++ b/app/api/pax8/sync/route.ts @@ -0,0 +1,54 @@ +import { NextRequest, NextResponse } from 'next/server'; +import { getPax8SyncService } from '@/lib/services/pax8-sync-service'; +import postgresClient from '@/lib/services/postgres-client'; + +export async function POST(req: NextRequest) { + const body = await req.json().catch(() => ({})); + const triggeredBy = body.triggeredBy || 'manual'; + + const svc = getPax8SyncService(); + if (svc.isSyncInProgress()) { + return NextResponse.json({ error: 'Sync already in progress' }, { status: 409 }); + } + + // Fire and forget — return immediately, sync runs in background + svc.fullSync(triggeredBy).catch(err => + console.error('[Pax8Sync] Background sync error:', err.message) + ); + + return NextResponse.json({ ok: true, message: 'PAX8 sync started' }); +} + +export async function GET() { + try { + const svc = getPax8SyncService(); + const inProgress = svc.isSyncInProgress(); + + const counts = await postgresClient.query(` + SELECT + (SELECT COUNT(*) FROM pax8_companies WHERE is_deleted = false) AS companies, + (SELECT COUNT(*) FROM pax8_subscriptions WHERE is_deleted = false) AS subscriptions, + (SELECT COUNT(*) FROM pax8_products WHERE is_deleted = false) AS products + `); + + const history = await postgresClient.query( + `SELECT id, sync_type, status, started_at, completed_at, + records_added, records_updated, records_deleted, error_message, triggered_by + FROM sync_history + WHERE entity_type = 'pax8' + ORDER BY started_at DESC + LIMIT 10` + ); + + return NextResponse.json({ + inProgress, + counts: counts.rows[0], + history: history.rows, + }); + } catch (err) { + return NextResponse.json( + { error: err instanceof Error ? err.message : 'Failed to get PAX8 sync status' }, + { status: 500 } + ); + } +}