web/app/api/collab/repos/%5Bowner%5D/%5Brepo%5D/notes/llm/route.tsblame
View source
0b4b5821import { NextRequest, NextResponse } from "next/server";
0b4b5822import { extractToken, verifyToken } from "@/server/collab-auth";
0b4b5823import { canAccessRepo, loadRoom, safeId } from "@/server/collab-rooms";
0b4b5824
0b4b5825export const dynamic = "force-dynamic";
0b4b5826
0b4b5827export async function GET(
0b4b5828 req: NextRequest,
0b4b5829 { params }: { params: Promise<{ owner: string; repo: string }> }
0b4b58210) {
0b4b58211 const { owner, repo } = await params;
0b4b58212 const token = extractToken(req);
0b4b58213
0b4b58214 if (!token) {
0b4b58215 return NextResponse.json({ error: "Unauthorized" }, { status: 401 });
0b4b58216 }
0b4b58217
0b4b58218 try {
0b4b58219 verifyToken(token);
0b4b58220 } catch {
0b4b58221 return NextResponse.json({ error: "Unauthorized" }, { status: 401 });
0b4b58222 }
0b4b58223
0b4b58224 if (!(await canAccessRepo(owner, repo, token))) {
0b4b58225 return NextResponse.json({ error: "Not found" }, { status: 404 });
0b4b58226 }
0b4b58227
0b4b58228 const notes = loadRoom(owner, repo);
0b4b58229 const timestamp = new Date().toISOString();
0b4b58230 let output = `# Diagram Review Notes\n# Repo: ${owner}/${repo}\n# Exported: ${timestamp}\n\n`;
0b4b58231
0b4b58232 for (const [diagramId, diagramNotes] of Object.entries(notes)) {
0b4b58233 if (diagramNotes.length === 0) continue;
0b4b58234 const diagramTitle = diagramNotes[0]?.diagramTitle || diagramId;
0b4b58235 output += `## ${diagramTitle} (${diagramId})\n\n`;
0b4b58236 for (const note of diagramNotes) {
0b4b58237 const location = note.targetNode
0b4b58238 ? ` [on: ${note.targetNode}]`
0b4b58239 : note.x != null
0b4b58240 ? ` [pinned at (${Math.round(note.x!)}, ${Math.round(note.y!)})]`
0b4b58241 : "";
0b4b58242 output += `- **${note.author}** (${note.timestamp})${location}: ${note.text}\n`;
0b4b58243 }
0b4b58244 output += "\n";
0b4b58245 }
0b4b58246
0b4b58247 const filename = `collab-notes-${safeId(owner)}-${safeId(repo)}-${timestamp.slice(0, 10)}.md`;
0b4b58248
0b4b58249 return new NextResponse(output, {
0b4b58250 headers: {
0b4b58251 "Content-Type": "text/markdown",
0b4b58252 "Content-Disposition": `attachment; filename="${filename}"`,
0b4b58253 },
0b4b58254 });
0b4b58255}