this repo has no description
1import { getDays, getDayDetail, getStats } from '../core/db';
2import { processCommand } from '../cli/process';
3
4type ApiHandler = (req: Request, url: URL) => Promise<Response>;
5
6const routes: Record<string, ApiHandler> = {
7 'GET /api/days': handleGetDays,
8 'GET /api/days/:date': handleGetDayDetail,
9 'GET /api/days/:date/brag': handleGetDayBrag,
10 'GET /api/stats': handleGetStats,
11 'POST /api/refresh': handleRefresh,
12};
13
14export async function handleApiRequest(
15 req: Request,
16 url: URL
17): Promise<Response> {
18 const method = req.method;
19 const path = url.pathname;
20
21 // Match routes
22 for (const [route, handler] of Object.entries(routes)) {
23 const [routeMethod, routePath] = route.split(' ');
24 if (method !== routeMethod) continue;
25
26 const params = matchPath(routePath, path);
27 if (params !== null) {
28 try {
29 // Attach params to URL for handler access
30 (url as any).params = params;
31 return await handler(req, url);
32 } catch (error) {
33 console.error('API error:', error);
34 return jsonResponse({ error: 'Internal server error' }, 500);
35 }
36 }
37 }
38
39 return jsonResponse({ error: 'Not found' }, 404);
40}
41
42function matchPath(
43 pattern: string,
44 path: string
45): Record<string, string> | null {
46 const patternParts = pattern.split('/');
47 const pathParts = path.split('/');
48
49 if (patternParts.length !== pathParts.length) return null;
50
51 const params: Record<string, string> = {};
52
53 for (let i = 0; i < patternParts.length; i++) {
54 const patternPart = patternParts[i];
55 const pathPart = pathParts[i];
56
57 if (patternPart.startsWith(':')) {
58 params[patternPart.slice(1)] = pathPart;
59 } else if (patternPart !== pathPart) {
60 return null;
61 }
62 }
63
64 return params;
65}
66
67function jsonResponse(data: unknown, status = 200): Response {
68 return new Response(JSON.stringify(data), {
69 status,
70 headers: {
71 'Content-Type': 'application/json',
72 'Access-Control-Allow-Origin': '*',
73 },
74 });
75}
76
77// Handlers
78
79async function handleGetDays(req: Request, url: URL): Promise<Response> {
80 const limit = parseInt(url.searchParams.get('limit') || '30');
81 const days = getDays(limit);
82 return jsonResponse(days);
83}
84
85async function handleGetDayDetail(req: Request, url: URL): Promise<Response> {
86 const params = (url as any).params as Record<string, string>;
87 const date = params.date;
88
89 const detail = getDayDetail(date);
90 if (!detail) {
91 return jsonResponse({ error: 'Day not found' }, 404);
92 }
93
94 return jsonResponse(detail);
95}
96
97async function handleGetDayBrag(req: Request, url: URL): Promise<Response> {
98 const params = (url as any).params as Record<string, string>;
99 const date = params.date;
100
101 const detail = getDayDetail(date);
102 if (!detail) {
103 return jsonResponse({ error: 'Day not found' }, 404);
104 }
105
106 return jsonResponse({
107 date,
108 bragSummary: detail.bragSummary || 'No summary available',
109 projectCount: detail.projects.length,
110 sessionCount: detail.stats.totalSessions,
111 });
112}
113
114async function handleGetStats(req: Request, url: URL): Promise<Response> {
115 const stats = getStats();
116 return jsonResponse(stats);
117}
118
119async function handleRefresh(req: Request, url: URL): Promise<Response> {
120 // Run processing in background
121 const startTime = Date.now();
122
123 try {
124 const result = await processCommand({
125 force: false,
126 verbose: false,
127 });
128
129 return jsonResponse({
130 success: true,
131 sessionsProcessed: result.sessionsProcessed,
132 errors: result.errors,
133 durationMs: Date.now() - startTime,
134 });
135 } catch (error) {
136 return jsonResponse(
137 {
138 success: false,
139 error: error instanceof Error ? error.message : 'Unknown error',
140 },
141 500
142 );
143 }
144}