/** * GET /api/workspaces/[slug]/apps/[uuid]/logs * * Runtime logs for a Coolify app. Compose-aware: Coolify's REST API * for non-compose build packs, SSH into the Coolify host for per- * service `docker logs` on compose apps. * * Query params: * lines – tail lines per container (default 200, max 5000) * service – limit to one compose service (optional) */ import { NextResponse } from 'next/server'; import { requireWorkspacePrincipal } from '@/lib/auth/workspace-auth'; import { getApplicationInProject, TenantError } from '@/lib/coolify'; import { getApplicationRuntimeLogs } from '@/lib/coolify-logs'; export async function GET( request: Request, { params }: { params: Promise<{ slug: string; uuid: string }> } ) { const { slug, uuid } = 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 url = new URL(request.url); const linesRaw = Number(url.searchParams.get('lines') ?? '200'); const lines = Number.isFinite(linesRaw) ? linesRaw : 200; const service = url.searchParams.get('service') ?? undefined; try { await getApplicationInProject(uuid, ws.coolify_project_uuid); const result = await getApplicationRuntimeLogs(uuid, { lines, service }); return NextResponse.json(result); } catch (err) { if (err instanceof TenantError) { return NextResponse.json({ error: err.message }, { status: 403 }); } return NextResponse.json( { error: 'Failed to fetch runtime logs', details: err instanceof Error ? err.message : String(err) }, { status: 502 } ); } }