- Mirror GitHub repos to Gitea as-is on import (skip scaffold) - Auto-trigger ImportAnalyzer agent after successful mirror - Add POST/GET /api/projects/[projectId]/analyze route - Fix project delete button visibility (was permanently opacity:0) - Store isImport, importAnalysisStatus, importAnalysisJobId on projects Made-with: Cursor
122 lines
3.9 KiB
TypeScript
122 lines
3.9 KiB
TypeScript
import { NextResponse } from 'next/server';
|
|
import { getServerSession } from 'next-auth';
|
|
import { authOptions } from '@/lib/auth/authOptions';
|
|
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 getServerSession(authOptions);
|
|
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 getServerSession(authOptions);
|
|
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 }
|
|
);
|
|
}
|
|
}
|