Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
58148e0
fix: Refactor MongoDB connection logic and enhance seed script diagno…
ArhanAnsari Apr 18, 2026
f61c3ba
fix: Update background gradient class for sign-in and sign-up pages
ArhanAnsari Apr 18, 2026
a6ae510
fix: Enhance PUT and DELETE endpoints for announcements with improved…
ArhanAnsari Apr 18, 2026
936e24c
fix: Improve error handling and authorization checks in PUT and DELET…
ArhanAnsari Apr 18, 2026
511f19a
fix: Refactor and enhance error handling in GET and POST endpoints fo…
ArhanAnsari Apr 18, 2026
eefe3a9
fix: Enhance error handling and validation in PUT and DELETE endpoint…
ArhanAnsari Apr 18, 2026
316f41f
fix: Refactor GET and PUT endpoints in profile API with improved erro…
ArhanAnsari Apr 18, 2026
7ed61ef
fix: Improve formatting and error handling in GET and POST endpoints …
ArhanAnsari Apr 18, 2026
c9b3530
fix: Enhance error handling and authorization checks in PUT and DELET…
ArhanAnsari Apr 18, 2026
78b16e0
fix: Simplify error response in GET assignments endpoint
ArhanAnsari Apr 18, 2026
10e65c4
fix: Validate studentId format in Attendance schema and query
ArhanAnsari Apr 18, 2026
b16a97e
fix: Validate studentId format in Grade schema and handle invalid cas…
ArhanAnsari Apr 18, 2026
d7afc42
fix: Improve error handling for dashboard data loading and adjust tot…
ArhanAnsari Apr 18, 2026
2eece6f
fix: Format GRADE_POINT for improved readability
ArhanAnsari Apr 18, 2026
8d95d84
fix: Format GRADE_POINT object for improved readability
ArhanAnsari Apr 18, 2026
bf49f6f
fix: apply PR review thread feedback across APIs and dashboard
Copilot Apr 18, 2026
9b0e536
chore: finalize validation feedback
Copilot Apr 18, 2026
3c45899
fix: keep Next.js route signature while avoiding unused warning
Copilot Apr 18, 2026
4cc36a8
chore: close remaining code review nits
Copilot Apr 18, 2026
d2fb705
fix: resolve remaining validation nits in profile and attendance routes
Copilot Apr 18, 2026
16f8467
chore: document strict objectid validation and keep request signature…
Copilot Apr 18, 2026
dbcc99b
fix: remove redundant upsert fields and avoid unnecessary grade lookup
Copilot Apr 18, 2026
e1063a7
fix: guard non-object payloads and enforce effective maxMarks validation
Copilot Apr 18, 2026
1e55bb3
Merge pull request #1 from ArhanAnsari/copilot/fix-security-validatio…
ArhanAnsari Apr 18, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion app/(auth)/sign-in/[[...sign-in]]/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ export const metadata: Metadata = {

export default function SignInPage() {
return (
<div className="min-h-screen flex items-center justify-center bg-gradient-to-br from-indigo-950 via-indigo-900 to-purple-900">
<div className="min-h-screen flex items-center justify-center bg-linear-to-br from-indigo-950 via-indigo-900 to-purple-900">
<div className="flex flex-col items-center gap-8">
<div className="text-center">
<h1 className="text-3xl font-bold text-white tracking-tight">EduDesk</h1>
Expand Down
2 changes: 1 addition & 1 deletion app/(auth)/sign-up/[[...sign-up]]/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { SignUp } from '@clerk/nextjs'

export default function SignUpPage() {
return (
<div className="min-h-screen flex items-center justify-center bg-gradient-to-br from-indigo-950 via-indigo-900 to-purple-900">
<div className="min-h-screen flex items-center justify-center bg-linear-to-br from-indigo-950 via-indigo-900 to-purple-900">
<div className="flex flex-col items-center gap-8">
<div className="text-center">
<h1 className="text-3xl font-bold text-white tracking-tight">EduDesk</h1>
Expand Down
112 changes: 74 additions & 38 deletions app/api/announcements/[id]/route.ts
Original file line number Diff line number Diff line change
@@ -1,79 +1,115 @@
import { auth } from '@clerk/nextjs/server'
import { NextRequest, NextResponse } from 'next/server'
import mongoose from 'mongoose'
import { connectDB } from '@/lib/mongodb'
import { Announcement } from '@/models/Announcement'
import { auth } from "@clerk/nextjs/server";
import { NextRequest, NextResponse } from "next/server";
import mongoose from "mongoose";
import { connectDB } from "@/lib/mongodb";
import { Announcement } from "@/models/Announcement";

const ALLOWED_FIELDS = ['title', 'content', 'body', 'audience', 'category', 'pinned', 'expiresAt']
const ALLOWED_FIELDS = [
"title",
"content",
"body",
"audience",
"category",
"pinned",
"expiresAt",
];

export async function PUT(req: NextRequest, ctx: { params: Promise<{ id: string }> }) {
const { userId } = await auth()
if (!userId) return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })
export async function PUT(
req: NextRequest,
ctx: { params: Promise<{ id: string }> },
) {
const { userId } = await auth();
if (!userId)
return NextResponse.json({ error: "Unauthorized" }, { status: 401 });

try {
const { id } = await ctx.params
const { id } = await ctx.params;

// Validate ObjectId
if (!mongoose.Types.ObjectId.isValid(id)) {
return NextResponse.json({ error: 'Invalid id' }, { status: 400 })
return NextResponse.json({ error: "Invalid id" }, { status: 400 });
}

await connectDB()
let body
await connectDB();

let body;
try {
body = await req.json()
body = await req.json();
} catch {
return NextResponse.json({ error: 'Invalid JSON request body' }, { status: 400 })
return NextResponse.json(
{ error: "Invalid JSON request body" },
{ status: 400 },
);
}
if (body === null || typeof body !== "object" || Array.isArray(body)) {
// Valid JSON can still be a primitive/null/array, but this route requires an object payload.
return NextResponse.json(
{ error: "Invalid JSON request body" },
{ status: 400 },
);
}

// Sanitize: only allow whitelisted fields
const sanitizedBody: Record<string, unknown> = {}
const sanitizedBody: Record<string, unknown> = {};
for (const key of ALLOWED_FIELDS) {
if (key in body) {
sanitizedBody[key] = body[key]
sanitizedBody[key] = body[key];
}
}
Comment thread
coderabbitai[bot] marked this conversation as resolved.

const announcement = await Announcement.findOneAndUpdate(
{ _id: id },
{ _id: id, teacherId: userId },
{ $set: sanitizedBody },
{ new: true, runValidators: true, context: 'query' }
)
if (!announcement) return NextResponse.json({ error: 'Not found' }, { status: 404 })
return NextResponse.json(announcement)
{ new: true, runValidators: true, context: "query" },
);
if (!announcement)
return NextResponse.json({ error: "Not found" }, { status: 404 });
return NextResponse.json(announcement);
} catch (error) {
if (error instanceof Error) {
console.error('PUT /api/announcements/[id] error:', error.message)
console.error("PUT /api/announcements/[id] error:", error.message);
}
return NextResponse.json({ error: 'Internal server error' }, { status: 500 })
return NextResponse.json(
{ error: "Internal server error" },
{ status: 500 },
);
}
}

export async function DELETE(_req: NextRequest, ctx: { params: Promise<{ id: string }> }) {
const { userId } = await auth()
if (!userId) return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })
export async function DELETE(
_req: NextRequest,
ctx: { params: Promise<{ id: string }> },
) {
const { userId } = await auth();
if (!userId)
return NextResponse.json({ error: "Unauthorized" }, { status: 401 });

try {
const { id } = await ctx.params
const { id } = await ctx.params;

// Validate ObjectId
if (!mongoose.Types.ObjectId.isValid(id)) {
return NextResponse.json({ error: 'Invalid id' }, { status: 400 })
return NextResponse.json({ error: "Invalid id" }, { status: 400 });
}

await connectDB()
const deleted = await Announcement.findOneAndDelete({ _id: id })

await connectDB();
const deleted = await Announcement.findOneAndDelete({
_id: id,
teacherId: userId,
});

if (!deleted) {
return NextResponse.json({ error: 'Not found' }, { status: 404 })
return NextResponse.json({ error: "Not found" }, { status: 404 });
}
return NextResponse.json({ success: true })

return NextResponse.json({ success: true });
} catch (error) {
if (error instanceof Error) {
console.error('DELETE /api/announcements/[id] error:', error.message)
console.error("DELETE /api/announcements/[id] error:", error.message);
}
return NextResponse.json({ error: 'Internal server error' }, { status: 500 })
return NextResponse.json(
{ error: "Internal server error" },
{ status: 500 },
);
}
}
113 changes: 75 additions & 38 deletions app/api/assignments/[id]/route.ts
Original file line number Diff line number Diff line change
@@ -1,79 +1,116 @@
import { auth } from '@clerk/nextjs/server'
import { NextRequest, NextResponse } from 'next/server'
import mongoose from 'mongoose'
import { connectDB } from '@/lib/mongodb'
import { Assignment } from '@/models/Assignment'
import { auth } from "@clerk/nextjs/server";
import { NextRequest, NextResponse } from "next/server";
import mongoose from "mongoose";
import { connectDB } from "@/lib/mongodb";
import { Assignment } from "@/models/Assignment";

const ALLOWED_UPDATE_FIELDS = ['title', 'description', 'dueDate', 'deadline', 'subject', 'class', 'status', 'kanbanStatus', 'maxMarks']
const ALLOWED_UPDATE_FIELDS = [
"title",
"description",
"dueDate",
"deadline",
"subject",
"class",
"status",
"kanbanStatus",
"maxMarks",
];

export async function PUT(req: NextRequest, ctx: { params: Promise<{ id: string }> }) {
const { userId } = await auth()
if (!userId) return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })
export async function PUT(
req: NextRequest,
ctx: { params: Promise<{ id: string }> },
) {
const { userId } = await auth();
if (!userId)
return NextResponse.json({ error: "Unauthorized" }, { status: 401 });

try {
const { id } = await ctx.params
const { id } = await ctx.params;

// Validate ObjectId
if (!mongoose.Types.ObjectId.isValid(id)) {
return NextResponse.json({ error: 'Invalid id' }, { status: 400 })
return NextResponse.json({ error: "Invalid id" }, { status: 400 });
}

await connectDB()
let body
await connectDB();

let body;
try {
body = await req.json()
body = await req.json();
} catch {
return NextResponse.json({ error: 'Invalid JSON in request body' }, { status: 400 })
return NextResponse.json(
{ error: "Invalid JSON in request body" },
{ status: 400 },
);
}
if (body === null || typeof body !== "object" || Array.isArray(body)) {
return NextResponse.json(
{ error: "Invalid JSON in request body" },
{ status: 400 },
);
}

// Sanitize: only allow whitelisted fields
const sanitizedBody: Record<string, unknown> = {}
const sanitizedBody: Record<string, unknown> = {};
for (const key of ALLOWED_UPDATE_FIELDS) {
if (key in body) {
sanitizedBody[key] = body[key]
sanitizedBody[key] = body[key];
}
}

const assignment = await Assignment.findOneAndUpdate(
{ _id: id },
{ _id: id, teacherId: userId },
sanitizedBody,
{ new: true }
)
if (!assignment) return NextResponse.json({ error: 'Not found' }, { status: 404 })
return NextResponse.json(assignment)
{ new: true, runValidators: true, context: "query" },
);
Comment thread
coderabbitai[bot] marked this conversation as resolved.
if (!assignment)
return NextResponse.json({ error: "Not found" }, { status: 404 });
return NextResponse.json(assignment);
} catch (error) {
if (error instanceof Error) {
console.error('PUT /api/assignments/[id] error:', error.message)
console.error("PUT /api/assignments/[id] error:", error.message);
}
return NextResponse.json({ error: 'Internal server error' }, { status: 500 })
return NextResponse.json(
{ error: "Internal server error" },
{ status: 500 },
);
}
}

export async function DELETE(_req: NextRequest, ctx: { params: Promise<{ id: string }> }) {
const { userId } = await auth()
if (!userId) return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })
export async function DELETE(
_req: NextRequest,
ctx: { params: Promise<{ id: string }> },
) {
const { userId } = await auth();
if (!userId)
return NextResponse.json({ error: "Unauthorized" }, { status: 401 });

try {
const { id } = await ctx.params
const { id } = await ctx.params;

// Validate ObjectId
if (!mongoose.Types.ObjectId.isValid(id)) {
return NextResponse.json({ error: 'Invalid id' }, { status: 400 })
return NextResponse.json({ error: "Invalid id" }, { status: 400 });
}

await connectDB()
const deleted = await Assignment.findOneAndDelete({ _id: id })

await connectDB();
const deleted = await Assignment.findOneAndDelete({
_id: id,
teacherId: userId,
});

if (!deleted) {
return NextResponse.json({ error: 'Not found' }, { status: 404 })
return NextResponse.json({ error: "Not found" }, { status: 404 });
}
return NextResponse.json({ success: true })

return NextResponse.json({ success: true });
} catch (error) {
if (error instanceof Error) {
console.error('DELETE /api/assignments/[id] error:', error.message)
console.error("DELETE /api/assignments/[id] error:", error.message);
}
return NextResponse.json({ error: 'Internal server error' }, { status: 500 })
return NextResponse.json(
{ error: "Internal server error" },
{ status: 500 },
);
}
}
2 changes: 1 addition & 1 deletion app/api/assignments/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ export async function GET(req: NextRequest) {
if (error instanceof Error) {
console.error('GET /api/assignments 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 })
}
}

Expand Down
Loading