diff --git a/packages/dashboard/src/app/api/deploy/route.ts b/packages/dashboard/src/app/api/deploy/route.ts index 0b089005..3bfdd642 100644 --- a/packages/dashboard/src/app/api/deploy/route.ts +++ b/packages/dashboard/src/app/api/deploy/route.ts @@ -21,6 +21,7 @@ export async function POST(request: NextRequest) { supabaseAccessToken, supabaseProjectRef, stripeKey, + workerIntervalSeconds: 30, }) // Create session to store credentials server-side (for Management API queries) diff --git a/packages/dashboard/src/app/api/sync-progress/route.ts b/packages/dashboard/src/app/api/sync-progress/route.ts new file mode 100644 index 00000000..035a91fb --- /dev/null +++ b/packages/dashboard/src/app/api/sync-progress/route.ts @@ -0,0 +1,61 @@ +import { NextRequest, NextResponse } from 'next/server' +import { getSession } from '@/lib/sessions' + +const SYNC_PROGRESS_QUERY = ` + SELECT json_build_object( + 'run', ( + SELECT row_to_json(r) FROM ( + SELECT account_id, started_at, closed_at, triggered_by, status, + total_processed, total_objects, complete_count, error_count, + running_count, pending_count, error_message + FROM stripe.sync_runs ORDER BY started_at DESC LIMIT 1 + ) r + ), + 'objects', COALESCE(( + SELECT json_agg(row_to_json(p) ORDER BY p.object) + FROM stripe.sync_obj_progress p + ), '[]'::json) + ) AS result +` + +export async function GET(request: NextRequest) { + try { + const sessionId = request.nextUrl.searchParams.get('sessionId') + + if (!sessionId) { + return NextResponse.json({ error: 'Missing sessionId' }, { status: 400 }) + } + + const session = getSession(sessionId) + if (!session) { + return NextResponse.json({ error: 'Session expired' }, { status: 401 }) + } + + const response = await fetch( + `https://api.supabase.com/v1/projects/${session.projectRef}/database/query`, + { + method: 'POST', + headers: { + Authorization: `Bearer ${session.accessToken}`, + 'Content-Type': 'application/json', + }, + body: JSON.stringify({ query: SYNC_PROGRESS_QUERY }), + } + ) + + if (!response.ok) { + const text = await response.text() + return NextResponse.json({ error: `Database query failed: ${text}` }, { status: 500 }) + } + + const rows = await response.json() + const data = rows?.[0]?.result ?? { run: null, objects: [] } + + return NextResponse.json(data) + } catch (error) { + return NextResponse.json( + { error: error instanceof Error ? error.message : 'Unknown error' }, + { status: 500 } + ) + } +} diff --git a/packages/dashboard/src/app/page.tsx b/packages/dashboard/src/app/page.tsx index c2ccd035..ffc8d4fb 100644 --- a/packages/dashboard/src/app/page.tsx +++ b/packages/dashboard/src/app/page.tsx @@ -3,6 +3,7 @@ import { useState } from 'react' import { DeployForm } from '@/components/DeployForm' import { SyncStatus } from '@/components/SyncStatus' +import { SyncProgress } from '@/components/SyncProgress' import { InstallationStatus } from '@/components/InstallationStatus' export default function Home() { @@ -15,16 +16,18 @@ export default function Home() {
Deploy Stripe sync to your Supabase project
-| Table | +Progress | +Rows | +
|---|---|---|
| + {obj.object} + | +
+
+
+
+
+
+
+ {pct.toFixed(1)}%
+
+ |
+ + {Number(obj.processed).toLocaleString()} + | +