Files
vibn-frontend/app/api/projects/[projectId]/analyze/route.ts
Mark Henderson 651ddf1e11 Rip out Theia, ship P5.1 attach E2E + Justine UI work-in-progress
Theia rip-out:
- Delete app/api/theia-auth/route.ts (Traefik ForwardAuth shim)
- Delete app/api/projects/[projectId]/workspace/route.ts and
  app/api/projects/prewarm/route.ts (Cloud Run Theia provisioning)
- Delete lib/cloud-run-workspace.ts and lib/coolify-workspace.ts
- Strip provisionTheiaWorkspace + theiaWorkspaceUrl/theiaAppUuid/
  theiaError from app/api/projects/create/route.ts response
- Remove Theia callbackUrl branch in app/auth/page.tsx
- Drop "Open in Theia" button + xterm/Theia PTY copy in build/page.tsx
- Drop theiaWorkspaceUrl from deployment/page.tsx Project type
- Strip Theia IDE line + theia-code-os from advisor + agent-chat
  context strings
- Scrub Theia mention from lib/auth/workspace-auth.ts comment

P5.1 (custom apex domains + DNS):
- lib/coolify.ts + lib/opensrs.ts: nameserver normalization, OpenSRS
  XML auth, Cloud DNS plumbing
- scripts/smoke-attach-e2e.ts: full prod GCP + sandbox OpenSRS +
  prod Coolify smoke covering register/zone/A/NS/PATCH/cleanup

In-progress (Justine onboarding/build, MVP setup, agent telemetry):
- New (justine)/stories, project (home) layouts, mvp-setup, run, tasks
  routes + supporting components
- Project shell + sidebar + nav refactor for the Stackless palette
- Agent session API hardening (sessions, events, stream, approve,
  retry, stop) + atlas-chat, advisor, design-surfaces refresh
- New scripts/sync-db-url-from-coolify.mjs +
  scripts/prisma-db-push.mjs + docker-compose.local-db.yml for
  local Prisma workflows
- lib/dev-bypass.ts, lib/chat-context-refs.ts, lib/prd-sections.ts
- Misc: stories CSS, debug/prisma route, modal-theme, BuildLivePlanPanel

Made-with: Cursor
2026-04-22 18:05:01 -07:00

121 lines
3.9 KiB
TypeScript

import { NextResponse } from 'next/server';
import { authSession } from "@/lib/auth/session-server";
import { query } from '@/lib/db-postgres';
const AGENT_RUNNER_URL = process.env.AGENT_RUNNER_URL ?? 'http://localhost:3333';
// GET — check the current analysis status for a project
export async function GET(
_req: Request,
{ params }: { params: Promise<{ projectId: string }> }
) {
const session = await authSession();
if (!session?.user?.email) {
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 });
}
const { projectId } = await params;
const rows = await query<{ data: any }>(
`SELECT data FROM fs_projects WHERE id = $1 LIMIT 1`,
[projectId]
);
if (!rows.length) return NextResponse.json({ error: 'Project not found' }, { status: 404 });
const project = rows[0].data;
if (!project.isImport) {
return NextResponse.json({ isImport: false });
}
const jobId = project.importAnalysisJobId;
let jobStatus: Record<string, unknown> | null = null;
// Fetch live job status from agent runner if we have a job ID
if (jobId) {
try {
const jobRes = await fetch(`${AGENT_RUNNER_URL}/api/jobs/${jobId}`);
if (jobRes.ok) {
jobStatus = await jobRes.json() as Record<string, unknown>;
// Sync terminal status back to the project record
const runnerStatus = jobStatus.status as string | undefined;
if (runnerStatus && runnerStatus !== project.importAnalysisStatus) {
await query(
`UPDATE fs_projects SET data = jsonb_set(data, '{importAnalysisStatus}', $1::jsonb) WHERE id = $2`,
[JSON.stringify(runnerStatus), projectId]
);
}
}
} catch {
// Agent runner unreachable — return last known status
}
}
return NextResponse.json({
isImport: true,
status: project.importAnalysisStatus ?? 'pending',
jobId,
job: jobStatus,
githubRepoUrl: project.githubRepoUrl,
giteaRepo: project.giteaRepo,
});
}
// POST — (re-)trigger an analysis job for a project
export async function POST(
_req: Request,
{ params }: { params: Promise<{ projectId: string }> }
) {
const session = await authSession();
if (!session?.user?.email) {
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 });
}
const { projectId } = await params;
const rows = await query<{ data: any }>(
`SELECT data FROM fs_projects WHERE id = $1 LIMIT 1`,
[projectId]
);
if (!rows.length) return NextResponse.json({ error: 'Project not found' }, { status: 404 });
const project = rows[0].data;
if (!project.giteaRepo) {
return NextResponse.json({ error: 'Project has no Gitea repo' }, { status: 400 });
}
try {
const jobRes = await fetch(`${AGENT_RUNNER_URL}/api/agent/run`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
agent: 'ImportAnalyzer',
task: `Analyze this codebase${project.githubRepoUrl ? ` (originally from ${project.githubRepoUrl})` : ''} and produce CODEBASE_MAP.md and MIGRATION_PLAN.md as described in your instructions.`,
repo: project.giteaRepo,
}),
});
if (!jobRes.ok) {
const detail = await jobRes.text();
return NextResponse.json({ error: 'Failed to start analysis', details: detail }, { status: 500 });
}
const jobData = await jobRes.json() as { jobId?: string };
const jobId = jobData.jobId ?? null;
await query(
`UPDATE fs_projects SET data = jsonb_set(jsonb_set(data, '{importAnalysisJobId}', $1::jsonb), '{importAnalysisStatus}', '"running"') WHERE id = $2`,
[JSON.stringify(jobId), projectId]
);
return NextResponse.json({ success: true, jobId, status: 'running' });
} catch (err) {
return NextResponse.json(
{ error: 'Failed to start analysis', details: err instanceof Error ? err.message : String(err) },
{ status: 500 }
);
}
}