fix: complete bug hunt patch set (authentication, APIs, UI guards)#6
fix: complete bug hunt patch set (authentication, APIs, UI guards)#6hdRutvik114 wants to merge 22 commits intoJavaScript-Mastery-Pro:mainfrom
Conversation
Made-with: Cursor
Made-with: Cursor
Made-with: Cursor
Made-with: Cursor
Made-with: Cursor
Made-with: Cursor
Made-with: Cursor
Made-with: Cursor
Made-with: Cursor
Made-with: Cursor
Made-with: Cursor
Made-with: Cursor
Made-with: Cursor
Made-with: Cursor
Made-with: Cursor
Made-with: Cursor
Made-with: Cursor
Made-with: Cursor
Made-with: Cursor
Made-with: Cursor
Made-with: Cursor
Made-with: Cursor
📝 WalkthroughWalkthroughThe PR implements authorization tightening across API endpoints by requiring Changes
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes Poem
🚥 Pre-merge checks | ✅ 2 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 4
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (3)
app/dashboard/assignments/AssignmentsClient.tsx (1)
101-226:⚠️ Potential issue | 🟡 Minor
AssignmentDrawerdoesn't guard againstNaNfromdaysUntil.
DeadlineBadgenow returns early on non-finitedays(good), butAssignmentDraweruses the samedaysat lines 212–225 without the guard. When the deadline is unparseable,daysisNaN, so everydays < 0/days <= 7comparison isfalseand the UI renders"NaNd left"plus a ~5% emerald progress bar — confusing for the user.🛠️ Suggested fix
const days = daysUntil(assignment.deadline); + const daysValid = Number.isFinite(days); const col = COLUMNS.find((c) => c.id === assignment.kanbanStatus);Then gate the "Deadline visual" block on
daysValid(or render an "Invalid date" placeholder), e.g.:- <div> + {daysValid ? ( + <div> <p className="text-xs text-gray-400 mb-2">Deadline status</p> ... - </div> + </div> + ) : ( + <p className="text-xs text-gray-400">Deadline: invalid date</p> + )}🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@app/dashboard/assignments/AssignmentsClient.tsx` around lines 101 - 226, The Deadline visual uses days computed by daysUntil without guarding for NaN; compute a daysValid flag (e.g., const daysValid = Number.isFinite(days)) after calling daysUntil and then conditionally render the "Deadline visual" block (or render an "Invalid deadline" placeholder) when daysValid is false; update the block that references days (the progress bar width logic and the text span) to only run when daysValid is true, and otherwise show a clear message like "Invalid deadline" so DeadlineBadge, days, and the progress bar are not used when the deadline is unparseable.app/api/grades/route.ts (1)
14-20:⚠️ Potential issue | 🟠 MajorValidation bypass when
maxMarksis omitted —markscan exceed the default 100.The
refineat lines 14–20 short-circuits totruewhenevermaxMarksis falsy, allowing requests withmarks: 9999and nomaxMarksto pass validation. The POST handler then defaultsmaxMarksto 100 at line 73, andcalcGrade(9999, 100)yields a grade of'A+', persisting invalid data.Fix by applying the default before validation:
🛠️ Suggested fix
const GradeSchema = z.object({ studentId: z.string().min(1), studentName: z.string().min(1), subject: z.string().min(1), marks: z.number().min(0), - maxMarks: z.number().min(1).optional(), + maxMarks: z.number().min(1).default(100), term: z.string().optional(), }).refine( - (data) => !data.maxMarks || data.marks <= data.maxMarks, + (data) => data.marks <= data.maxMarks, { message: 'marks must be less than or equal to maxMarks', path: ['marks'], } )Then update line 73:
- const max = data.maxMarks ?? 100 + const max = data.maxMarksWith
.default(100),data.maxMarksis always defined in the parsed output, so the refine now properly validates against the effective cap.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@app/api/grades/route.ts` around lines 14 - 20, The refine currently returns true when maxMarks is falsy, letting huge marks slip through because the POST handler later defaults maxMarks to 100; update the Zod schema so maxMarks has a default of 100 (e.g., use .default(100) on the maxMarks schema) before the .refine that checks (data) => data.marks <= data.maxMarks, so validation uses the effective cap; ensure the POST handler relies on the parsed schema output (with defaulted maxMarks) when calling calcGrade and persisting data (referencing the schema where .refine is defined and the POST handler that calls calcGrade).app/api/attendance/route.ts (1)
33-58:⚠️ Potential issue | 🟠 MajorReuse this normalization on writes too.
normalizeDateonly runs inGET;POSTstill persists any non-emptydatestring. An ISO timestamp can be stored as-is, thenGET ?date=YYYY-MM-DDwon’t find it and the uniqueness key can split the same calendar day.🐛 Proposed direction
- // Normalize calendar dates to YYYY-MM-DD without UTC day-shift (date-only input) - const normalizeDate = (dateStr: string): string | null => { +// Move this helper to module scope and apply it in both GET and POST before querying/upserting. +const normalizeDate = (dateStr: string): string | null => { const trimmed = dateStr.trim(); const ymd = /^(\d{4})-(\d{2})-(\d{2})$/.exec(trimmed); if (ymd) { const y = Number(ymd[1]); const m = Number(ymd[2]); const day = Number(ymd[3]); const local = new Date(y, m - 1, day); if ( local.getFullYear() === y && local.getMonth() === m - 1 && local.getDate() === day ) { return `${ymd[1]}-${ymd[2]}-${ymd[3]}`; } return null; } try { const d = new Date(trimmed); if (isNaN(d.getTime())) return null; return d.toISOString().split("T")[0]; } catch { return null; } - }; +};Then normalize
record.datebefore building the bulk/singlefindOneAndUpdatefilters.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@app/api/attendance/route.ts` around lines 33 - 58, The POST path is persisting raw date strings while normalizeDate is only used in GET, causing mismatched keys; before building the bulk or single upsert filters (the findOneAndUpdate calls) convert incoming record.date using normalizeDate (or reject/null-check if normalization fails) so stored dates are in YYYY-MM-DD form; update the code paths that prepare the bulk operations and the single-record upsert to call normalizeDate(record.date) and use that normalized value in both the persisted document and the filter to ensure uniqueness per calendar day.
🧹 Nitpick comments (2)
app/api/grades/route.ts (1)
76-81: Minor:upsert+ unconditionalstatus: 201.
findOneAndUpdate({ upsert: true })returns an updated-or-inserted document; returning201 Createdeven when the existing record was merely updated is misleading. Consider using the third callback/result argument orrawResult: trueto distinguish, and return200on update.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@app/api/grades/route.ts` around lines 76 - 81, The response always returns 201 despite using Grade.findOneAndUpdate with upsert; change the call to include rawResult: true (e.g., Grade.findOneAndUpdate(..., { upsert: true, new: true, rawResult: true })) then inspect the returned object's lastErrorObject.updatedExisting to determine whether the operation updated an existing document or inserted a new one, use result.value (or result.value document) as the payload, and return status 200 for updates and 201 for inserts instead of unconditionally returning 201.app/dashboard/OverviewClient.tsx (1)
203-242: Parse error bodies defensively.These
json()calls happen beforeres.ok; if an error response is empty or non-JSON, the thrownSyntaxErrorhides the endpoint/status-specific message.♻️ Proposed helper
+ const readJson = async (res: Response) => res.json().catch(() => ({})); + - const students = await studentsRes.json(); + const students = await readJson(studentsRes); if (!studentsRes.ok) { throw new Error( typeof students.error === "string" ? students.error : `Students failed (${studentsRes.status})`, ); } - const assignmentsData = await assignmentsRes.json(); + const assignmentsData = await readJson(assignmentsRes); if (!assignmentsRes.ok) { throw new Error( typeof assignmentsData.error === "string" ? assignmentsData.error : `Assignments failed (${assignmentsRes.status})`, ); } - const attendanceRaw = await attendanceRes.json(); + const attendanceRaw = await readJson(attendanceRes);🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@app/dashboard/OverviewClient.tsx` around lines 203 - 242, The fetch response JSON parsing should be defensive: don't call studentsRes.json(), assignmentsRes.json(), attendanceRes.json(), gradesRes.json(), or announcementsRes.json() unconditionally before checking res.ok because non-JSON or empty error bodies will throw and hide the endpoint/status message; instead, for each response (studentsRes, assignmentsRes, attendanceRes, gradesRes, announcementsRes) first check res.ok and only parse JSON on success, or if res.ok is false attempt to parse JSON inside a try/catch and fall back to res.text() (or a default message) to construct the thrown Error (use the existing local names students, assignmentsData, attendanceRaw, gradesRaw, announcementsRaw when available) so errors include either the parsed error string or the HTTP status.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@app/api/announcements/`[id]/route.ts:
- Around line 30-45: The handler must validate that the parsed request body is a
plain object before iterating or accessing properties: before the ALLOWED_FIELDS
loop and the legacy map (the code that writes to sanitizedBody and checks
body.body), add a guard that checks typeof body === 'object' && body !== null &&
!Array.isArray(body); if that check fails, return a 400 error (bad request)
instead of proceeding. Update the logic that builds sanitizedBody (the for...of
over ALLOWED_FIELDS and the legacy body→content mapping) to run only after this
guard so Announcement.findOneAndUpdate is never called with a malformed body.
In `@app/api/grades/`[id]/route.ts:
- Around line 53-73: Sanitize and enforce numeric/range validation for marks and
maxMarks before building updatePayload: ensure sanitizedBody.marks and
sanitizedBody.maxMarks are numbers (use nextMarks/nextMax logic) and within
schema bounds (marks >= 0, maxMarks >= 1, and maxMarks !== 0) — drop any
non-number or out-of-range values from updatePayload instead of blindly
spreading sanitizedBody; compute updatePayload.grade with calcGrade(nextMarks,
nextMax) only when both numbers are valid; and call Grade.findOneAndUpdate with
the option { new: true, runValidators: true } so Mongoose schema validators
(min: 0 / min: 1) are enforced.
In `@app/dashboard/students/StudentsClient.tsx`:
- Around line 574-579: The catch block that handles student fetch failures
clears students, total, and pages but leaves the selection state intact; update
the catch in StudentsClient.tsx to also clear the selection by calling the
selection state setter (e.g., setSelected(...) or reset the selected Set/array
that backs `selected`) so the bulk-delete bar is disabled when the fetch fails.
In `@models/Attendance.ts`:
- Around line 27-30: Add a migration or startup task that drops the legacy
unique index on { studentId: 1, date: 1 } and ensures the new unique index {
teacherId: 1, studentId: 1, date: 1 } exists: connect to MongoDB using the same
mongoose connection used by AttendanceSchema/Attendance model, call
collection.dropIndex for the legacy index (use dropIndex("studentId_1_date_1")
or dropIndex({ studentId: 1, date: 1 }) in a try/catch to be idempotent), then
call collection.createIndex({ teacherId: 1, studentId: 1, date: 1 }, { unique:
true }) or ensureIndexes on AttendanceSchema to create the new index; wrap this
in a migration runner or startup hook so it runs once and logs success/failure.
---
Outside diff comments:
In `@app/api/attendance/route.ts`:
- Around line 33-58: The POST path is persisting raw date strings while
normalizeDate is only used in GET, causing mismatched keys; before building the
bulk or single upsert filters (the findOneAndUpdate calls) convert incoming
record.date using normalizeDate (or reject/null-check if normalization fails) so
stored dates are in YYYY-MM-DD form; update the code paths that prepare the bulk
operations and the single-record upsert to call normalizeDate(record.date) and
use that normalized value in both the persisted document and the filter to
ensure uniqueness per calendar day.
In `@app/api/grades/route.ts`:
- Around line 14-20: The refine currently returns true when maxMarks is falsy,
letting huge marks slip through because the POST handler later defaults maxMarks
to 100; update the Zod schema so maxMarks has a default of 100 (e.g., use
.default(100) on the maxMarks schema) before the .refine that checks (data) =>
data.marks <= data.maxMarks, so validation uses the effective cap; ensure the
POST handler relies on the parsed schema output (with defaulted maxMarks) when
calling calcGrade and persisting data (referencing the schema where .refine is
defined and the POST handler that calls calcGrade).
In `@app/dashboard/assignments/AssignmentsClient.tsx`:
- Around line 101-226: The Deadline visual uses days computed by daysUntil
without guarding for NaN; compute a daysValid flag (e.g., const daysValid =
Number.isFinite(days)) after calling daysUntil and then conditionally render the
"Deadline visual" block (or render an "Invalid deadline" placeholder) when
daysValid is false; update the block that references days (the progress bar
width logic and the text span) to only run when daysValid is true, and otherwise
show a clear message like "Invalid deadline" so DeadlineBadge, days, and the
progress bar are not used when the deadline is unparseable.
---
Nitpick comments:
In `@app/api/grades/route.ts`:
- Around line 76-81: The response always returns 201 despite using
Grade.findOneAndUpdate with upsert; change the call to include rawResult: true
(e.g., Grade.findOneAndUpdate(..., { upsert: true, new: true, rawResult: true
})) then inspect the returned object's lastErrorObject.updatedExisting to
determine whether the operation updated an existing document or inserted a new
one, use result.value (or result.value document) as the payload, and return
status 200 for updates and 201 for inserts instead of unconditionally returning
201.
In `@app/dashboard/OverviewClient.tsx`:
- Around line 203-242: The fetch response JSON parsing should be defensive:
don't call studentsRes.json(), assignmentsRes.json(), attendanceRes.json(),
gradesRes.json(), or announcementsRes.json() unconditionally before checking
res.ok because non-JSON or empty error bodies will throw and hide the
endpoint/status message; instead, for each response (studentsRes,
assignmentsRes, attendanceRes, gradesRes, announcementsRes) first check res.ok
and only parse JSON on success, or if res.ok is false attempt to parse JSON
inside a try/catch and fall back to res.text() (or a default message) to
construct the thrown Error (use the existing local names students,
assignmentsData, attendanceRaw, gradesRaw, announcementsRaw when available) so
errors include either the parsed error string or the HTTP status.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: 96ea12f2-24fc-41c8-81a7-3ec7b7a3c8df
📒 Files selected for processing (14)
app/api/announcements/[id]/route.tsapp/api/assignments/[id]/route.tsapp/api/assignments/route.tsapp/api/attendance/route.tsapp/api/grades/[id]/route.tsapp/api/grades/route.tsapp/api/profile/route.tsapp/api/students/[id]/route.tsapp/api/students/route.tsapp/dashboard/OverviewClient.tsxapp/dashboard/assignments/AssignmentsClient.tsxapp/dashboard/attendance/AttendanceClient.tsxapp/dashboard/students/StudentsClient.tsxmodels/Attendance.ts
| // Sanitize: only allow whitelisted fields (map legacy `body` → `content`) | ||
| const sanitizedBody: Record<string, unknown> = {} | ||
| for (const key of ALLOWED_FIELDS) { | ||
| if (key in body) { | ||
| sanitizedBody[key] = body[key] | ||
| } | ||
| } | ||
| if ( | ||
| typeof body.body === 'string' && | ||
| sanitizedBody.content === undefined | ||
| ) { | ||
| sanitizedBody.content = body.body | ||
| } | ||
|
|
||
| const announcement = await Announcement.findOneAndUpdate( | ||
| { _id: id }, | ||
| { _id: id, teacherId: userId }, |
There was a problem hiding this comment.
Harden against non-object JSON bodies.
req.json() happily resolves for null, arrays, strings, etc. On those inputs, key in body (line 33) throws TypeError for primitives and silently iterates array indices, and body.body (line 38) works unexpectedly. It's caught by the outer try and surfaces as a 500 instead of a clean 400.
🛠️ Suggested fix
let body
try {
body = await req.json()
} catch {
return NextResponse.json({ error: 'Invalid JSON request body' }, { status: 400 })
}
+ if (body === null || typeof body !== 'object' || Array.isArray(body)) {
+ return NextResponse.json({ error: 'Request body must be a JSON object' }, { status: 400 })
+ }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@app/api/announcements/`[id]/route.ts around lines 30 - 45, The handler must
validate that the parsed request body is a plain object before iterating or
accessing properties: before the ALLOWED_FIELDS loop and the legacy map (the
code that writes to sanitizedBody and checks body.body), add a guard that checks
typeof body === 'object' && body !== null && !Array.isArray(body); if that check
fails, return a 400 error (bad request) instead of proceeding. Update the logic
that builds sanitizedBody (the for...of over ALLOWED_FIELDS and the legacy
body→content mapping) to run only after this guard so
Announcement.findOneAndUpdate is never called with a malformed body.
| const nextMarks = | ||
| typeof sanitizedBody.marks === 'number' ? sanitizedBody.marks : existing.marks | ||
| const nextMax = | ||
| typeof sanitizedBody.maxMarks === 'number' | ||
| ? sanitizedBody.maxMarks | ||
| : existing.maxMarks | ||
|
|
||
| if (nextMarks > nextMax) { | ||
| return NextResponse.json( | ||
| { error: 'marks must be less than or equal to maxMarks' }, | ||
| { status: 400 }, | ||
| ) | ||
| } | ||
|
|
||
| const updatePayload: Record<string, unknown> = { ...sanitizedBody } | ||
| updatePayload.grade = calcGrade(nextMarks, nextMax) | ||
|
|
||
| const grade = await Grade.findOneAndUpdate( | ||
| { _id: id }, | ||
| sanitizedBody, | ||
| { new: true } | ||
| { _id: id, teacherId: userId }, | ||
| { $set: updatePayload }, | ||
| { new: true }, |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Description: Inspect grade update paths for explicit Mongoose update validation.
rg -n -C3 'Grade\.findOneAndUpdate|runValidators|marks must be less than or equal to maxMarks' --type=tsRepository: JavaScript-Mastery-Pro/discord-challenge
Length of output: 3404
🏁 Script executed:
cat -n app/api/grades/[id]/route.ts | head -100Repository: JavaScript-Mastery-Pro/discord-challenge
Length of output: 3928
🏁 Script executed:
# Check the Zod schema definition for grades
rg -n -B5 -A10 'marks|maxMarks' app/api/grades/\[id\]/route.ts | head -60Repository: JavaScript-Mastery-Pro/discord-challenge
Length of output: 1548
🏁 Script executed:
# Check the complete Mongoose Grade schema and validators
cat -n models/Grade.tsRepository: JavaScript-Mastery-Pro/discord-challenge
Length of output: 2735
🏁 Script executed:
# Check if there are other PUT/PATCH handlers for grades that might have similar issues
rg -n 'Grade\.findOneAndUpdate|runValidators' app/api/grades/ --type=tsRepository: JavaScript-Mastery-Pro/discord-challenge
Length of output: 238
Add explicit validation for marks and maxMarks before spreading them into the update payload.
Currently, non-number values pass through sanitizedBody into updatePayload (line 67) without type validation, even though nextMarks/nextMax skip them. Additionally, the findOneAndUpdate call lacks runValidators: true, so schema constraints (min: 0 for marks, min: 1 for maxMarks) are bypassed. The pre-hook only validates marks > maxMarks but ignores negative or zero values, risking invalid grades and division-by-zero in calcGrade.
Proposed fix
+ if (
+ 'marks' in sanitizedBody &&
+ (typeof sanitizedBody.marks !== 'number' || sanitizedBody.marks < 0)
+ ) {
+ return NextResponse.json(
+ { error: 'marks must be a non-negative number' },
+ { status: 400 },
+ )
+ }
+
+ if (
+ 'maxMarks' in sanitizedBody &&
+ (typeof sanitizedBody.maxMarks !== 'number' || sanitizedBody.maxMarks < 1)
+ ) {
+ return NextResponse.json(
+ { error: 'maxMarks must be a positive number' },
+ { status: 400 },
+ )
+ }
+
const nextMarks =
typeof sanitizedBody.marks === 'number' ? sanitizedBody.marks : existing.marks
const nextMax =
typeof sanitizedBody.maxMarks === 'number'
? sanitizedBody.maxMarks
: existing.maxMarks🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@app/api/grades/`[id]/route.ts around lines 53 - 73, Sanitize and enforce
numeric/range validation for marks and maxMarks before building updatePayload:
ensure sanitizedBody.marks and sanitizedBody.maxMarks are numbers (use
nextMarks/nextMax logic) and within schema bounds (marks >= 0, maxMarks >= 1,
and maxMarks !== 0) — drop any non-number or out-of-range values from
updatePayload instead of blindly spreading sanitizedBody; compute
updatePayload.grade with calcGrade(nextMarks, nextMax) only when both numbers
are valid; and call Grade.findOneAndUpdate with the option { new: true,
runValidators: true } so Mongoose schema validators (min: 0 / min: 1) are
enforced.
| } catch (e) { | ||
| toast(e instanceof Error ? e.message : "Failed to load students", "error"); | ||
| setStudents([]); | ||
| setTotal(0); | ||
| setPages(1); | ||
| } finally { |
There was a problem hiding this comment.
Clear stale selections when the student fetch fails.
This clears the list but leaves selected intact, so the bulk-delete bar can stay enabled for students no longer visible after an error.
🐛 Proposed fix
} catch (e) {
toast(e instanceof Error ? e.message : "Failed to load students", "error");
setStudents([]);
+ setSelected(new Set());
setTotal(0);
setPages(1);📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| } catch (e) { | |
| toast(e instanceof Error ? e.message : "Failed to load students", "error"); | |
| setStudents([]); | |
| setTotal(0); | |
| setPages(1); | |
| } finally { | |
| } catch (e) { | |
| toast(e instanceof Error ? e.message : "Failed to load students", "error"); | |
| setStudents([]); | |
| setSelected(new Set()); | |
| setTotal(0); | |
| setPages(1); | |
| } finally { |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@app/dashboard/students/StudentsClient.tsx` around lines 574 - 579, The catch
block that handles student fetch failures clears students, total, and pages but
leaves the selection state intact; update the catch in StudentsClient.tsx to
also clear the selection by calling the selection state setter (e.g.,
setSelected(...) or reset the selected Set/array that backs `selected`) so the
bulk-delete bar is disabled when the fetch fails.
| AttendanceSchema.index( | ||
| { teacherId: 1, studentId: 1, date: 1 }, | ||
| { unique: true }, | ||
| ) |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Description: Search for migrations or index-management code touching Attendance indexes.
rg -n -C3 'Attendance.*index|studentId.*date|teacherId.*studentId.*date|dropIndex|syncIndexes' --type=ts --type=jsRepository: JavaScript-Mastery-Pro/discord-challenge
Length of output: 1593
Add database migration to drop the legacy unique index.
The schema update correctly adds { teacherId: 1, studentId: 1, date: 1 } as a unique index, and application code uses the new pattern. However, no migration or index cleanup code was found in the repository. Existing databases will retain the old { studentId: 1, date: 1 } unique index, which will continue to block upserts with the same student/date across different teachers.
Create a migration script or startup task to:
- Drop the old unique index on
{ studentId: 1, date: 1 } - Ensure the new unique index on
{ teacherId: 1, studentId: 1, date: 1 }exists
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@models/Attendance.ts` around lines 27 - 30, Add a migration or startup task
that drops the legacy unique index on { studentId: 1, date: 1 } and ensures the
new unique index { teacherId: 1, studentId: 1, date: 1 } exists: connect to
MongoDB using the same mongoose connection used by AttendanceSchema/Attendance
model, call collection.dropIndex for the legacy index (use
dropIndex("studentId_1_date_1") or dropIndex({ studentId: 1, date: 1 }) in a
try/catch to be idempotent), then call collection.createIndex({ teacherId: 1,
studentId: 1, date: 1 }, { unique: true }) or ensureIndexes on AttendanceSchema
to create the new index; wrap this in a migration runner or startup hook so it
runs once and logs success/failure.
This PR includes a complete set of bug fixes that were identified during the bug-hunt round and resolved one by one in separate commits.
What’s fixed:
Each fix has been committed separately using:
fix: <short description>Status:
Summary by CodeRabbit
Release Notes
Bug Fixes
Security & Authorization
Improvements