From 380af0796fa09cbb0a640817aa635a2cd7b4d581 Mon Sep 17 00:00:00 2001 From: vedant7007 Date: Wed, 5 Aug 2026 11:13:48 +0530 Subject: [PATCH] fix: expire the AI dashboard summary instead of caching it forever MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit getDashboardSummary stored the generated summary in user.dashboardSummary.content and returned it forever — nothing regenerated or invalidated it (not a Codeforces sync, not new contest results), so a user's AI "growth insight" went permanently stale and could contradict their current stats. Compare the existing lastGeneratedAt field against a 24-hour TTL and regenerate when stale. Closes #303 --- server/modules/ai/service.js | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/server/modules/ai/service.js b/server/modules/ai/service.js index fa565da..2c4c3bb 100644 --- a/server/modules/ai/service.js +++ b/server/modules/ai/service.js @@ -227,12 +227,20 @@ class AiService { const user = await User.findById(userId).select("name email dashboardSummary").lean(); if (!user) throw new ApiError(401, "User not found."); - // 2. Return cached summary if it exists - if (user.dashboardSummary && user.dashboardSummary.content) { - return user.dashboardSummary.content; + // 2. Return cached summary if it's still fresh. Without a TTL the summary + // was returned forever — nothing (not a CF sync, not new contests) + // regenerated it, so the "growth insight" could permanently contradict + // the user's current stats. + const SUMMARY_MAX_AGE_MS = 24 * 60 * 60 * 1000; // 24 hours + const cached = user.dashboardSummary; + const summaryAge = cached?.lastGeneratedAt + ? Date.now() - new Date(cached.lastGeneratedAt).getTime() + : Infinity; + if (cached && cached.content && summaryAge < SUMMARY_MAX_AGE_MS) { + return cached.content; } - console.log(`[AI] ▶ No cached summary found for ${user.name}. Generating new summary...`); + console.log(`[AI] ▶ No fresh cached summary for ${user.name}. Generating new summary...`); // 3. Fetch data for generation const [cfProfile, recentContests] = await Promise.all([