178 lines
6.4 KiB
TypeScript
178 lines
6.4 KiB
TypeScript
import type { IncomingMessage, ServerResponse } from 'node:http';
|
|
import type { ProjectType } from '../../../shared/project-config';
|
|
import type { ProjectIdentityChoice } from '../../../shared/coding-project-contracts';
|
|
import type { CodingProjectConfigSnapshot } from '../../coding-projects/project-service';
|
|
import type { CodingProject } from '../../coding-projects/project-store';
|
|
import type { HostApiContext } from '../context';
|
|
import { parseJsonBody, sendJson, sendNoContent } from '../route-utils';
|
|
import { sendCodingRouteError } from './coding-route-errors';
|
|
|
|
function isProjectRoute(pathname: string): boolean {
|
|
return pathname === '/api/coding/projects'
|
|
|| pathname.startsWith('/api/coding/projects/');
|
|
}
|
|
|
|
function publicProject(project: CodingProject | null): Omit<CodingProject, 'path'> | null {
|
|
if (!project) return null;
|
|
const { path: _path, ...safe } = project;
|
|
return safe;
|
|
}
|
|
|
|
function publicProjectSnapshot(snapshot: CodingProjectConfigSnapshot) {
|
|
return {
|
|
...snapshot,
|
|
project: publicProject(snapshot.project),
|
|
};
|
|
}
|
|
|
|
export async function handleCodingProjectRoutes(
|
|
req: IncomingMessage,
|
|
res: ServerResponse,
|
|
url: URL,
|
|
ctx: HostApiContext,
|
|
): Promise<boolean> {
|
|
if (!isProjectRoute(url.pathname)) return false;
|
|
const projects = ctx.codingProducts?.projects;
|
|
const conversations = ctx.codingProducts?.conversations;
|
|
if (!projects || !conversations) {
|
|
sendJson(res, 503, {
|
|
success: false,
|
|
code: 'CODING_CORE_UNAVAILABLE',
|
|
error: '本地编程服务暂时不可用。',
|
|
});
|
|
return true;
|
|
}
|
|
|
|
try {
|
|
if (url.pathname === '/api/coding/projects' && req.method === 'GET') {
|
|
const [items, activeProject] = await Promise.all([
|
|
projects.listProjects(),
|
|
projects.getActiveProject(),
|
|
]);
|
|
sendJson(res, 200, {
|
|
projects: items.map((project) => publicProject(project)),
|
|
activeProjectId: activeProject?.id ?? null,
|
|
});
|
|
return true;
|
|
}
|
|
if (url.pathname === '/api/coding/projects/open' && req.method === 'POST') {
|
|
const body = await parseJsonBody<{ projectPath?: string }>(req);
|
|
sendJson(res, 200, { project: publicProject(await projects.openProject(body.projectPath ?? '')) });
|
|
return true;
|
|
}
|
|
if (url.pathname === '/api/coding/projects/create' && req.method === 'POST') {
|
|
const body = await parseJsonBody<{
|
|
projectPath?: string;
|
|
parentPath?: string;
|
|
projectName?: string;
|
|
projectType?: ProjectType;
|
|
identity: ProjectIdentityChoice;
|
|
}>(req);
|
|
sendJson(res, 201, { snapshot: publicProjectSnapshot(await projects.createProject(body)) });
|
|
return true;
|
|
}
|
|
if (url.pathname === '/api/coding/projects/identity' && req.method === 'POST') {
|
|
const body = await parseJsonBody<{
|
|
localProjectId?: string;
|
|
identity: ProjectIdentityChoice;
|
|
}>(req);
|
|
sendJson(res, 200, {
|
|
snapshot: publicProjectSnapshot(
|
|
await projects.resolveProjectIdentity(
|
|
body.localProjectId ?? '',
|
|
body.identity,
|
|
),
|
|
),
|
|
});
|
|
return true;
|
|
}
|
|
if (url.pathname === '/api/coding/projects/identity/independent-copy' && req.method === 'POST') {
|
|
const body = await parseJsonBody<{
|
|
localProjectId?: string;
|
|
confirmed: unknown;
|
|
}>(req);
|
|
sendJson(res, 200, {
|
|
snapshot: publicProjectSnapshot(
|
|
await projects.makeProjectIndependentCopy(
|
|
body.localProjectId ?? '',
|
|
body.confirmed as true,
|
|
),
|
|
),
|
|
});
|
|
return true;
|
|
}
|
|
if (url.pathname === '/api/coding/projects/remove' && req.method === 'POST') {
|
|
const body = await parseJsonBody<{ projectId?: string }>(req);
|
|
await projects.removeProject(body.projectId ?? '');
|
|
sendNoContent(res);
|
|
return true;
|
|
}
|
|
if (url.pathname === '/api/coding/projects/active' && req.method === 'GET') {
|
|
sendJson(res, 200, { project: publicProject(await projects.getActiveProject()) });
|
|
return true;
|
|
}
|
|
if (url.pathname === '/api/coding/projects/active' && req.method === 'POST') {
|
|
const body = await parseJsonBody<{ projectId?: string }>(req);
|
|
sendJson(res, 200, { project: publicProject(await projects.setActiveProject(body.projectId ?? '')) });
|
|
return true;
|
|
}
|
|
if (url.pathname === '/api/coding/projects/config' && req.method === 'GET') {
|
|
sendJson(res, 200, {
|
|
snapshot: publicProjectSnapshot(
|
|
await projects.getConfig(url.searchParams.get('projectId')?.trim() || undefined),
|
|
),
|
|
});
|
|
return true;
|
|
}
|
|
if (url.pathname === '/api/coding/projects/config' && req.method === 'PUT') {
|
|
const body = await parseJsonBody<{ projectId?: string; config?: unknown }>(req);
|
|
sendJson(res, 200, {
|
|
snapshot: publicProjectSnapshot(await projects.saveConfig(body.projectId ?? '', body.config)),
|
|
});
|
|
return true;
|
|
}
|
|
if (url.pathname === '/api/coding/projects/knowledge' && req.method === 'POST') {
|
|
const body = await parseJsonBody<{
|
|
projectId?: string;
|
|
fileName?: string;
|
|
contentBase64?: string;
|
|
}>(req);
|
|
sendJson(res, 201, {
|
|
knowledgeFiles: await projects.addKnowledgeFile({
|
|
projectId: body.projectId ?? '',
|
|
fileName: body.fileName ?? '',
|
|
contentBase64: body.contentBase64 ?? '',
|
|
}),
|
|
});
|
|
return true;
|
|
}
|
|
if (url.pathname === '/api/coding/projects/legacy-conversation-notice/acknowledge'
|
|
&& req.method === 'POST') {
|
|
const body = await parseJsonBody<{ projectId?: string }>(req);
|
|
sendJson(res, 200, {
|
|
snapshot: publicProjectSnapshot(
|
|
await projects.acknowledgeLegacyConversationNotice(body.projectId ?? ''),
|
|
),
|
|
});
|
|
return true;
|
|
}
|
|
if (url.pathname === '/api/coding/projects/conversations' && req.method === 'GET') {
|
|
sendJson(res, 200, {
|
|
conversations: await conversations.listConversations(
|
|
url.searchParams.get('projectId')?.trim() || undefined,
|
|
),
|
|
});
|
|
return true;
|
|
}
|
|
if (url.pathname === '/api/coding/projects/conversations' && req.method === 'POST') {
|
|
const body = await parseJsonBody<{ projectId?: string; agentId?: unknown; title?: unknown }>(req);
|
|
sendJson(res, 201, { conversation: await conversations.createConversation(body) });
|
|
return true;
|
|
}
|
|
} catch (error) {
|
|
sendCodingRouteError(res, error);
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|