/** * QBO Sync API * POST /api/qbo/sync — trigger a full or incremental sync * GET /api/qbo/sync — get last sync status and record counts */ import { NextRequest, NextResponse } from 'next/server'; import { QboSyncService } from '@/lib/services/qbo-sync-service'; import { QboClient } from '@/lib/services/qbo-client'; import postgresClient from '@/lib/services/postgres-client'; export async function POST(request: NextRequest) { try { const body = await request.json().catch(() => ({})); const syncType: 'full' | 'incremental' = body.syncType === 'incremental' ? 'incremental' : 'full'; const triggeredBy = body.triggeredBy || 'api'; const client = new QboClient(); const service = new QboSyncService(client); if (service.isSyncInProgress()) { return NextResponse.json({ error: 'QBO sync already in progress' }, { status: 409 }); } // Run async — return immediately service[syncType === 'full' ? 'fullSync' : 'incrementalSync'](triggeredBy).catch((err) => { console.error('[QBO Sync API] Sync failed:', err); }); return NextResponse.json({ message: `QBO ${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 [invoices, payments, deposits, transactions, reports] = await Promise.all([ postgresClient.query(`SELECT COUNT(*) as count, MAX(synced_at) as last_sync FROM qbo_invoices`), postgresClient.query(`SELECT COUNT(*) as count, MAX(synced_at) as last_sync FROM qbo_payments`), postgresClient.query(`SELECT COUNT(*) as count, MAX(synced_at) as last_sync FROM qbo_deposits`), postgresClient.query(`SELECT COUNT(*) as count, MAX(synced_at) as last_sync FROM qbo_transactions`), postgresClient.query(`SELECT COUNT(*) as count, MAX(synced_at) as last_sync FROM qbo_reports`), ]); // Check token status let tokenStatus: 'valid' | 'expired' | 'missing' = 'missing'; try { const client = new QboClient(); const token = await client.loadToken(); if (token) { tokenStatus = new Date() < new Date(token.access_token_expires_at) ? 'valid' : 'expired'; } } catch { tokenStatus = 'missing'; } return NextResponse.json({ tokenStatus, counts: { invoices: parseInt(invoices.rows[0]?.count || '0'), payments: parseInt(payments.rows[0]?.count || '0'), deposits: parseInt(deposits.rows[0]?.count || '0'), transactions: parseInt(transactions.rows[0]?.count || '0'), reports: parseInt(reports.rows[0]?.count || '0'), }, lastSync: { invoices: invoices.rows[0]?.last_sync ?? null, payments: payments.rows[0]?.last_sync ?? null, deposits: deposits.rows[0]?.last_sync ?? null, transactions: transactions.rows[0]?.last_sync ?? null, reports: reports.rows[0]?.last_sync ?? null, }, }); } catch (err) { const msg = err instanceof Error ? err.message : String(err); return NextResponse.json({ error: msg }, { status: 500 }); } }