Skip to content

Commit cd25fb3

Browse files
committed
fix(server): prevent submit crash and adjust proxy trust
1 parent 468749e commit cd25fb3

4 files changed

Lines changed: 158 additions & 29 deletions

File tree

crackcode/server/server.js

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -212,9 +212,9 @@ import progressRoutes from "./src/modules/Career Map/progress/progress.routes.js
212212

213213
const app = express();
214214

215-
// If the app is running behind a proxy/load-balancer (nginx, cloud LB),
216-
// enable trust proxy so req.secure and x-forwarded-* headers are populated.
217-
app.set('trust proxy', true);
215+
// If the app is running behind a single proxy/load-balancer (nginx, cloud LB),
216+
// trust one hop so req.secure and x-forwarded-* headers are populated.
217+
app.set('trust proxy', 1);
218218

219219
const __filename = fileURLToPath(import.meta.url);
220220
const __dirname = path.dirname(__filename);
@@ -261,6 +261,7 @@ const allowedOrigins = [
261261
process.env.CLIENT_URL,
262262
...(process.env.CLIENT_URLS ? process.env.CLIENT_URLS.split(",") : []),
263263
]
264+
.filter(Boolean)
264265
.map((origin) => origin.trim())
265266
.filter(Boolean);
266267

crackcode/server/src/modules/codeEditor/codeEditor.submit.controller.js

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import { submitSolutionService } from "./codeEditor.submit.service.js";
55
export const submitSolution = async (req, res) => {
66
try {
77
const userId = req.user?._id || req.userId;
8-
const { questionId, code, languageId } = req.body;
8+
const { questionId, code, languageId, sourceArea, testCases, collectionName } = req.body;
99

1010
// Validate inputs
1111
if (!questionId) {
@@ -36,14 +36,17 @@ export const submitSolution = async (req, res) => {
3636
});
3737
}
3838

39-
console.log(`📤 Submit called - userId: ${userId}, questionId: ${questionId}`);
39+
console.log(`📤 Submit called - userId: ${userId}, questionId: ${questionId}, sourceArea: ${sourceArea}, collectionName: ${collectionName}, testCasesProvided: ${testCases?.length || 0}`);
4040

4141
// Call submit service
4242
const result = await submitSolutionService({
4343
userId,
4444
questionId,
4545
code,
46-
languageId
46+
languageId,
47+
sourceArea: sourceArea || 'learn_page',
48+
collectionName: collectionName || null,
49+
preloadedTestCases: testCases || []
4750
});
4851

4952
// Return response to frontend

crackcode/server/src/modules/codeEditor/codeEditor.submit.service.js

Lines changed: 137 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -60,60 +60,174 @@ const findQuestionInLearnCollections = async (problemId) => {
6060
return null;
6161
};
6262

63+
// Helper: Search for question in caseLog collection
64+
const findQuestionInCaseLog = async (problemId) => {
65+
try {
66+
console.log(` ↳ Searching in caseLog collection for problemId: "${problemId}"`);
67+
68+
// Search by problemId first (in case caseLog documents have this field)
69+
let question = await mongoose.connection.db
70+
.collection('caseLog')
71+
.findOne({ problemId });
72+
73+
if (question) {
74+
console.log(` ✅ Found in caseLog collection by problemId`);
75+
return question;
76+
}
77+
78+
// Try searching by _id if it's a valid MongoDB ObjectId
79+
if (problemId.match(/^[0-9a-f]{24}$/i)) {
80+
try {
81+
console.log(` ↳ CaseLog: Trying to find by _id (ObjectId): "${problemId}"`);
82+
const objId = new mongoose.Types.ObjectId(problemId);
83+
question = await mongoose.connection.db
84+
.collection('caseLog')
85+
.findOne({ _id: objId });
86+
87+
if (question) {
88+
console.log(` ✅ Found in caseLog collection by _id (ObjectId)`);
89+
return question;
90+
}
91+
} catch (objIdErr) {
92+
console.log(` ⚠️ CaseLog ObjectId conversion failed: ${objIdErr.message}`);
93+
}
94+
95+
// Fallback: try searching with string _id
96+
try {
97+
console.log(` ↳ CaseLog: Trying to find by _id (string): "${problemId}"`);
98+
question = await mongoose.connection.db
99+
.collection('caseLog')
100+
.findOne({ _id: problemId });
101+
102+
if (question) {
103+
console.log(` ✅ Found in caseLog collection by _id (string)`);
104+
return question;
105+
}
106+
} catch (strIdErr) {
107+
console.log(` ⚠️ CaseLog string _id search failed: ${strIdErr.message}`);
108+
}
109+
}
110+
111+
console.log(` ❌ CaseLog: Document not found with problemId: "${problemId}"`);
112+
} catch (err) {
113+
console.log(` ⚠️ CaseLog collection search failed:`, err.message);
114+
}
115+
return null;
116+
};
117+
63118
// Submit solution and award rewards only on first-time completion
64119
export const submitSolutionService = async ({
65120
userId,
66121
questionId,
67122
code,
68-
languageId
123+
languageId,
124+
sourceArea = 'learn_page',
125+
collectionName = null,
126+
preloadedTestCases = []
69127
}) => {
70128
try {
71129
// Debug: Log received identifiers
72-
console.log("📝 Submit received - userId:", userId, "questionId:", questionId);
130+
console.log("📝 Submit received - userId:", userId, "questionId:", questionId, "sourceArea:", sourceArea, "collectionName:", collectionName, "preloadedTestCases:", preloadedTestCases.length);
73131

74132
// Step 1: Validate user exists
75133
const user = await User.findById(userId);
76134
if (!user) {
77135
throw new Error("User not found");
78136
}
79137

80-
// Step 2: Validate question exists search learn collections first, then main Question collection
81-
console.log(`🔍 Searching for question with problemId: "${questionId}"`);
82-
83-
// Try learn collections first (where most questions live)
84-
let question = await findQuestionInLearnCollections(questionId);
85-
86-
// If not found in learn collections, try main Question collection
138+
let testCases = [];
139+
let question = null;
140+
141+
console.log(`📊 TEST CASES ANALYSIS:`);
142+
console.log(` - preloadedTestCases.length: ${preloadedTestCases?.length || 0}`);
143+
console.log(` - preloadedTestCases: ${JSON.stringify(preloadedTestCases?.slice(0, 1))}`);
144+
console.log(` - collectionName provided: ${collectionName ? 'YES - ' + collectionName : 'NO'}`);
145+
console.log(` - sourceArea: ${sourceArea}`);
146+
147+
console.log(`🔍 Searching for question with questionId: "${questionId}"`);
148+
149+
if (collectionName) {
150+
console.log(` → Direct lookup in collection: "${collectionName}"`);
151+
console.log(` → Searching for problemId: ${questionId}`);
152+
question = await mongoose.connection.db
153+
.collection(collectionName)
154+
.findOne({ problemId: questionId });
155+
156+
console.log(` → Result from problemId search: ${question ? 'FOUND' : 'NOT FOUND'}`);
157+
158+
if (!question && questionId.match(/^[0-9a-f]{24}$/i)) {
159+
try {
160+
const objId = new mongoose.Types.ObjectId(questionId);
161+
console.log(` → Trying ObjectId search for: ${objId}`);
162+
question = await mongoose.connection.db
163+
.collection(collectionName)
164+
.findOne({ _id: objId });
165+
console.log(` → Result from ObjectId search: ${question ? 'FOUND' : 'NOT FOUND'}`);
166+
} catch (err) {
167+
console.log(` → ObjectId conversion failed: ${err.message}`);
168+
console.log(` → Trying string _id search for: ${questionId}`);
169+
question = await mongoose.connection.db
170+
.collection(collectionName)
171+
.findOne({ _id: questionId });
172+
console.log(` → Result from string _id search: ${question ? 'FOUND' : 'NOT FOUND'}`);
173+
}
174+
}
175+
176+
if (question) {
177+
console.log(` ✅ Found in collection: ${collectionName}, has test_cases: ${question.test_cases ? question.test_cases.length : 0}`);
178+
} else {
179+
console.log(` ❌ Not found in collection: ${collectionName}, attempting fallback...`);
180+
}
181+
} else {
182+
console.log(`⚠️ WARNING: collectionName is NULL! This means frontend didn't pass it.`);
183+
}
184+
185+
if (!question && sourceArea === 'case_log') {
186+
console.log(` → Fallback: Searching caseLog collection`);
187+
question = await findQuestionInCaseLog(questionId);
188+
}
189+
87190
if (!question) {
88-
console.log(` ↳ Not found in learn collections, trying main Question collection`);
191+
console.log(` → Fallback: Searching learn collections`);
192+
question = await findQuestionInLearnCollections(questionId);
193+
}
194+
195+
if (!question) {
196+
console.log(` ↳ Fallback: Trying main Question collection`);
89197
question = await Question.findOne({ problemId: questionId });
90198
if (question) {
91199
console.log(` ✅ Found in main Question collection`);
92200
}
93201
}
94-
95-
// Last resort: try finding by _id (MongoDB ID)
202+
96203
if (!question && questionId.match(/^[0-9a-f]{24}$/i)) {
97-
console.log(` ↳ Not found by problemId, trying _id: "${questionId}"`);
204+
console.log(` ↳ Fallback: Trying by MongoDB _id`);
98205
question = await Question.findById(questionId);
99206
if (question) {
100207
console.log(` ✅ Found by MongoDB _id`);
101208
}
102209
}
103-
104-
console.log(`✅ Question found:`, question ? `${question.problemId} (${question.original?.title})` : "NOT FOUND");
105-
if (!question) {
106-
throw new Error("Question not found");
210+
211+
if (preloadedTestCases && preloadedTestCases.length > 0) {
212+
console.log(`✅ Using preloaded test cases from frontend (${preloadedTestCases.length} cases)`);
213+
testCases = preloadedTestCases;
214+
} else {
215+
console.log(`⚠️ WARNING: preloadedTestCases is EMPTY or NULL. Will use question document test cases.`);
107216
}
108217

109-
// Step 3: Get test cases from question
110-
let testCases = question.test_cases || [];
111218
if (testCases.length === 0) {
112-
throw new Error("No test cases found for this question");
219+
testCases = question?.test_cases || question?.testCases || [];
113220
}
114221

222+
const questionDifficulty = question?.difficulty || null;
223+
224+
console.log(`📦 Test cases found: ${testCases.length}`);
115225
console.log(`📦 Raw test cases from DB:`, JSON.stringify(testCases.slice(0, 1), null, 2));
116226

227+
if (testCases.length === 0) {
228+
throw new Error("No test cases found for this question");
229+
}
230+
117231
// Normalize test cases: ensure they have the right field names
118232
testCases = testCases.map(tc => ({
119233
input: tc.input || tc.input_data || "",
@@ -137,7 +251,7 @@ export const submitSolutionService = async ({
137251
console.log(` Test cases:`, JSON.stringify(visibleTestCases, null, 2));
138252
// Record this attempt (creates progress entry if first time)
139253
try {
140-
await incrementAttempt(userId, questionId, 'coding', question.difficulty || null, languageName);
254+
await incrementAttempt(userId, questionId, 'coding', questionDifficulty, languageName);
141255
} catch (err) {
142256
console.warn('Could not increment attempt in progress service:', err.message);
143257
}
@@ -180,7 +294,7 @@ export const submitSolutionService = async ({
180294
console.log(`ℹ️ Question already completed by user ${userId}`);
181295
// Ensure progress record marks solved=true and has metadata
182296
try {
183-
await markQuestionSolved(userId, questionId, 'coding', 'code_editor', passedCount, visibleTestCases.length, question.difficulty || null, languageName);
297+
await markQuestionSolved(userId, questionId, 'coding', 'code_editor', passedCount, visibleTestCases.length, questionDifficulty, languageName);
184298
} catch (err) {
185299
console.warn('Could not mark question solved on alreadyCompleted path:', err.message);
186300
}
@@ -192,7 +306,7 @@ export const submitSolutionService = async ({
192306
}
193307

194308
// Step 7: First-time completion - award rewards
195-
const rewardConfig = getRewardConfig("coding", question.difficulty);
309+
const rewardConfig = getRewardConfig("coding", questionDifficulty);
196310
const earnedXP = rewardConfig.xp || 25;
197311
const earnedTokens = rewardConfig.tokens || 10;
198312

crackcode/server/src/modules/learn/question.controller.js

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -290,6 +290,17 @@ export const getChallengesCollection = async (req, res) => {
290290

291291
const items = await mongoose.connection.db.collection(collectionName).find({}).toArray();
292292

293+
// Debug logging for caseLog collection
294+
if (collectionName === 'caseLog' && items.length > 0) {
295+
console.log(`📚 CASELOG COLLECTION DEBUG:`);
296+
console.log(` Total items: ${items.length}`);
297+
console.log(` First item structure: ${JSON.stringify(Object.keys(items[0]))}`);
298+
console.log(` First item has test_cases: ${items[0].test_cases ? 'YES - ' + items[0].test_cases.length + ' tests' : 'NO'}`);
299+
console.log(` First item has testCases: ${items[0].testCases ? 'YES - ' + items[0].testCases.length + ' tests' : 'NO'}`);
300+
console.log(` First item has problemId: ${items[0].problemId || 'NO'}`);
301+
console.log(` First item _id: ${items[0]._id}`);
302+
}
303+
293304
return res.status(200).json({ success: true, count: items.length, collectionName, data: items });
294305
} catch (error) {
295306
console.error('❌ getChallengesCollection error:', error);

0 commit comments

Comments
 (0)