/** * GET /api/workspaces/[slug]/deployments/[deploymentUuid]/logs * * Raw deployment logs. We can't tell from a deployment UUID alone * which project it belongs to, so we require `?appUuid=...` and * verify that app belongs to the workspace first. This keeps the * tenant boundary intact even though Coolify's log endpoint is * global. */ import { NextResponse } from 'next/server'; import { requireWorkspacePrincipal } from '@/lib/auth/workspace-auth'; import { getApplicationInProject, getDeploymentLogs, TenantError, } from '@/lib/coolify'; export async function GET( request: Request, { params }: { params: Promise<{ slug: string; deploymentUuid: string }> } ) { const { slug, deploymentUuid } = await params; const principal = await requireWorkspacePrincipal(request, { targetSlug: slug }); if (principal instanceof NextResponse) return principal; const ws = principal.workspace; if (!ws.coolify_project_uuid) { return NextResponse.json({ error: 'Workspace has no Coolify project yet' }, { status: 503 }); } const appUuid = new URL(request.url).searchParams.get('appUuid'); if (!appUuid) { return NextResponse.json( { error: 'Query param "appUuid" is required for tenant enforcement' }, { status: 400 } ); } try { await getApplicationInProject(appUuid, ws.coolify_project_uuid); const logs = await getDeploymentLogs(deploymentUuid); return NextResponse.json(logs); } catch (err) { if (err instanceof TenantError) { return NextResponse.json({ error: err.message }, { status: 403 }); } return NextResponse.json( { error: 'Coolify request failed', details: String(err) }, { status: 502 } ); } }