···
1
-
import { eq, desc, or } from "drizzle-orm";
1
+
import { eq, desc } from "drizzle-orm";
import { db } from "../../../libs/db";
import { takes as takesTable, users as usersTable } from "../../../libs/schema";
import { handleApiError } from "../../../libs/apiError";
5
-
import { fetchUserData } from "../../../libs/cachet";
5
+
import { userService } from "../../../libs/cachet";
export type RecentTake = {
···
userName?: string; // Add userName field
21
-
// Cache for user data from cachet
22
-
const userCache: Record<string, { name: string; timestamp: number }> = {};
23
-
const CACHE_TTL = 5 * 60 * 1000; // 5 minutes
24
-
// Track pending requests to avoid duplicate API calls
25
-
const pendingRequests: Record<string, Promise<string>> = {};
27
-
// Function to get user name from cache or fetch it
28
-
async function getUserName(userId: string): Promise<string> {
29
-
const now = Date.now();
31
-
// Check if user data is in cache and still valid
32
-
if (userCache[userId] && now - userCache[userId].timestamp < CACHE_TTL) {
33
-
return userCache[userId].name;
36
-
// If there's already a pending request for this user, return that promise
37
-
// instead of creating a new request
38
-
if (pendingRequests[userId]) {
39
-
return pendingRequests[userId];
42
-
// Create a new promise for this user and store it
43
-
const fetchPromise = (async () => {
45
-
const userData = await fetchUserData(userId);
46
-
const userName = userData?.displayName || "Unknown User";
48
-
userCache[userId] = {
55
-
console.error("Error fetching user data:", error);
56
-
return "Unknown User";
58
-
// Clean up the pending request when done
59
-
delete pendingRequests[userId];
63
-
// Store the promise
64
-
pendingRequests[userId] = fetchPromise;
66
-
// Return the promise
67
-
return fetchPromise;
21
+
// Recent takes cache to reduce database queries
22
+
const takesCache = new Map<string, { data: RecentTake[]; timestamp: number }>();
23
+
const TAKES_CACHE_TTL = 30 * 1000; // 30 seconds cache TTL - shorter since takes change frequently
export async function recentTakes(url: URL): Promise<Response> {
const userId = url.searchParams.get("user");
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(
39
+
"Content-Type": "application/json",
// Verify user exists if userId provided
···
98
-
const recentTakes = await db
69
+
// Use a JOIN query to get takes and user data in a single operation
70
+
const takesWithUserData = await db
74
+
userId: takesTable.userId,
75
+
notes: takesTable.notes,
76
+
createdAt: takesTable.createdAt,
77
+
media: takesTable.media,
78
+
elapsedTime: takesTable.elapsedTime,
81
+
projectName: usersTable.projectName,
82
+
totalTakesTime: usersTable.totalTakesTime,
86
+
.leftJoin(usersTable, eq(takesTable.userId, usersTable.id))
87
+
.where(userId ? eq(takesTable.userId, userId) : undefined)
.orderBy(desc(takesTable.createdAt))
102
-
.where(eq(takesTable.userId, userId ? userId : takesTable.userId))
105
-
if (recentTakes.length === 0) {
91
+
if (takesWithUserData.length === 0) {
···
119
-
const userIds = [...new Set(recentTakes.map((take) => take.userId))];
121
-
// Query users from takes table
122
-
const users = await db
125
-
.where(or(...userIds.map((id) => eq(usersTable.id, id))));
106
+
...new Set(takesWithUserData.map((item) => item.take.userId)),
127
-
// Create map of user data by ID
128
-
const userMap = users.reduce(
130
-
acc[user.id] = user;
133
-
{} as Record<string, (typeof users)[number]>,
109
+
// Fetch all user names from shared user service
110
+
const userNamesPromises = userIds.map((id) =>
111
+
userService.getUserName(id),
136
-
// Fetch all user names from cache or API
137
-
const userNamesPromises = userIds.map((id) => getUserName(id));
const userNames = await Promise.all(userNamesPromises);
// Create a map of user names
···
userNameMap[id] = userNames[index] || "unknown";
146
-
const takes: RecentTake[] =
147
-
recentTakes.map((take) => ({
149
-
userId: take.userId,
151
-
createdAt: new Date(take.createdAt),
152
-
mediaUrls: take.media ? JSON.parse(take.media) : [],
153
-
elapsedTime: take.elapsedTime,
154
-
project: userMap[take.userId]?.projectName || "unknown project",
156
-
userMap[take.userId]?.totalTakesTime || take.elapsedTime,
157
-
userName: userNameMap[take.userId] || "Unknown User",
121
+
// Map the joined results to the expected format
122
+
const takes: RecentTake[] = takesWithUserData.map((item) => ({
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",
134
+
// Store results in cache
135
+
takesCache.set(cacheKey, {
137
+
timestamp: Date.now(),