+server.ts
1import { json } from '@sveltejs/kit';
2import {
3 addComment,
4 getThreads,
5 resolveThread,
6 deleteComment,
7 deleteThread,
8 getStats,
9 markOutdated
10} from '$lib/server/review-store';
11import { getRepoDir } from '$lib/server/vcs';
12import { readFile } from 'fs/promises';
13import { join } from 'path';
14import type { RequestHandler } from './$types.ts';
15
16export const GET: RequestHandler = async ({ url }) => {
17 const filePath = url.searchParams.get('file') || undefined;
18 const threads = getThreads(filePath);
19 const stats = getStats();
20 return json({ threads, stats });
21};
22
23export const POST: RequestHandler = async ({ request }) => {
24 const body = await request.json();
25 const { filePath, lineNumber, side, text, type, threadId, originalLineContent } = body;
26
27 if (!filePath || lineNumber === undefined || !text) {
28 return json({ error: 'Missing required fields' }, { status: 400 });
29 }
30
31 const thread = addComment(filePath, lineNumber, side || 'new', text, type || 'comment', threadId, originalLineContent);
32 return json(thread);
33};
34
35export const PATCH: RequestHandler = async ({ request }) => {
36 const body = await request.json();
37 const { threadId, action } = body;
38
39 if (action === 'resolve' && threadId) {
40 resolveThread(threadId);
41 return json({ success: true });
42 }
43
44 if (action === 'check-outdated') {
45 const threads = getThreads();
46 const repoDir = getRepoDir();
47 const fileCache = new Map<string, string[]>();
48 let outdatedCount = 0;
49
50 for (const thread of threads) {
51 if (!thread.originalLineContent || thread.resolved) continue;
52
53 // Read file content (cached)
54 let lines = fileCache.get(thread.filePath);
55 if (!lines) {
56 try {
57 const content = await readFile(join(repoDir, thread.filePath), 'utf-8');
58 lines = content.split('\n');
59 fileCache.set(thread.filePath, lines);
60 } catch {
61 continue;
62 }
63 }
64
65 // Check if the line content still matches
66 const lineIdx = thread.lineNumber - 1;
67 if (lineIdx >= 0 && lineIdx < lines.length) {
68 const currentContent = lines[lineIdx].trim();
69 const originalContent = thread.originalLineContent.trim();
70 const isOutdated = currentContent !== originalContent;
71 if (isOutdated !== thread.outdated) {
72 markOutdated(thread.id, isOutdated);
73 if (isOutdated) outdatedCount++;
74 }
75 } else {
76 // Line no longer exists — outdated
77 if (!thread.outdated) {
78 markOutdated(thread.id, true);
79 outdatedCount++;
80 }
81 }
82 }
83
84 return json({ success: true, outdatedCount, threads: getThreads() });
85 }
86
87 return json({ error: 'Unknown action' }, { status: 400 });
88};
89
90export const DELETE: RequestHandler = async ({ request }) => {
91 const body = await request.json();
92 const { commentId, threadId } = body;
93
94 if (threadId) {
95 deleteThread(threadId);
96 return json({ success: true });
97 }
98
99 if (commentId) {
100 deleteComment(commentId);
101 return json({ success: true });
102 }
103
104 return json({ error: 'Missing commentId or threadId' }, { status: 400 });
105};