This repository has no description
1import { eq, desc } from "drizzle-orm";
2import { db } from "../../../libs/db";
3import { takes as takesTable, users as usersTable } from "../../../libs/schema";
4import { handleApiError } from "../../../libs/apiError";
5import { userService } from "../../../libs/cachet";
6
7export type RecentTake = {
8 id: string;
9 userId: string;
10 notes: string;
11 createdAt: Date;
12 mediaUrls: string[];
13 /* elapsed time in seconds */
14 elapsedTime: number;
15 project: string;
16 /* total time in seconds */
17 totalTakesTime: number;
18 userName?: string; // Add userName field
19};
20
21// Recent takes cache to reduce database queries
22const takesCache = new Map<string, { data: RecentTake[]; timestamp: number }>();
23const TAKES_CACHE_TTL = 30 * 1000; // 30 seconds cache TTL - shorter since takes change frequently
24
25export async function recentTakes(url: URL): Promise<Response> {
26 try {
27 const userId = url.searchParams.get("user");
28
29 // Check cache before querying database
30 const cacheKey = userId || "all_takes";
31 const cached = takesCache.get(cacheKey);
32 if (cached && Date.now() - cached.timestamp < TAKES_CACHE_TTL) {
33 return new Response(
34 JSON.stringify({
35 takes: cached.data,
36 }),
37 {
38 headers: {
39 "Content-Type": "application/json",
40 },
41 },
42 );
43 }
44
45 if (userId) {
46 // Verify user exists if userId provided
47 const user = await db
48 .select()
49 .from(usersTable)
50 .where(eq(usersTable.id, userId))
51 .limit(1);
52
53 if (user.length === 0) {
54 return new Response(
55 JSON.stringify({
56 error: "User not found",
57 takes: [],
58 }),
59 {
60 status: 404,
61 headers: {
62 "Content-Type": "application/json",
63 },
64 },
65 );
66 }
67 }
68
69 // Use a JOIN query to get takes and user data in a single operation
70 const takesWithUserData = await db
71 .select({
72 take: {
73 id: takesTable.id,
74 userId: takesTable.userId,
75 notes: takesTable.notes,
76 createdAt: takesTable.createdAt,
77 media: takesTable.media,
78 elapsedTime: takesTable.elapsedTime,
79 },
80 user: {
81 projectName: usersTable.projectName,
82 totalTakesTime: usersTable.totalTakesTime,
83 },
84 })
85 .from(takesTable)
86 .leftJoin(usersTable, eq(takesTable.userId, usersTable.id))
87 .where(userId ? eq(takesTable.userId, userId) : undefined)
88 .orderBy(desc(takesTable.createdAt))
89 .limit(40);
90
91 if (takesWithUserData.length === 0) {
92 return new Response(
93 JSON.stringify({
94 takes: [],
95 }),
96 {
97 headers: {
98 "Content-Type": "application/json",
99 },
100 },
101 );
102 }
103
104 // Get unique user IDs
105 const userIds = [
106 ...new Set(takesWithUserData.map((item) => item.take.userId)),
107 ];
108
109 // Fetch all user names from shared user service
110 const userNamesPromises = userIds.map((id) =>
111 userService.getUserName(id),
112 );
113 const userNames = await Promise.all(userNamesPromises);
114
115 // Create a map of user names
116 const userNameMap: Record<string, string> = {};
117 userIds.forEach((id, index) => {
118 userNameMap[id] = userNames[index] || "unknown";
119 });
120
121 // Map the joined results to the expected format
122 const takes: RecentTake[] = takesWithUserData.map((item) => ({
123 id: item.take.id,
124 userId: item.take.userId,
125 notes: item.take.notes,
126 createdAt: new Date(item.take.createdAt),
127 mediaUrls: item.take.media ? JSON.parse(item.take.media) : [],
128 elapsedTime: item.take.elapsedTime,
129 project: item.user?.projectName || "unknown project",
130 totalTakesTime: item.user?.totalTakesTime || item.take.elapsedTime,
131 userName: userNameMap[item.take.userId] || "Unknown User",
132 }));
133
134 // Store results in cache
135 takesCache.set(cacheKey, {
136 data: takes,
137 timestamp: Date.now(),
138 });
139
140 return new Response(
141 JSON.stringify({
142 takes,
143 }),
144 {
145 headers: {
146 "Content-Type": "application/json",
147 },
148 },
149 );
150 } catch (error) {
151 return handleApiError(error, "recentTakes");
152 }
153}