Skip to content

Comments

PR: Implement Request Page Components (Card, Requests, Reject Modal)#232

Open
codewkaushik404 wants to merge 26 commits intoOpenLake:mainfrom
codewkaushik404:feature/certificate
Open

PR: Implement Request Page Components (Card, Requests, Reject Modal)#232
codewkaushik404 wants to merge 26 commits intoOpenLake:mainfrom
codewkaushik404:feature/certificate

Conversation

@codewkaushik404
Copy link

@codewkaushik404 codewkaushik404 commented Feb 20, 2026

Changes Introduced

  • Added:
    • Request page layout and structure.
    • Reusable Card component for displaying request details.
    • Requests component to manage and render multiple request items.
    • Modal component for rejecting requests with proper UI interaction.
  • Updated:
    • Integrated request components into the main workflow.
    • Connected modal state handling with request actions.

Why This Change?

  • Problem: The application lacked a structured interface for viewing and managing incoming requests.
  • Solution: Implemented a dedicated Requests page with modular components and a rejection modal to improve request handling.
  • Impact:
    • Improves admin workflow by providing a clean UI to review and manage requests.
    • Enhances user experience with structured request cards.
    • Adds rejection functionality through an interactive modal.

Screenshots / Video (if applicable)

Before After
N/A
image image |

Testing

  • [] Ran unit tests and all passed (npm test in the relevant directory).
  • Manually tested the following scenarios:
    • [x]Test Case 1:** Open Requests page → Requests render correctly.
    • [x]Test Case 2:** Click Reject → Modal opens successfully.
    • [x]Test Case 3:** Confirm rejection → Modal closes and state updates correctly.
  • Tested on different browsers (Chrome, Firefox) for UI changes.
  • Verified there are no new console warnings or errors.

Documentation Updates

  • Updated the README.md with new instructions.
  • Added clear code comments where logic is complex.
  • N/A

Checklist

  • I have created a new branch for this PR (git checkout -b feature/request-page-components).
  • I have starred the repository.
  • My code follows the project's coding style and conventions.
  • My commit messages are clear and follow the project's guidelines.
  • I have performed a self-review of my own code.
  • I have added tests that prove my fix is effective or that my feature works.
  • All new and existing tests passed locally with my changes.
  • This PR introduces no breaking changes (or they are clearly documented).

Deployment Notes

  • Requires a database migration/schema update.
  • Requires new environment variables to be set.
  • N/A

💬Additional Notes

  • Components are designed to be reusable for future request-related features.
  • UI structure allows easy integration of approval or additional action workflows in future updates.

Summary by CodeRabbit

Release Notes

  • New Features

    • Added Google OAuth sign-in and local authentication with username/password.
    • Introduced certificates management system with batch creation and approval workflow.
    • Added requests/records dashboard for tracking and managing submissions.
    • Expanded role-based navigation with role-specific menu items.
    • Enhanced user onboarding with improved form validation and error handling.
  • Bug Fixes

    • Improved authentication error messages and response handling.
    • Fixed session management and credential validation across login flows.

Switched to session-based authentication and configured persistent session storage with connect-mongo.
…ession-based authentication and configured persistent session storage with connect-mongo.
@vercel
Copy link

vercel bot commented Feb 20, 2026

@codewkaushik404 is attempting to deploy a commit to the openlake's projects Team on Vercel.

A member of the Team first needs to authorize it.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Feb 20, 2026

Walkthrough

This PR refactors the monolithic data model into individual schema files, implements Passport.js authentication with Google OAuth and local strategies, introduces a certificate batch management feature, and updates frontend context patterns with new Request and Certificate components and pages.

Changes

Cohort / File(s) Summary
Database Schema Refactoring
backend/models/schema.js, backend/models/userSchema.js, backend/models/achievementSchema.js, backend/models/eventSchema.js, backend/models/feedbackSchema.js, backend/models/organizationSchema.js, backend/models/positionSchema.js, backend/models/positionHolderSchema.js
Extracted seven models from monolithic schema.js into separate dedicated schema files, each with complete field definitions and Mongoose model exports. Schema.js now only exports UserSkill, Skill, and Announcement; timestamps auto-enabled for Announcement.
Passport & Authentication Configuration
backend/config/passportConfig.js, backend/models/passportConfig.js, backend/middlewares/isAuthenticated.js
Implemented Passport.js with Google OAuth (email validation for IIT Bhilai domain) and Local strategy (bcrypt password verification), added serializeUser/deserializeUser for session handling. Introduced new jwtIsAuthenticated middleware for JWT cookie-based token verification alongside existing session auth.
Certificate Feature
backend/models/certificateSchema.js, backend/controllers/certificateController.js, backend/routes/certificateRoutes.js, backend/utils/batchValidate.js
Introduced CertificateBatch and Certificate models with comprehensive indexing; created createBatch controller with role-based authorization (CLUB_COORDINATOR), council hierarchy validation, and approver lookup; wired routes at /api/certificate-batches.
Authentication Routes & Validation
backend/routes/auth.js, backend/utils/authValidate.js
Refactored login to use custom Passport callback with explicit error handling; updated registration with Zod validation and streamlined user creation; enhanced Google verify flow with manual session login and conditional onboarding redirects; introduced loginValidate and registerValidate schemas.
Route Model Import Updates
backend/routes/achievements.js, backend/routes/dashboard.js, backend/routes/events.js, backend/routes/feedbackRoutes.js, backend/routes/onboarding.js, backend/routes/positionRoutes.js, backend/routes/profile.js, backend/routes/skillsRoutes.js, backend/routes/orgUnit.js, backend/routes/analytics.js
Updated all route imports to reference individual schema modules instead of aggregated schema.js; standardized isAuthenticated as named import from middleware module.
Core Backend Setup
backend/index.js, backend/config/db.js, backend/package.json
Moved DB connection to separate module and async server startup; integrated MongoStore for session persistence; replaced body-parser with express.json(); added bcrypt, connect-mongo, cookie-parser, zod; removed mongoose-findorcreate and passport-local-mongoose.
Controller Import Updates
backend/controllers/analyticsController.js, backend/controllers/dashboardController.js, backend/controllers/eventControllers.js
Updated model imports from aggregated schema to individual schema files; changed Event import to default export from eventSchema.
Frontend Context & Hooks
frontend/src/context/AdminContext.js, frontend/src/context/RequestContext.js, frontend/src/hooks/useAuth.js
Added useAdminContext hook for centralized context access; created RequestContext with pending/approved/rejected/total counters and useRequest hook; updated useAuth to guard handleLogin input and refactored fetchCredentials response handling.
Frontend Certificate Components
frontend/src/Components/Certificates/CertificatesList.jsx, frontend/src/pages/certificatesPage.jsx
Added CertificatesList component with filtering by status (ALL/Pending/Approved/Rejected), search across event and issuer, and action buttons for viewing/downloading approved certificates; created certificatesPage wrapper with responsive grid layout.
Frontend Request Management
frontend/src/Components/Requests/Requests.jsx, frontend/src/Components/Requests/Card.jsx, frontend/src/Components/Requests/RejectModal.jsx, frontend/src/pages/requestsPage.jsx
Introduced Requests listing with status counters and search filtering; Card component for individual request display with Approve/Reject/View/Edit actions; RejectModal for reason capture; requestsPage wrapper with RequestProvider context.
Frontend Auth & Navigation
frontend/src/Components/Auth/Login.jsx, frontend/src/Components/Auth/Register.jsx, frontend/src/Components/Auth/RoleRedirect.jsx, frontend/src/Components/Auth/UserOnboarding.jsx, frontend/src/config/navbarConfig.js
Updated Login/Register to use useAdminContext, redirected Google auth to backend endpoint, replaced form placeholders with functional submission; enhanced Onboarding with useAdminContext and pre-redirect guard; expanded NavbarConfig with Certificates and Requests navigation across roles.
Frontend Dashboard Updates
frontend/src/Components/Dashboard/Dashboard.jsx, frontend/src/Components/Dashboard/Home.jsx, frontend/src/Components/Dashboard/Sidebar.jsx, frontend/src/Components/common/Sidebar.jsx, frontend/src/config/dashboardComponents.js
Migrated Dashboard and Home to useAdminContext; added onboarding completion gating to loading state; removed Sidebar.jsx from Dashboard; updated common Sidebar with adjusted widths and tooltip logic; extended dashboardComponents with certificates and requests page entries.
Frontend Services
frontend/src/services/auth.js
Updated registerUser(username, password, name) and loginUser(username, password) signatures; changed completeOnboarding from POST to PUT with error handling; return full response objects instead of nested data.
Minor Updates
frontend/src/App.jsx, frontend/src/index.jsx
Added debug console.log comment in App; fixed trailing comma in React.StrictMode JSX.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~55 minutes

The PR spans multiple interconnected systems: significant schema refactoring (high repetition but spread across 8 files), new authentication implementation with Passport and JWT, a complete certificate feature with models/controllers/routes, systematic import updates across 10+ route files, and substantial frontend changes including new context patterns, components, and pages. While many changes follow consistent patterns, the heterogeneity across authentication systems, data models, and frontend architecture requires independent reasoning for each cohort.

Possibly related PRs

  • Feature/announcements frontend #217: Modifies announcements implementation in backend/models/schema.js and backend/routes/announcements.js, touching the same schema centralization and route import patterns as this PR.

Suggested reviewers

  • harshitap1305

Poem

🐰 Models scattered now on shelves so neat,
Passports guard the entry, heartbeat complete,
Certificates dance through batches bright,
Contexts hook together, frontend's light,
Schemas sing in files, a refactor's delight! 🎓

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 21.74% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The PR title accurately describes the main changes: implementation of request page components including Card, Requests, and Reject Modal components.
Description check ✅ Passed The PR description follows the template structure with all major sections completed: Changes Introduced, Why This Change, Screenshots, Testing, Checklist, and Deployment Notes. Most critical sections are populated.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
  • 📝 Generate docstrings (stacked PR)
  • 📝 Generate docstrings (commit on current branch)
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

Tip

Issue Planner is now in beta. Read the docs and try it out! Share your feedback on Discord.


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.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 11

Note

Due to the large number of review comments, Critical severity comments were prioritized as inline comments.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (15)
backend/config/db.js (1)

11-13: ⚠️ Potential issue | 🟠 Major

Server starts silently without a DB connection on failure.

The catch block only logs the error and returns normally. When connectDB is awaited in index.js at startup, a connection failure is silently absorbed and the HTTP server boots without a live database, causing all subsequent DB operations to fail at request time.

🛡️ Proposed fix
  } catch (error) {
    console.error("MongoDB Connection Error:", error);
+   process.exit(1);
  }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@backend/config/db.js` around lines 11 - 13, The catch in connectDB currently
only logs the error which lets startup continue without a DB; update the
connectDB error handling (in backend/config/db.js, function connectDB) to fail
fast by either rethrowing the caught error or calling process.exit(1) after
logging so the awaited call in index.js rejects and the server does not start
without a DB; ensure you preserve the existing log (console.error) but then
propagate the failure (throw error or return Promise.reject(error)) so callers
see the failure.
backend/controllers/analyticsController.js (2)

379-383: ⚠️ Potential issue | 🟠 Major

Unsafe property access on populated fields will crash the endpoint for orphaned references.

If a referenced user_id or position_id document has been deleted, Mongoose .populate() silently sets those fields to null. Accessing .personal_info.name, .username, or .title on null throws a TypeError, taking down the entire getClubCoordinatorAnalytics response.

🛡️ Proposed fix
-currentPositionHolders: team.map(h => ({
-  name: h.user_id.personal_info.name,
-  username: h.user_id.username,
-  position: h.position_id.title
-})),
+currentPositionHolders: team
+  .filter(h => h.user_id && h.position_id)
+  .map(h => ({
+    name: h.user_id.personal_info?.name,
+    username: h.user_id.username,
+    position: h.position_id.title,
+  })),
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@backend/controllers/analyticsController.js` around lines 379 - 383, The
current mapping in getClubCoordinatorAnalytics that builds
currentPositionHolders from team.map(h => ({ name: h.user_id.personal_info.name,
username: h.user_id.username, position: h.position_id.title })) will throw when
populated user_id or position_id are null; update the mapping to defensively
handle orphaned references by checking for null before property access (e.g.,
use optional chaining or explicit null checks on h.user_id and h.position_id),
provide safe fallback values like 'Unknown' or skip entries, and keep the symbol
names team, currentPositionHolders, getClubCoordinatorAnalytics and the map
callback intact so the change is easy to locate.

233-233: ⚠️ Potential issue | 🟡 Minor

Typo: "Analyltics" → "Analytics" in the error log message.

✏️ Proposed fix
-console.error("President Dashboard Analyltics Error:", error);
+console.error("President Dashboard Analytics Error:", error);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@backend/controllers/analyticsController.js` at line 233, Fix the typo in the
error log inside analyticsController.js: update the console.error call that
currently logs "President Dashboard Analyltics Error:" to correctly read
"President Dashboard Analytics Error:" so the message uses "Analytics" (locate
the console.error(...) in the President Dashboard error handling block).
backend/models/passportConfig.js (1)

55-66: ⚠️ Potential issue | 🟠 Major

serializeUser stores the full user object in the session — PII leak risk.

done(null, user) serializes the entire Mongoose document (email, name, profile picture, role, etc.) into the session store. Any session store breach (or accidental log) exposes full user PII. The standard and safe pattern is to serialize only the opaque ID; the deserializer already fetches the fresh record from the DB on every request so nothing is lost.

🔒 Proposed fix
 passport.serializeUser((user, done) => {
-  done(null, user);
+  done(null, user._id.toString());
 });

 passport.deserializeUser(async (userKey, done) => {
   try {
-    let user = await User.findById(userKey._id);
+    let user = await User.findById(userKey);
     done(null, user);
   } catch (err) {
     done(err);
   }
 });
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@backend/models/passportConfig.js` around lines 55 - 66, serializeUser
currently stores the whole user object (done(null, user)) which leaks PII;
change passport.serializeUser to only serialize the user's ID (e.g., done(null,
user._id || user.id)) and update passport.deserializeUser to accept that id
(userId) and call User.findById(userId) (keeping the existing try/catch and
done(null, user) behavior); also add a defensive null/validation check in
deserializeUser to call done(null, false) or done(new Error(...)) if the id is
missing or no user is found.
backend/routes/orgUnit.js (1)

153-153: ⚠️ Potential issue | 🔴 Critical

Transaction atomicity broken: save(session) should be save({ session }).

Mongoose's Document.prototype.save() expects save([options]) where options.session is the ClientSession. Passing the ClientSession directly as the first argument means the session is not properly registered with the save operation, so newUnit is saved outside the transaction. If existingUser is found afterward and abortTransaction() is called, newUnit remains committed — leaving an orphaned org-unit.

Compare with the correct form on line 178: await newUser.save({ session });.

🐛 Proposed fix
-await newUnit.save(session);
+await newUnit.save({ session });
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@backend/routes/orgUnit.js` at line 153, The newUnit save is passing the
ClientSession object directly (newUnit.save(session)), which causes the document
to be written outside the transaction; change the call to pass an options object
with the session (i.e., use newUnit.save({ session })) so the save participates
in the transaction; update the save invocation near the newUnit creation (same
area that checks existingUser and may call abortTransaction) to match how
newUser.save({ session }) is used.
backend/routes/events.js (3)

271-280: ⚠️ Potential issue | 🟠 Major

GET /:id silently returns null (HTTP 200) when the event is not found.

findById() returns null for a missing document; res.json(null) sends an HTTP 200 with a null body rather than a proper 404. Callers cannot distinguish "event exists but is empty" from "no such event".

🐛 Proposed fix
  const event = await Event.findById(req.params.id)
    .populate("organizing_unit_id", "name")
    .populate("organizers", "personal_info.name");
+ if (!event) return res.status(404).json({ message: "Event not found." });
  res.json(event);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@backend/routes/events.js` around lines 271 - 280, The GET handler for
router.get("/:id") uses Event.findById(...) and blindly returns res.json(event),
which sends HTTP 200 with a null body when not found; update the async route
handler to check the result of Event.findById (the variable event) and if it's
null respond with res.status(404).json({ message: "Event not found" })
(otherwise continue to return the event), keeping the existing catch block for
server errors and error logging intact; locate this logic in the
router.get("/:id", async (req, res) => { ... }) function and modify the
post-findById flow accordingly.

444-477: ⚠️ Potential issue | 🟡 Minor

Remove debug console.log dumps from the production PUT route.

Lines 444–467 log the full request body and event document (before and after update) on every call. This is a performance overhead, can expose sensitive event data in server logs, and should not be in a merge-ready branch.

🐛 Proposed fix
-   // 🔍 DEBUG LOGS - START
-   console.log("\n=== 📝 UPDATE EVENT DEBUG ===");
-   console.log("Event ID:", eventId);
-   console.log("Updates received (full body):", JSON.stringify(updates, null, 2));
-   console.log("Number of fields to update:", Object.keys(updates).length);
-   console.log("Fields being updated:", Object.keys(updates));
-   console.log("========================\n");
-   const eventBefore = await Event.findById(eventId);
-   console.log("Event BEFORE update:", JSON.stringify(eventBefore, null, 2));
    const event = await Event.findByIdAndUpdate(eventId, updates, {
      new: true,
      runValidators: true,
    });
-   console.log("\n=== ✅ UPDATE RESULT ===");
-   console.log("Event AFTER update:", JSON.stringify(event, null, 2));
-   console.log("Update successful:", !!event);
-   console.log("========================\n");
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@backend/routes/events.js` around lines 444 - 477, Remove the development
debug console.log dumps from the update PUT handler so production logs don't
contain full request bodies or database documents: delete the console.log block
that prints "=== 📝 UPDATE EVENT DEBUG ===" and the subsequent logs that print
eventId, updates, Event.findById result (eventBefore), and the "=== ✅ UPDATE
RESULT ===" logs; keep the actual update logic using Event.findByIdAndUpdate and
the existing try/catch and error handling (the function surrounding
Event.findById, Event.findByIdAndUpdate, and the catch block should remain
unchanged), but replace verbose debug prints with concise logging if needed
(e.g., a single info or error log) or remove them entirely.

415-420: ⚠️ Potential issue | 🟡 Minor

Add reviewed_at field to track review timestamp separately from request submission time.

requested_at is intended to capture when the room request was originally submitted (defined with default: Date.now in the schema), but it's being overwritten to the current timestamp when the request status is reviewed at line 418. This destroys the original submission timestamp.

Add a separate reviewed_at field to the room_requests schema to record when the request was actually reviewed, alongside the existing reviewed_by field.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@backend/routes/events.js` around lines 415 - 420, The current handler that
updates a room request (using event.room_requests.id(requestId) and setting
request.status, request.requested_at, and request.reviewed_by) incorrectly
overwrites the original submission timestamp; instead, stop updating
request.requested_at and add a separate reviewed_at field on the room_requests
schema and set request.reviewed_at = new Date() when reviewing; update the code
that assigns reviewed_by to also assign reviewed_at, and ensure the
room_requests schema includes reviewed_at (Date) so the original requested_at
(default: Date.now) remains unchanged.
backend/routes/positionRoutes.js (1)

93-93: ⚠️ Potential issue | 🟡 Minor

Debug console.log(req.body) left in production path.

req.body in this route carries user_id, position_id, and appointment_details. Remove before merging.

🐛 Proposed fix
-  console.log(req.body);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@backend/routes/positionRoutes.js` at line 93, Remove the stray
console.log(req.body) left in the route handler in positionRoutes.js — this
prints sensitive request payload (user_id, position_id, appointment_details) to
stdout; either delete the log line or replace it with a safe debug call (e.g.,
processLogger.debug) that omits or redacts sensitive fields before logging, and
ensure you only log non-sensitive identifiers if absolutely necessary in the
route handler where console.log(req.body) appears.
backend/controllers/dashboardController.js (1)

111-117: ⚠️ Potential issue | 🟠 Major

console.log(email) leaks PII to server logs.

User email is a personally-identifiable identifier. Logging it unconditionally in the CLUB_COORDINATOR branch violates GDPR/privacy best practices and can accumulate PII in log aggregation systems.

🛡️ Proposed fix
-  console.log(email);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@backend/controllers/dashboardController.js` around lines 111 - 117, The
branch handling ROLES.CLUB_COORDINATOR currently logs the raw user email
(console.log(email)), leaking PII; remove that unconditional logging and instead
log only non-identifying context when OrganizationalUnit.findOne returns null
(e.g., a message including the role and request id or user id, or a
deterministic non-reversible hash of the email). Update the CLUB_COORDINATOR
case around the clubUnit lookup (the code using OrganizationalUnit.findOne and
the console.log(email) line) to delete the console.log(email) call and replace
it with a privacy-safe log (no raw email) or no log at all, or compute and log a
secure hash of the email if you need a traceable token.
backend/routes/feedbackRoutes.js (1)

50-50: ⚠️ Potential issue | 🔴 Critical

Typo: "ture" should be "true"is_anonymous string check is broken.

The comparison is_anonymous === "ture" will never match, so a string "true" value from the client will be treated as false.

🐛 Proposed fix
-      is_anonymous: is_anonymous === "ture" || is_anonymous === true,
+      is_anonymous: is_anonymous === "true" || is_anonymous === true,
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@backend/routes/feedbackRoutes.js` at line 50, The is_anonymous string
comparison in feedbackRoutes.js is misspelled ("ture") so string "true" from
clients is treated as false; update the check in the feedback submission handler
that sets is_anonymous (variable is_anonymous) to compare against "true" (and
consider normalizing input with .toLowerCase() or accepting both boolean and
string forms) so that is_anonymous: is_anonymous === "true" || is_anonymous ===
true correctly detects true values.
backend/routes/onboarding.js (1)

8-23: ⚠️ Potential issue | 🔴 Critical

No input validation — Program.trim() will throw a TypeError if Program is missing.

None of the destructured body fields (add_year, Program, discipline, mobile_no) are validated before use. If Program is undefined, line 20 crashes with Cannot read properties of undefined (reading 'trim'). Similarly, add_year and discipline are passed directly to the DB without checks.

Add validation (or use a Zod schema like other routes in this PR) before accessing these fields.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@backend/routes/onboarding.js` around lines 8 - 23, The handler is using
Program.trim(), add_year, and discipline directly which can throw when Program
is undefined and allows invalid data into the DB; add a validation step (e.g.,
Zod schema or explicit guards) that requires Program (string), discipline
(string), and add_year (number or numeric-string) before proceeding, return 400
on validation failure, and then use the validated values in the
User.findByIdAndUpdate call (or use safe access like (Program||'').trim() only
as a fallback). Update the onboarding handler to validate/parse add_year (e.g.,
parseInt) and discipline, and ensure mobile_no defaults to "" when missing
before constructing personal_info.
backend/routes/profile.js (1)

106-111: ⚠️ Potential issue | 🟠 Major

PII logged to console — updatedDetails can contain personal data.

Lines 110–111 log userId and updatedDetails (which may include name, email, phone, date of birth, hostel, room number, social links). This is a compliance/privacy risk in production. Remove or redact these logs.

🛡️ Suggested fix
-    console.log("Received userId:", userId);
-    console.log("Received updatedDetails:", updatedDetails);
+    // Avoid logging PII — use structured logging with redaction in production
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@backend/routes/profile.js` around lines 106 - 111, The code is logging
sensitive PII (userId and updatedDetails) in the route handler for
router.put("/updateStudentProfile", isAuthenticated, async (req, res) => { ...
}); remove those console.log calls (or replace them with non-PII telemetry) so
that userId and updatedDetails are not printed; if you need logging for
debugging, log a redact-safe summary (e.g., operation start and sanitized user
identifier or request id) rather than the full updatedDetails object and ensure
any retained logs do not include name, email, phone, DOB, hostel, room, or
social links.
backend/routes/auth.js (2)

220-234: ⚠️ Potential issue | 🟡 Minor

console.log(req.params) leaks token to logs.

Line 223 logs req.params, which includes the JWT reset token. An attacker with log access could use this to reset arbitrary passwords. Remove or redact.

🛡️ Fix
-    console.log(req.params);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@backend/routes/auth.js` around lines 220 - 234, The handler for
router.get("/reset-password/:id/:token") is leaking sensitive data by logging
req.params (which contains the reset token); remove the console.log(req.params)
call or replace it with a safe log that only includes non-sensitive fields
(e.g., log req.params.id or a redacted placeholder), ensuring you do not log the
token value anywhere in the resetPassword verification flow (refer to the route
handler function and the req.params variable).

237-260: ⚠️ Potential issue | 🔴 Critical

Critical regression: user.setPassword() no longer exists after the schema rewrite.

The userSchema.js was refactored to use a custom pre("save") hook for password hashing instead of the passport-local-mongoose plugin. The reset-password route at line 247 still calls user.setPassword(password, ...), which will throw TypeError: user.setPassword is not a function at runtime and break password resets entirely.

Set the password directly and let the pre-save hook handle hashing:

Proposed fix
   try {
     jwt.verify(token, secret);
-    user.setPassword(password, async (error) => {
-      if (error) {
-        return res.status(500).json({ message: "Error resetting password" });
-      }
-      await user.save();
-      return res
-        .status(200)
-        .json({ message: "Password has been reset successfully" });
-    });
+    user.password = password;
+    await user.save(); // pre-save hook hashes the password
+    return res
+      .status(200)
+      .json({ message: "Password has been reset successfully" });
   } catch (error) {
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@backend/routes/auth.js` around lines 237 - 260, The reset-password handler
still calls the removed method user.setPassword(password, ...); update the
router.post("/reset-password/:id/:token") flow to assign the plain password
directly to the User document (e.g. user.password = password), then await
user.save() so the schema's pre("save") hook performs hashing, and handle errors
from save with proper status responses; remove the setPassword callback usage
and keep the existing jwt.verify try/catch around the save operation.
🟠 Major comments (13)
backend/utils/batchValidate.js-3-3 (1)

3-3: ⚠️ Potential issue | 🟠 Major

ObjectId regex accepts non-hex characters — will allow invalid MongoDB ObjectIds.

MongoDB ObjectIds are 24-character hex strings (0-9, a-f). The current regex /^[0-9a-zA-Z]{24}$/ also accepts non-hex characters like g-z and G-Z, meaning invalid IDs will pass validation but fail at the database layer.

🐛 Proposed fix
-const zodObjectId = zod.string().regex(/^[0-9a-zA-Z]{24}$/, "Invalid ObjectId");
+const zodObjectId = zod.string().regex(/^[0-9a-fA-F]{24}$/, "Invalid ObjectId");
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@backend/utils/batchValidate.js` at line 3, The zodObjectId validator in
backend/utils/batchValidate.js currently allows non-hex characters; update the
regex used by zodObjectId so it only permits 24 hex characters (0-9 and a-f/A-F)
and keep the same "Invalid ObjectId" message; locate the zodObjectId constant
and replace its pattern with one that enforces exactly 24 hex digits.
frontend/src/services/auth.js-9-31 (1)

9-31: ⚠️ Potential issue | 🟠 Major

Inconsistent return shapes across service functions — callers can't safely consume these.

The functions in this module return wildly different shapes on success and error:

Function Success Error
fetchCredentials response.data throws
completeOnboarding response.data error.response (Axios response obj)
registerUser response (full Axios response!) error.response
loginUser res.data null

Specifically:

  • Line 27: registerUser returns the full Axios response object (with status, headers, config, etc.), while loginUser at line 38 returns res.data. This inconsistency means callers need to access response.data.data for registerUser vs result.someField for loginUser.
  • Lines 13-15 and 29-30: On error, completeOnboarding and registerUser return error.response (the raw Axios error response), which is a fundamentally different shape from the success path. Callers will need to check for status, data, etc.

Standardize all functions to return the same shape (e.g., always return response.data on success and null or a structured error object on error).

Proposed fix for registerUser
-    return response; 
+    return response.data; 
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@frontend/src/services/auth.js` around lines 9 - 31, completeOnboarding and
registerUser return inconsistent shapes (full axios response or error.response)
which breaks callers; update both to always return response.data on success and
a consistent error sentinel (e.g., null) on failure. Specifically, in
completeOnboarding replace returning error.response with null, and in
registerUser return response.data (not the whole response) on success and return
null in the catch block; ensure both functions keep the try/catch but normalize
their return shapes to response.data / null so callers can consume results
uniformly.
frontend/src/Components/Dashboard/Dashboard.jsx-15-15 (1)

15-15: ⚠️ Potential issue | 🟠 Major

Replace <div>Kaushik</div> with a proper fallback.

This substitutes what was previously the Home component with a raw developer string. Any user navigating to an unrecognised route will see "Kaushik" instead of a real UI. Restore the original fallback or use a proper "Not Found" / redirect component.

🐛 Proposed fix
-    DashboardComponents[selectedRoute] || (() => <div>Kaushik</div>);
+    DashboardComponents[selectedRoute] || Home;
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@frontend/src/Components/Dashboard/Dashboard.jsx` at line 15, The fallback for
unknown routes was replaced with a developer string; revert to a proper UI by
replacing the inline <div>Kaushik</div> in DashboardComponents[selectedRoute] ||
(() => <div>Kaushik</div>) with the original Home component or a
NotFound/Redirect component. Locate the DashboardComponents array/object and the
selectedRoute usage in Dashboard.jsx and return the Home component (or a
dedicated NotFound component) as the default render function so users see the
expected UI for unrecognized routes.
frontend/src/Components/Certificates/CertificatesList.jsx-22-52 (1)

22-52: ⚠️ Potential issue | 🟠 Major

Mock data will ship to production; api import is dead code until the endpoint is wired.

The real API call is commented out and the TODO is untracked. Merge this only after the /api/certificates/:id endpoint is ready and the mock block is removed, or gate the mock behind a dev-only flag so it can't accidentally reach production.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@frontend/src/Components/Certificates/CertificatesList.jsx` around lines 22 -
52, The component CertificatesList.jsx currently assigns hard-coded
mockCertificates and calls setCertificates, and the api import remains unused;
either remove the mock block and dead import before merging or gate the mock
data behind an environment/dev-only flag so it never runs in production. Replace
the mockCertificates + setCertificates path with the real API call to fetch
/api/certificates/:id when that endpoint exists (or wrap the mock assignment in
a check like process.env.NODE_ENV === 'development' / a feature flag) and clean
up the unused api import to prevent shipping dead code.
backend/controllers/eventControllers.js-6-9 (1)

6-9: ⚠️ Potential issue | 🟠 Major

Sort field updated_at (snake_case) doesn't match the selected/used field updatedAt (camelCase).

Mongoose's built-in timestamps use camelCase (updatedAt). Sorting by updated_at references a non-existent field, so the .limit(4) result order is effectively undefined — the "latest events" may not be the latest ones at all.

🐛 Proposed fix
-  .sort({updated_at: -1})
+  .sort({updatedAt: -1})
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@backend/controllers/eventControllers.js` around lines 6 - 9, The query that
builds latestEvents uses sort({updated_at: -1}) which references a non-existent
snake_case timestamp; change the sort to use Mongoose's camelCase timestamp
field (sort({updatedAt: -1})) so Event.find(...).sort(...) correctly orders by
the actual updatedAt field that is already being selected.
frontend/src/Components/Certificates/CertificatesList.jsx-14-64 (1)

14-64: ⚠️ Potential issue | 🟠 Major

Perpetual loading state when isUserLoggedIn is empty/null.

loading is initialised to true but the else branch (guard fails) never calls setLoading(false). Any user who lands on this page before isUserLoggedIn is populated — or without an active session — sees the "Loading certificates…" spinner indefinitely.

🐛 Proposed fix
    if (isUserLoggedIn && Object.keys(isUserLoggedIn).length > 0) {
      fetchCertificates();
+   } else {
+     setLoading(false);
    }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@frontend/src/Components/Certificates/CertificatesList.jsx` around lines 14 -
64, The useEffect/fetchCertificates flow leaves loading true when the guard if
(isUserLoggedIn && Object.keys(isUserLoggedIn).length > 0) fails; ensure
setLoading(false) is called in that case so the spinner doesn't show forever —
either initialize loading to false and only set true when starting
fetchCertificates, or add an else branch after the guard that calls
setLoading(false) (or explicitly setLoading(false) before returning) so that
setLoading is balanced whenever fetch is not invoked.
frontend/src/Components/Requests/Card.jsx-68-73 (1)

68-73: ⚠️ Potential issue | 🟠 Major

Object.assign(req, updatedRequest) mutates the parent's prop object in place.

Directly mutating props is a React anti-pattern — it bypasses React's rendering cycle, so the parent component won't know the data changed and won't re-render. This can lead to stale UI and subtle bugs.

Instead, lift the update to the parent via a callback, or maintain a local copy of the request in state.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@frontend/src/Components/Requests/Card.jsx` around lines 68 - 73, handleSave
currently mutates the prop object via Object.assign(req, updatedRequest), which
mutates the parent's prop and breaks React's render model; update handleSave to
avoid in-place mutation by calling a parent-provided updater (e.g.,
onUpdateRequest or similar prop) with the updatedRequest, or update a local copy
stored in state (e.g., useState for request) and setRequest(updatedCopy) instead
of mutating req directly; locate the handleSave function and replace the direct
Object.assign(req, updatedRequest) use with a call to the parent callback or a
setState update to ensure React re-renders.
backend/config/passportConfig.js-25-25 (1)

25-25: ⚠️ Potential issue | 🟠 Major

User email (PII) logged to stdout.

Logging raw email addresses may create compliance concerns (GDPR/institutional policy). Consider logging a redacted form or omitting the address entirely.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@backend/config/passportConfig.js` at line 25, The console.log in the Google
OAuth flow is printing a user's raw email (profile.emails[0].value); remove or
replace this PII output in passportConfig.js by either omitting the address
entirely or logging a redacted/hash form (e.g., mask local-part or log only
domain) and use the app's logger (e.g., processLogger.warn/info) instead of
console.log to maintain consistent logging; update the OAuth verify callback
where console.log("Google OAuth blocked for: ", profile.emails[0].value) appears
to use the redacted value or no email.
frontend/src/Components/Requests/Requests.jsx-155-158 (1)

155-158: ⚠️ Potential issue | 🟠 Major

Missing key prop on <Card> inside .map().

React requires a unique key for list-rendered elements to avoid reconciliation bugs and console warnings. Each req already has an id field.

🐛 Proposed fix
-            <Card req={req} statusColor={statusColor} />
+            <Card key={req.id} req={req} statusColor={statusColor} />
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@frontend/src/Components/Requests/Requests.jsx` around lines 155 - 158, The
Card elements rendered in Requests.jsx inside the filteredRequests.map do not
include a unique key, causing React list-key warnings; update the mapping to
pass a key prop to the Card (e.g., key={req.id}) when rendering each <Card
req={req} statusColor={statusColor} /> so React can properly reconcile the list
(use req.id as the primary key and fall back to index only if id may be
missing).
backend/routes/onboarding.js-33-34 (1)

33-34: ⚠️ Potential issue | 🟠 Major

Internal error details leaked to client.

error.message can contain Mongoose validation errors, stack traces, or internal details. Return a generic message to the client and keep the detailed log server-side.

Proposed fix
     console.error("Onboarding failed:", error.message);
-    res.status(500).json({ message: error.message || "Onboarding failed"  });
+    res.status(500).json({ message: "Onboarding failed" });
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@backend/routes/onboarding.js` around lines 33 - 34, The route currently leaks
internal error details by returning error.message to the client; update the
onboarding error handling so that console.error (or your server logger) logs the
full error/stack (e.g., use console.error("Onboarding failed:", error) or
processLogger.error(error)) and change the response to a generic message like
res.status(500).json({ message: "Onboarding failed" }) instead of
res.status(500).json({ message: error.message || "Onboarding failed" }); locate
and update the console.error and res.status(...).json(...) calls inside the
onboarding route handler.
backend/routes/profile.js-17-31 (1)

17-31: ⚠️ Potential issue | 🟠 Major

Missing authorization check — any authenticated user can modify any profile.

All three routes (/photo-update, /photo-delete, /updateStudentProfile) accept an ID_No or userId from the request body/query and operate on that user's profile. There is no check that req.user owns the targeted profile. An authenticated user could update or delete another user's photo or profile data.

Compare the incoming ID_No/userId against req.user.user_id (or equivalent) before proceeding.

Also applies to: 80-90, 106-117

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@backend/routes/profile.js` around lines 17 - 31, Add an ownership
authorization check to the profile routes so authenticated users cannot mutate
others' profiles: in the handlers for "/photo-update", "/photo-delete", and
"/updateStudentProfile" (the async route callbacks using ID_No or userId from
req.body/query), compare the supplied ID_No/userId with req.user.user_id (or the
canonical identifier on req.user) and immediately return a 403 Forbidden JSON
response if they differ; do this before calling User.findOne or performing any
update/delete and reuse the existing isAuthenticated middleware presence to
trust req.user.
backend/index.js-83-89 (1)

83-89: ⚠️ Potential issue | 🟠 Major

Server starts even if the DB connection fails.

connectDB() (per backend/config/db.js) catches and logs the error but does not re-throw or call process.exit(1). The await connectDB() here will resolve successfully even on failure, and the server will start listening without a working database. This leads to 500s on every request rather than a clear startup failure.

🛡️ Suggested fix — propagate DB errors

In backend/config/db.js, re-throw after logging:

   } catch (error) {
     console.error("MongoDB Connection Error:", error);
+    throw error;
   }

Or guard in index.js:

 (async function(){
-  await connectDB();
-  app.listen(process.env.PORT || 5000, () => {
-    console.log(`connected to port ${process.env.PORT || 5000}`);
-  });
+  try {
+    await connectDB();
+    app.listen(process.env.PORT || 5000, () => {
+      console.log(`connected to port ${process.env.PORT || 5000}`);
+    });
+  } catch (err) {
+    console.error("Failed to start server:", err);
+    process.exit(1);
+  }
 })();
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@backend/index.js` around lines 83 - 89, connectDB() currently swallows DB
errors so await connectDB() in the IIFE can resolve even on failure; either (A)
modify the implementation in backend/config/db.js so connectDB() re-throws the
error after logging (preserve current log but throw the caught error) or (B)
update the startup IIFE in backend/index.js to check the result and abort
startup on failure (wrap await connectDB() in try/catch and call process.exit(1)
on error before calling app.listen). Target the connectDB() function in
backend/config/db.js and the IIFE that calls await connectDB() / app.listen in
backend/index.js.
backend/models/userSchema.js-24-30 (1)

24-30: ⚠️ Potential issue | 🟠 Major

Password hash can leak in API responses — add select: false.

The password field lacks select: false, so it is included in every query result by default. In backend/routes/profile.js line 228, the full user object (including the hashed password) is returned in the updatedStudent response. Even hashed passwords should never leave the server.

Adding select: false ensures password is excluded unless explicitly requested (e.g., in login flows via .select("+password")).

Proposed fix
     password: {
       type: String,
       required: function () {
         return this.strategy === "local";
       },
       minLength: 8,
+      select: false,
     },
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@backend/models/userSchema.js` around lines 24 - 30, The password field on the
Mongoose schema (userSchema) is currently returned by default; add select: false
to the password schema definition so hashed passwords are excluded from query
results unless explicitly requested (e.g., with .select("+password") in login
flows). Update the password property in the userSchema (the password field
block) and ensure any code that needs the password (such as
authentication/login) uses .select("+password"); also check the profile route
where updatedStudent is returned to avoid sending the full user object with the
password.

Comment on lines +44 to +48
const position = await Position.findById(positionHolder.position_id);
console.log(position._id);
if (!position) {
return res.status(403).json({ message: "Your position is invalid" });
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

Null-pointer crash: position._id accessed before the null check.

Line 45 dereferences position._id via console.log, but the !position guard is on line 46. If Position.findById returns null, this line throws a TypeError.

🐛 Proposed fix
     const position = await Position.findById(positionHolder.position_id);
-    console.log(position._id);
     if (!position) {
       return res.status(403).json({ message: "Your position is invalid" });
     }
📝 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.

Suggested change
const position = await Position.findById(positionHolder.position_id);
console.log(position._id);
if (!position) {
return res.status(403).json({ message: "Your position is invalid" });
}
const position = await Position.findById(positionHolder.position_id);
if (!position) {
return res.status(403).json({ message: "Your position is invalid" });
}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@backend/controllers/certificateController.js` around lines 44 - 48, The code
logs position._id before verifying Position.findById returned a value, which can
throw when position is null; in the certificate controller locate the call to
Position.findById(positionHolder.position_id) and move the null-check before any
access or logging of position._id (or use safe access like position?._id),
returning the existing 403 response when position is falsy and only then
logging/using position._id.

Comment on lines +105 to +114
const presidentHolder = await PositionHolder.findOne({
position_id: presidentPosition._id,
});
const presidentId = presidentHolder.user_id.toString();
//console.log(presidentId);
const presidentObj = await User.findById(presidentId);

console.log(presidentObj._id);
if (!presidentObj) {
return res.status(500).json({ message: "President not found" });
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

Null-pointer crashes: presidentHolder and presidentObj accessed before null checks.

  • Line 108: presidentHolder.user_id.toString() — if PositionHolder.findOne returns null, this throws.
  • Line 112: console.log(presidentObj._id) runs before the !presidentObj check on line 113.
🐛 Proposed fix
     const presidentHolder = await PositionHolder.findOne({
       position_id: presidentPosition._id,
     });
+    if (!presidentHolder) {
+      return res.status(500).json({ message: "President holder not found for council" });
+    }
     const presidentId = presidentHolder.user_id.toString();
-    //console.log(presidentId);
     const presidentObj = await User.findById(presidentId);
-
-    console.log(presidentObj._id);
     if (!presidentObj) {
       return res.status(500).json({ message: "President not found" });
     }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@backend/controllers/certificateController.js` around lines 105 - 114, The
code accesses presidentHolder.user_id and presidentObj._id before verifying
those objects exist; update the logic around PositionHolder.findOne and
User.findById so you first check that presidentHolder is not null (e.g., after
calling PositionHolder.findOne) before using presidentHolder.user_id.toString(),
and only call User.findById if the holder exists; also move or remove the
console.log(presidentObj._id) so it occurs after you verify presidentObj is
non-null (and return the res.status(500).json({ message: "President not found"
}) immediately when either presidentHolder or presidentObj is missing). Ensure
you reference PositionHolder.findOne, presidentHolder, presidentHolder.user_id,
User.findById, presidentObj, and the res.status error return in your fix.

Comment on lines +119 to +131
const userChecks = await Promise.all(
users.map(async (uid) => {
const validation = zodObjectId.safeParse(uid);
if (!validation) {
return { uid, ok: false, reason: "Invalid ID" };
}

const userObj = await User.findById(uid);
if (!userObj) return { uid, ok: false, reason: "User not found" };

return { uid, ok: true };
}),
);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

safeParse always returns an object — !validation is always false.

zodObjectId.safeParse(uid) returns { success: boolean, ... }, which is truthy regardless of validation outcome. The check on line 122 should be !validation.success.

🐛 Proposed fix
-        const validation = zodObjectId.safeParse(uid);
-        if (!validation) {
+        const validation = zodObjectId.safeParse(uid);
+        if (!validation.success) {
           return { uid, ok: false, reason: "Invalid ID" };
         }
📝 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.

Suggested change
const userChecks = await Promise.all(
users.map(async (uid) => {
const validation = zodObjectId.safeParse(uid);
if (!validation) {
return { uid, ok: false, reason: "Invalid ID" };
}
const userObj = await User.findById(uid);
if (!userObj) return { uid, ok: false, reason: "User not found" };
return { uid, ok: true };
}),
);
const userChecks = await Promise.all(
users.map(async (uid) => {
const validation = zodObjectId.safeParse(uid);
if (!validation.success) {
return { uid, ok: false, reason: "Invalid ID" };
}
const userObj = await User.findById(uid);
if (!userObj) return { uid, ok: false, reason: "User not found" };
return { uid, ok: true };
}),
);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@backend/controllers/certificateController.js` around lines 119 - 131, The
validation logic in the users.map block is incorrect because
zodObjectId.safeParse(uid) returns an object with a success property, not a
falsy value on failure; update the check in the async mapper that currently does
`if (!validation)` to `if (!validation.success)` (and keep the rest of the flow
returning `{ uid, ok: false, reason: "Invalid ID" }`), referencing the
userChecks Promise.all, the users.map iterator, and zodObjectId.safeParse to
locate and fix the condition.

Comment on lines +48 to 54
store: MongoStore.create({
mongoUrl: process.env.MONGODB_URI,
ttl: 60*60*1000,
collectionName: "sessions"
}),
name: "token"
}),
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

cat -n backend/index.js | sed -n '45,60p'

Repository: OpenLake/Student_Database_COSA

Length of output: 612


🌐 Web query:

connect-mongo ttl option documentation seconds milliseconds

💡 Result:

In connect-mongo, the ttl option is expressed in seconds (not milliseconds). The README shows the default as ttl: 14 * 24 * 60 * 60 (14 days), which is a seconds calculation. [1]

This aligns with MongoDB’s TTL indexes, which use expireAfterSeconds—also seconds. [2]

If you’re mixing this with express-session, note that cookie.maxAge is in milliseconds, while store ttl is in seconds. [1]

Sources: [1] connect-mongo README, [2] MongoDB TTL index docs.


Fix MongoStore ttl unit mismatch — sessions will accumulate in MongoDB for ~42 days instead of 1 hour.

The ttl option in MongoStore expects seconds, but 60*60*1000 (3,600,000 seconds ≈ 41.67 days) is being passed. This should be 60 * 60 (3,600 seconds) to match the 1-hour maxAge on line 46. Sessions will unnecessarily bloat MongoDB storage.

Suggested fix
     store: MongoStore.create({
       mongoUrl: process.env.MONGODB_URI,
-      ttl: 60*60*1000,
+      ttl: 60 * 60, // 1 hour in seconds
       collectionName: "sessions"
     }),
📝 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.

Suggested change
store: MongoStore.create({
mongoUrl: process.env.MONGODB_URI,
ttl: 60*60*1000,
collectionName: "sessions"
}),
name: "token"
}),
store: MongoStore.create({
mongoUrl: process.env.MONGODB_URI,
ttl: 60 * 60, // 1 hour in seconds
collectionName: "sessions"
}),
name: "token"
}),
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@backend/index.js` around lines 48 - 54, The MongoStore.create call is passing
ttl in milliseconds (ttl: 60*60*1000) but MongoStore expects seconds, so replace
the ttl expression with seconds (e.g., ttl: 60*60 or 3600) to match the 1-hour
maxAge used in the session config; update the ttl value in the session store
configuration (MongoStore.create) to use seconds to prevent sessions piling up
in MongoDB.

Comment on lines +6 to +9
unit_id: {
type: mongoose.Schema.Types.ObjectId,
ref: "Oraganizational_Unit",
},
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

Typo in ref: "Oraganizational_Unit" should be "Organizational_Unit".

The model registered in organizationSchema.js is "Organizational_Unit". This misspelling will cause .populate('unit_id') to silently return null for every batch.

Proposed fix
     unit_id: {
       type: mongoose.Schema.Types.ObjectId,
-      ref: "Oraganizational_Unit",
+      ref: "Organizational_Unit",
     },
📝 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.

Suggested change
unit_id: {
type: mongoose.Schema.Types.ObjectId,
ref: "Oraganizational_Unit",
},
unit_id: {
type: mongoose.Schema.Types.ObjectId,
ref: "Organizational_Unit",
},
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@backend/models/certificateSchema.js` around lines 6 - 9, The
certificateSchema has a typo in the Mongoose populate reference: change the ref
on field unit_id in certificateSchema (currently "Oraganizational_Unit") to
match the model name "Organizational_Unit" used in organizationSchema.js so that
.populate('unit_id') returns the related documents correctly.

Comment on lines +25 to +30
router.get(
"/club-coordinator",
authorizeRole(["CLUB_COORDINATOR"]),
isAuthenticated,
controller.getClubCoordinatorAnalytics,
);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

Critical: authorizeRole runs before isAuthenticated on /club-coordinator.

authorizeRole depends on req.user being populated by isAuthenticated. With the middlewares reversed, req.user is undefined when authorizeRole executes — either crashing with a 500 or, depending on authorizeRole's null-check behavior, silently passing unauthenticated requests through. Every other route in this file (and across the codebase) correctly places isAuthenticated first.

🔒 Proposed fix
 router.get(
   "/club-coordinator",
-  authorizeRole(["CLUB_COORDINATOR"]),
   isAuthenticated,
+  authorizeRole(["CLUB_COORDINATOR"]),
   controller.getClubCoordinatorAnalytics,
 );
📝 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.

Suggested change
router.get(
"/club-coordinator",
authorizeRole(["CLUB_COORDINATOR"]),
isAuthenticated,
controller.getClubCoordinatorAnalytics,
);
router.get(
"/club-coordinator",
isAuthenticated,
authorizeRole(["CLUB_COORDINATOR"]),
controller.getClubCoordinatorAnalytics,
);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@backend/routes/analytics.js` around lines 25 - 30, The route registration for
router.get("/club-coordinator", ...) calls authorizeRole before isAuthenticated,
which causes authorizeRole to run without req.user; swap the middleware order so
isAuthenticated runs before authorizeRole (i.e., call isAuthenticated then
authorizeRole) in the router.get for "/club-coordinator" referencing
controller.getClubCoordinatorAnalytics; ensure the same middleware order pattern
(isAuthenticated then authorizeRole) is used consistently across this file.

const router = express.Router();
const { User } = require("../models/schema");
const isAuthenticated = require("../middlewares/isAuthenticated");
const User = require("../models/userSchema");
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Check how userSchema.js exports the User model
fd "userSchema.js" --type f --exec cat {}

# Check all import styles for userSchema across the codebase
rg -n "require.*userSchema" --type js

Repository: OpenLake/Student_Database_COSA

Length of output: 3046


Critical: profile.js uses incorrect import pattern for User model.

The userSchema.js exports the User model as a default export (module.exports = User), but backend/routes/profile.js (line 7) imports it with destructuring syntax (const { User } = require(...)). This causes User to be undefined in profile.js, breaking all code that references the model.

Use const User = require("../models/userSchema"); instead. All other route files use the correct default import pattern.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@backend/routes/onboarding.js` at line 3, profile.js incorrectly imports the
User model using destructuring (const { User } = require(...)) which returns
undefined because userSchema.js exports the model as module.exports = User;
update the import in profile.js to use the default import pattern (require the
module directly into a User variable) so references to User in functions like
the route handlers work correctly.

const cloudinary = require("cloudinary").v2;
//const { Student } = require("../models/student");
const { User } = require("../models/schema");
const { User } = require("../models/userSchema");
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
echo "=== Export in userSchema.js ==="
fd "userSchema.js" --type f --exec cat -n {} \;

echo ""
echo "=== All imports of userSchema across the codebase ==="
rg -n "require.*userSchema" --type js -A 1 -B 1

Repository: OpenLake/Student_Database_COSA

Length of output: 5295


Fix the User import destructuring — it will cause undefined and break all queries.

userSchema.js (line 108) exports User as the default export: module.exports = User. Destructuring it as { User } attempts to extract a property from the model, yielding undefined. Every User.findOne(), User.updateOne(), etc. call will throw TypeError: Cannot read properties of undefined at runtime.

All other files in the codebase (auth.js, events.js, onboarding.js, etc.) correctly use const User = require("../models/userSchema") without destructuring.

-const { User } = require("../models/userSchema");
+const User = require("../models/userSchema");
📝 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.

Suggested change
const { User } = require("../models/userSchema");
const User = require("../models/userSchema");
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@backend/routes/profile.js` at line 7, The import incorrectly destructures
User causing undefined; replace the destructured import with the default export
import so the User model is assigned correctly — change the require call that
currently reads as const { User } = require("../models/userSchema") to use a
single identifier (const User = require("../models/userSchema")) so calls like
User.findOne and User.updateOne work; update any similar imports in this file
that destructure the model.

Comment on lines +71 to +108
<form onSubmit={handleSubmit} className="px-6 pb-6">
<div className="mb-6">
<textarea
value={reason}
onChange={(e) => setReason(e.target.value)}
required
minLength={10}
rows={3}
className="w-full px-4 py-3 bg-gray-700 border-gray-600 rounded-xl text-white placeholder-gray-400 focus:outline-none focus:ring-2 focus:ring-gray-500 resize-none transition-all"
placeholder="Enter rejection reason..."
/>
{reason.length < 10 && (
<p className="text-red-400 text-xs mt-2 ml-1">
{10 - reason.length} more characters required
</p>
)}
</div>

{/* Buttons */}
<div className="flex gap-3">
<button
type="button"
onClick={handleClose}
disabled={isSubmitting}
className="flex-end px-3 !py-1 bg-gray-600 text-gray-200 !rounded-xl font-medium hover:bg-gray-500 transition-all duration-200 disabled:opacity-50 disabled:cursor-not-allowed"
>
Close
</button>
<button
type="submit"
onClick={handleSubmit}
disabled={
isSubmitting || !reason.trim() || reason.trim().length < 10
}
className="flex-end px-3 !py-1 bg-red-600 text-white !rounded-xl font-medium hover:bg-red-700 transition-all duration-200 disabled:opacity-50 disabled:cursor-not-allowed"
>
{isSubmitting ? "Rejecting..." : "Reject"}
</button>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

Double-submission bug: handleSubmit is called twice on every Reject click.

The Reject button has both onClick={handleSubmit} and type="submit" inside a <form onSubmit={handleSubmit}>. When clicked, onClick fires first (before React re-renders), then the form's submit event fires. Because React state updates are batched, isSubmitting is still false in both invocations, so both pass validation and call onReject(reason.trim()) — the rejection is submitted twice.

Fix: remove the redundant onClick from the submit button; the form's onSubmit is sufficient.

🐛 Proposed fix
  <button
    type="submit"
-   onClick={handleSubmit}
    disabled={
      isSubmitting || !reason.trim() || reason.trim().length < 10
    }
    className="flex-end px-3 !py-1 bg-red-600 text-white !rounded-xl font-medium hover:bg-red-700 transition-all duration-200 disabled:opacity-50 disabled:cursor-not-allowed"
  >
    {isSubmitting ? "Rejecting..." : "Reject"}
  </button>
📝 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.

Suggested change
<form onSubmit={handleSubmit} className="px-6 pb-6">
<div className="mb-6">
<textarea
value={reason}
onChange={(e) => setReason(e.target.value)}
required
minLength={10}
rows={3}
className="w-full px-4 py-3 bg-gray-700 border-gray-600 rounded-xl text-white placeholder-gray-400 focus:outline-none focus:ring-2 focus:ring-gray-500 resize-none transition-all"
placeholder="Enter rejection reason..."
/>
{reason.length < 10 && (
<p className="text-red-400 text-xs mt-2 ml-1">
{10 - reason.length} more characters required
</p>
)}
</div>
{/* Buttons */}
<div className="flex gap-3">
<button
type="button"
onClick={handleClose}
disabled={isSubmitting}
className="flex-end px-3 !py-1 bg-gray-600 text-gray-200 !rounded-xl font-medium hover:bg-gray-500 transition-all duration-200 disabled:opacity-50 disabled:cursor-not-allowed"
>
Close
</button>
<button
type="submit"
onClick={handleSubmit}
disabled={
isSubmitting || !reason.trim() || reason.trim().length < 10
}
className="flex-end px-3 !py-1 bg-red-600 text-white !rounded-xl font-medium hover:bg-red-700 transition-all duration-200 disabled:opacity-50 disabled:cursor-not-allowed"
>
{isSubmitting ? "Rejecting..." : "Reject"}
</button>
<form onSubmit={handleSubmit} className="px-6 pb-6">
<div className="mb-6">
<textarea
value={reason}
onChange={(e) => setReason(e.target.value)}
required
minLength={10}
rows={3}
className="w-full px-4 py-3 bg-gray-700 border-gray-600 rounded-xl text-white placeholder-gray-400 focus:outline-none focus:ring-2 focus:ring-gray-500 resize-none transition-all"
placeholder="Enter rejection reason..."
/>
{reason.length < 10 && (
<p className="text-red-400 text-xs mt-2 ml-1">
{10 - reason.length} more characters required
</p>
)}
</div>
{/* Buttons */}
<div className="flex gap-3">
<button
type="button"
onClick={handleClose}
disabled={isSubmitting}
className="flex-end px-3 !py-1 bg-gray-600 text-gray-200 !rounded-xl font-medium hover:bg-gray-500 transition-all duration-200 disabled:opacity-50 disabled:cursor-not-allowed"
>
Close
</button>
<button
type="submit"
disabled={
isSubmitting || !reason.trim() || reason.trim().length < 10
}
className="flex-end px-3 !py-1 bg-red-600 text-white !rounded-xl font-medium hover:bg-red-700 transition-all duration-200 disabled:opacity-50 disabled:cursor-not-allowed"
>
{isSubmitting ? "Rejecting..." : "Reject"}
</button>
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@frontend/src/Components/Requests/RejectModal.jsx` around lines 71 - 108, The
Reject button triggers handleSubmit twice because the form has
onSubmit={handleSubmit} and the submit button also has onClick={handleSubmit};
remove the redundant onClick from the submit button so the form-level onSubmit
handles submissions (keep the disabled logic that checks isSubmitting,
reason.trim(), and length, and ensure handleSubmit still sets isSubmitting and
calls onReject(reason.trim())). Reference: the <form onSubmit={handleSubmit}>,
the submit button's onClick={handleSubmit}, handleSubmit, isSubmitting, and
onReject.

import ProfilePage from "../pages/profilePage";
import AnnouncementsPage from "../pages/announcementsPage";
import CertificatesPage from "../pages/certificatesPage";
import RequestsPage from "../pages/requestsPage";
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Search for requestsPage files and check for default exports
fd -t f 'requestsPage' --exec grep -H "export default" {} \;

# Also check if the file exists in the expected location
if [ -f "frontend/src/pages/requestsPage.jsx" ]; then
  echo "File exists at expected location: frontend/src/pages/requestsPage.jsx"
  echo "File contents:"
  cat "frontend/src/pages/requestsPage.jsx"
else
  echo "File NOT found at expected location: frontend/src/pages/requestsPage.jsx"
  # Search more broadly
  echo "Searching for any requestsPage files in the repository:"
  fd -t f 'requestsPage'
fi

Repository: OpenLake/Student_Database_COSA

Length of output: 1006


Rename CertificatesPage to RequestsPage in frontend/src/pages/requestsPage.jsx.

The default export function is named CertificatesPage but should be RequestsPage to match the file name and its actual purpose. This naming mismatch indicates a copy-paste error from certificatesPage.jsx.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@frontend/src/config/dashboardComponents.js` at line 28, The default exported
React component in requestsPage.jsx is misnamed: change the function name from
CertificatesPage to RequestsPage so the default export and component name match;
locate the default export declaration (CertificatesPage) in requestsPage.jsx and
rename the function to RequestsPage, update any internal references if present,
and ensure the file still exports that component as the default.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant