Swadhin Biswas Feature/ai code reviewer (#73) · 8/2/2026
105a287
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
import type { APIRoute } from "astro";
import { getDatabase, schema } from "@/db";
import { eq, and, gte } from "drizzle-orm";
import { withErrorHandler } from "@/lib/errors";
import { success, unauthorized } from "@/lib/api";
import { getUserFromRequest } from "@/lib/auth";
export const GET: APIRoute = withErrorHandler(async ({ request, locals }) => {
// Try locals first (session auth), then fallback to header token (CLI auth)
let userId = locals.user?.id;
if (!userId) {
const tokenPayload = await getUserFromRequest(request);
if (!tokenPayload) {
return unauthorized();
}
userId = tokenPayload.userId;
}
const url = new URL(request.url);
const weeks = parseInt(url.searchParams.get("weeks") || "4");
// Calculate date range
const now = new Date();
const startDate = new Date();
startDate.setDate(now.getDate() - (weeks * 7));
const db = getDatabase();
// 1. Authored PRs Stats
const authoredPrs = await db.query.pullRequests.findMany({
where: and(
eq(schema.pullRequests.authorId, userId),
gte(schema.pullRequests.createdAt, startDate)
)
});
const mergedPrs = authoredPrs.filter(pr => pr.isMerged);
// Calculate avg time to merge
let totalMergeTime = 0;
mergedPrs.forEach(pr => {
if (pr.mergedAt && pr.createdAt) {
totalMergeTime += (pr.mergedAt.getTime() - pr.createdAt.getTime());
}
});
// Convert to hours
const avgTimeToMerge = mergedPrs.length > 0
? (totalMergeTime / mergedPrs.length) / (1000 * 60 * 60)
: 0;
// 2. Review Stats
const reviews = await db.query.pullRequestReviews.findMany({
where: and(
eq(schema.pullRequestReviews.reviewerId, userId),
gte(schema.pullRequestReviews.createdAt, startDate)
)
});
const approvals = reviews.filter(r => r.state === "approved").length;
const changesRequested = reviews.filter(r => r.state === "changes_requested").length;
// 3. Calculate Trends (Weekly)
const trends = [];
for (let i = 0; i < weeks; i++) {
const weekStart = new Date();
weekStart.setDate(now.getDate() - ((i + 1) * 7));
const weekEnd = new Date();
weekEnd.setDate(now.getDate() - (i * 7));
const weekLabel = `${weekStart.toLocaleDateString()} - ${weekEnd.toLocaleDateString()}`;
const weekAuthored = authoredPrs.filter(
pr => pr.createdAt >= weekStart && pr.createdAt < weekEnd
).length;
const weekReviewed = reviews.filter(
r => r.createdAt >= weekStart && r.createdAt < weekEnd
).length;
trends.unshift({
week: weekLabel,
authored: weekAuthored,
reviewed: weekReviewed
});
}
return success({
authored: {
total: authoredPrs.length,
merged: mergedPrs.length,
avgTimeToMerge // in hours
},
reviewed: {
total: reviews.length,
approvals,
changesRequested
},
trends
});
});