Skip to content

Commit 10bb27f

Browse files
committed
feat: Implement platform data update throttling and user challenge tracking; add isPOTDSolvedToday flag and update logic
1 parent 939e4fc commit 10bb27f

File tree

9 files changed

+76
-23
lines changed

9 files changed

+76
-23
lines changed

client-test/src/components/ProfilePage/index.tsx

Lines changed: 23 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -123,17 +123,35 @@ export default function ProfilePage() {
123123
useEffect(() => {
124124
const updatePlatforms = async () => {
125125
if (!profileUser || !profileUser.username) return
126-
if (!isPlatformDataStale(profileUser.username, 10)) { return }
126+
if (!isPlatformDataStale(profileUser.username, 60)) { return }
127127

128128
try {
129129
console.log("Updating platforms...")
130130
const updatedUser = await fetchPlatformsData(profileUser)
131-
setProfileUser(updatedUser)
132-
toast.success("Data updated successfully")
133-
axios.post(`${import.meta.env.VITE_API_BASE_URL}/api/user/${profileUser.username}/update-platforms`, updatedUser);
134-
updatePlatformCacheTimestamp(profileUser.username)
131+
const response = await axios.post(`${import.meta.env.VITE_API_BASE_URL}/api/user/${profileUser.username}/update-platforms`, updatedUser);
132+
133+
if (response.status === 200) {
134+
setProfileUser(updatedUser)
135+
toast.success("Data updated successfully")
136+
updatePlatformCacheTimestamp(profileUser.username)
137+
} else if (response.status === 429) {
138+
const nextUpdateTime = new Date(response.data.nextUpdateTime)
139+
toast.success(`Please wait until ${nextUpdateTime.toLocaleTimeString()} to update platform data again`)
140+
updatePlatformCacheTimestamp(profileUser.username)
141+
}
135142
} catch (error) {
136143
console.error("Error updating platforms:", error)
144+
if (axios.isAxiosError(error) && error.response?.status === 429) {
145+
const nextUpdateTime = error.response.data?.nextUpdateTime
146+
if (nextUpdateTime) {
147+
const nextTime = new Date(nextUpdateTime)
148+
toast.success(`Please wait until ${nextTime.toLocaleTimeString()} to update platform data again`)
149+
} else {
150+
toast.success("Platform data was updated recently. Please wait before updating again.")
151+
}
152+
} else {
153+
toast.error("Failed to update platform data")
154+
}
137155
updatePlatformCacheTimestamp(profileUser.username)
138156
}
139157
}

server/controllers/adminControllers.js

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -127,7 +127,6 @@ export const addChallenge = async (req, res) => {
127127
// Ensure categories exist in database (create if they don't)
128128
await ensureCategoriesExist(category);
129129

130-
// Create and save Challenge
131130
const newChallenge = new Challenge({
132131
title,
133132
description,

server/controllers/challengeController.js

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,6 @@ export const getChallenges = async (req, res) => {
6969

7070
// Add user-specific solved status if needed
7171
if (targetUser && status) {
72-
const mongoose = await import('mongoose');
7372
pipeline.push(
7473
{
7574
$addFields: {
@@ -181,16 +180,16 @@ export const getDailyChallenge = async (req, res) => {
181180

182181
if (userId) {
183182
try {
184-
targetUser = await User.findById(userId).select('solveChallenges _id');
183+
targetUser = await User.findById(userId).select('isPOTDSolvedToday _id');
185184
if (targetUser) {
186-
isSolved = dailyChallenge.solvedUsers?.includes(targetUser._id) || false;
185+
isSolved = targetUser.isPOTDSolvedToday || false;
187186
}
188187
} catch (error) {
189188
// Invalid user ID, continue without user context
190189
}
191190
} else if (authenticatedUser) {
192191
targetUser = authenticatedUser;
193-
isSolved = dailyChallenge.solvedUsers?.includes(authenticatedUser._id) || false;
192+
isSolved = authenticatedUser.isPOTDSolvedToday || false;
194193
}
195194

196195
// Return challenge with solved status
@@ -316,7 +315,7 @@ export const checkPOTDStatus = async (req, res) => {
316315
return res.status(404).json({ message: 'Daily challenge not found', isSolved: false });
317316
}
318317

319-
let isSolved = dailyChallenge.solvedUsers?.includes(userId) || false;
318+
let isSolved = user.isPOTDSolvedToday || false;
320319

321320
if (isSolved) {
322321
return res.status(200).json({ message: "Daily challenge already solved", isSolved });

server/models/Challenge.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ const challengeSchema = new mongoose.Schema({
77
points: { type: Number, required: true },
88
problemLink: { type: String, required: true },
99
platform: { type: String, required: true },
10+
isPOTD: { type: Boolean, default: false },
1011
createdAt: { type: Date, default: Date.now },
1112
solvedUsers: [
1213
{

server/models/User.js

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,9 +56,11 @@ const userSchema = new mongoose.Schema(
5656
points: { type: Number, default: 0 },
5757
rank: { type: Number, default: 0 },
5858
streak: { type: Number, default: 0 },
59+
isPOTDSolvedToday: { type: Boolean, default: false },
60+
platformsUpdatedAt: { type: Date, default: null },
5961

6062
// All challenges solved (non-POTD)
61-
solveChallenges:
63+
solveChallenges:
6264
{
6365
easy: [
6466
{

server/routes/userRoutes.js

Lines changed: 32 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,22 @@ router.post("/:username/update-platforms", async (req, res) => {
2626
const updatedUser = req.body;
2727

2828
const user = await User.findOne({ username });
29+
if (!user) {
30+
return res.status(404).json({ message: "User not found" });
31+
}
32+
33+
// Check if platforms data is stale (older than 1 hour)
34+
const oneHourAgo = new Date(Date.now() - 60 * 60 * 1000); // 1 hour ago
35+
const isStale = !user.platformsUpdatedAt || user.platformsUpdatedAt < oneHourAgo;
36+
37+
if (!isStale) {
38+
return res.status(429).json({
39+
message: "Platform data was updated recently. Please wait before updating again.",
40+
nextUpdateTime: new Date(user.platformsUpdatedAt.getTime() + 60 * 60 * 1000) // 1 hour from last update
41+
});
42+
}
43+
44+
// Update platform data
2945
if(updatedUser.leetCode?.username) {
3046
user.leetCode.solved = updatedUser.leetCode.solved || user.leetCode.solved || 0;
3147
user.leetCode.rank = updatedUser.leetCode.rank || user.leetCode.rank || 0;
@@ -38,14 +54,25 @@ router.post("/:username/update-platforms", async (req, res) => {
3854
user.codeforces.rating = updatedUser.codeforces.rating || user.codeforces.rating || 0;
3955
}
4056

57+
// Set the timestamp for when platforms were last updated
58+
user.platformsUpdatedAt = new Date();
59+
4160
await user.save();
42-
console.log(`Platforms data updated for ${username}`);
43-
if (!user) {
44-
console.log("User not found");
45-
}
61+
62+
console.log(`Platforms data updated for ${username} at ${user.platformsUpdatedAt}`);
63+
res.status(200).json({
64+
message: "Platform data updated successfully",
65+
updatedAt: user.platformsUpdatedAt,
66+
nextUpdateTime: new Date(user.platformsUpdatedAt.getTime() + 60 * 60 * 1000)
67+
});
4668

4769
} catch (error) {
48-
console.error(error);
70+
console.error("Error updating platforms:", error);
71+
auditService.error("Update platforms failed", error, {
72+
requestId: req.auditContext?.requestId,
73+
username: req.params.username
74+
});
75+
res.status(500).json({ message: "Internal server error" });
4976
}
5077
});
5178

server/server.js

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -41,10 +41,10 @@ const startServer = async () => {
4141
try {
4242
await connectDB();
4343

44-
// const PORT = process.env.PORT || 5000;
45-
// app.listen(PORT, () => {
46-
// console.log(`Server is running at ${PORT}`);
47-
// });
44+
const PORT = process.env.PORT || 5000;
45+
app.listen(PORT, () => {
46+
console.log(`Server is running at ${PORT}`);
47+
});
4848
} catch (error) {
4949
console.error('Failed to start server:', error.message);
5050
process.exit(1);
@@ -53,4 +53,4 @@ const startServer = async () => {
5353

5454
startServer();
5555

56-
export const handler = serverless(app);
56+
// export const handler = serverless(app);

server/utils/postPOTD.js

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,13 @@ export const postPotdChallenge = async (userId, timestamp, challengeId, difficul
115115
{ session }
116116
);
117117

118+
// Set the isPOTDSolvedToday flag to true
119+
await User.updateOne(
120+
{ _id: userId },
121+
{ $set: { isPOTDSolvedToday: true } },
122+
{ session }
123+
);
124+
118125
return {
119126
success: true,
120127
message: 'POTD challenge recorded successfully',

server/utils/streakResetJob.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ const resetUserStreaks = async () => {
6262
const bulkOps = streakResetUpdates.map(user => ({
6363
updateOne: {
6464
filter: { _id: user._id },
65-
update: { $set: { streak: 0 } }
65+
update: { $set: { streak: 0, isPOTDSolvedToday: false } }
6666
}
6767
}));
6868

0 commit comments

Comments
 (0)