/** * POST /api/webhooks/coolify?projectId={projectId} * * Receives deployment status events from Coolify. * Updates the project's contextSnapshot.lastDeployment in Postgres. */ import { NextRequest, NextResponse } from 'next/server'; import { query } from '@/lib/db-postgres'; export async function POST(request: NextRequest) { const projectId = request.nextUrl.searchParams.get('projectId'); if (!projectId) { return NextResponse.json({ error: 'Missing projectId' }, { status: 400 }); } let payload: any; try { payload = await request.json(); } catch { return NextResponse.json({ error: 'Invalid JSON' }, { status: 400 }); } const rows = await query<{ id: string; data: any }>( `SELECT id, data FROM fs_projects WHERE id = $1 LIMIT 1`, [projectId] ); if (rows.length === 0) { return NextResponse.json({ error: 'Project not found' }, { status: 404 }); } const project = rows[0]; const existingSnapshot = project.data?.contextSnapshot ?? {}; // Coolify sends status events like: queued, in_progress, finished, failed, cancelled const status = payload.status ?? payload.data?.status ?? 'unknown'; const applicationUuid = payload.application_uuid ?? payload.data?.application_uuid; const deploymentUuid = payload.deployment_uuid ?? payload.data?.deployment_uuid; const url = payload.fqdn ?? payload.data?.fqdn ?? null; const newSnapshot = { ...existingSnapshot, lastDeployment: { status, applicationUuid, deploymentUuid, url, timestamp: new Date().toISOString(), }, updatedAt: new Date().toISOString(), }; await query(` UPDATE fs_projects SET data = jsonb_set(data, '{contextSnapshot}', $1::jsonb) WHERE id = $2 `, [JSON.stringify(newSnapshot), projectId]); console.log(`[webhook/coolify] deploy ${status} for project ${projectId}`); return NextResponse.json({ ok: true, status, projectId }); }