From c97f95b80d39d058b970d41ecc31d58b22058564 Mon Sep 17 00:00:00 2001 From: theniteshdev <0911nitesh@gmail.com> Date: Sat, 18 Apr 2026 21:10:36 +0530 Subject: [PATCH 01/56] fix: use ObjectId for teacherId reference --- models/Announcement.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/models/Announcement.ts b/models/Announcement.ts index cf5260c..433e93b 100644 --- a/models/Announcement.ts +++ b/models/Announcement.ts @@ -14,7 +14,7 @@ export interface IAnnouncement { const AnnouncementSchema = new Schema( { - teacherId: { type: String, required: true, index: true }, + teacherId: { type: Schema.Types.ObjectId, ref: 'Teacher', required: true, index: true }, title: { type: String, required: true }, content: { type: String, required: true }, audience: { type: String, default: 'All' }, From a34e2a4dc8706b3ee43ba0478124937f079d992d Mon Sep 17 00:00:00 2001 From: theniteshdev <0911nitesh@gmail.com> Date: Sat, 18 Apr 2026 21:17:46 +0530 Subject: [PATCH 02/56] fix: restrict audience with enum values --- models/Announcement.ts | 23 ++++++++++++++++------- 1 file changed, 16 insertions(+), 7 deletions(-) diff --git a/models/Announcement.ts b/models/Announcement.ts index 433e93b..c6f6350 100644 --- a/models/Announcement.ts +++ b/models/Announcement.ts @@ -14,15 +14,24 @@ export interface IAnnouncement { const AnnouncementSchema = new Schema( { - teacherId: { type: Schema.Types.ObjectId, ref: 'Teacher', required: true, index: true }, - title: { type: String, required: true }, - content: { type: String, required: true }, - audience: { type: String, default: 'All' }, - category: { type: String, enum: ['academic', 'events', 'admin', 'general'], default: 'general' }, + teacherId: { + type: Schema.Types.ObjectId, + ref: "Teacher", + required: true, + index: true, + }, + title: { type: String, required: true, trim: true, minlength: 3 }, + content: { type: String, required: true, trim: true, minlength: 5 }, + audience: { type: String, enum: ["All", "CS-A", "CS-B"], default: "All" }, + category: { + type: String, + enum: ["academic", "events", "admin", "general"], + default: "general", + }, pinned: { type: Boolean, default: false }, }, - { timestamps: true } -) + { timestamps: true }, +); export const Announcement = models.Announcement ?? model('Announcement', AnnouncementSchema) From dd9f0903ab3239ca3d659daaff38f44dcfebfaf6 Mon Sep 17 00:00:00 2001 From: theniteshdev <0911nitesh@gmail.com> Date: Sat, 18 Apr 2026 21:24:25 +0530 Subject: [PATCH 03/56] feat: add compound indexes for teacherId and pinned queries to improve performance --- models/Announcement.ts | 30 +++++++++++++++++------------- 1 file changed, 17 insertions(+), 13 deletions(-) diff --git a/models/Announcement.ts b/models/Announcement.ts index c6f6350..94a0368 100644 --- a/models/Announcement.ts +++ b/models/Announcement.ts @@ -1,22 +1,21 @@ -import mongoose, { Schema, model, models } from 'mongoose' +import mongoose, { Schema, model, models } from "mongoose"; export interface IAnnouncement { - _id: mongoose.Types.ObjectId - teacherId: string - title: string - content: string - audience: string - category: 'academic' | 'events' | 'admin' | 'general' - pinned: boolean - createdAt: Date - updatedAt: Date + _id: mongoose.Types.ObjectId; + teacherId: string; + title: string; + content: string; + audience: string; + category: "academic" | "events" | "admin" | "general"; + pinned: boolean; + createdAt: Date; + updatedAt: Date; } const AnnouncementSchema = new Schema( { teacherId: { - type: Schema.Types.ObjectId, - ref: "Teacher", + type: String, required: true, index: true, }, @@ -33,5 +32,10 @@ const AnnouncementSchema = new Schema( { timestamps: true }, ); +AnnouncementSchema.index({ teacherId: 1, pinned: 1 }); + +AnnouncementSchema.index({ teacherId: 1, createdAt: -1 }); + export const Announcement = - models.Announcement ?? model('Announcement', AnnouncementSchema) + models.Announcement ?? + model("Announcement", AnnouncementSchema); \ No newline at end of file From d419ed86028f76d0f0f6f417088a9d3ad9b50b46 Mon Sep 17 00:00:00 2001 From: theniteshdev <0911nitesh@gmail.com> Date: Sat, 18 Apr 2026 21:26:48 +0530 Subject: [PATCH 04/56] fix: replace teacherId string with ObjectId reference --- models/Assignment.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/models/Assignment.ts b/models/Assignment.ts index 874f1fc..8c25627 100644 --- a/models/Assignment.ts +++ b/models/Assignment.ts @@ -17,7 +17,7 @@ export interface IAssignment { const AssignmentSchema = new Schema( { - teacherId: { type: String, required: true, index: true }, + teacherId: { type: Schema.Types.ObjectId, ref: 'Teacher', required: true, index: true }, title: { type: String, required: true }, description: { type: String, default: '' }, subject: { type: String, required: true }, From 93125d9b0601a6c7ba387789496da47d25350a42 Mon Sep 17 00:00:00 2001 From: theniteshdev <0911nitesh@gmail.com> Date: Sat, 18 Apr 2026 21:29:21 +0530 Subject: [PATCH 05/56] fix: add validation for title, description, subject, and class fields --- models/Assignment.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/models/Assignment.ts b/models/Assignment.ts index 8c25627..eba5f31 100644 --- a/models/Assignment.ts +++ b/models/Assignment.ts @@ -18,10 +18,10 @@ export interface IAssignment { const AssignmentSchema = new Schema( { teacherId: { type: Schema.Types.ObjectId, ref: 'Teacher', required: true, index: true }, - title: { type: String, required: true }, - description: { type: String, default: '' }, - subject: { type: String, required: true }, - class: { type: String, required: true }, + title: {type: String, required: true, trim: true, minlength: 3}, + description: { type: String, default: '', maxlength: 1000 }, + subject: {enum: ['Mathematics', 'Data Structures', 'Operating Systems', 'DBMS', 'Computer Networks']}, + class: { enum: ['CS-A', 'CS-B'] }, deadline: { type: Date, required: true }, status: { type: String, enum: ['active', 'closed'], default: 'active' }, kanbanStatus: { type: String, enum: ['todo', 'in_progress', 'submitted'], default: 'todo' }, From 07e9f161d1865e1c7b825bc2a4171f9f00d3a070 Mon Sep 17 00:00:00 2001 From: theniteshdev <0911nitesh@gmail.com> Date: Sat, 18 Apr 2026 21:30:32 +0530 Subject: [PATCH 06/56] fix: enforce deadline to be in the future --- models/Assignment.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/models/Assignment.ts b/models/Assignment.ts index eba5f31..0856064 100644 --- a/models/Assignment.ts +++ b/models/Assignment.ts @@ -22,7 +22,10 @@ const AssignmentSchema = new Schema( description: { type: String, default: '', maxlength: 1000 }, subject: {enum: ['Mathematics', 'Data Structures', 'Operating Systems', 'DBMS', 'Computer Networks']}, class: { enum: ['CS-A', 'CS-B'] }, - deadline: { type: Date, required: true }, + deadline: { validate: { + validator: (value: Date) => value > new Date(), + message: 'Deadline must be in the future' + } }, status: { type: String, enum: ['active', 'closed'], default: 'active' }, kanbanStatus: { type: String, enum: ['todo', 'in_progress', 'submitted'], default: 'todo' }, maxMarks: { type: Number, default: 100 }, From 89e8c8ddb08a5354db3885109b8fe165243616e7 Mon Sep 17 00:00:00 2001 From: theniteshdev <0911nitesh@gmail.com> Date: Sat, 18 Apr 2026 21:31:52 +0530 Subject: [PATCH 07/56] fix: add constraints to maxMarks field --- models/Assignment.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/models/Assignment.ts b/models/Assignment.ts index 0856064..6be2a55 100644 --- a/models/Assignment.ts +++ b/models/Assignment.ts @@ -28,7 +28,7 @@ const AssignmentSchema = new Schema( } }, status: { type: String, enum: ['active', 'closed'], default: 'active' }, kanbanStatus: { type: String, enum: ['todo', 'in_progress', 'submitted'], default: 'todo' }, - maxMarks: { type: Number, default: 100 }, + maxMarks: { type: Number, default: 100, min: 1, max: 1000 }, }, { timestamps: true } ) From 2f5d289365e221a1330d4e57a0e4600a0ef9dfd4 Mon Sep 17 00:00:00 2001 From: theniteshdev <0911nitesh@gmail.com> Date: Sat, 18 Apr 2026 21:33:41 +0530 Subject: [PATCH 08/56] feat: add indexes for performance optimization --- models/Assignment.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/models/Assignment.ts b/models/Assignment.ts index 6be2a55..f70689d 100644 --- a/models/Assignment.ts +++ b/models/Assignment.ts @@ -32,5 +32,7 @@ const AssignmentSchema = new Schema( }, { timestamps: true } ) - +AssignmentSchema.index({ teacherId: 1, class: 1 }); +AssignmentSchema.index({ status: 1 }); +AssignmentSchema.index({ deadline: 1 }); export const Assignment = models.Assignment ?? model('Assignment', AssignmentSchema) From ed4412849b6c6804b6106b792f63dd1c8fb2792a Mon Sep 17 00:00:00 2001 From: theniteshdev <0911nitesh@gmail.com> Date: Sat, 18 Apr 2026 21:34:44 +0530 Subject: [PATCH 09/56] fix: change attendance date type from string to Date --- models/Attendance.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/models/Attendance.ts b/models/Attendance.ts index 3f05fb1..defc556 100644 --- a/models/Attendance.ts +++ b/models/Attendance.ts @@ -18,7 +18,7 @@ const AttendanceSchema = new Schema( studentId: { type: Schema.Types.ObjectId, ref: 'Student', required: true }, studentName: { type: String, required: true }, class: { type: String, required: true }, - date: { type: String, required: true }, + date: { type: Date, required: true }, status: { type: String, enum: ['present', 'absent', 'late'], required: true }, }, { timestamps: true } From d4fe8e17a38796e690977b6b19086c83afac9adc Mon Sep 17 00:00:00 2001 From: theniteshdev <0911nitesh@gmail.com> Date: Sat, 18 Apr 2026 21:35:52 +0530 Subject: [PATCH 10/56] fix: strengthen unique index with teacherId --- models/Attendance.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/models/Attendance.ts b/models/Attendance.ts index defc556..33fee9c 100644 --- a/models/Attendance.ts +++ b/models/Attendance.ts @@ -24,6 +24,6 @@ const AttendanceSchema = new Schema( { timestamps: true } ) -AttendanceSchema.index({ studentId: 1, date: 1 }, { unique: true }) +AttendanceSchema.index( { teacherId: 1, studentId: 1, date: 1 }, { unique: true }) export const Attendance = models.Attendance ?? model('Attendance', AttendanceSchema) From 9e9c61441ce8153be1a724316f16b0b107ed294e Mon Sep 17 00:00:00 2001 From: theniteshdev <0911nitesh@gmail.com> Date: Sat, 18 Apr 2026 21:36:39 +0530 Subject: [PATCH 11/56] fix: replace teacherId with ObjectId reference --- models/Attendance.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/models/Attendance.ts b/models/Attendance.ts index 33fee9c..a88cb67 100644 --- a/models/Attendance.ts +++ b/models/Attendance.ts @@ -14,7 +14,7 @@ export interface IAttendance { const AttendanceSchema = new Schema( { - teacherId: { type: String, required: true, index: true }, + teacherId: { type: Schema.Types.ObjectId, ref: 'Teacher', required: true, index: true }, studentId: { type: Schema.Types.ObjectId, ref: 'Student', required: true }, studentName: { type: String, required: true }, class: { type: String, required: true }, From bababd37f502307515447def910577b32401b45a Mon Sep 17 00:00:00 2001 From: theniteshdev <0911nitesh@gmail.com> Date: Sat, 18 Apr 2026 21:39:03 +0530 Subject: [PATCH 12/56] fix: remove redundant studentName and class fields --- models/Attendance.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/models/Attendance.ts b/models/Attendance.ts index a88cb67..20a09f4 100644 --- a/models/Attendance.ts +++ b/models/Attendance.ts @@ -16,8 +16,8 @@ const AttendanceSchema = new Schema( { teacherId: { type: Schema.Types.ObjectId, ref: 'Teacher', required: true, index: true }, studentId: { type: Schema.Types.ObjectId, ref: 'Student', required: true }, - studentName: { type: String, required: true }, - class: { type: String, required: true }, + // revome the studentName because If student name changes → data becomes inconsistent + // remove because Data inconsistency date: { type: Date, required: true }, status: { type: String, enum: ['present', 'absent', 'late'], required: true }, }, From dd0b2ec393a12de2f8aa70286cfef303523ddb0a Mon Sep 17 00:00:00 2001 From: theniteshdev <0911nitesh@gmail.com> Date: Sat, 18 Apr 2026 21:42:10 +0530 Subject: [PATCH 13/56] fix: improve attendance schema consistency and data integrity --- models/Attendance.ts | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/models/Attendance.ts b/models/Attendance.ts index 20a09f4..177468a 100644 --- a/models/Attendance.ts +++ b/models/Attendance.ts @@ -24,6 +24,16 @@ const AttendanceSchema = new Schema( { timestamps: true } ) -AttendanceSchema.index( { teacherId: 1, studentId: 1, date: 1 }, { unique: true }) +// existing uniqueness constraint +AttendanceSchema.index({ studentId: 1, date: 1 }, { unique: true }) + +// NEW: optimize teacher dashboard queries +AttendanceSchema.index({ teacherId: 1, date: 1 }) + +// NEW: optimize class-wise filtering +AttendanceSchema.index({ teacherId: 1, class: 1 }) + +// NEW: optimize status filtering (present/absent/late) +AttendanceSchema.index({ teacherId: 1, status: 1 }) export const Attendance = models.Attendance ?? model('Attendance', AttendanceSchema) From 91865869fe098ef0fd58faa8ce99b9a1c112f658 Mon Sep 17 00:00:00 2001 From: theniteshdev <0911nitesh@gmail.com> Date: Sat, 18 Apr 2026 21:43:34 +0530 Subject: [PATCH 14/56] fix: replace teacherId with ObjectId reference --- models/Grade.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/models/Grade.ts b/models/Grade.ts index 311b757..fbbbdb4 100644 --- a/models/Grade.ts +++ b/models/Grade.ts @@ -16,7 +16,7 @@ export interface IGrade { const GradeSchema = new Schema( { - teacherId: { type: String, required: true, index: true }, + teacherId: { type: Schema.Types.ObjectId, ref: "Teacher", required: true, index: true }, studentId: { type: Schema.Types.ObjectId, ref: "Student", required: true }, studentName: { type: String, required: true }, subject: { type: String, required: true }, From eb1dace8e4e25ed5282ee9c206ee3c933f2b6e66 Mon Sep 17 00:00:00 2001 From: theniteshdev <0911nitesh@gmail.com> Date: Sat, 18 Apr 2026 21:45:03 +0530 Subject: [PATCH 15/56] fix: remove redundant studentName --- models/Grade.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/models/Grade.ts b/models/Grade.ts index fbbbdb4..ff704e0 100644 --- a/models/Grade.ts +++ b/models/Grade.ts @@ -18,7 +18,7 @@ const GradeSchema = new Schema( { teacherId: { type: Schema.Types.ObjectId, ref: "Teacher", required: true, index: true }, studentId: { type: Schema.Types.ObjectId, ref: "Student", required: true }, - studentName: { type: String, required: true }, + // removes the studentName because can cause- data inconsistency subject: { type: String, required: true }, marks: { type: Number, From d12bc5e0a59d23e9f8cf32f710cf13ce0282870b Mon Sep 17 00:00:00 2001 From: theniteshdev <0911nitesh@gmail.com> Date: Sat, 18 Apr 2026 21:46:56 +0530 Subject: [PATCH 16/56] fix: add enum validation for subject and term fields --- models/Grade.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/models/Grade.ts b/models/Grade.ts index ff704e0..6bb974f 100644 --- a/models/Grade.ts +++ b/models/Grade.ts @@ -19,7 +19,7 @@ const GradeSchema = new Schema( teacherId: { type: Schema.Types.ObjectId, ref: "Teacher", required: true, index: true }, studentId: { type: Schema.Types.ObjectId, ref: "Student", required: true }, // removes the studentName because can cause- data inconsistency - subject: { type: String, required: true }, + subject: {enum: ['Mathematics', 'Data Structures', 'Operating Systems', 'DBMS', 'Computer Networks']}, marks: { type: Number, required: true, @@ -27,7 +27,7 @@ const GradeSchema = new Schema( }, maxMarks: { type: Number, default: 100, min: 1 }, grade: { type: String, default: "" }, - term: { type: String, default: "Term 1" }, + term: { enum: ["Term 1", "Term 2"] }, }, { timestamps: true }, ); From 2c48ed08ec67c006045ce42a789455ef3ada7fbc Mon Sep 17 00:00:00 2001 From: theniteshdev <0911nitesh@gmail.com> Date: Sat, 18 Apr 2026 21:50:46 +0530 Subject: [PATCH 17/56] fix: define subject field type with enum validation --- models/Grade.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/models/Grade.ts b/models/Grade.ts index 6bb974f..fecdf28 100644 --- a/models/Grade.ts +++ b/models/Grade.ts @@ -19,7 +19,9 @@ const GradeSchema = new Schema( teacherId: { type: Schema.Types.ObjectId, ref: "Teacher", required: true, index: true }, studentId: { type: Schema.Types.ObjectId, ref: "Student", required: true }, // removes the studentName because can cause- data inconsistency - subject: {enum: ['Mathematics', 'Data Structures', 'Operating Systems', 'DBMS', 'Computer Networks']}, + subject: { type: String, + enum: ['Mathematics', 'Data Structures', 'Operating Systems', 'DBMS', 'Computer Networks'], + required: true,}, marks: { type: Number, required: true, From fcf5426553e178c74f596c180eae10122e5f21f0 Mon Sep 17 00:00:00 2001 From: theniteshdev <0911nitesh@gmail.com> Date: Sat, 18 Apr 2026 21:51:18 +0530 Subject: [PATCH 18/56] fix: add type and default value for term field --- models/Grade.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/models/Grade.ts b/models/Grade.ts index fecdf28..e679297 100644 --- a/models/Grade.ts +++ b/models/Grade.ts @@ -29,7 +29,9 @@ const GradeSchema = new Schema( }, maxMarks: { type: Number, default: 100, min: 1 }, grade: { type: String, default: "" }, - term: { enum: ["Term 1", "Term 2"] }, + term: { type: String, + enum: ["Term 1", "Term 2"], + default: "Term 1", }, }, { timestamps: true }, ); From 6e0aaba4447f2dbc5959a4809f9e92131ce7d25f Mon Sep 17 00:00:00 2001 From: theniteshdev <0911nitesh@gmail.com> Date: Sat, 18 Apr 2026 21:52:09 +0530 Subject: [PATCH 19/56] fix: restore studentName field to prevent data inconsistency issues --- models/Grade.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/models/Grade.ts b/models/Grade.ts index e679297..cb1cc5a 100644 --- a/models/Grade.ts +++ b/models/Grade.ts @@ -18,7 +18,7 @@ const GradeSchema = new Schema( { teacherId: { type: Schema.Types.ObjectId, ref: "Teacher", required: true, index: true }, studentId: { type: Schema.Types.ObjectId, ref: "Student", required: true }, - // removes the studentName because can cause- data inconsistency + studentName: { type: String, required: true }, subject: { type: String, enum: ['Mathematics', 'Data Structures', 'Operating Systems', 'DBMS', 'Computer Networks'], required: true,}, From fef2da76358f70d0a51d262e33ad156d85f1fcaa Mon Sep 17 00:00:00 2001 From: theniteshdev <0911nitesh@gmail.com> Date: Sat, 18 Apr 2026 21:54:50 +0530 Subject: [PATCH 20/56] fix: add trimming to required student fields to ensure data consistency --- models/Student.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/models/Student.ts b/models/Student.ts index 85ed899..35af2ea 100644 --- a/models/Student.ts +++ b/models/Student.ts @@ -18,9 +18,9 @@ export interface IStudent { const StudentSchema = new Schema( { teacherId: { type: String, required: true, index: true }, - name: { type: String, required: true }, - rollNo: { type: String, required: true }, - class: { type: String, required: true }, + name: { type: String, required: true, trim: true }, + rollNo: { type: String, required: true, trim: true }, + class: { type: String, required: true, trime: true }, email: { type: String, default: '' }, phone: { type: String, default: '' }, address: { type: String, default: '' }, From 4d4e8dab32e2f4d408c91cb933f05e36e742abcd Mon Sep 17 00:00:00 2001 From: theniteshdev <0911nitesh@gmail.com> Date: Sat, 18 Apr 2026 21:55:18 +0530 Subject: [PATCH 21/56] fix: add email format validation to student schema --- models/Student.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/models/Student.ts b/models/Student.ts index 35af2ea..5fb2cb9 100644 --- a/models/Student.ts +++ b/models/Student.ts @@ -21,7 +21,10 @@ const StudentSchema = new Schema( name: { type: String, required: true, trim: true }, rollNo: { type: String, required: true, trim: true }, class: { type: String, required: true, trime: true }, - email: { type: String, default: '' }, + email: { type: String, + default: '', + trim: true, + match: [/^\S+@\S+\.\S+$/, 'Invalid email format'], }, phone: { type: String, default: '' }, address: { type: String, default: '' }, parentName: { type: String, default: '' }, From ecb9abe1a5464e4d7b093e93a6ac316d465e1bdd Mon Sep 17 00:00:00 2001 From: theniteshdev <0911nitesh@gmail.com> Date: Sat, 18 Apr 2026 21:56:13 +0530 Subject: [PATCH 22/56] fix: normalize optional student fields by adding trimming --- models/Student.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/models/Student.ts b/models/Student.ts index 5fb2cb9..e5171ca 100644 --- a/models/Student.ts +++ b/models/Student.ts @@ -25,10 +25,10 @@ const StudentSchema = new Schema( default: '', trim: true, match: [/^\S+@\S+\.\S+$/, 'Invalid email format'], }, - phone: { type: String, default: '' }, - address: { type: String, default: '' }, - parentName: { type: String, default: '' }, - parentPhone: { type: String, default: '' }, + phone: { type: String, default: '', trim: true }, + address: { type: String, default: '', trim: true }, + parentName: { type: String, default: '', trim: true }, + parentPhone: { type: String, default: '', trim: true }, }, { timestamps: true } ) From be7c74f3c449c057bb0e288a97c352fd6747548d Mon Sep 17 00:00:00 2001 From: theniteshdev <0911nitesh@gmail.com> Date: Sat, 18 Apr 2026 21:56:54 +0530 Subject: [PATCH 23/56] fix: normalize rollNo to uppercase to enforce uniqueness consistency --- models/Student.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/models/Student.ts b/models/Student.ts index e5171ca..f3253e9 100644 --- a/models/Student.ts +++ b/models/Student.ts @@ -19,7 +19,7 @@ const StudentSchema = new Schema( { teacherId: { type: String, required: true, index: true }, name: { type: String, required: true, trim: true }, - rollNo: { type: String, required: true, trim: true }, + rollNo: { type: String, required: true, trim: true, uppercase: true, }, class: { type: String, required: true, trime: true }, email: { type: String, default: '', From 56ad31bf6c4e202eb7197d57e656f99cd5047930 Mon Sep 17 00:00:00 2001 From: theniteshdev <0911nitesh@gmail.com> Date: Sat, 18 Apr 2026 21:58:02 +0530 Subject: [PATCH 24/56] fix: add index for teacherId and class to optimize student queries --- models/Student.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/models/Student.ts b/models/Student.ts index f3253e9..e089081 100644 --- a/models/Student.ts +++ b/models/Student.ts @@ -33,6 +33,6 @@ const StudentSchema = new Schema( { timestamps: true } ) -StudentSchema.index({ teacherId: 1, rollNo: 1 }, { unique: true }) +StudentSchema.index({ teacherId: 1, class: 1 }, { unique: true }); export const Student = models.Student ?? model('Student', StudentSchema) From b5aa1a07d2ee62d0003f42e9f53e364aadecd88d Mon Sep 17 00:00:00 2001 From: theniteshdev <0911nitesh@gmail.com> Date: Sat, 18 Apr 2026 21:59:31 +0530 Subject: [PATCH 25/56] fix: enforce minimum length validation on required student fields --- models/Student.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/models/Student.ts b/models/Student.ts index e089081..4c5f19e 100644 --- a/models/Student.ts +++ b/models/Student.ts @@ -17,10 +17,10 @@ export interface IStudent { const StudentSchema = new Schema( { - teacherId: { type: String, required: true, index: true }, - name: { type: String, required: true, trim: true }, - rollNo: { type: String, required: true, trim: true, uppercase: true, }, - class: { type: String, required: true, trime: true }, + teacherId: { type: String, required: true, index: true, minlength: 1 }, + name: { type: String, required: true, trim: true, minlength: 1 }, + rollNo: { type: String, required: true, trim: true, uppercase: true,minlength: 1 }, + class: { type: String, required: true, trime: true,minlength: 1 }, email: { type: String, default: '', trim: true, From f259b72bebdc639b8fb306df60fc36de44f9f793 Mon Sep 17 00:00:00 2001 From: theniteshdev <0911nitesh@gmail.com> Date: Sat, 18 Apr 2026 22:00:55 +0530 Subject: [PATCH 26/56] fix: add trimming to teacher fields for data consistency --- models/Teacher.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/models/Teacher.ts b/models/Teacher.ts index 837bbc8..3e21aa2 100644 --- a/models/Teacher.ts +++ b/models/Teacher.ts @@ -23,9 +23,9 @@ export interface ITeacher { const TeacherSchema = new Schema( { clerkId: { type: String, required: true, unique: true }, - name: { type: String, required: true }, - email: { type: String, required: true }, - department: { type: String, default: "" }, + name: { type: String, required: true, trim: true }, + email: { type: String, required: true, trim: true }, + department: { type: String, default: "", trim: true }, subjects: { type: [String], default: [] }, phone: { type: String, default: "" }, bio: { type: String, default: "" }, From 22dab3eb2d03c288b6713a11ba23846f43dc6fce Mon Sep 17 00:00:00 2001 From: theniteshdev <0911nitesh@gmail.com> Date: Sat, 18 Apr 2026 22:01:20 +0530 Subject: [PATCH 27/56] fix: add email format validation to teacher schema --- models/Teacher.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/models/Teacher.ts b/models/Teacher.ts index 3e21aa2..a2351a2 100644 --- a/models/Teacher.ts +++ b/models/Teacher.ts @@ -24,7 +24,10 @@ const TeacherSchema = new Schema( { clerkId: { type: String, required: true, unique: true }, name: { type: String, required: true, trim: true }, - email: { type: String, required: true, trim: true }, + email: { type: String, + required: true, + trim: true, + match: [/^\S+@\S+\.\S+$/, 'Invalid email format'], }, department: { type: String, default: "", trim: true }, subjects: { type: [String], default: [] }, phone: { type: String, default: "" }, From 2f6f66de3f60b2bb535b5b277edd3699a99b6960 Mon Sep 17 00:00:00 2001 From: theniteshdev <0911nitesh@gmail.com> Date: Sat, 18 Apr 2026 22:01:56 +0530 Subject: [PATCH 28/56] fix: validate subjects array to prevent empty or invalid values --- models/Teacher.ts | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/models/Teacher.ts b/models/Teacher.ts index a2351a2..fa5cfda 100644 --- a/models/Teacher.ts +++ b/models/Teacher.ts @@ -29,7 +29,12 @@ const TeacherSchema = new Schema( trim: true, match: [/^\S+@\S+\.\S+$/, 'Invalid email format'], }, department: { type: String, default: "", trim: true }, - subjects: { type: [String], default: [] }, + subjects: { type: [String], + default: [], + validate: { + validator: (arr: string[]) => arr.every(s => typeof s === 'string' && s.trim().length > 0), + message: 'Subjects must be non-empty strings', + }, }, phone: { type: String, default: "" }, bio: { type: String, default: "" }, academicHistory: { From a9b06a0bba5f86099c9c803d556ad75b6bd4cc69 Mon Sep 17 00:00:00 2001 From: theniteshdev <0911nitesh@gmail.com> Date: Sat, 18 Apr 2026 22:02:39 +0530 Subject: [PATCH 29/56] fix: add trimming to academicHistory fields --- models/Teacher.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/models/Teacher.ts b/models/Teacher.ts index fa5cfda..7c39478 100644 --- a/models/Teacher.ts +++ b/models/Teacher.ts @@ -40,9 +40,9 @@ const TeacherSchema = new Schema( academicHistory: { type: [ { - year: { type: String, required: true }, - title: { type: String, required: true }, - description: { type: String }, + year: { type: String, required: true, trim: true }, + title: { type: String, required: true, trim: true }, + description: { type: String, trim: true }, }, ], default: [], From ac536766765a3caa34d7342880165ee30cd7aae1 Mon Sep 17 00:00:00 2001 From: theniteshdev <0911nitesh@gmail.com> Date: Sat, 18 Apr 2026 22:03:35 +0530 Subject: [PATCH 30/56] fix: normalize optional teacher fields by adding trimming --- models/Teacher.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/models/Teacher.ts b/models/Teacher.ts index 7c39478..c82d106 100644 --- a/models/Teacher.ts +++ b/models/Teacher.ts @@ -35,8 +35,8 @@ const TeacherSchema = new Schema( validator: (arr: string[]) => arr.every(s => typeof s === 'string' && s.trim().length > 0), message: 'Subjects must be non-empty strings', }, }, - phone: { type: String, default: "" }, - bio: { type: String, default: "" }, + phone: { type: String, default: "", trim: true }, + bio: { type: String, default: "", trim: true }, academicHistory: { type: [ { From 39fab09feff55ed2922e8754f2280f3119d0be88 Mon Sep 17 00:00:00 2001 From: theniteshdev <0911nitesh@gmail.com> Date: Sat, 18 Apr 2026 22:04:07 +0530 Subject: [PATCH 31/56] fix: add indexes for department and subjects to improve query performance --- models/Teacher.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/models/Teacher.ts b/models/Teacher.ts index c82d106..d7c64ef 100644 --- a/models/Teacher.ts +++ b/models/Teacher.ts @@ -51,4 +51,7 @@ const TeacherSchema = new Schema( { timestamps: true }, ); +TeacherSchema.index({ department: 1 }); +TeacherSchema.index({ subjects: 1 }); + export const Teacher = models.Teacher ?? model('Teacher', TeacherSchema) From 3601932f87ec1a713b09f153fdfb27e682f21385 Mon Sep 17 00:00:00 2001 From: theniteshdev <0911nitesh@gmail.com> Date: Sat, 18 Apr 2026 22:08:10 +0530 Subject: [PATCH 32/56] fix: add missing await for findOneAndUpdate in grades POST endpoint --- app/api/grades/route.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/api/grades/route.ts b/app/api/grades/route.ts index b9da63d..d37502d 100644 --- a/app/api/grades/route.ts +++ b/app/api/grades/route.ts @@ -73,7 +73,7 @@ export async function POST(req: NextRequest) { const max = data.maxMarks! const term = data.term ?? 'Term 1' - const grade = Grade.findOneAndUpdate( + const grade = await Grade.findOneAndUpdate( { teacherId: userId, studentId: data.studentId, subject: data.subject, term }, { $set: { ...data, term, teacherId: userId, grade: calcGrade(data.marks, max) } }, { upsert: true, new: true } From e415ec138649b9724f5b0c9331ad4f275b40fc29 Mon Sep 17 00:00:00 2001 From: theniteshdev <0911nitesh@gmail.com> Date: Sat, 18 Apr 2026 22:08:55 +0530 Subject: [PATCH 33/56] fix: provide default value for maxMarks to prevent runtime errors --- app/api/grades/route.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/api/grades/route.ts b/app/api/grades/route.ts index d37502d..61b8c9f 100644 --- a/app/api/grades/route.ts +++ b/app/api/grades/route.ts @@ -70,7 +70,7 @@ export async function POST(req: NextRequest) { if (!parsed.success) return NextResponse.json({ error: parsed.error.flatten() }, { status: 400 }) const data = parsed.data - const max = data.maxMarks! + const max = data.maxMarks ?? 100; const term = data.term ?? 'Term 1' const grade = await Grade.findOneAndUpdate( From 7706a23a3218aab491375336384c930b3c2f192b Mon Sep 17 00:00:00 2001 From: theniteshdev <0911nitesh@gmail.com> Date: Sat, 18 Apr 2026 22:09:31 +0530 Subject: [PATCH 34/56] fix: correct grade calculation boundary for A+ grade --- app/api/grades/route.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/api/grades/route.ts b/app/api/grades/route.ts index 61b8c9f..6d62e61 100644 --- a/app/api/grades/route.ts +++ b/app/api/grades/route.ts @@ -21,7 +21,7 @@ const GradeSchema = z.object({ function calcGrade(marks: number, max: number): string { const pct = (marks / max) * 100 - if (pct > 90) return 'A+' + if (pct >= 90) return 'A+' if (pct >= 80) return 'A' if (pct >= 70) return 'B+' if (pct >= 60) return 'B' From 0eef839778bda9bf2ba03ab75947c434e3b04f6d Mon Sep 17 00:00:00 2001 From: theniteshdev <0911nitesh@gmail.com> Date: Sat, 18 Apr 2026 22:10:37 +0530 Subject: [PATCH 35/56] fix: validate studentId as a valid MongoDB ObjectId --- app/api/grades/route.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/api/grades/route.ts b/app/api/grades/route.ts index 6d62e61..edab58a 100644 --- a/app/api/grades/route.ts +++ b/app/api/grades/route.ts @@ -5,7 +5,7 @@ import { Grade } from '@/models/Grade' import { z } from 'zod' const GradeSchema = z.object({ - studentId: z.string().min(1), + studentId: z.string().regex(/^[0-9a-fA-F]{24}$/, 'Invalid studentId'), studentName: z.string().min(1), subject: z.string().min(1), marks: z.number().min(0), From 9511c9a5e9de6b2189d7bf678248e0ad1d716776 Mon Sep 17 00:00:00 2001 From: theniteshdev <0911nitesh@gmail.com> Date: Sat, 18 Apr 2026 22:12:29 +0530 Subject: [PATCH 36/56] fix: convert studentId to ObjectId in GET query for correct filtering --- app/api/grades/route.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/app/api/grades/route.ts b/app/api/grades/route.ts index edab58a..601ae96 100644 --- a/app/api/grades/route.ts +++ b/app/api/grades/route.ts @@ -3,6 +3,7 @@ import { NextRequest, NextResponse } from 'next/server' import { connectDB } from '@/lib/mongodb' import { Grade } from '@/models/Grade' import { z } from 'zod' +import mongoose from "mongoose"; const GradeSchema = z.object({ studentId: z.string().regex(/^[0-9a-fA-F]{24}$/, 'Invalid studentId'), @@ -41,7 +42,9 @@ export async function GET(req: NextRequest) { const subject = searchParams.get('subject') const query: Record = { teacherId: userId } - if (studentId) query.studentId = studentId + if (studentId && mongoose.Types.ObjectId.isValid(studentId)) { + query.studentId = new mongoose.Types.ObjectId(studentId); + } if (subject) query.subject = subject const grades = await Grade.find(query).sort({ createdAt: -1 }).lean() From dab7aea230376a946481779d771dfa65b832f56e Mon Sep 17 00:00:00 2001 From: theniteshdev <0911nitesh@gmail.com> Date: Sat, 18 Apr 2026 22:14:38 +0530 Subject: [PATCH 37/56] fix: prevent exposure of internal error stack in API response --- app/api/grades/route.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/api/grades/route.ts b/app/api/grades/route.ts index 601ae96..3b6232d 100644 --- a/app/api/grades/route.ts +++ b/app/api/grades/route.ts @@ -51,7 +51,7 @@ export async function GET(req: NextRequest) { return NextResponse.json(grades) } catch (error) { console.error('GET /api/grades error:', error instanceof Error ? error.message : error) - return NextResponse.json({ error: error instanceof Error ? error.stack : 'Internal server error' }, { status: 500 }) + return NextResponse.json({ error: 'Internal server error' }, { status: 500 }) } } @@ -86,6 +86,6 @@ export async function POST(req: NextRequest) { if (error instanceof Error) { console.error('POST /api/grades error:', error.message) } - return NextResponse.json({ error: error instanceof Error ? error.stack : 'Internal server error' }, { status: 500 }) + return NextResponse.json({ error: 'Internal server error' }, { status: 500 }) } } From cf32081132820cf0377de366ed03a43613ff26fd Mon Sep 17 00:00:00 2001 From: theniteshdev <0911nitesh@gmail.com> Date: Sat, 18 Apr 2026 22:15:49 +0530 Subject: [PATCH 38/56] fix: normalize input fields by trimming strings before database operation --- app/api/grades/route.ts | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/app/api/grades/route.ts b/app/api/grades/route.ts index 3b6232d..ab7b49c 100644 --- a/app/api/grades/route.ts +++ b/app/api/grades/route.ts @@ -72,7 +72,11 @@ export async function POST(req: NextRequest) { const parsed = GradeSchema.safeParse(body) if (!parsed.success) return NextResponse.json({ error: parsed.error.flatten() }, { status: 400 }) - const data = parsed.data + const data = { + ...parsed.data, + studentName: parsed.data.studentName.trim(), + subject: parsed.data.subject.trim(), + }; const max = data.maxMarks ?? 100; const term = data.term ?? 'Term 1' From d0a8b6726a66dfa92f7e95798f6e62c9c26f67f3 Mon Sep 17 00:00:00 2001 From: theniteshdev <0911nitesh@gmail.com> Date: Sat, 18 Apr 2026 22:22:47 +0530 Subject: [PATCH 39/56] fix: handle null currentUser to prevent creating invalid teacher records --- app/api/profile/route.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/app/api/profile/route.ts b/app/api/profile/route.ts index a3d98bf..cd5654e 100644 --- a/app/api/profile/route.ts +++ b/app/api/profile/route.ts @@ -20,6 +20,9 @@ export async function GET(req: NextRequest) { if (!teacher) { const clerkUser = await currentUser() + if (!clerkUser) { + return NextResponse.json({ error: "User not found" }, { status: 404 }); + } const created = await Teacher.create({ clerkId: userId, name: clerkUser?.fullName ?? '', From cf3581b4adc476869ee236761198e2d6d9df6a21 Mon Sep 17 00:00:00 2001 From: theniteshdev <0911nitesh@gmail.com> Date: Sat, 18 Apr 2026 22:24:51 +0530 Subject: [PATCH 40/56] fix: strengthen input validation by trimming string fields --- app/api/profile/route.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/api/profile/route.ts b/app/api/profile/route.ts index cd5654e..b9b09fc 100644 --- a/app/api/profile/route.ts +++ b/app/api/profile/route.ts @@ -63,7 +63,7 @@ export async function PUT(req: NextRequest) { if (department !== undefined && typeof department !== 'string') { return NextResponse.json({ error: 'department must be a string' }, { status: 400 }) } - if (!Array.isArray(subjects) || !subjects.every((s) => typeof s === 'string')) { + if (!Array.isArray(subjects) || !subjects.every((s) => typeof s === 'string' && s.trim())) { return NextResponse.json({ error: 'subjects must be an array of strings' }, { status: 400 }) } if (phone !== undefined && typeof phone !== 'string') { From 20e910ef43e143d933b835343a60a66c5a9bbcf9 Mon Sep 17 00:00:00 2001 From: theniteshdev <0911nitesh@gmail.com> Date: Sat, 18 Apr 2026 22:26:03 +0530 Subject: [PATCH 41/56] fix: validate subjects array to prevent empty or invalid values --- app/api/profile/route.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/app/api/profile/route.ts b/app/api/profile/route.ts index b9b09fc..2c3f08c 100644 --- a/app/api/profile/route.ts +++ b/app/api/profile/route.ts @@ -63,7 +63,8 @@ export async function PUT(req: NextRequest) { if (department !== undefined && typeof department !== 'string') { return NextResponse.json({ error: 'department must be a string' }, { status: 400 }) } - if (!Array.isArray(subjects) || !subjects.every((s) => typeof s === 'string' && s.trim())) { + if ( !Array.isArray(subjects) || + !subjects.every(s => typeof s === 'string' && s.trim().length > 0)) { return NextResponse.json({ error: 'subjects must be an array of strings' }, { status: 400 }) } if (phone !== undefined && typeof phone !== 'string') { From fd57eab948c96fe896060262f0ecb4c8744bb550 Mon Sep 17 00:00:00 2001 From: theniteshdev <0911nitesh@gmail.com> Date: Sat, 18 Apr 2026 22:26:41 +0530 Subject: [PATCH 42/56] fix: normalize input data before updating teacher profile --- app/api/profile/route.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/app/api/profile/route.ts b/app/api/profile/route.ts index 2c3f08c..9bb88ed 100644 --- a/app/api/profile/route.ts +++ b/app/api/profile/route.ts @@ -92,7 +92,10 @@ export async function PUT(req: NextRequest) { } } - const updatePayload: Record = { name, subjects } + const updatePayload: Record = { + name: name.trim(), + subjects: subjects.map((s) => s.trim()), + }; if (department !== undefined) updatePayload.department = department if (phone !== undefined) updatePayload.phone = phone if (bio !== undefined) updatePayload.bio = bio From 78912f0903f6b653e23f9984574e8a6a74ff8747 Mon Sep 17 00:00:00 2001 From: theniteshdev <0911nitesh@gmail.com> Date: Sat, 18 Apr 2026 22:27:17 +0530 Subject: [PATCH 43/56] fix: enable schema validation in findOneAndUpdate operation --- app/api/profile/route.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/api/profile/route.ts b/app/api/profile/route.ts index 9bb88ed..6e22809 100644 --- a/app/api/profile/route.ts +++ b/app/api/profile/route.ts @@ -104,8 +104,8 @@ export async function PUT(req: NextRequest) { const teacher = await Teacher.findOneAndUpdate( { clerkId: userId }, { $set: updatePayload }, - { new: true } - ) + { new: true, runValidators: true }, + ); if (!teacher) { return NextResponse.json({ error: 'Teacher not found' }, { status: 404 }) From 4998e95ce1993bd79885575c3aca0644368ce6fd Mon Sep 17 00:00:00 2001 From: theniteshdev <0911nitesh@gmail.com> Date: Sat, 18 Apr 2026 22:30:23 +0530 Subject: [PATCH 44/56] fix: trim optional fields before updating teacher profile --- app/api/profile/route.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/app/api/profile/route.ts b/app/api/profile/route.ts index 6e22809..d80be29 100644 --- a/app/api/profile/route.ts +++ b/app/api/profile/route.ts @@ -81,7 +81,7 @@ export async function PUT(req: NextRequest) { (entry: unknown) => entry !== null && typeof entry === 'object' && - typeof (entry as Record).year === 'string' && + typeof (entry as Record).year === 'string' && typeof (entry as Record).title === 'string', ) ) { @@ -97,8 +97,8 @@ export async function PUT(req: NextRequest) { subjects: subjects.map((s) => s.trim()), }; if (department !== undefined) updatePayload.department = department - if (phone !== undefined) updatePayload.phone = phone - if (bio !== undefined) updatePayload.bio = bio + if (phone !== undefined) updatePayload.phone = phone.trim() + if (bio !== undefined) updatePayload.bio = bio.trim() if (academicHistory !== undefined) updatePayload.academicHistory = academicHistory const teacher = await Teacher.findOneAndUpdate( From c7ce708e32935ba36758a3e2901e54a244e80d60 Mon Sep 17 00:00:00 2001 From: "nitesh.asm" <0911nitesh@gmail.com> Date: Sat, 18 Apr 2026 23:18:02 +0530 Subject: [PATCH 45/56] fix: resolve teacher ObjectId from clerkId for correct grade queries and writes --- app/api/grades/route.ts | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/app/api/grades/route.ts b/app/api/grades/route.ts index ab7b49c..7f18cf9 100644 --- a/app/api/grades/route.ts +++ b/app/api/grades/route.ts @@ -4,6 +4,12 @@ import { connectDB } from '@/lib/mongodb' import { Grade } from '@/models/Grade' import { z } from 'zod' import mongoose from "mongoose"; +import { Teacher } from '@/models/Teacher' + +const teacher = await Teacher.findOne({ clerkId: userId }).select('_id').lean() +if (!teacher) { + return NextResponse.json({ error: 'Teacher not found' }, { status: 404 }) +} const GradeSchema = z.object({ studentId: z.string().regex(/^[0-9a-fA-F]{24}$/, 'Invalid studentId'), @@ -41,7 +47,7 @@ export async function GET(req: NextRequest) { const studentId = searchParams.get('studentId') const subject = searchParams.get('subject') - const query: Record = { teacherId: userId } + const query: Record = { teacherId: teacher._id } if (studentId && mongoose.Types.ObjectId.isValid(studentId)) { query.studentId = new mongoose.Types.ObjectId(studentId); } @@ -81,8 +87,8 @@ export async function POST(req: NextRequest) { const term = data.term ?? 'Term 1' const grade = await Grade.findOneAndUpdate( - { teacherId: userId, studentId: data.studentId, subject: data.subject, term }, - { $set: { ...data, term, teacherId: userId, grade: calcGrade(data.marks, max) } }, + { teacherId: teacher._id, studentId: new mongoose.Types.ObjectId(data.studentId), subject: data.subject, term }, + { $set: { ...data, term, teacherId: teacher._id, grade: calcGrade(data.marks, max) } }, { upsert: true, new: true } ) return NextResponse.json(grade, { status: 201 }) From 5299665992c972c357e0cd999f20714c8954070e Mon Sep 17 00:00:00 2001 From: "nitesh.asm" <0911nitesh@gmail.com> Date: Sat, 18 Apr 2026 23:24:18 +0530 Subject: [PATCH 46/56] fix: Enhance subjects validation in profile route --- app/api/profile/route.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/api/profile/route.ts b/app/api/profile/route.ts index d80be29..c85b6af 100644 --- a/app/api/profile/route.ts +++ b/app/api/profile/route.ts @@ -63,8 +63,8 @@ export async function PUT(req: NextRequest) { if (department !== undefined && typeof department !== 'string') { return NextResponse.json({ error: 'department must be a string' }, { status: 400 }) } - if ( !Array.isArray(subjects) || - !subjects.every(s => typeof s === 'string' && s.trim().length > 0)) { + + if ( !Array.isArray(subjects) || subjects.length === 0 || !subjects.every(s => typeof s === 'string' && s.trim().length > 0){ return NextResponse.json({ error: 'subjects must be an array of strings' }, { status: 400 }) } if (phone !== undefined && typeof phone !== 'string') { From 7ffd9029aadbba566ba5377eb38831fa80fe4288 Mon Sep 17 00:00:00 2001 From: "nitesh.asm" <0911nitesh@gmail.com> Date: Sat, 18 Apr 2026 23:32:21 +0530 Subject: [PATCH 47/56] fix: Change teacherId type to ObjectId in Announcement model and remove restrictive enum from audience to align with flexible UI and API validation --- models/Announcement.ts | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/models/Announcement.ts b/models/Announcement.ts index 94a0368..502988d 100644 --- a/models/Announcement.ts +++ b/models/Announcement.ts @@ -2,7 +2,7 @@ import mongoose, { Schema, model, models } from "mongoose"; export interface IAnnouncement { _id: mongoose.Types.ObjectId; - teacherId: string; + teacherId: mongoose.Types.ObjectId; title: string; content: string; audience: string; @@ -15,13 +15,14 @@ export interface IAnnouncement { const AnnouncementSchema = new Schema( { teacherId: { - type: String, + type: Schema.Types.ObjectId, + ref: "Teacher", required: true, index: true, }, title: { type: String, required: true, trim: true, minlength: 3 }, content: { type: String, required: true, trim: true, minlength: 5 }, - audience: { type: String, enum: ["All", "CS-A", "CS-B"], default: "All" }, + audience: { type: String, default: "All", trim: true }, category: { type: String, enum: ["academic", "events", "admin", "general"], @@ -38,4 +39,4 @@ AnnouncementSchema.index({ teacherId: 1, createdAt: -1 }); export const Announcement = models.Announcement ?? - model("Announcement", AnnouncementSchema); \ No newline at end of file + model("Announcement", AnnouncementSchema); From d03cbf258f4f721ad5296051c34ff4f93045c087 Mon Sep 17 00:00:00 2001 From: "nitesh.asm" <0911nitesh@gmail.com> Date: Sat, 18 Apr 2026 23:44:11 +0530 Subject: [PATCH 48/56] Refactor: teacherId type and update schema fields fix: remove invalid runtime logic from Assignment model and correct indexes --- models/Assignment.ts | 40 ++++++++++++++++++++++++++++------------ 1 file changed, 28 insertions(+), 12 deletions(-) diff --git a/models/Assignment.ts b/models/Assignment.ts index f70689d..98bdf2e 100644 --- a/models/Assignment.ts +++ b/models/Assignment.ts @@ -2,7 +2,7 @@ import mongoose, { Schema, model, models } from 'mongoose' export interface IAssignment { _id: mongoose.Types.ObjectId - teacherId: string + teacherId: mongoose.Types.ObjectId title: string description: string subject: string @@ -18,21 +18,37 @@ export interface IAssignment { const AssignmentSchema = new Schema( { teacherId: { type: Schema.Types.ObjectId, ref: 'Teacher', required: true, index: true }, - title: {type: String, required: true, trim: true, minlength: 3}, + title: { type: String, required: true, trim: true, minlength: 3 }, description: { type: String, default: '', maxlength: 1000 }, - subject: {enum: ['Mathematics', 'Data Structures', 'Operating Systems', 'DBMS', 'Computer Networks']}, - class: { enum: ['CS-A', 'CS-B'] }, - deadline: { validate: { - validator: (value: Date) => value > new Date(), - message: 'Deadline must be in the future' - } }, + subject: { + type: String, + enum: ['Mathematics', 'Data Structures', 'Operating Systems', 'DBMS', 'Computer Networks'], + required: true + }, + class: { + type: String, + enum: ['CS-A', 'CS-B'], + required: true + }, + deadline: { + type: Date, + required: true, + validate: { + validator: (value: Date) => value > new Date(), + message: 'Deadline must be in the future' + } + }, status: { type: String, enum: ['active', 'closed'], default: 'active' }, kanbanStatus: { type: String, enum: ['todo', 'in_progress', 'submitted'], default: 'todo' }, maxMarks: { type: Number, default: 100, min: 1, max: 1000 }, }, { timestamps: true } ) -AssignmentSchema.index({ teacherId: 1, class: 1 }); -AssignmentSchema.index({ status: 1 }); -AssignmentSchema.index({ deadline: 1 }); -export const Assignment = models.Assignment ?? model('Assignment', AssignmentSchema) + +// ✅ Correct indexes +AssignmentSchema.index({ teacherId: 1, class: 1 }) +AssignmentSchema.index({ status: 1 }) +AssignmentSchema.index({ deadline: 1 }) + +export const Assignment = + models.Assignment ?? model('Assignment', AssignmentSchema) From dc4009cd76ac292793613428af8d8cb3231ec637 Mon Sep 17 00:00:00 2001 From: "nitesh.asm" <0911nitesh@gmail.com> Date: Sat, 18 Apr 2026 23:55:10 +0530 Subject: [PATCH 49/56] refactor: Attendance model to use ObjectId and remove fields Changed teacherId to use ObjectId type and removed studentName and class properties. --- models/Attendance.ts | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/models/Attendance.ts b/models/Attendance.ts index 177468a..83077e7 100644 --- a/models/Attendance.ts +++ b/models/Attendance.ts @@ -2,10 +2,9 @@ import mongoose, { Schema, model, models } from 'mongoose' export interface IAttendance { _id: mongoose.Types.ObjectId - teacherId: string + teacherId: mongoose.Types.ObjectId studentId: mongoose.Types.ObjectId - studentName: string - class: string + // removed the class and studentName date: string status: 'present' | 'absent' | 'late' createdAt: Date From cad1c1dd67bfd04d58045cb0af96325377130da9 Mon Sep 17 00:00:00 2001 From: "nitesh.asm" <0911nitesh@gmail.com> Date: Sat, 18 Apr 2026 23:59:01 +0530 Subject: [PATCH 50/56] remove: studentName from IAttendance interface Removed the studentName field to prevent data inconsistency. --- models/Attendance.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/models/Attendance.ts b/models/Attendance.ts index 83077e7..b01b1bc 100644 --- a/models/Attendance.ts +++ b/models/Attendance.ts @@ -4,7 +4,8 @@ export interface IAttendance { _id: mongoose.Types.ObjectId teacherId: mongoose.Types.ObjectId studentId: mongoose.Types.ObjectId - // removed the class and studentName + // removed the studentName + class: string date: string status: 'present' | 'absent' | 'late' createdAt: Date @@ -17,6 +18,7 @@ const AttendanceSchema = new Schema( studentId: { type: Schema.Types.ObjectId, ref: 'Student', required: true }, // revome the studentName because If student name changes → data becomes inconsistent // remove because Data inconsistency + class: { type: String, required: true, trim: true }, date: { type: Date, required: true }, status: { type: String, enum: ['present', 'absent', 'late'], required: true }, }, From cecc46fe64953311ee4f99327717f77f9264c5ea Mon Sep 17 00:00:00 2001 From: "nitesh.asm" <0911nitesh@gmail.com> Date: Sun, 19 Apr 2026 00:01:05 +0530 Subject: [PATCH 51/56] Change teacherId type from string to ObjectId --- models/Grade.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/models/Grade.ts b/models/Grade.ts index cb1cc5a..e32cd68 100644 --- a/models/Grade.ts +++ b/models/Grade.ts @@ -2,7 +2,7 @@ import mongoose, { Schema, model, models } from 'mongoose' export interface IGrade { _id: mongoose.Types.ObjectId - teacherId: string + teacherId: mongoose.Types.ObjectId studentId: mongoose.Types.ObjectId studentName: string subject: string From 077de99c60a1b48cdd9a928bc38a524f34ee8bea Mon Sep 17 00:00:00 2001 From: "nitesh.asm" <0911nitesh@gmail.com> Date: Sun, 19 Apr 2026 00:05:12 +0530 Subject: [PATCH 52/56] fix: email validation and update unique index --- models/Student.ts | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/models/Student.ts b/models/Student.ts index 4c5f19e..2453869 100644 --- a/models/Student.ts +++ b/models/Student.ts @@ -20,11 +20,13 @@ const StudentSchema = new Schema( teacherId: { type: String, required: true, index: true, minlength: 1 }, name: { type: String, required: true, trim: true, minlength: 1 }, rollNo: { type: String, required: true, trim: true, uppercase: true,minlength: 1 }, - class: { type: String, required: true, trime: true,minlength: 1 }, - email: { type: String, - default: '', - trim: true, - match: [/^\S+@\S+\.\S+$/, 'Invalid email format'], }, + class: { type: String, required: true, trim: true,minlength: 1 }, + email: { type: String, trim: true, + validate:{ + validator: (value?: string) => !value || /^\S+@\S+\.\S+$/.test(value), + message: 'Invalid email format', + } + }, phone: { type: String, default: '', trim: true }, address: { type: String, default: '', trim: true }, parentName: { type: String, default: '', trim: true }, @@ -33,6 +35,6 @@ const StudentSchema = new Schema( { timestamps: true } ) -StudentSchema.index({ teacherId: 1, class: 1 }, { unique: true }); +StudentSchema.index({ teacherId: 1, rollNo: 1 }, { unique: true }); export const Student = models.Student ?? model('Student', StudentSchema) From 7a799a34f92842901d4ca33997cb08693e84bf47 Mon Sep 17 00:00:00 2001 From: "nitesh.asm" <0911nitesh@gmail.com> Date: Sun, 19 Apr 2026 00:12:05 +0530 Subject: [PATCH 53/56] Refactor teacher lookup into a separate function Refactored teacher retrieval logic into a separate function and updated its usage in the API route. --- app/api/grades/route.ts | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/app/api/grades/route.ts b/app/api/grades/route.ts index 7f18cf9..7ad3bba 100644 --- a/app/api/grades/route.ts +++ b/app/api/grades/route.ts @@ -3,14 +3,13 @@ import { NextRequest, NextResponse } from 'next/server' import { connectDB } from '@/lib/mongodb' import { Grade } from '@/models/Grade' import { z } from 'zod' -import mongoose from "mongoose"; +import mongoose from 'mongoose' import { Teacher } from '@/models/Teacher' -const teacher = await Teacher.findOne({ clerkId: userId }).select('_id').lean() -if (!teacher) { - return NextResponse.json({ error: 'Teacher not found' }, { status: 404 }) -} - +async function findTeacherByClerkId(userId: string){ + return Teacher.findOne({ clerkId: userId }).select('_id').lean() +}; + const GradeSchema = z.object({ studentId: z.string().regex(/^[0-9a-fA-F]{24}$/, 'Invalid studentId'), studentName: z.string().min(1), @@ -42,7 +41,11 @@ export async function GET(req: NextRequest) { if (!userId) return NextResponse.json({ error: 'Unauthorized' }, { status: 401 }) try { - await connectDB() + await connectDB(); + + const teacher = await findTeacherByClerkId(userId); + if (!teacher) return NextResponse.json({ error: 'Teacher not found' }, { status: 404 }); + const { searchParams } = new URL(req.url) const studentId = searchParams.get('studentId') const subject = searchParams.get('subject') @@ -67,6 +70,9 @@ export async function POST(req: NextRequest) { try { await connectDB() + + const teacher = await findTeacherByClerkId(userId); + if (!teacher) return NextResponse.json({ error: 'Teacher not found' }, { status: 404 }); let body try { From 9038946f90bb29b46813cbd342b600ddbd15727f Mon Sep 17 00:00:00 2001 From: "nitesh.asm" <0911nitesh@gmail.com> Date: Sun, 19 Apr 2026 00:15:22 +0530 Subject: [PATCH 54/56] Enhance studentId validation in grades route Refactor studentId validation to improve error handling. --- app/api/grades/route.ts | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/app/api/grades/route.ts b/app/api/grades/route.ts index 7ad3bba..124e205 100644 --- a/app/api/grades/route.ts +++ b/app/api/grades/route.ts @@ -50,10 +50,13 @@ export async function GET(req: NextRequest) { const studentId = searchParams.get('studentId') const subject = searchParams.get('subject') - const query: Record = { teacherId: teacher._id } - if (studentId && mongoose.Types.ObjectId.isValid(studentId)) { - query.studentId = new mongoose.Types.ObjectId(studentId); - } + const query: Record = { teacherId: teacher._id }; + + if (studentId) { + if (!mongoose.Types.ObjectId.isValid(studentId)) return NextResponse.json({ error: 'Invalid studentId' }, { status: 400 }); + query.studentId = new mongoose.Types.ObjectId(studentId) + }; + if (subject) query.subject = subject const grades = await Grade.find(query).sort({ createdAt: -1 }).lean() From 7095a26dbc06d2360cc76ed932694bda090c0b14 Mon Sep 17 00:00:00 2001 From: "nitesh.asm" <0911nitesh@gmail.com> Date: Sun, 19 Apr 2026 00:18:19 +0530 Subject: [PATCH 55/56] fix: validation for subjects array in route.ts --- app/api/profile/route.ts | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/app/api/profile/route.ts b/app/api/profile/route.ts index c85b6af..bbd7c51 100644 --- a/app/api/profile/route.ts +++ b/app/api/profile/route.ts @@ -63,10 +63,15 @@ export async function PUT(req: NextRequest) { if (department !== undefined && typeof department !== 'string') { return NextResponse.json({ error: 'department must be a string' }, { status: 400 }) } - - if ( !Array.isArray(subjects) || subjects.length === 0 || !subjects.every(s => typeof s === 'string' && s.trim().length > 0){ + + if ( + !Array.isArray(subjects) || + subjects.length === 0 || + !subjects.every((s: unknown) => typeof s === 'string' && s.trim().length > 0) + ) { return NextResponse.json({ error: 'subjects must be an array of strings' }, { status: 400 }) } + if (phone !== undefined && typeof phone !== 'string') { return NextResponse.json({ error: 'phone must be a string' }, { status: 400 }) } From 6978b98078f709a1ccc1c80f3cc35d4ff2491735 Mon Sep 17 00:00:00 2001 From: "nitesh.asm" <0911nitesh@gmail.com> Date: Sun, 19 Apr 2026 00:31:19 +0530 Subject: [PATCH 56/56] Update route.ts --- app/api/assignments/route.ts | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/app/api/assignments/route.ts b/app/api/assignments/route.ts index 19021d8..fed2776 100644 --- a/app/api/assignments/route.ts +++ b/app/api/assignments/route.ts @@ -4,13 +4,16 @@ import { connectDB } from '@/lib/mongodb' import { Assignment } from '@/models/Assignment' import { z } from 'zod' +const SUBJECTS = ['Mathematics', 'Data Structures', 'Operating Systems', 'DBMS', 'Computer Networks'] as const; +const CLASSES = ['CS-A', 'CS-B'] as const; + const AssignmentSchema = z.object({ - title: z.string().min(1), - description: z.string().optional(), - subject: z.string().min(1), - class: z.string().min(1), - deadline: z.string().min(1), - maxMarks: z.number().min(1).optional(), + title: z.string().trim().min(3), + description: z.string().max(1000).optional(), + subject: z.enum(SUBJECTS), + class: z.enum(CLASSES), + deadline: z.string().refine((value) => new Date(value) > new Date(), 'Deadline must be in the future'), + maxMarks: z.number().min(1).max(1000).optional(), status: z.enum(['active', 'closed']).optional(), kanbanStatus: z.enum(['todo', 'in_progress', 'submitted']).optional(), })