Files
vibn-frontend/app/api/projects/[projectId]/save-phase/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

107 lines
3.5 KiB
TypeScript

import { NextRequest, NextResponse } from "next/server";
import { authSession } from "@/lib/auth/session-server";
import { query } from "@/lib/db-postgres";
// ---------------------------------------------------------------------------
// POST — save a completed discovery phase into the project data
// ---------------------------------------------------------------------------
export async function POST(
req: NextRequest,
{ params }: { params: Promise<{ projectId: string }> }
) {
const session = await authSession();
if (!session?.user?.email) {
return NextResponse.json({ error: "Unauthorized" }, { status: 401 });
}
const { projectId } = await params;
let body: { phase: string; title: string; summary: string; data: Record<string, unknown> };
try {
body = await req.json();
} catch {
return NextResponse.json({ error: "Invalid JSON" }, { status: 400 });
}
const { phase, title, summary, data } = body;
if (!phase || !data) {
return NextResponse.json({ error: "phase and data are required" }, { status: 400 });
}
try {
// Ensure the table has a phases column and merge the new phase in
await query(`
CREATE TABLE IF NOT EXISTS atlas_phases (
project_id TEXT NOT NULL,
phase TEXT NOT NULL,
title TEXT,
summary TEXT,
data JSONB NOT NULL DEFAULT '{}'::jsonb,
saved_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
PRIMARY KEY (project_id, phase)
)
`);
await query(
`INSERT INTO atlas_phases (project_id, phase, title, summary, data, saved_at)
VALUES ($1, $2, $3, $4, $5::jsonb, NOW())
ON CONFLICT (project_id, phase) DO UPDATE
SET title = EXCLUDED.title,
summary = EXCLUDED.summary,
data = EXCLUDED.data,
saved_at = NOW()`,
[projectId, phase, title ?? phase, summary ?? "", JSON.stringify(data)]
);
// Also mirror into fs_projects.data.phases for easy access elsewhere
await query(
`UPDATE fs_projects
SET data = jsonb_set(
COALESCE(data, '{}'::jsonb),
ARRAY['phases', $2],
$3::jsonb,
true
),
updated_at = NOW()
WHERE id = $1`,
[projectId, phase, JSON.stringify({ title, summary, data, savedAt: new Date().toISOString() })]
);
console.log(`[save-phase] Saved phase "${phase}" for project ${projectId}`);
return NextResponse.json({ saved: true, phase });
} catch (err) {
console.error("[save-phase] Error:", err);
return NextResponse.json({ error: "Failed to save phase" }, { status: 500 });
}
}
// ---------------------------------------------------------------------------
// GET — load all saved phases for this project
// ---------------------------------------------------------------------------
export async function GET(
_req: NextRequest,
{ params }: { params: Promise<{ projectId: string }> }
) {
const session = await authSession();
if (!session?.user?.email) {
return NextResponse.json({ error: "Unauthorized" }, { status: 401 });
}
const { projectId } = await params;
try {
const rows = await query<{ phase: string; title: string; summary: string; data: unknown; saved_at: string }>(
`SELECT phase, title, summary, data, saved_at
FROM atlas_phases
WHERE project_id = $1
ORDER BY saved_at ASC`,
[projectId]
);
return NextResponse.json({ phases: rows });
} catch {
return NextResponse.json({ phases: [] });
}
}