Skip to content

Refactor/login register#222

Open
codewkaushik404 wants to merge 21 commits intoOpenLake:mainfrom
codewkaushik404:refactor/login-register
Open

Refactor/login register#222
codewkaushik404 wants to merge 21 commits intoOpenLake:mainfrom
codewkaushik404:refactor/login-register

Conversation

@codewkaushik404
Copy link

@codewkaushik404 codewkaushik404 commented Jan 29, 2026


name: "Pull Request"
about: Propose and submit changes to the project for review
title: "PR: Refactor login/register and add Zod validation"
labels: ""
assignees: harshitap1305, sakshi1755

Changes Introduced

  • Moved: DB connection and Passport strategy to config folder for cleaner project structure
  • Modified: User schema to ensure login & register routes work as intended
  • Implemented: Manual login and registration logic (replacing broken User.register() and plugins)
  • Added: Zod validation for input fields during POST requests
    • Integrated email regex check for "iitbhilai.ac.in" directly in Zod
    • Validation logic moved to utils folder for modularity

Why This Change?

  • Problem:
    • Previous Passport User.register() implementation was broken
    • Input validation and email checks were scattered and inconsistent
  • Solution:
    • Refactored authentication flow manually for reliability
    • Centralized configuration and validation logic
  • Impact:
    • Ensures only valid IIT Bhilai emails can register
    • Makes login/register routes maintainable and error-free

Screenshots

image

Testing

  • Ran unit tests and all passed (npm test)
  • Manually tested login and registration with valid and invalid inputs
    • Test Case 1: Login with correct credentials → Success
    • Test Case 2: Login with wrong credentials → Fails as expected
    • Test Case 3: Register with non-IITB email → Fails
    • Test Case 4: Register with valid IITB email → Success
  • Verified 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

  • Created a new branch for this PR (refactor/login-register)
  • Starred the repository
  • Code follows project coding style and conventions
  • Commit messages are clear and follow guidelines
  • Performed a self-review of the code
  • Added tests that prove fixes/features work
  • All new and existing tests passed locally
  • No breaking changes introduced

Deployment Notes

  • Requires database migration/schema update
  • Requires new environment variables
  • N/A

Additional Notes

  • Refactored authentication flow to be independent of broken Passport plugins
  • Centralized configuration and validation improves maintainability

Summary by CodeRabbit

Release Notes

  • New Features

    • Certificate batch processing system with multi-level approval workflow
    • Google OAuth 2.0 authentication support with domain verification
    • JWT token-based session management for enhanced security
  • Improvements

    • Standardized API authentication responses across all endpoints
    • Enhanced error handling and validation in authentication flows
    • Improved onboarding user experience with updated form fields
  • Refactor

    • Modernized authentication system architecture
    • Reorganized data model structure for better maintainability
    • Updated dependency stack for improved security and performance

@vercel
Copy link

vercel bot commented Jan 29, 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 Jan 29, 2026

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

Walkthrough

This PR introduces a comprehensive authentication system refactoring, moving from passport-local-mongoose plugins to explicit User schema management with support for both local and Google OAuth 2.0 strategies. It separates a monolithic schema.js into dedicated model files (User, Event, Achievement, Feedback, etc.), implements JWT-based middleware alongside Passport, adds a certificate batch system with multi-level approval workflows, and refactors frontend authentication logic to use hook-based context consumption. Dependencies are updated to use bcrypt, connect-mongo, cookie-parser, and zod validation.

Changes

Cohort / File(s) Summary
Database & Configuration
backend/config/db.js, backend/config/passportConfig.js
New MongoDB connection module and Passport config with Google OAuth 2.0 and Local strategies supporting IIT Bhilai email validation and multi-strategy user creation.
Authentication Models & Schema
backend/models/userSchema.js, backend/models/schema.js
New User schema supporting local/google strategies with bcrypt password hashing and pre-save middleware; schema.js retains only UserSkill, Skill, Announcement; removed passport plugins and 7 model exports.
Separated Model Modules
backend/models/achievementSchema.js, backend/models/eventSchema.js, backend/models/feedbackSchema.js, backend/models/organizationSchema.js, backend/models/positionHolderSchema.js, backend/models/positionSchema.js
Six new dedicated model files extracted from monolithic schema.js with enhanced field definitions, timestamps, and references.
Certificate System
backend/models/certificateSchema.js, backend/controllers/certificateController.js, backend/routes/certificateRoutes.js
New certificate batch system with CertificateBatch and Certificate models supporting multi-level approval (PendingL1/L2), batch creation with authorization checks, unit hierarchy validation, and approver resolution.
Authentication Routes
backend/routes/auth.js
Rewritten login/register flows: manual passport.authenticate callbacks, explicit session handling, registerValidate validation, forgot/reset password error handling, and structured response formats.
Route Import Refactoring
backend/routes/achievements.js, backend/routes/announcements.js, backend/routes/dashboard.js, backend/routes/events.js, backend/routes/feedbackRoutes.js, backend/routes/onboarding.js, backend/routes/orgUnit.js, backend/routes/positionRoutes.js, backend/routes/skillsRoutes.js
Updated imports to use specific model files instead of schema.js; isAuthenticated converted to named exports; minor formatting standardization.
Profile & Events Routes
backend/routes/profile.js, backend/routes/events.js
Updated User/Event model imports; isAuthenticated as named export; profile route handles userId/updatedDetails; events route uses new Event model and CastError handling.
Middleware & Utilities
backend/middlewares/isAuthenticated.js, backend/utils/authValidate.js, backend/utils/batchValidate.js
New jwtIsAuthenticated middleware for token-based auth; loginValidate and registerValidate Zod validators with IIT Bhilai email regex; validateBatchSchema for certificate batches.
Controllers
backend/controllers/analyticsController.js, backend/controllers/dashboardController.js, backend/controllers/eventControllers.js
Updated model imports from schema.js to specific files; preserved logic but aligned with new model organization.
Server Entry Point
backend/index.js, backend/package.json
Async DB connection before server start, MongoDB session store via MongoStore, cookieParser middleware, Passport init after session, new certificate routes; dependencies: added bcrypt, connect-mongo, cookie-parser, zod; removed body-parser, passport plugins.
Frontend Context & Hooks
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/Components/Dashboard/Dashboard.jsx
Replaced direct AdminContext imports with useAdminContext hook; updated login response handling for response.success/data; Register form now controlled with username/password/name; Google login uses window.location redirect; onboarding changed to PUT; added optional chaining for user data.
Frontend Services
frontend/src/hooks/useAuth.js, frontend/src/services/auth.js
useAuth now handles response objects from fetchCredentials; robust userData validation; auth service updated: registerUser(username, password, name), loginUser(username, password), new logoutUser(), completeOnboarding changed to PUT.
Frontend App Setup
frontend/src/App.jsx, frontend/src/index.jsx
New router-driven App with authentication-based route composition, getPublicRoutes/getAdminRoutes/getStudentRoutes/getDashboardRoutes, AdminContext.Provider, LoadingScreen, onboarding/unauthorised routes; syntax fix in index.jsx.

Sequence Diagrams

sequenceDiagram
    actor User
    participant Client as Frontend
    participant Server as Backend
    participant Passport as Passport.js
    participant DB as MongoDB
    participant Mailer as Email Service

    User->>Client: Enter credentials (username, password)
    Client->>Server: POST /api/auth/login
    Server->>Passport: passport.authenticate("local")
    Passport->>DB: Find User by username
    DB-->>Passport: User document
    Passport->>Passport: bcrypt compare password
    alt Password valid
        Passport-->>Server: User object
        Server->>Server: req.login(user, callback)
        Server->>DB: Update session store
        DB-->>Server: Session created
        Server-->>Client: {success: true, data: user}
        Client->>Client: handleLogin(user)
        Client-->>User: Redirect to /onboarding
    else Password invalid
        Passport-->>Server: Authentication failed
        Server-->>Client: {success: false, error}
        Client-->>User: Display error toast
    end
Loading
sequenceDiagram
    actor Coordinator as Club Coordinator
    participant Client as Frontend
    participant Server as Backend
    participant AuthMW as JWT Middleware
    participant Ctrl as Certificate Controller
    participant DB as MongoDB
    participant Models as Schema Models

    Coordinator->>Client: Submit certificate batch (title, users, etc.)
    Client->>Server: POST /api/certificate-batches
    Server->>AuthMW: Verify JWT token from cookies
    AuthMW->>DB: Decode and validate token
    DB-->>AuthMW: Valid user_id
    AuthMW-->>Server: Attach user to req
    
    Server->>Ctrl: createBatch(req, res)
    Ctrl->>DB: Fetch User by ID
    DB-->>Ctrl: User document
    
    Ctrl->>Ctrl: Verify role is CLUB_COORDINATOR
    Ctrl->>DB: Fetch PositionHolder & Position
    DB-->>Ctrl: Position details
    
    Ctrl->>Ctrl: Validate batch schema (title, unit_id, users)
    Ctrl->>DB: Fetch Organizational_Unit (Club)
    DB-->>Ctrl: Unit document
    
    Ctrl->>Ctrl: Verify unit is Club, belongs to Council
    Ctrl->>DB: Find council GENSEC and President
    DB-->>Ctrl: Approver users
    
    Ctrl->>DB: Validate all user IDs exist
    alt All users valid
        Ctrl->>DB: Create CertificateBatch
        DB-->>Ctrl: Batch created with status PendingL1
        Ctrl-->>Server: {success: true, batch}
        Server-->>Client: Batch ID & status
        Client-->>Coordinator: Show success toast
    else User validation fails
        Ctrl-->>Server: {error: "Invalid user"}
        Server-->>Client: Error response
        Client-->>Coordinator: Display error
    end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

  • #217: Modifies Announcement model and related routes; this PR refactors announcement route imports but changes primarily focus on authentication and certificate systems rather than announcement features.

Suggested reviewers

  • harshitap1305

Poem

🐰 Ears twitch with excitement
Auth flows reorganized with care and might,
Certificates approved through levels of light,
Schemas split free from monolithic walls,
JWT hops through secure protocol halls,
From tangled vine to modular tree,
This rabbit's proud of what's come to be! 🌟

🚥 Pre-merge checks | ✅ 1 | ❌ 2

❌ Failed checks (1 warning, 1 inconclusive)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 17.65% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Title check ❓ Inconclusive The title 'Refactor/login register' is vague and uses generic phrasing; it does not clearly summarize the main changes like Zod validation integration, schema restructuring, or authentication flow refactoring. Consider a more specific title such as 'Refactor authentication with manual login/register and Zod validation' to better convey the substantive changes introduced.
✅ Passed checks (1 passed)
Check name Status Explanation
Description check ✅ Passed The PR description is comprehensive and well-structured, covering changes introduced, problem/solution, testing, and deployment notes. It closely follows the required template with most sections completed.

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

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

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: 8

Caution

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

⚠️ Outside diff range comments (1)
backend/routes/auth.js (1)

208-226: user.setPassword() does not exist on the User model and will throw at runtime.

The User schema does not have the passport-local-mongoose plugin attached, so the setPassword method is unavailable. Line 218 will crash with "user.setPassword is not a function" when a password reset is attempted. The inconsistency is problematic—register and login both use bcrypt, but reset-password relies on a non-existent method.

Use bcrypt.hash() to hash the password, consistent with the register route pattern:

Suggested 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" });
-    });
+    const hashed = await bcrypt.hash(password, Number(process.env.SALT));
+    user.password = hashed;
+    await user.save();
+    return res
+      .status(200)
+      .json({ message: "Password has been reset successfully" });
   } catch (error) {
     console.log(error);
     return res.status(400).json({ message: "Invalid or expired token" });
   }
🤖 Fix all issues with AI agents
In `@backend/config/passportConfig.js`:
- Around line 1-2: The import for GoogleStrategy is wrong: change the require
for GoogleStrategy so it references the Strategy property from the
passport-google-oauth20 package (so that new GoogleStrategy(...) is a valid
constructor); locate the GoogleStrategy import statement (currently const
GoogleStrategy = require("passport-google-oauth20")) and update it to pull
.Strategy to avoid the "not a constructor" runtime error when instantiating
GoogleStrategy in passport configuration.
- Around line 54-62: Change passport.serializeUser to store only the user's
unique identifier (e.g., user._id) instead of the whole user object, and adjust
passport.deserializeUser to accept that id (e.g., userId) and call
User.findById(userId) to load the full user; update references to
passport.serializeUser, passport.deserializeUser, and User.findById (remove
reliance on userKey._id) so sessions contain only the ID and deserialization
fetches the user record.
- Around line 14-21: The verify callback for the Google Passport strategy
accesses profile.emails[0].value without checking for existence; add a defensive
guard similar to the profile.photos check: ensure profile.emails is an array and
has at least one entry before reading .value (e.g., if (!profile.emails ||
profile.emails.length === 0) return done(null, false, { message: "Google profile
has no email" })), then use profile.emails[0].value for isIITBhilaiEmail and
logging; update all usages in the async (accessToken, refreshToken, profile,
done) => callback to avoid crashes when emails are missing.

In `@backend/routes/auth.js`:
- Around line 70-101: Remove trusting client-supplied role: stop extracting role
from req.body and do not pass it into User.create; instead determine role
server-side (e.g., set a default like "user" or map/whitelist based on
authenticated context) before creating the user. Update the destructuring that
currently includes role (from req.body) and replace the role passed to
User.create with the server-controlled value (not the request value) and, if you
need role assignment rules, implement a small whitelist/mapping in the route
handler to derive role securely.
- Around line 44-52: Fix the response syntax and guard against non-local
accounts before comparing passwords: replace the incorrect
res.json(401).json(...) calls with res.status(401).json(...), and before calling
bcrypt.compare(password, user.password) (after User.findOne({ username })),
ensure the account is a local/password account—e.g., check for a local strategy
flag (user.authStrategy === 'local') or at minimum that user.password exists; if
not, return res.status(401).json({ message: "Invalid user credentials" })
instead of attempting bcrypt.compare. Use the existing User.findOne, username,
bcrypt.compare and res.status symbols to locate where to add the guard and
change the response call.
- Around line 44-52: The error responses in the authentication flow use
res.json(401).json(...) which incorrectly sets a 200 status; update each
occurrence to use res.status(401).json(...) instead — specifically change the
early-return after User.findOne (the block using User.findOne({ username }) and
returning on missing user) and the password check block using
bcrypt.compare(password, user.password), plus any other similar occurrences
around the authentication routes in auth.js, so the responses call
res.status(401).json({ message: "Invalid user credentials" }) to correctly set
the HTTP 401 status.
- Around line 95-107: The registration response returns the created Mongoose
document `newUser` which includes the hashed `password`; before calling
`res.json` remove or omit the password field (e.g., convert `newUser` to a plain
object and delete the `password` property or re-query with a projection) so that
`res.json({ message: "Registered Successfully", user: ... })` never contains the
hashed password; update the code around `User.create`/`newUser` and the
`res.json` call to return the sanitized user object instead.

In `@backend/utils/validate.js`:
- Line 1: The module backend/utils/validate.js requires the zod package (const
zod = require("zod")), but zod is missing from backend/package.json; add "zod"
to the dependencies in backend/package.json (e.g., use npm install --save zod or
add the dependency entry) and run the install so the require("zod") in
validate.js resolves at runtime.
🧹 Nitpick comments (3)
backend/utils/validate.js (1)

3-14: DRY the IIT Bhilai email regex to avoid drift.

Lines 3-13 duplicate the same regex (and another check exists in backend/utils/isIITBhilaiEmail). Extract a shared constant/helper so all auth entry points stay consistent.

♻️ Proposed refactor
+const IITB_EMAIL_REGEX = /^[a-zA-Z0-9._%+-]+@iitbhilai\.ac\.in$/i;
+
 const loginValidate = zod.object({
-  username: zod.string().regex(/^[a-zA-Z0-9._%+-]+@iitbhilai\.ac\.in$/i),
+  username: zod.string().regex(IITB_EMAIL_REGEX),
   password: zod.string().min(8),
 });

 const registerValidate = zod.object({
-  username: zod.string().regex(/^[a-zA-Z0-9._%+-]+@iitbhilai\.ac\.in$/i),
+  username: zod.string().regex(IITB_EMAIL_REGEX),
   password: zod.string().min(8),
   user_id: zod.string().min(2),
   name: zod.string().min(5),
   role: zod.string().min(5),
 });
backend/config/db.js (1)

17-25: Return the close promise so shutdown can await completion.

Line 17 doesn’t return/await the close operation, so callers can’t reliably wait for cleanup.

♻️ Suggested refactor
-const closeDB = () => {
-  mongoose.connection
-    .close()
-    .then(() => {
-      console.log("MongoDB connection closed");
-    })
-    .catch((err) => {
-      console.error("Error while closing MongoDB connection:", err);
-    });
-};
+const closeDB = async () => {
+  try {
+    await mongoose.connection.close();
+    console.log("MongoDB connection closed");
+  } catch (err) {
+    console.error("Error while closing MongoDB connection:", err);
+  }
+};
backend/models/schema.js (1)

22-28: Consider adding select: false to prevent accidental password hash exposure.

The minLength: 8 option is correctly spelled and fully supported in Mongoose v7.6.8 (both minLength and minlength are valid). However, add select: false to ensure password hashes are not returned in default queries.

🛠️ Suggested update
    password: {
      type: String,
      required: function () {
        return this.strategy === "local";
      },
      minLength: 8,
+     select: false,
    },

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: 6

Caution

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

⚠️ Outside diff range comments (4)
backend/models/passportConfig.js (3)

31-43: ⚠️ Potential issue | 🔴 Critical

role: "STUDENT" is set but the User schema has no role field — this value will be silently dropped.

The userSchema.js model does not define a role field. With Mongoose's default strict mode, this property will be discarded on save. If roles are needed for authorization, add the field to the schema.

#!/bin/bash
# Verify that the User schema does not have a role field
rg -n "role" backend/models/userSchema.js

55-57: ⚠️ Potential issue | 🟠 Major

Serializing the entire user object into the session is inefficient and leaks the password hash.

serializeUser should store only the user ID. The full document (including the hashed password) ends up in the session store.

Proposed fix
 passport.serializeUser((user, done) => {
-  done(null, user);
+  done(null, user._id);
 });
 
-passport.deserializeUser(async (userKey, done) => {
+passport.deserializeUser(async (id, done) => {
   try {
-    let user = await User.findById(userKey._id);
+    let user = await User.findById(id);
     done(null, user);
   } catch (err) {
     done(err);
   }
 });

17-18: ⚠️ Potential issue | 🟡 Minor

Logging user email on OAuth rejection is a PII/compliance concern.

Consider logging a sanitized identifier or omitting the email entirely.

Proposed fix
-        console.log("Google OAuth blocked for: ", profile.emails[0].value);
+        console.log("Google OAuth blocked: non-institutional email domain");
backend/routes/auth.js (1)

224-247: ⚠️ Potential issue | 🔴 Critical

user.setPassword() will fail at runtime — passport-local-mongoose was removed.

The /reset-password POST handler at line 234 calls user.setPassword(password, callback), which is a passport-local-mongoose plugin method that no longer exists. This will throw a TypeError: user.setPassword is not a function at runtime.

Replace this with direct password assignment and save, relying on the pre-save hook at backend/models/userSchema.js:97-100 to handle bcrypt 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();
+    return res
+      .status(200)
+      .json({ message: "Password has been reset successfully" });
   } catch (error) {
🤖 Fix all issues with AI agents
In `@backend/models/organizationSchema.js`:
- Around line 70-78: The organizationSchema currently defines created_at and
updated_at with default: Date.now so updated_at won't change on document
updates; instead remove those manual fields and enable Mongoose's automatic
timestamps by adding the timestamps: true option when defining
organizationSchema (matching userSchema and positionHolderSchema), or
alternatively implement a pre('save') / pre('findOneAndUpdate') hook on
organizationSchema to set updated_at = Date.now() on modifications; update any
code that referenced the manual created_at/updated_at fields if their names
change (e.g., to createdAt/updatedAt) to match Mongoose's timestamp naming if
you choose timestamps: true.

In `@backend/models/userSchema.js`:
- Around line 97-100: The pre-save hook userSchema.pre currently passes
Number(process.env.SALT) into bcrypt.hash which becomes NaN if SALT is
undefined; update the hook to validate/parses the env value and provide a safe
default (e.g., 10) before calling bcrypt.hash, or throw a clear error at startup
if SALT must be provided; specifically ensure the value passed to bcrypt.hash is
a finite integer (use parsing + fallback) and add a clear error or default at
initialization so bcrypt.hash never receives NaN.
- Around line 31-39: personal_info.email currently lacks a uniqueness constraint
and can diverge from username via the profile update endpoint; add a uniqueness
index to the Mongoose schema (set personal_info.email: { type: String, required:
true, unique: true }) in userSchema.js and also add a schema-level validation or
pre('save')/pre('findOneAndUpdate') hook that enforces personal_info.email ===
username (or rejects updates that attempt to change email independently) so the
profile update handler cannot create duplicates—also update the profile update
logic to validate/reject mismatched email changes to keep lookups consistent.

In `@backend/routes/auth.js`:
- Around line 57-67: The current access token created by jwt.sign(payload,
secretKey, { expiresIn: "5m" }) and set in res.cookie("token", token, { maxAge:
5 * 60 * 1000 }) is too short-lived; either increase the session duration by
updating the expiresIn and corresponding maxAge to a realistic value (e.g.,
hours/days) or implement a refresh-token flow: issue a short-lived access token
via jwt.sign and a long-lived refresh token (stored as an HttpOnly cookie), add
a /refreshToken endpoint that validates and rotates the refresh token
(persisting it or its identifier server-side, e.g., in the User model), and
update jwt-signing and cookie logic in the login handler (the payload creation
in this file) to support issuing both tokens and renewing access tokens when
/refreshToken is called.
- Line 120: Remove the insecure console.log(newUser) that prints the entire user
document (including hashed password); instead either delete the log entirely or
replace it with a safe log that only outputs non-sensitive identifiers (e.g.,
newUser.id or newUser.email) or a simple confirmation message. Locate the
statement referencing newUser in the user creation flow (the console.log call)
and remove or change it so no password or full document is emitted to server
logs.
- Around line 48-50: The route handler incorrectly continues executing after
sending the 401 response for Google-authenticated users; add an immediate return
after res.status(401).json({ message: "Use Google sign-in for this acount." })
so execution does not fall through to bcrypt.compare(password, user.password)
(and avoid calling bcrypt.compare when user.password is undefined). Ensure the
early-return happens in the same conditional that checks user.strategy !==
"local" || !user.password.
🧹 Nitpick comments (7)
backend/package.json (1)

56-58: Consider removing unused passport-local and passport-local-mongoose dependencies.

The LocalStrategy import is commented out in passportConfig.js, and the PR replaces Passport-based local auth with manual bcrypt+JWT. These packages appear to be dead weight now.

backend/models/positionSchema.js (1)

1-56: Inconsistent timestamp approach across models.

This schema manually defines created_at without updated_at, while userSchema.js and positionHolderSchema.js use Mongoose's { timestamps: true }. Consider standardizing on one approach across all models for consistency.

backend/models/feedbackSchema.js (2)

14-22: target_id lacks a ref or refPathpopulate() won't work automatically.

If you intend to populate target_id based on target_type, use Mongoose's refPath to enable dynamic references:

Proposed fix using refPath
   target_id: {
     type: mongoose.Schema.Types.ObjectId,
-    //required: true,
-    // We'll dynamically interpret this field based on target_type
+    refPath: "target_type",
   },
   target_type: {
     type: String,
     required: true,
+    enum: ["Event", "Organizational_Unit", "Position"], // constrain to valid model names
   },

28-32: Remove commented-out category code.

If this is no longer needed, remove it to keep the schema clean. It can always be retrieved from version control.

backend/routes/auth.js (2)

5-5: Remove commented-out import.

Dead code — if isIITBhilaiEmail is no longer used in this file, remove the comment entirely.

🧹 Proposed fix
-//const isIITBhilaiEmail = require("../utils/isIITBhilaiEmail");

78-88: role is hardcoded but still validated by registerValidate — consider removing it from validation.

role is hardcoded to "STUDENT" on line 81 (good — addresses the previous privilege-escalation concern), but it's still passed to registerValidate.safeParse() which validates role: zod.string().min(5). This validation of a server-controlled constant is unnecessary. Consider removing role from the Zod schema or from the safeParse call to keep the validation schema aligned with actual user input.

backend/models/eventSchema.js (1)

107-115: updated_at won't auto-update on document modifications.

default: Date.now only sets the value on creation. On subsequent updates, updated_at will remain stale unless manually set. Consider using Mongoose's built-in timestamps option which automatically manages both createdAt and updatedAt:

♻️ Proposed refactor

Remove the manual created_at and updated_at fields and add timestamps: true to the schema options:

-  created_at: {
-    type: Date,
-    default: Date.now,
-  },
-  updated_at: {
-    type: Date,
-    default: Date.now,
-  },
-});
+});
+
+eventSchema.set("timestamps", true);

Or pass it as a schema option:

const eventSchema = new mongoose.Schema({ /* ... */ }, { timestamps: true });

Note: This renames the fields to createdAt/updatedAt (camelCase). If you need snake_case, you can configure it:

{ timestamps: { createdAt: 'created_at', updatedAt: 'updated_at' } }

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Feb 9, 2026

Caution

Failed to replace (edit) comment. This is likely due to insufficient permissions or the comment being deleted.

Error details
{"name":"HttpError","status":500,"request":{"method":"PATCH","url":"https://api.github.com/repos/OpenLake/Student_Database_COSA/issues/comments/3820089225","headers":{"accept":"application/vnd.github.v3+json","user-agent":"octokit.js/0.0.0-development octokit-core.js/7.0.6 Node.js/24","authorization":"token [REDACTED]","content-type":"application/json; charset=utf-8"},"body":{"body":"<!-- This is an auto-generated comment: summarize by coderabbit.ai -->\n<!-- walkthrough_start -->\n\n## Walkthrough\n\nThis PR refactors authentication from Passport LocalStrategy to JWT tokens with bcrypt, introduces Google OAuth with IIT Bhilai email domain validation, reorganizes monolithic schema into separate model files, adds certificate batch management with multi-level approval workflows, and updates route imports for consistency.\n\n## Changes\n\n|Cohort / File(s)|Summary|\n|---|---|\n|**Database & Configuration** <br> `backend/config/db.js`, `backend/config/passportConfig.js`|MongoDB connection initialization via mongoose with connectDB/closeDB utilities; Google OAuth strategy with email domain validation and automatic user creation.|\n|**Schema Reorganization - New Individual Models** <br> `backend/models/userSchema.js`, `backend/models/achievementSchema.js`, `backend/models/eventSchema.js`, `backend/models/feedbackSchema.js`, `backend/models/organizationSchema.js`, `backend/models/positionHolderSchema.js`, `backend/models/positionSchema.js`|Extracted individual Mongoose schema definitions from monolithic schema file; User model includes bcrypt pre-save password hashing, Google/local strategy support, and nested personal/academic/contact info.|\n|**Schema Reorganization - Consolidation** <br> `backend/models/schema.js`, `backend/models/passportConfig.js`|Removed User, Feedback, Achievement, Event, Position, PositionHolder, OrganizationalUnit exports; retained only UserSkill, Skill, Announcement; removed passport-local-mongoose and mongoose-findorcreate plugins; updated User import path.|\n|**Certificate Management** <br> `backend/models/certificateSchema.js`, `backend/controllers/certificateController.js`, `backend/routes/certificateRoutes.js`|New CertificateBatch and Certificate schemas with multi-level approval workflow (PendingL1/PendingL2 states); controller validates batch creation, enforces role-based authorization, resolves approvers (General Secretary/President), and validates user participation.|\n|**Authentication Refactor** <br> `backend/routes/auth.js`, `backend/middlewares/isAuthenticated.js`|Replaced Passport LocalStrategy with custom JWT-based authentication; manual password hashing/verification via bcrypt; added jwtIsAuthenticated middleware for token verification; Google OAuth routes retained with updated User model references.|\n|**Route Import Updates** <br> `backend/routes/achievements.js`, `backend/routes/analytics.js`, `backend/routes/announcements.js`, `backend/routes/dashboard.js`, `backend/routes/events.js`, `backend/routes/feedbackRoutes.js`, `backend/routes/onboarding.js`, `backend/routes/orgUnit.js`, `backend/routes/positionRoutes.js`, `backend/routes/profile.js`, `backend/routes/skillsRoutes.js`|Updated isAuthenticated imports from default to named exports; adjusted Achievement import from schema.js to achievementSchema.js; standardized string quotes and formatting.|\n|**Utilities** <br> `backend/utils/authValidate.js`, `backend/utils/batchValidate.js`|New Zod validators for login/registration (IIT Bhilai email domain + password constraints) and batch creation (title, unit_id, template_id, users with ObjectId validation).|\n|**Server Setup & Dependencies** <br> `backend/index.js`, `backend/package.json`, `package.json`|Added connectDB invocation, cookie-parser middleware, and certificate routes at `/api/certificate-batches`; replaced body-parser with express.json(); updated passport import path; added bcrypt, cookie-parser, zod dependencies; removed passport-local packages.|\n\n## Sequence Diagram(s)\n\n```mermaid\nsequenceDiagram\n    participant Client\n    participant Server as Auth Handler\n    participant UserDB as User DB\n    participant BCrypt as BCrypt\n    participant JWT as JWT Signer\n\n    Client->>Server: POST /login (username, password)\n    Server->>Server: Validate input with loginValidate\n    alt Invalid Input\n        Server-->>Client: 400 Validation Error\n    end\n    Server->>UserDB: Query user by username\n    alt User Not Found\n        UserDB-->>Server: null\n        Server-->>Client: 401 Invalid credentials\n    end\n    UserDB-->>Server: User document\n    Server->>BCrypt: Verify password hash\n    alt Password Mismatch\n        BCrypt-->>Server: false\n        Server-->>Client: 401 Invalid credentials\n    end\n    BCrypt-->>Server: true\n    Server->>JWT: Sign token (user_id, 30 min expiry)\n    JWT-->>Server: JWT token\n    Server->>Server: Set httpOnly cookie\n    Server-->>Client: 200 Login success + cookie\n```\n\n```mermaid\nsequenceDiagram\n    participant Client\n    participant GoogleOAuth as Google OAuth\n    participant Server as Callback Handler\n    participant UserDB as User DB\n\n    Client->>GoogleOAuth: Request Google auth\n    GoogleOAuth-->>Client: Redirect to Google login\n    Client->>GoogleOAuth: Authenticate\n    GoogleOAuth-->>Server: Callback with profile\n    Server->>Server: Validate IIT Bhilai email domain\n    alt Non-IIT Email\n        Server-->>Client: 403 Invalid domain\n    end\n    Server->>UserDB: Query user by email\n    alt User Exists\n        UserDB-->>Server: User document\n    else User Not Found\n        Server->>UserDB: Create new user (STUDENT role, google strategy)\n        UserDB-->>Server: New user document\n    end\n    Server->>Server: Serialize user\n    Server-->>Client: 200 Auth success\n```\n\n```mermaid\nsequenceDiagram\n    participant Client\n    participant CertController as Certificate Controller\n    participant UserDB as User DB\n    participant OrgDB as Organization DB\n    participant ApproverDB as Approver Lookup\n    participant CertDB as Certificate DB\n\n    Client->>CertController: POST /api/certificate-batches (batch data)\n    CertController->>CertController: Validate with jwtIsAuthenticated\n    CertController->>UserDB: Fetch initiator user\n    alt User Not Found\n        UserDB-->>CertController: null\n        CertController-->>Client: 404 User not found\n    end\n    CertController->>CertController: Check role = CLUB_COORDINATOR\n    alt Unauthorized\n        CertController-->>Client: 403 Forbidden\n    end\n    CertController->>CertController: Validate batch schema (title, unit_id, users)\n    alt Validation Fails\n        CertController-->>Client: 400 Validation errors\n    end\n    CertController->>OrgDB: Fetch organization unit (club)\n    CertController->>CertController: Verify coordinator in unit\n    alt Not coordinator of club\n        CertController-->>Client: 403 Forbidden\n    end\n    CertController->>OrgDB: Validate parent (Council)\n    CertController->>ApproverDB: Resolve General Secretary\n    CertController->>ApproverDB: Resolve President\n    alt Approvers Not Found\n        ApproverDB-->>CertController: null\n        CertController-->>Client: 500 Approvers not found\n    end\n    CertController->>CertController: Validate each user ID\n    alt Invalid user ID\n        CertController-->>Client: 400 Invalid user\n    end\n    CertController->>CertDB: Create CertificateBatch\n    CertDB-->>CertController: Batch created\n    CertController-->>Client: 200 Batch created with details\n```\n\n## Estimated code review effort\n\n🎯 4 (Complex) | ⏱️ ~75 minutes\n\n## Poem\n\n> 🐰 *Hop hop, the auth now flows with tokens so bright,*  \n> *JWT and bcrypt replacing old Passport's light,*  \n> *Schemas split clean like carrots in spring,*  \n> *Certificates batch with approvals to bring,*  \n> *Google's domain guard says \"Welcome home, friend!\"*\n\n<!-- walkthrough_end -->\n\n\n<!-- pre_merge_checks_walkthrough_start -->\n\n<details>\n<summary>🚥 Pre-merge checks | ✅ 1 | ❌ 2</summary>\n\n<details>\n<summary>❌ Failed checks (2 warnings)</summary>\n\n|     Check name     | Status     | Explanation                                                                                          | Resolution                                                                                                                                       |\n| :----------------: | :--------- | :--------------------------------------------------------------------------------------------------- | :----------------------------------------------------------------------------------------------------------------------------------------------- |\n|     Title check    | ⚠️ Warning | The PR title 'Refactor/login register' is vague and lacks specificity about the actual changes made. | Use a more descriptive title such as 'Refactor authentication with manual login/register and Zod validation' to clearly convey the main changes. |\n| Docstring Coverage | ⚠️ Warning | Docstring coverage is 66.67% which is insufficient. The required threshold is 80.00%.                | Write docstrings for the functions missing them to satisfy the coverage threshold.                                                               |\n\n</details>\n<details>\n<summary>✅ Passed checks (1 passed)</summary>\n\n|     Check name    | Status   | Explanation                                                                                                                                                     |\n| :---------------: | :------- | :-------------------------------------------------------------------------------------------------------------------------------------------------------------- |\n| Description check | ✅ Passed | The PR description is comprehensive and well-structured, covering all key aspects including changes, motivation, testing, and deployment notes as per template. |\n\n</details>\n\n<sub>✏️ Tip: You can configure your own custom pre-merge checks in the settings.</sub>\n\n</details>\n\n<!-- pre_merge_checks_walkthrough_end -->\n\n<!-- finishing_touch_checkbox_start -->\n\n<details>\n<summary>✨ Finishing touches</summary>\n\n- [ ] <!-- {\"checkboxId\": \"7962f53c-55bc-4827-bfbf-6a18da830691\"} --> 📝 Generate docstrings\n<details>\n<summary>🧪 Generate unit tests (beta)</summary>\n\n- [ ] <!-- {\"checkboxId\": \"f47ac10b-58cc-4372-a567-0e02b2c3d479\", \"radioGroupId\": \"utg-output-choice-group-unknown_comment_id\"} -->   Create PR with unit tests\n- [ ] <!-- {\"checkboxId\": \"07f1e7d6-8a8e-4e23-9900-8731c2c87f58\", \"radioGroupId\": \"utg-output-choice-group-unknown_comment_id\"} -->   Post copyable unit tests in a comment\n\n</details>\n\n</details>\n\n<!-- finishing_touch_checkbox_end -->\n\n<!-- tips_start -->\n\n---\n\nThanks for using [CodeRabbit](https://coderabbit.ai?utm_source=oss&utm_medium=github&utm_campaign=OpenLake/Student_Database_COSA&utm_content=222)! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.\n\n<details>\n<summary>❤️ Share</summary>\n\n- [X](https://twitter.com/intent/tweet?text=I%20just%20used%20%40coderabbitai%20for%20my%20code%20review%2C%20and%20it%27s%20fantastic%21%20It%27s%20free%20for%20OSS%20and%20offers%20a%20free%20trial%20for%20the%20proprietary%20code.%20Check%20it%20out%3A&url=https%3A//coderabbit.ai)\n- [Mastodon](https://mastodon.social/share?text=I%20just%20used%20%40coderabbitai%20for%20my%20code%20review%2C%20and%20it%27s%20fantastic%21%20It%27s%20free%20for%20OSS%20and%20offers%20a%20free%20trial%20for%20the%20proprietary%20code.%20Check%20it%20out%3A%20https%3A%2F%2Fcoderabbit.ai)\n- [Reddit](https://www.reddit.com/submit?title=Great%20tool%20for%20code%20review%20-%20CodeRabbit&text=I%20just%20used%20CodeRabbit%20for%20my%20code%20review%2C%20and%20it%27s%20fantastic%21%20It%27s%20free%20for%20OSS%20and%20offers%20a%20free%20trial%20for%20proprietary%20code.%20Check%20it%20out%3A%20https%3A//coderabbit.ai)\n- [LinkedIn](https://www.linkedin.com/sharing/share-offsite/?url=https%3A%2F%2Fcoderabbit.ai&mini=true&title=Great%20tool%20for%20code%20review%20-%20CodeRabbit&summary=I%20just%20used%20CodeRabbit%20for%20my%20code%20review%2C%20and%20it%27s%20fantastic%21%20It%27s%20free%20for%20OSS%20and%20offers%20a%20free%20trial%20for%20proprietary%20code)\n\n</details>\n\n<sub>Comment `@coderabbitai help` to get the list of available commands and usage tips.</sub>\n\n<!-- tips_end -->\n\n<!-- internal state start -->\n\n\n<!-- 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 -->\n\n<!-- internal state end -->"},"request":{"retryCount":3,"retries":3,"retryAfter":16}},"response":{"url":"https://api.github.com/repos/OpenLake/Student_Database_COSA/issues/comments/3820089225","status":500,"headers":{"access-control-allow-origin":"*","access-control-expose-headers":"ETag, Link, Location, Retry-After, X-GitHub-OTP, X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Used, X-RateLimit-Resource, X-RateLimit-Reset, X-OAuth-Scopes, X-Accepted-OAuth-Scopes, X-Poll-Interval, X-GitHub-Media-Type, X-GitHub-SSO, X-GitHub-Request-Id, Deprecation, Sunset","content-length":"0","content-security-policy":"default-src 'none'","content-type":"application/json; charset=utf-8","date":"Mon, 09 Feb 2026 16:23:32 GMT","referrer-policy":"origin-when-cross-origin, strict-origin-when-cross-origin","server":"github.com","strict-transport-security":"max-age=31536000; includeSubdomains; preload","vary":"Accept-Encoding, Accept, X-Requested-With","x-accepted-github-permissions":"issues=write; pull_requests=write","x-content-type-options":"nosniff","x-frame-options":"deny","x-github-api-version-selected":"2022-11-28","x-github-media-type":"github.v3; format=json","x-github-request-id":"A03C:51096:EAFCFE:3EFD3C7:698A0A03","x-ratelimit-limit":"8650","x-ratelimit-remaining":"8587","x-ratelimit-reset":"1770657425","x-ratelimit-resource":"core","x-ratelimit-used":"63","x-xss-protection":"0"},"data":""}}

3 similar comments
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Feb 9, 2026

Caution

Failed to replace (edit) comment. This is likely due to insufficient permissions or the comment being deleted.

Error details
{"name":"HttpError","status":500,"request":{"method":"PATCH","url":"https://api.github.com/repos/OpenLake/Student_Database_COSA/issues/comments/3820089225","headers":{"accept":"application/vnd.github.v3+json","user-agent":"octokit.js/0.0.0-development octokit-core.js/7.0.6 Node.js/24","authorization":"token [REDACTED]","content-type":"application/json; charset=utf-8"},"body":{"body":"<!-- This is an auto-generated comment: summarize by coderabbit.ai -->\n<!-- walkthrough_start -->\n\n## Walkthrough\n\nThis PR refactors authentication from Passport LocalStrategy to JWT tokens with bcrypt, introduces Google OAuth with IIT Bhilai email domain validation, reorganizes monolithic schema into separate model files, adds certificate batch management with multi-level approval workflows, and updates route imports for consistency.\n\n## Changes\n\n|Cohort / File(s)|Summary|\n|---|---|\n|**Database & Configuration** <br> `backend/config/db.js`, `backend/config/passportConfig.js`|MongoDB connection initialization via mongoose with connectDB/closeDB utilities; Google OAuth strategy with email domain validation and automatic user creation.|\n|**Schema Reorganization - New Individual Models** <br> `backend/models/userSchema.js`, `backend/models/achievementSchema.js`, `backend/models/eventSchema.js`, `backend/models/feedbackSchema.js`, `backend/models/organizationSchema.js`, `backend/models/positionHolderSchema.js`, `backend/models/positionSchema.js`|Extracted individual Mongoose schema definitions from monolithic schema file; User model includes bcrypt pre-save password hashing, Google/local strategy support, and nested personal/academic/contact info.|\n|**Schema Reorganization - Consolidation** <br> `backend/models/schema.js`, `backend/models/passportConfig.js`|Removed User, Feedback, Achievement, Event, Position, PositionHolder, OrganizationalUnit exports; retained only UserSkill, Skill, Announcement; removed passport-local-mongoose and mongoose-findorcreate plugins; updated User import path.|\n|**Certificate Management** <br> `backend/models/certificateSchema.js`, `backend/controllers/certificateController.js`, `backend/routes/certificateRoutes.js`|New CertificateBatch and Certificate schemas with multi-level approval workflow (PendingL1/PendingL2 states); controller validates batch creation, enforces role-based authorization, resolves approvers (General Secretary/President), and validates user participation.|\n|**Authentication Refactor** <br> `backend/routes/auth.js`, `backend/middlewares/isAuthenticated.js`|Replaced Passport LocalStrategy with custom JWT-based authentication; manual password hashing/verification via bcrypt; added jwtIsAuthenticated middleware for token verification; Google OAuth routes retained with updated User model references.|\n|**Route Import Updates** <br> `backend/routes/achievements.js`, `backend/routes/analytics.js`, `backend/routes/announcements.js`, `backend/routes/dashboard.js`, `backend/routes/events.js`, `backend/routes/feedbackRoutes.js`, `backend/routes/onboarding.js`, `backend/routes/orgUnit.js`, `backend/routes/positionRoutes.js`, `backend/routes/profile.js`, `backend/routes/skillsRoutes.js`|Updated isAuthenticated imports from default to named exports; adjusted Achievement import from schema.js to achievementSchema.js; standardized string quotes and formatting.|\n|**Utilities** <br> `backend/utils/authValidate.js`, `backend/utils/batchValidate.js`|New Zod validators for login/registration (IIT Bhilai email domain + password constraints) and batch creation (title, unit_id, template_id, users with ObjectId validation).|\n|**Server Setup & Dependencies** <br> `backend/index.js`, `backend/package.json`, `package.json`|Added connectDB invocation, cookie-parser middleware, and certificate routes at `/api/certificate-batches`; replaced body-parser with express.json(); updated passport import path; added bcrypt, cookie-parser, zod dependencies; removed passport-local packages.|\n\n## Sequence Diagram(s)\n\n```mermaid\nsequenceDiagram\n    participant Client\n    participant Server as Auth Handler\n    participant UserDB as User DB\n    participant BCrypt as BCrypt\n    participant JWT as JWT Signer\n\n    Client->>Server: POST /login (username, password)\n    Server->>Server: Validate input with loginValidate\n    alt Invalid Input\n        Server-->>Client: 400 Validation Error\n    end\n    Server->>UserDB: Query user by username\n    alt User Not Found\n        UserDB-->>Server: null\n        Server-->>Client: 401 Invalid credentials\n    end\n    UserDB-->>Server: User document\n    Server->>BCrypt: Verify password hash\n    alt Password Mismatch\n        BCrypt-->>Server: false\n        Server-->>Client: 401 Invalid credentials\n    end\n    BCrypt-->>Server: true\n    Server->>JWT: Sign token (user_id, 30 min expiry)\n    JWT-->>Server: JWT token\n    Server->>Server: Set httpOnly cookie\n    Server-->>Client: 200 Login success + cookie\n```\n\n```mermaid\nsequenceDiagram\n    participant Client\n    participant GoogleOAuth as Google OAuth\n    participant Server as Callback Handler\n    participant UserDB as User DB\n\n    Client->>GoogleOAuth: Request Google auth\n    GoogleOAuth-->>Client: Redirect to Google login\n    Client->>GoogleOAuth: Authenticate\n    GoogleOAuth-->>Server: Callback with profile\n    Server->>Server: Validate IIT Bhilai email domain\n    alt Non-IIT Email\n        Server-->>Client: 403 Invalid domain\n    end\n    Server->>UserDB: Query user by email\n    alt User Exists\n        UserDB-->>Server: User document\n    else User Not Found\n        Server->>UserDB: Create new user (STUDENT role, google strategy)\n        UserDB-->>Server: New user document\n    end\n    Server->>Server: Serialize user\n    Server-->>Client: 200 Auth success\n```\n\n```mermaid\nsequenceDiagram\n    participant Client\n    participant CertController as Certificate Controller\n    participant UserDB as User DB\n    participant OrgDB as Organization DB\n    participant ApproverDB as Approver Lookup\n    participant CertDB as Certificate DB\n\n    Client->>CertController: POST /api/certificate-batches (batch data)\n    CertController->>CertController: Validate with jwtIsAuthenticated\n    CertController->>UserDB: Fetch initiator user\n    alt User Not Found\n        UserDB-->>CertController: null\n        CertController-->>Client: 404 User not found\n    end\n    CertController->>CertController: Check role = CLUB_COORDINATOR\n    alt Unauthorized\n        CertController-->>Client: 403 Forbidden\n    end\n    CertController->>CertController: Validate batch schema (title, unit_id, users)\n    alt Validation Fails\n        CertController-->>Client: 400 Validation errors\n    end\n    CertController->>OrgDB: Fetch organization unit (club)\n    CertController->>CertController: Verify coordinator in unit\n    alt Not coordinator of club\n        CertController-->>Client: 403 Forbidden\n    end\n    CertController->>OrgDB: Validate parent (Council)\n    CertController->>ApproverDB: Resolve General Secretary\n    CertController->>ApproverDB: Resolve President\n    alt Approvers Not Found\n        ApproverDB-->>CertController: null\n        CertController-->>Client: 500 Approvers not found\n    end\n    CertController->>CertController: Validate each user ID\n    alt Invalid user ID\n        CertController-->>Client: 400 Invalid user\n    end\n    CertController->>CertDB: Create CertificateBatch\n    CertDB-->>CertController: Batch created\n    CertController-->>Client: 200 Batch created with details\n```\n\n## Estimated code review effort\n\n🎯 4 (Complex) | ⏱️ ~75 minutes\n\n## Poem\n\n> 🐰 *Hop hop, the auth now flows with tokens so bright,*  \n> *JWT and bcrypt replacing old Passport's light,*  \n> *Schemas split clean like carrots in spring,*  \n> *Certificates batch with approvals to bring,*  \n> *Google's domain guard says \"Welcome home, friend!\"*\n\n<!-- walkthrough_end -->\n\n\n<!-- pre_merge_checks_walkthrough_start -->\n\n<details>\n<summary>🚥 Pre-merge checks | ✅ 1 | ❌ 2</summary>\n\n<details>\n<summary>❌ Failed checks (2 warnings)</summary>\n\n|     Check name     | Status     | Explanation                                                                                          | Resolution                                                                                                                                       |\n| :----------------: | :--------- | :--------------------------------------------------------------------------------------------------- | :----------------------------------------------------------------------------------------------------------------------------------------------- |\n|     Title check    | ⚠️ Warning | The PR title 'Refactor/login register' is vague and lacks specificity about the actual changes made. | Use a more descriptive title such as 'Refactor authentication with manual login/register and Zod validation' to clearly convey the main changes. |\n| Docstring Coverage | ⚠️ Warning | Docstring coverage is 66.67% which is insufficient. The required threshold is 80.00%.                | Write docstrings for the functions missing them to satisfy the coverage threshold.                                                               |\n\n</details>\n<details>\n<summary>✅ Passed checks (1 passed)</summary>\n\n|     Check name    | Status   | Explanation                                                                                                                                                     |\n| :---------------: | :------- | :-------------------------------------------------------------------------------------------------------------------------------------------------------------- |\n| Description check | ✅ Passed | The PR description is comprehensive and well-structured, covering all key aspects including changes, motivation, testing, and deployment notes as per template. |\n\n</details>\n\n<sub>✏️ Tip: You can configure your own custom pre-merge checks in the settings.</sub>\n\n</details>\n\n<!-- pre_merge_checks_walkthrough_end -->\n\n<!-- finishing_touch_checkbox_start -->\n\n<details>\n<summary>✨ Finishing touches</summary>\n\n- [ ] <!-- {\"checkboxId\": \"7962f53c-55bc-4827-bfbf-6a18da830691\"} --> 📝 Generate docstrings\n<details>\n<summary>🧪 Generate unit tests (beta)</summary>\n\n- [ ] <!-- {\"checkboxId\": \"f47ac10b-58cc-4372-a567-0e02b2c3d479\", \"radioGroupId\": \"utg-output-choice-group-unknown_comment_id\"} -->   Create PR with unit tests\n- [ ] <!-- {\"checkboxId\": \"07f1e7d6-8a8e-4e23-9900-8731c2c87f58\", \"radioGroupId\": \"utg-output-choice-group-unknown_comment_id\"} -->   Post copyable unit tests in a comment\n\n</details>\n\n</details>\n\n<!-- finishing_touch_checkbox_end -->\n\n<!-- tips_start -->\n\n---\n\nThanks for using [CodeRabbit](https://coderabbit.ai?utm_source=oss&utm_medium=github&utm_campaign=OpenLake/Student_Database_COSA&utm_content=222)! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.\n\n<details>\n<summary>❤️ Share</summary>\n\n- [X](https://twitter.com/intent/tweet?text=I%20just%20used%20%40coderabbitai%20for%20my%20code%20review%2C%20and%20it%27s%20fantastic%21%20It%27s%20free%20for%20OSS%20and%20offers%20a%20free%20trial%20for%20the%20proprietary%20code.%20Check%20it%20out%3A&url=https%3A//coderabbit.ai)\n- [Mastodon](https://mastodon.social/share?text=I%20just%20used%20%40coderabbitai%20for%20my%20code%20review%2C%20and%20it%27s%20fantastic%21%20It%27s%20free%20for%20OSS%20and%20offers%20a%20free%20trial%20for%20the%20proprietary%20code.%20Check%20it%20out%3A%20https%3A%2F%2Fcoderabbit.ai)\n- [Reddit](https://www.reddit.com/submit?title=Great%20tool%20for%20code%20review%20-%20CodeRabbit&text=I%20just%20used%20CodeRabbit%20for%20my%20code%20review%2C%20and%20it%27s%20fantastic%21%20It%27s%20free%20for%20OSS%20and%20offers%20a%20free%20trial%20for%20proprietary%20code.%20Check%20it%20out%3A%20https%3A//coderabbit.ai)\n- [LinkedIn](https://www.linkedin.com/sharing/share-offsite/?url=https%3A%2F%2Fcoderabbit.ai&mini=true&title=Great%20tool%20for%20code%20review%20-%20CodeRabbit&summary=I%20just%20used%20CodeRabbit%20for%20my%20code%20review%2C%20and%20it%27s%20fantastic%21%20It%27s%20free%20for%20OSS%20and%20offers%20a%20free%20trial%20for%20proprietary%20code)\n\n</details>\n\n<sub>Comment `@coderabbitai help` to get the list of available commands and usage tips.</sub>\n\n<!-- tips_end -->\n\n<!-- internal state start -->\n\n\n<!-- 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 -->\n\n<!-- internal state end -->"},"request":{"retryCount":3,"retries":3,"retryAfter":16}},"response":{"url":"https://api.github.com/repos/OpenLake/Student_Database_COSA/issues/comments/3820089225","status":500,"headers":{"access-control-allow-origin":"*","access-control-expose-headers":"ETag, Link, Location, Retry-After, X-GitHub-OTP, X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Used, X-RateLimit-Resource, X-RateLimit-Reset, X-OAuth-Scopes, X-Accepted-OAuth-Scopes, X-Poll-Interval, X-GitHub-Media-Type, X-GitHub-SSO, X-GitHub-Request-Id, Deprecation, Sunset","content-length":"0","content-security-policy":"default-src 'none'","content-type":"application/json; charset=utf-8","date":"Mon, 09 Feb 2026 16:23:32 GMT","referrer-policy":"origin-when-cross-origin, strict-origin-when-cross-origin","server":"github.com","strict-transport-security":"max-age=31536000; includeSubdomains; preload","vary":"Accept-Encoding, Accept, X-Requested-With","x-accepted-github-permissions":"issues=write; pull_requests=write","x-content-type-options":"nosniff","x-frame-options":"deny","x-github-api-version-selected":"2022-11-28","x-github-media-type":"github.v3; format=json","x-github-request-id":"A03C:51096:EAFCFE:3EFD3C7:698A0A03","x-ratelimit-limit":"8650","x-ratelimit-remaining":"8587","x-ratelimit-reset":"1770657425","x-ratelimit-resource":"core","x-ratelimit-used":"63","x-xss-protection":"0"},"data":""}}

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Feb 9, 2026

Caution

Failed to replace (edit) comment. This is likely due to insufficient permissions or the comment being deleted.

Error details
{"name":"HttpError","status":500,"request":{"method":"PATCH","url":"https://api.github.com/repos/OpenLake/Student_Database_COSA/issues/comments/3820089225","headers":{"accept":"application/vnd.github.v3+json","user-agent":"octokit.js/0.0.0-development octokit-core.js/7.0.6 Node.js/24","authorization":"token [REDACTED]","content-type":"application/json; charset=utf-8"},"body":{"body":"<!-- This is an auto-generated comment: summarize by coderabbit.ai -->\n<!-- walkthrough_start -->\n\n## Walkthrough\n\nThis PR refactors authentication from Passport LocalStrategy to JWT tokens with bcrypt, introduces Google OAuth with IIT Bhilai email domain validation, reorganizes monolithic schema into separate model files, adds certificate batch management with multi-level approval workflows, and updates route imports for consistency.\n\n## Changes\n\n|Cohort / File(s)|Summary|\n|---|---|\n|**Database & Configuration** <br> `backend/config/db.js`, `backend/config/passportConfig.js`|MongoDB connection initialization via mongoose with connectDB/closeDB utilities; Google OAuth strategy with email domain validation and automatic user creation.|\n|**Schema Reorganization - New Individual Models** <br> `backend/models/userSchema.js`, `backend/models/achievementSchema.js`, `backend/models/eventSchema.js`, `backend/models/feedbackSchema.js`, `backend/models/organizationSchema.js`, `backend/models/positionHolderSchema.js`, `backend/models/positionSchema.js`|Extracted individual Mongoose schema definitions from monolithic schema file; User model includes bcrypt pre-save password hashing, Google/local strategy support, and nested personal/academic/contact info.|\n|**Schema Reorganization - Consolidation** <br> `backend/models/schema.js`, `backend/models/passportConfig.js`|Removed User, Feedback, Achievement, Event, Position, PositionHolder, OrganizationalUnit exports; retained only UserSkill, Skill, Announcement; removed passport-local-mongoose and mongoose-findorcreate plugins; updated User import path.|\n|**Certificate Management** <br> `backend/models/certificateSchema.js`, `backend/controllers/certificateController.js`, `backend/routes/certificateRoutes.js`|New CertificateBatch and Certificate schemas with multi-level approval workflow (PendingL1/PendingL2 states); controller validates batch creation, enforces role-based authorization, resolves approvers (General Secretary/President), and validates user participation.|\n|**Authentication Refactor** <br> `backend/routes/auth.js`, `backend/middlewares/isAuthenticated.js`|Replaced Passport LocalStrategy with custom JWT-based authentication; manual password hashing/verification via bcrypt; added jwtIsAuthenticated middleware for token verification; Google OAuth routes retained with updated User model references.|\n|**Route Import Updates** <br> `backend/routes/achievements.js`, `backend/routes/analytics.js`, `backend/routes/announcements.js`, `backend/routes/dashboard.js`, `backend/routes/events.js`, `backend/routes/feedbackRoutes.js`, `backend/routes/onboarding.js`, `backend/routes/orgUnit.js`, `backend/routes/positionRoutes.js`, `backend/routes/profile.js`, `backend/routes/skillsRoutes.js`|Updated isAuthenticated imports from default to named exports; adjusted Achievement import from schema.js to achievementSchema.js; standardized string quotes and formatting.|\n|**Utilities** <br> `backend/utils/authValidate.js`, `backend/utils/batchValidate.js`|New Zod validators for login/registration (IIT Bhilai email domain + password constraints) and batch creation (title, unit_id, template_id, users with ObjectId validation).|\n|**Server Setup & Dependencies** <br> `backend/index.js`, `backend/package.json`, `package.json`|Added connectDB invocation, cookie-parser middleware, and certificate routes at `/api/certificate-batches`; replaced body-parser with express.json(); updated passport import path; added bcrypt, cookie-parser, zod dependencies; removed passport-local packages.|\n\n## Sequence Diagram(s)\n\n```mermaid\nsequenceDiagram\n    participant Client\n    participant Server as Auth Handler\n    participant UserDB as User DB\n    participant BCrypt as BCrypt\n    participant JWT as JWT Signer\n\n    Client->>Server: POST /login (username, password)\n    Server->>Server: Validate input with loginValidate\n    alt Invalid Input\n        Server-->>Client: 400 Validation Error\n    end\n    Server->>UserDB: Query user by username\n    alt User Not Found\n        UserDB-->>Server: null\n        Server-->>Client: 401 Invalid credentials\n    end\n    UserDB-->>Server: User document\n    Server->>BCrypt: Verify password hash\n    alt Password Mismatch\n        BCrypt-->>Server: false\n        Server-->>Client: 401 Invalid credentials\n    end\n    BCrypt-->>Server: true\n    Server->>JWT: Sign token (user_id, 30 min expiry)\n    JWT-->>Server: JWT token\n    Server->>Server: Set httpOnly cookie\n    Server-->>Client: 200 Login success + cookie\n```\n\n```mermaid\nsequenceDiagram\n    participant Client\n    participant GoogleOAuth as Google OAuth\n    participant Server as Callback Handler\n    participant UserDB as User DB\n\n    Client->>GoogleOAuth: Request Google auth\n    GoogleOAuth-->>Client: Redirect to Google login\n    Client->>GoogleOAuth: Authenticate\n    GoogleOAuth-->>Server: Callback with profile\n    Server->>Server: Validate IIT Bhilai email domain\n    alt Non-IIT Email\n        Server-->>Client: 403 Invalid domain\n    end\n    Server->>UserDB: Query user by email\n    alt User Exists\n        UserDB-->>Server: User document\n    else User Not Found\n        Server->>UserDB: Create new user (STUDENT role, google strategy)\n        UserDB-->>Server: New user document\n    end\n    Server->>Server: Serialize user\n    Server-->>Client: 200 Auth success\n```\n\n```mermaid\nsequenceDiagram\n    participant Client\n    participant CertController as Certificate Controller\n    participant UserDB as User DB\n    participant OrgDB as Organization DB\n    participant ApproverDB as Approver Lookup\n    participant CertDB as Certificate DB\n\n    Client->>CertController: POST /api/certificate-batches (batch data)\n    CertController->>CertController: Validate with jwtIsAuthenticated\n    CertController->>UserDB: Fetch initiator user\n    alt User Not Found\n        UserDB-->>CertController: null\n        CertController-->>Client: 404 User not found\n    end\n    CertController->>CertController: Check role = CLUB_COORDINATOR\n    alt Unauthorized\n        CertController-->>Client: 403 Forbidden\n    end\n    CertController->>CertController: Validate batch schema (title, unit_id, users)\n    alt Validation Fails\n        CertController-->>Client: 400 Validation errors\n    end\n    CertController->>OrgDB: Fetch organization unit (club)\n    CertController->>CertController: Verify coordinator in unit\n    alt Not coordinator of club\n        CertController-->>Client: 403 Forbidden\n    end\n    CertController->>OrgDB: Validate parent (Council)\n    CertController->>ApproverDB: Resolve General Secretary\n    CertController->>ApproverDB: Resolve President\n    alt Approvers Not Found\n        ApproverDB-->>CertController: null\n        CertController-->>Client: 500 Approvers not found\n    end\n    CertController->>CertController: Validate each user ID\n    alt Invalid user ID\n        CertController-->>Client: 400 Invalid user\n    end\n    CertController->>CertDB: Create CertificateBatch\n    CertDB-->>CertController: Batch created\n    CertController-->>Client: 200 Batch created with details\n```\n\n## Estimated code review effort\n\n🎯 4 (Complex) | ⏱️ ~75 minutes\n\n## Poem\n\n> 🐰 *Hop hop, the auth now flows with tokens so bright,*  \n> *JWT and bcrypt replacing old Passport's light,*  \n> *Schemas split clean like carrots in spring,*  \n> *Certificates batch with approvals to bring,*  \n> *Google's domain guard says \"Welcome home, friend!\"*\n\n<!-- walkthrough_end -->\n\n\n<!-- pre_merge_checks_walkthrough_start -->\n\n<details>\n<summary>🚥 Pre-merge checks | ✅ 1 | ❌ 2</summary>\n\n<details>\n<summary>❌ Failed checks (2 warnings)</summary>\n\n|     Check name     | Status     | Explanation                                                                                          | Resolution                                                                                                                                       |\n| :----------------: | :--------- | :--------------------------------------------------------------------------------------------------- | :----------------------------------------------------------------------------------------------------------------------------------------------- |\n|     Title check    | ⚠️ Warning | The PR title 'Refactor/login register' is vague and lacks specificity about the actual changes made. | Use a more descriptive title such as 'Refactor authentication with manual login/register and Zod validation' to clearly convey the main changes. |\n| Docstring Coverage | ⚠️ Warning | Docstring coverage is 66.67% which is insufficient. The required threshold is 80.00%.                | Write docstrings for the functions missing them to satisfy the coverage threshold.                                                               |\n\n</details>\n<details>\n<summary>✅ Passed checks (1 passed)</summary>\n\n|     Check name    | Status   | Explanation                                                                                                                                                     |\n| :---------------: | :------- | :-------------------------------------------------------------------------------------------------------------------------------------------------------------- |\n| Description check | ✅ Passed | The PR description is comprehensive and well-structured, covering all key aspects including changes, motivation, testing, and deployment notes as per template. |\n\n</details>\n\n<sub>✏️ Tip: You can configure your own custom pre-merge checks in the settings.</sub>\n\n</details>\n\n<!-- pre_merge_checks_walkthrough_end -->\n\n<!-- finishing_touch_checkbox_start -->\n\n<details>\n<summary>✨ Finishing touches</summary>\n\n- [ ] <!-- {\"checkboxId\": \"7962f53c-55bc-4827-bfbf-6a18da830691\"} --> 📝 Generate docstrings\n<details>\n<summary>🧪 Generate unit tests (beta)</summary>\n\n- [ ] <!-- {\"checkboxId\": \"f47ac10b-58cc-4372-a567-0e02b2c3d479\", \"radioGroupId\": \"utg-output-choice-group-unknown_comment_id\"} -->   Create PR with unit tests\n- [ ] <!-- {\"checkboxId\": \"07f1e7d6-8a8e-4e23-9900-8731c2c87f58\", \"radioGroupId\": \"utg-output-choice-group-unknown_comment_id\"} -->   Post copyable unit tests in a comment\n\n</details>\n\n</details>\n\n<!-- finishing_touch_checkbox_end -->\n\n<!-- tips_start -->\n\n---\n\nThanks for using [CodeRabbit](https://coderabbit.ai?utm_source=oss&utm_medium=github&utm_campaign=OpenLake/Student_Database_COSA&utm_content=222)! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.\n\n<details>\n<summary>❤️ Share</summary>\n\n- [X](https://twitter.com/intent/tweet?text=I%20just%20used%20%40coderabbitai%20for%20my%20code%20review%2C%20and%20it%27s%20fantastic%21%20It%27s%20free%20for%20OSS%20and%20offers%20a%20free%20trial%20for%20the%20proprietary%20code.%20Check%20it%20out%3A&url=https%3A//coderabbit.ai)\n- [Mastodon](https://mastodon.social/share?text=I%20just%20used%20%40coderabbitai%20for%20my%20code%20review%2C%20and%20it%27s%20fantastic%21%20It%27s%20free%20for%20OSS%20and%20offers%20a%20free%20trial%20for%20the%20proprietary%20code.%20Check%20it%20out%3A%20https%3A%2F%2Fcoderabbit.ai)\n- [Reddit](https://www.reddit.com/submit?title=Great%20tool%20for%20code%20review%20-%20CodeRabbit&text=I%20just%20used%20CodeRabbit%20for%20my%20code%20review%2C%20and%20it%27s%20fantastic%21%20It%27s%20free%20for%20OSS%20and%20offers%20a%20free%20trial%20for%20proprietary%20code.%20Check%20it%20out%3A%20https%3A//coderabbit.ai)\n- [LinkedIn](https://www.linkedin.com/sharing/share-offsite/?url=https%3A%2F%2Fcoderabbit.ai&mini=true&title=Great%20tool%20for%20code%20review%20-%20CodeRabbit&summary=I%20just%20used%20CodeRabbit%20for%20my%20code%20review%2C%20and%20it%27s%20fantastic%21%20It%27s%20free%20for%20OSS%20and%20offers%20a%20free%20trial%20for%20proprietary%20code)\n\n</details>\n\n<sub>Comment `@coderabbitai help` to get the list of available commands and usage tips.</sub>\n\n<!-- tips_end -->\n\n<!-- internal state start -->\n\n\n<!-- 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 -->\n\n<!-- internal state end -->"},"request":{"retryCount":3,"retries":3,"retryAfter":16}},"response":{"url":"https://api.github.com/repos/OpenLake/Student_Database_COSA/issues/comments/3820089225","status":500,"headers":{"access-control-allow-origin":"*","access-control-expose-headers":"ETag, Link, Location, Retry-After, X-GitHub-OTP, X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Used, X-RateLimit-Resource, X-RateLimit-Reset, X-OAuth-Scopes, X-Accepted-OAuth-Scopes, X-Poll-Interval, X-GitHub-Media-Type, X-GitHub-SSO, X-GitHub-Request-Id, Deprecation, Sunset","content-length":"0","content-security-policy":"default-src 'none'","content-type":"application/json; charset=utf-8","date":"Mon, 09 Feb 2026 16:23:32 GMT","referrer-policy":"origin-when-cross-origin, strict-origin-when-cross-origin","server":"github.com","strict-transport-security":"max-age=31536000; includeSubdomains; preload","vary":"Accept-Encoding, Accept, X-Requested-With","x-accepted-github-permissions":"issues=write; pull_requests=write","x-content-type-options":"nosniff","x-frame-options":"deny","x-github-api-version-selected":"2022-11-28","x-github-media-type":"github.v3; format=json","x-github-request-id":"A03C:51096:EAFCFE:3EFD3C7:698A0A03","x-ratelimit-limit":"8650","x-ratelimit-remaining":"8587","x-ratelimit-reset":"1770657425","x-ratelimit-resource":"core","x-ratelimit-used":"63","x-xss-protection":"0"},"data":""}}

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Feb 9, 2026

Caution

Failed to replace (edit) comment. This is likely due to insufficient permissions or the comment being deleted.

Error details
{"name":"HttpError","status":500,"request":{"method":"PATCH","url":"https://api.github.com/repos/OpenLake/Student_Database_COSA/issues/comments/3820089225","headers":{"accept":"application/vnd.github.v3+json","user-agent":"octokit.js/0.0.0-development octokit-core.js/7.0.6 Node.js/24","authorization":"token [REDACTED]","content-type":"application/json; charset=utf-8"},"body":{"body":"<!-- This is an auto-generated comment: summarize by coderabbit.ai -->\n<!-- walkthrough_start -->\n\n## Walkthrough\n\nThis PR refactors authentication from Passport LocalStrategy to JWT tokens with bcrypt, introduces Google OAuth with IIT Bhilai email domain validation, reorganizes monolithic schema into separate model files, adds certificate batch management with multi-level approval workflows, and updates route imports for consistency.\n\n## Changes\n\n|Cohort / File(s)|Summary|\n|---|---|\n|**Database & Configuration** <br> `backend/config/db.js`, `backend/config/passportConfig.js`|MongoDB connection initialization via mongoose with connectDB/closeDB utilities; Google OAuth strategy with email domain validation and automatic user creation.|\n|**Schema Reorganization - New Individual Models** <br> `backend/models/userSchema.js`, `backend/models/achievementSchema.js`, `backend/models/eventSchema.js`, `backend/models/feedbackSchema.js`, `backend/models/organizationSchema.js`, `backend/models/positionHolderSchema.js`, `backend/models/positionSchema.js`|Extracted individual Mongoose schema definitions from monolithic schema file; User model includes bcrypt pre-save password hashing, Google/local strategy support, and nested personal/academic/contact info.|\n|**Schema Reorganization - Consolidation** <br> `backend/models/schema.js`, `backend/models/passportConfig.js`|Removed User, Feedback, Achievement, Event, Position, PositionHolder, OrganizationalUnit exports; retained only UserSkill, Skill, Announcement; removed passport-local-mongoose and mongoose-findorcreate plugins; updated User import path.|\n|**Certificate Management** <br> `backend/models/certificateSchema.js`, `backend/controllers/certificateController.js`, `backend/routes/certificateRoutes.js`|New CertificateBatch and Certificate schemas with multi-level approval workflow (PendingL1/PendingL2 states); controller validates batch creation, enforces role-based authorization, resolves approvers (General Secretary/President), and validates user participation.|\n|**Authentication Refactor** <br> `backend/routes/auth.js`, `backend/middlewares/isAuthenticated.js`|Replaced Passport LocalStrategy with custom JWT-based authentication; manual password hashing/verification via bcrypt; added jwtIsAuthenticated middleware for token verification; Google OAuth routes retained with updated User model references.|\n|**Route Import Updates** <br> `backend/routes/achievements.js`, `backend/routes/analytics.js`, `backend/routes/announcements.js`, `backend/routes/dashboard.js`, `backend/routes/events.js`, `backend/routes/feedbackRoutes.js`, `backend/routes/onboarding.js`, `backend/routes/orgUnit.js`, `backend/routes/positionRoutes.js`, `backend/routes/profile.js`, `backend/routes/skillsRoutes.js`|Updated isAuthenticated imports from default to named exports; adjusted Achievement import from schema.js to achievementSchema.js; standardized string quotes and formatting.|\n|**Utilities** <br> `backend/utils/authValidate.js`, `backend/utils/batchValidate.js`|New Zod validators for login/registration (IIT Bhilai email domain + password constraints) and batch creation (title, unit_id, template_id, users with ObjectId validation).|\n|**Server Setup & Dependencies** <br> `backend/index.js`, `backend/package.json`, `package.json`|Added connectDB invocation, cookie-parser middleware, and certificate routes at `/api/certificate-batches`; replaced body-parser with express.json(); updated passport import path; added bcrypt, cookie-parser, zod dependencies; removed passport-local packages.|\n\n## Sequence Diagram(s)\n\n```mermaid\nsequenceDiagram\n    participant Client\n    participant Server as Auth Handler\n    participant UserDB as User DB\n    participant BCrypt as BCrypt\n    participant JWT as JWT Signer\n\n    Client->>Server: POST /login (username, password)\n    Server->>Server: Validate input with loginValidate\n    alt Invalid Input\n        Server-->>Client: 400 Validation Error\n    end\n    Server->>UserDB: Query user by username\n    alt User Not Found\n        UserDB-->>Server: null\n        Server-->>Client: 401 Invalid credentials\n    end\n    UserDB-->>Server: User document\n    Server->>BCrypt: Verify password hash\n    alt Password Mismatch\n        BCrypt-->>Server: false\n        Server-->>Client: 401 Invalid credentials\n    end\n    BCrypt-->>Server: true\n    Server->>JWT: Sign token (user_id, 30 min expiry)\n    JWT-->>Server: JWT token\n    Server->>Server: Set httpOnly cookie\n    Server-->>Client: 200 Login success + cookie\n```\n\n```mermaid\nsequenceDiagram\n    participant Client\n    participant GoogleOAuth as Google OAuth\n    participant Server as Callback Handler\n    participant UserDB as User DB\n\n    Client->>GoogleOAuth: Request Google auth\n    GoogleOAuth-->>Client: Redirect to Google login\n    Client->>GoogleOAuth: Authenticate\n    GoogleOAuth-->>Server: Callback with profile\n    Server->>Server: Validate IIT Bhilai email domain\n    alt Non-IIT Email\n        Server-->>Client: 403 Invalid domain\n    end\n    Server->>UserDB: Query user by email\n    alt User Exists\n        UserDB-->>Server: User document\n    else User Not Found\n        Server->>UserDB: Create new user (STUDENT role, google strategy)\n        UserDB-->>Server: New user document\n    end\n    Server->>Server: Serialize user\n    Server-->>Client: 200 Auth success\n```\n\n```mermaid\nsequenceDiagram\n    participant Client\n    participant CertController as Certificate Controller\n    participant UserDB as User DB\n    participant OrgDB as Organization DB\n    participant ApproverDB as Approver Lookup\n    participant CertDB as Certificate DB\n\n    Client->>CertController: POST /api/certificate-batches (batch data)\n    CertController->>CertController: Validate with jwtIsAuthenticated\n    CertController->>UserDB: Fetch initiator user\n    alt User Not Found\n        UserDB-->>CertController: null\n        CertController-->>Client: 404 User not found\n    end\n    CertController->>CertController: Check role = CLUB_COORDINATOR\n    alt Unauthorized\n        CertController-->>Client: 403 Forbidden\n    end\n    CertController->>CertController: Validate batch schema (title, unit_id, users)\n    alt Validation Fails\n        CertController-->>Client: 400 Validation errors\n    end\n    CertController->>OrgDB: Fetch organization unit (club)\n    CertController->>CertController: Verify coordinator in unit\n    alt Not coordinator of club\n        CertController-->>Client: 403 Forbidden\n    end\n    CertController->>OrgDB: Validate parent (Council)\n    CertController->>ApproverDB: Resolve General Secretary\n    CertController->>ApproverDB: Resolve President\n    alt Approvers Not Found\n        ApproverDB-->>CertController: null\n        CertController-->>Client: 500 Approvers not found\n    end\n    CertController->>CertController: Validate each user ID\n    alt Invalid user ID\n        CertController-->>Client: 400 Invalid user\n    end\n    CertController->>CertDB: Create CertificateBatch\n    CertDB-->>CertController: Batch created\n    CertController-->>Client: 200 Batch created with details\n```\n\n## Estimated code review effort\n\n🎯 4 (Complex) | ⏱️ ~75 minutes\n\n## Poem\n\n> 🐰 *Hop hop, the auth now flows with tokens so bright,*  \n> *JWT and bcrypt replacing old Passport's light,*  \n> *Schemas split clean like carrots in spring,*  \n> *Certificates batch with approvals to bring,*  \n> *Google's domain guard says \"Welcome home, friend!\"*\n\n<!-- walkthrough_end -->\n\n\n<!-- pre_merge_checks_walkthrough_start -->\n\n<details>\n<summary>🚥 Pre-merge checks | ✅ 1 | ❌ 2</summary>\n\n<details>\n<summary>❌ Failed checks (2 warnings)</summary>\n\n|     Check name     | Status     | Explanation                                                                                          | Resolution                                                                                                                                       |\n| :----------------: | :--------- | :--------------------------------------------------------------------------------------------------- | :----------------------------------------------------------------------------------------------------------------------------------------------- |\n|     Title check    | ⚠️ Warning | The PR title 'Refactor/login register' is vague and lacks specificity about the actual changes made. | Use a more descriptive title such as 'Refactor authentication with manual login/register and Zod validation' to clearly convey the main changes. |\n| Docstring Coverage | ⚠️ Warning | Docstring coverage is 66.67% which is insufficient. The required threshold is 80.00%.                | Write docstrings for the functions missing them to satisfy the coverage threshold.                                                               |\n\n</details>\n<details>\n<summary>✅ Passed checks (1 passed)</summary>\n\n|     Check name    | Status   | Explanation                                                                                                                                                     |\n| :---------------: | :------- | :-------------------------------------------------------------------------------------------------------------------------------------------------------------- |\n| Description check | ✅ Passed | The PR description is comprehensive and well-structured, covering all key aspects including changes, motivation, testing, and deployment notes as per template. |\n\n</details>\n\n<sub>✏️ Tip: You can configure your own custom pre-merge checks in the settings.</sub>\n\n</details>\n\n<!-- pre_merge_checks_walkthrough_end -->\n\n<!-- finishing_touch_checkbox_start -->\n\n<details>\n<summary>✨ Finishing touches</summary>\n\n- [ ] <!-- {\"checkboxId\": \"7962f53c-55bc-4827-bfbf-6a18da830691\"} --> 📝 Generate docstrings\n<details>\n<summary>🧪 Generate unit tests (beta)</summary>\n\n- [ ] <!-- {\"checkboxId\": \"f47ac10b-58cc-4372-a567-0e02b2c3d479\", \"radioGroupId\": \"utg-output-choice-group-unknown_comment_id\"} -->   Create PR with unit tests\n- [ ] <!-- {\"checkboxId\": \"07f1e7d6-8a8e-4e23-9900-8731c2c87f58\", \"radioGroupId\": \"utg-output-choice-group-unknown_comment_id\"} -->   Post copyable unit tests in a comment\n\n</details>\n\n</details>\n\n<!-- finishing_touch_checkbox_end -->\n\n<!-- tips_start -->\n\n---\n\nThanks for using [CodeRabbit](https://coderabbit.ai?utm_source=oss&utm_medium=github&utm_campaign=OpenLake/Student_Database_COSA&utm_content=222)! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.\n\n<details>\n<summary>❤️ Share</summary>\n\n- [X](https://twitter.com/intent/tweet?text=I%20just%20used%20%40coderabbitai%20for%20my%20code%20review%2C%20and%20it%27s%20fantastic%21%20It%27s%20free%20for%20OSS%20and%20offers%20a%20free%20trial%20for%20the%20proprietary%20code.%20Check%20it%20out%3A&url=https%3A//coderabbit.ai)\n- [Mastodon](https://mastodon.social/share?text=I%20just%20used%20%40coderabbitai%20for%20my%20code%20review%2C%20and%20it%27s%20fantastic%21%20It%27s%20free%20for%20OSS%20and%20offers%20a%20free%20trial%20for%20the%20proprietary%20code.%20Check%20it%20out%3A%20https%3A%2F%2Fcoderabbit.ai)\n- [Reddit](https://www.reddit.com/submit?title=Great%20tool%20for%20code%20review%20-%20CodeRabbit&text=I%20just%20used%20CodeRabbit%20for%20my%20code%20review%2C%20and%20it%27s%20fantastic%21%20It%27s%20free%20for%20OSS%20and%20offers%20a%20free%20trial%20for%20proprietary%20code.%20Check%20it%20out%3A%20https%3A//coderabbit.ai)\n- [LinkedIn](https://www.linkedin.com/sharing/share-offsite/?url=https%3A%2F%2Fcoderabbit.ai&mini=true&title=Great%20tool%20for%20code%20review%20-%20CodeRabbit&summary=I%20just%20used%20CodeRabbit%20for%20my%20code%20review%2C%20and%20it%27s%20fantastic%21%20It%27s%20free%20for%20OSS%20and%20offers%20a%20free%20trial%20for%20proprietary%20code)\n\n</details>\n\n<sub>Comment `@coderabbitai help` to get the list of available commands and usage tips.</sub>\n\n<!-- tips_end -->\n\n<!-- internal state start -->\n\n\n<!-- DwQgtGAEAqAWCWBnSTIEMB26CuAXA9mAOYCmGJATmriQCaQDG+Ats2bgFyQAOFk+AIwBWJBrngA3EsgEBPRvlqU0AgfFwA6NPEgQAfACgjoCEYDEZyAAUASpETZWaCrKPR1AGxJcbJAGZoYvgUAPQe+ETwWBQkkYg0fAAUZgBMaQCUkJAGAIJ4sMFcTEoA7gDWaNiICGUALAAMtVkGAMr42BQMJJACVBgMsFwxAUGh4ZEYYDFxCZCASYQwzqS4PX0DXMzaWNkAwjHUdFwp9SkAbGD1AIxgKQCc0McclwAcHCnPAFrNAKo2ADJcWC4XDcRAcEIhSK4WDYAQaJjMEIAeW4ZD+aDKJBCLVw2CUGFwAH0ACLUFRoRAkQk7JEtHIhbjYDweEJpFLNHHUKpcfCojBGHawTCkZBRXAURTYLq0DgGKAAWXwUnoxIAQgoMOQxPB8FhMPQrBTENxgit4lQaER5AR0JAAAZMDB+eBEO2QPz4DxKCgaOWQRW0eDOuiQb6UviIAYkTaQG1kBwxSDjKKQABkkGmSFmErw0kgJWCZXQooJZCUtF9UF83A8gRDvXwmKwhsQxtN9rDlA0mfilES6Td+ozJFrNHoNewE2QJXUsFtmww2DQHiTERTQ574uoOqw8GYNej7G3usrkBytHLkA+ikgEmX8Fox6wHr4Vlp0GHAEdsNIVlFGbgiAADQoP0Hh4lERDoFg0baCu0wkAAHowsCiEWMTmvA2oYFBm4Wjusb4Pa8DqAICC1vAWgMBoUR2qeirKre96PuIuqrpEDCEbadp4PAHiIG6HpepQ7rBJAzCSrWFDqLIp47OwVAePAABeIaOs6RAdE+0H0HeSksQRyYMAA3OgF5qYo3QImwBLTqhiZGSgyAIgeiG+gY0C/pBaYoPugScH63wYOosa/sgFDYFg4QMMuHjyIksU8EadDpPRmBLiuNDxGCBhZFAfxrlgM7QgoFAxGIjAxPi4jLsgCwtFKXStr6eWQAVEz5rO+YSjhlV0Ow8C1fMkAAGJwcgFKQEhqJiHQLW6JAvgzCJxVzhgupgAAkpt0DqrBfHDWNfGIPNVaxFmK1dXpD6QNtu1TZsB31Y10gnX6ABqlBBvAIbrZA5AlBqiCet0JTOMFOGICElAShQb1nd+8AYZALHkpS4kuvhuohJGqExtg3Asd0Q4A1NGASEjuo2Ssd7SSoXjw4tACiOTEvKTP/fgKyyCQKwE0TFYCiwzAhQ4TguLKUDDP5wQJfk6RcMaSlmlGmwljalLcM4BzunxeZDs6yHRcuOAlY5gAoBPYNai6raAo/4UTqDuE0YPQaC0EIVQrMbK6VNCA0xaxu77l41PaTaMQ1nWqyNmQSWtiaFArHhT4MuBU46fYBOJys/vielJt+3OfjhCU7kWNYdhKJG0ncEHvloCK5iWJtmwiqBlf2zX8B1zubioaJzL4DOvV7o3eYlJQ3RRAw4GXimee2F3DC10HGgwKh6PVyvPdB9OU+QKQ5AWiGfgSswsYD/5GUNyKsoGBYLdt90lxcDkVibTAXm9dA+Cegt7UUy+G/L+TOvg2wYEpP3VAqA7Y1xIPGAoKx8B+GgmeD+oV4jeTFJQEY3QErcFRM4ZANoBDdCsPgeIC5+ARj3HxZwzMWgflnj9AkmRqjD28nbN8TCvw/niFxO2yYYKuxNGKdyAAqCRwD+ErGJLzcakBEi/24JAFoogg4KykX6AAEtAaAVh/S8wKDKaw74/RM1EfgMUXA7Q+wKPEDgABWeoriQhFzCIVO0fpVSKHkI6GgBIlFUEBgAKRaEiAAcqJCgmxcAK1yvaO0dohDA35FkAA3okrIAAiKolAMBoDYDkjgOSBCRQwLIAA0gAARIrgMi9DKKBBohgHJQFsmQByVrVsBYKC0BKTk12I8GAlH6CUHJiSAC+iTkneIVEycQB5YwqGQBTRA8ABBeC4MSfADBgLWG1swA5eRoTBBUk+ECOiSDu0oMgRIlx6jpBAr42g8VVqHxiHHKIgZA7BGeWonedd7kfKIF83crssLUH+SBdRwJIKICMFI8BJpIHdHkbgRRiRfHAhYGojRO4tESL9JyXEYJIBSOOPUSASIqlSKURwsZHcwUIIwOkP0KLdTo3EGwLgABmBo4lEVnQgejDZqkuCnD5QAdkgKqDl0hUXowEH4pR4SokxLiQkrIczUm6kSVkrIuS2CtnHiUrpgCsANQYE1RAfgmTtM6TkggTZzU5JILIUJsABAAHEGDwCRPAUJm1vjKU2pcSJ8BNqIE2hgGwTiGA7E2qcTaZRuAAA13o7FCbcDQHrQnwAAJoAHUACKuAKwaA0AAbXCL1UJxaPwurjphHCABdSZWQZk6uSUiiRVSPWQHekgG+TNQ7sDBNoqAPrwX2ExWS0CvzoURgKCPKCDgbWvXtfBRVXKSB+gbU22OWB1mbK8B3POGElX4LQIQm5cMuJkNtGQ5wIlm3PjEkXAOT52VQEiWgCmRBw6rPsKu7yrzZAgR2H/MoP0DnXNuXDECQ5PICPAYs5AlJtRcpJQB7onK0WZy5vZSAvFlbyF5HvW8SAz3dDYpel0QJoHICUBJSBW4sq2g3ba7dptUIEihfXEuw9OolRXv1ATy4wACApCGYRw5cQUAhlBO2h7CJNncvoYw4AoBln4Kgv2hAj7KDHAoVg7AuC8H4MIAlUgZD+MslQVQ6gtA6C0yYKAcAYEu1NkZsgJmLLmYJEMNAgMxabBcD0Bz3oVBqE0NoXQYBDDadMAYGTDAmy0BCOpF0IRaBwlSbKHJxWH6WByJtYg/mT70HC84CjqCBjCmkEYc8tAJr/RIIDdLmXsu6g0nlgryAJK0CZN0aE1BQJO3vKpZAZAKY9WpkxOmWzpAgV/PTJAm9bSKhwvgNUGotT1wpnbNjRA/7oyqN5eUUSfVIjVISX4m1kOux4BKCm1dbSz0oSQfbZGZLrzgFZXUh39v2v6PXWBiBZD9FgD1doBzHSHenLOdoKwlC8FEAceglHnYgXGJhl6rYaFTTKsEZ79BpqmhkMRg7Gj9tDi+5SNU68ciMHCEz9UqEPCoj4IzvMedTvnaB5qAlbEPnjAmFBF8WdN1E7EjDYIJ0N7dGG6NqaiEc7IAybTsQaoQJ8/21MmJ6uEiFJXFUce7kjBQBGpFbDepzKmIdMDunqo3QpjtN1ssvWnS5fyxoVJ8zRp2/ru7cstiDdu47p7wIPWctEAGwHgSforCwiUpxSnSd7AukKYpqyQocJ0HcY72xquvB5o11TyAABeSA2vEeu/1+zn76opnu6wDHjL3v4+J8D0YP4UQ8yNcL6YgA1Hy+oIQLhGCZlguJFklDDgpp1qafgXycCMYGRwBhiuTLlKlr3rsff9Z6W2JO0HfdECT0VkrFdyuVePljrO4t6soSa4i3IF5kA+r/kQc9SJTk5wv0BNA4CJ48tIIcsBD8sse9T8c4L8NIk9SMNleoWwz8Vghw4DTRiBf8vAwB8Ai5jh155FnRyB2sf8IgvAcQT4rRRM5wWF2A+pqpBp+JM4YpmRutQx/gQJroDJeo7o5VyIEt9oVxaAWAth14kQopCp9dUIMtkBpckIsxvJ8k+A5AHo4JTIgxXtpB2AQIYhFNIEUBcAtDUF1pcB9d9hOM7ZSZVC6CMwQY1FoBvhiQmZIloAQJzQDhaCztKCSByd7YAhFkeA7ldRlxCQogPQlFCk2A1tHoPAQJeAUE9YrAsJMghxdQVVnBAwcJoMQ5eZugAh+J/DL4W08MXZ6ADCOgjC85bDwx15LEXwmoUZxCUwMJxQsJ651Ctk9lYNep1pJg7pVQhCdARDkcSpMASdYZxJXpLdbowI8Q8xwwWCLlQ8Xtq4vppttJHJpc847ChxM9AJSiNQNIOgQwsCs8oh4hMAugGjSc+AC9aAlJeomApAH0oheCWjHpogSARB7cDlUYZN0YFcH0RtpJepwhGwCZssrCdwAjlitig48tdC6YlJlJtIAg+Izi3oU808sJoZK8k41IC875Ek/0V9DjS9JISAK9Nca9450D28ehY9u8+tcsLjcAECXQk8yTIBIkV9GQtksJ3QQ8dxbEOSNAES0SSBOwKA3QPkNkiBc8zj7REhVCQIxDyABxeT+TAZBT08RTwcxT7QJSNjUSVIZTwx5SupFTlTEw7Q1TwwB1IMWitTvFSszwPAEgnwSEiI84lBZ5tYqMUF1cc4QwxJ9ThSBpxBms/RdTQzTQi9IyM8BNcBZBxSjQc43RBA/iVgz48VO8482SE8OSuSr9A9qM7Y7Qy8aTDjkBa8OSg94zDiky8TOIwd7cMyE5TRJTNjpTZTHTKANTdQSABxo9oDj92TMzTQyyk8myKTCSxwGQ2zDTOyTTpyk4NAzSVjVIBzVDnThy3TxyWSj9YCNzOTiy5yjAFwgxfxCRnQvADAtYMtLc9V+RgAQgby/A7yHySBksB8yC38R8uBR9ThpUp96gd8St98wAjAJzhsRwoZcZYJr8oLJk78KtjNqtn8ItX9h8m4FUJJGIkJAkNkpBkDKApgRwn9kKYwlBSCnYuVM5/YkYeAVyELWDEhZSQIRoEF8tY8QIcgBgfopBqYQImYpACQQIKENkg4dFPRvRpLKFGKMAQIkQKAgNgp0Sg5lwgp1A1tcBqJ0iXsWK+BkyEyk5GZfBMVB9scMA4o2KhSM9FyFCxJZSWhYNmRYVPKEjM4chNR2h+hDwCRTxfAiKQwLdSBM49w6SQyOSwAfYwBBdvtM5krKQwBSCxDOgrDugJwpxTx5EvBTNEojQ9lBpTNaK7YAypIfTUrLIVxHYao0TardiB4Yhwr6AmBmRRdIFTJMQ65+B7L5B3KfLvK+JfKhx/L1o7dgqVgOLGYprAquhFsOLOZAZ8kSE9x1t9x7kw8QxtceVtrQQuBxQfxIApkAUhwBdqTaSq91pAZKdKRkBdQHKRrxqxqvK/KAqZrqZy5LBm5PTvSqMbR/TRAargzUEWzsczKVzoy4NrdIBCqCjxxYbFzbFZS3RVqPd4L6qkLbYryoAkbTNzLKT7ReK6ButMb6rjyu8j95qcZ8a+9CaRxkbHKDTSa7QhKEASBRL2AqalAGqO8caBa8a8Y0ACbEaWbibUac5bEJK+bxJqbsaTyst6bKqJaibziZbTRbEZKVL5LhI5TFaBaaaes1bGbk9maiqtanKLKN87Q9ag5+aRxTbvdzaxaNapabb2a0b7R1LNLVidxdLgpcBnbBb7RhbEKGaPambFp9plM2aoyXL0bwwPLxq7QQI7Q07mQM77RFqfqFblbabVbcbo6ULY6wqlQQwYrTQQhIq6NUE8qrjZRWpNb6Aa6k4uz0CCp2CdtfDKRvFW6vb279wdb7Q0qSAMqflghxMDgg8277ZAysYjCQzL1owq6atbYKjSjWL5qQIZ455IIW6FoeJU7bZB6T7ggA7tKg6PA9LcAWhz7eS7QTRZKdxH6xaL6oAX7lK5KFLKAP7YIv77Qeb2BAHNhgG7RAhubeaCRwG0BIHvyKbY94H57h7jaXbGqdyWqxI16Or7At796FiciiBj7v6J6NAOLEgclZT2kKKKB4HtTWpqzdR+6aSqGclyb+KMs6GkHuGyhGHIGKGOGuaRLZq6HoGxHqZBHn7hH6rqH5aCQ6HQG4HbYmGT65GBbqHHadxCQDbvQ6HX79b/6GG1GhHWGhdKH5GckdHdRDHf736zHZGLHvsrGtGcl/bMBA6wiPAHsQ66Gr6vGb6fH76ZGoBzxPZex6AazbrLKuJ2qq7jiBbWb5rM5MQSBVEXrrQ2r47vISbUy4MyG478G6zjc7QMbM6uHKbM7RHQHqZc67RFHQ7M7bGMB9HKAGnWmGnPGtKnxg71BgHrKtgQxSnpdynU6fKGns6PAGn86gr6m/QAxvpDgzxvr5nGDKq1qKLNqTVMUdqlE9r6AMkLrM4obiwkos84qVz35P4PlDrridq/r+9B9nISSVnR8Ugp8nFahpUZ858n9ihugYhl8Hq18x6dFGM0LrcD8VaQgcwsp3F8hULd8PT78sKaLHBcL9MgKCKzoo5pRVx2CGSc5pNZN6B5NhNAYPk7YGAvY8UKRocBg4cqh2IUxHivA+BxsaZmIDgSwAI8cYNkACZji7D1DVCYiSiyAmi8wfY50aCXTeDeWGS+l6APkBAV5ZA6597Wx+FbQ1MmBGwfolEJ8MZFwspMg2JuNXoAiqilNMNMVXZsiLSKd7iZjTURRQqMnawCWBDhimkNCDogMaB0nuBsFpCJg4XzovD64Pl/w8AmJ9JtJjtWWMB3oeWaBM5NxKA03E2aBTJbWjCGhqUPlFX64TVEBx5nrnw4IzjTwWhiooxKio2OMCJKX3Rz5iXsCgS1JYT6MiJAxyo+Zww+ptJLtepZSMHfKMdwwKZeoJRz1JomEXC3CPwhx4hzlepxWikSjT8VWAiec0lwjIj8B8317GIYhPwNB5MhxSr/Un9KXTxY1xRJQCXpp08Qp1WXABr67jdd3ghdIvpnRQC2Ib3HcuNRADDnSlF8l6Bpc1NbTIJMg7nj1QykZ5BKRc4iITWRYzXpBTJ30nJ7BeZznJigQQQpCHKDXYMaS/QImvY5NCpI2Zhl7WXOIbQecXwL432sIQo7ClDewgqUI0IEcqoBoTY3jvpgPVLM5rjHX+lnWpixJy3K3TIKC/9ugAD8gHDcxwoFFbL7CEwcxIUIS1xOJ+Yn8bRAhrJFAdZSZgDxApP3RS56IogxJzQpRFMTZ8LpAuBzPTMO6jjpcJ3Vqb38hv0y2HwnjOtX1EBT2bLyBYOr6uYwA/3+kGQjQVXhwMOyZaAxFbJM41P/9ACkpoQcSAacgvSTNnYuJQal7aqQyzmIzYaCnYy/0Rz15IkiJkyCSwyKdUz0Oc8uRkZEgOy94iGEg8EDkxJUY5WPPsSkOD5Dn9Cz26AQIlPFBln269R8w0BsmJtvOSFUJWLzKbmAc2rUcRJ2qthZtFzVueh42cElMTYYrx0CRarJ5EwHABBZOmqHKYgShpJgQyB3IAKh83mx9bhLgp9ahTg/meUAXLIl8fpQX18uB5Q6B4Bt8UWYK4LYX6bJG6mwGLab90KytMKqsMWX9sWDuWsv9bRSY+6hdSWu96AZvVrpcCfYGjiUxI7+J3FhLCfVGY6lcSCXn0ABeuf4H7DgwvRyVOfZqIj6A1TgoQFlvEYqoAVVDFfgl/AuJuK+EkYUoQJxBcBz1EgL3sBDfaAAVt5V44TGBvDgh4oLereAU0zUQ8dQHfKiZCR5eleXeNfEiHHdQ1tJKiQbpzfdebQmm1ve4fGNT/BKgvT/omQPAAUugk5JODhCQOhfKJPgwlf6Kk+8zapRyeDAOfpaBCR1DI/UEbR9ei/gjFxmRUpIBLF6Z9Y8BxCHPYwtqHnQ2cJ149gblrCsBamufJ3228U85NmDiXLjDznrqRtz1TvyvKvl7fSkmwagzquGvbvobE6UyTf4a4yFzevlzbboz0z7RHQBFx/Zr6TNGRxqH7/qYJGJfZrGHjImTCy3bS75fpGFtN0AsDFASgRsBLSaEvxdr0UsGJ4U/g9X34X92a/XKksv1rLz9a8r/dgN/1dp01/+H/QAcL2AF21Du3QLAUEg4og8xe3nMfN8wgpw8x4pmQFkjwpJgtO6m+THswChY480sePUuiowfrE8oWGFB/AFhqyYs6s1PN5h/laztYGeLjdKt23oBNMUYZISfhNl55QwBB8DJPMQQdiAVJiKgzZqtHvi6Yw+2vSPurzu6RR4AICYyvQBN5m8A+KUP0Djh8bLw7e+qKAIHFiBO8yYjgIoIsi0i+UaAAwYKOwU8Ka5kMMUVjFhDW7+wKAfod3t0FbREBXBGlIJpBBz4h1FeXAJEDZjECbQm2dfIiD028Zm4/G+lA3lVHSHX07kXAZwFQFfz5DcyRQ4cH4E36yk/QyFNAVnAEBiFaW1MRWJiiThrZXY5fRcCUQQpKIyAAQwas8RKIoIS4g+ECLAFkC9AHwv6YcMx20hfcBhjgCzLrBHBtZjczg2gJ4RGEWEcuIEJBgck2CIRCQWsTPv6i1i2Q/Q5SWgMsD6H7Chh6AIeD4POFWw9CVsLlIrgOZlRdu2LVIYgC2HXEF0sw5gJZlrAi5ARFjSCPrhYAHgxw+uG4iOC8C0BTIjfZPlHBRF+gnhDnHuJgEAgNCIRzQgobgDaHDBOh4YP0CPGPhy9aR2LbXKoTyH0jGRuvfXkYyDhcBqC3kGZFAD4bdZCQtWCWDSnpF+g2AgYOBLCB+GHCx4IoHgg+BICUJhygwidOCKaHPVUE0IrYRKBYCEgLeYUb4XsgOFBJGhkIkwaoIzaWDLegfXvmwB15WDre+hP+BfBdGu8E+3cOPtJzhEstEgCI6wGWEgghAcghCN7EXl8C5k6AhIxPsESsBRicIAKS0VE194rBEgRIlYPdSzE81kedAavvFBaEaJ+RqCWUlsPuZ7NQQSiWemOByBXC/OdAVseygMBMx5+ecFQatWTYxM6yf1QGlVyYog0B41VHfkxT369dic+TY/q1zPBgdSYUNJAVGRQFt8w+k7Xki0EG554zI4eIGCsBUG14n+HgBRmH2UZh8v+vJHsTnEPErMhxGArcdgN5IjQ9YXATQdDBvEW0Aa8oLxt+QEQfiV+ZuWQKpESFGAPS3wQmJTyxaYoiAkuZ5oBRoEgVLg6E+gdjwgCwVeBxdT8qXUlEoMhBKLEQeiwqoSDIsIZGngYCfZgCpQ+sDrIDEZ7fZSWlIWDnxU4Kz8XsWNKAnwJFohBCJGWHQakj0GkEGJVTWPAQzFrS8fosvLgIJLKAWCbBqvaoUb1jCat8EZwt3ksF5gWDKxhQwEW8hiLClHuGOedARHMJqSRhywQkMkM9Gui1JCk8sUon0kMjihevcMGrwcneiMw24XqA8jABOJ0+wsCdNq194DFZAEkMMQWPdCl8AUSAC0dIE9CMR8xqY5PkUUpCXV/itkjEHHFSlBFk+0YOuOhw6KZjLCI/MsRNnynF81qxY4GB4GVC5ilEMU5vmn2W71TGpNfVydWI8lDlAiNU1qa33vFV484EkjLOoKGw3VhxyuXWOegPqLFnIIU0sLQHwLxt8WJAAoIbVcq85HekWIcEZKKTClrJxHdlt5Esk4k786/WqhOO6BTiN+2LRrjDUv4tcP85JBAXOKv62IxpZQMOh3F/IR0+JUdBScJOTxD06u9cW0kN28A38uUKwb6Y/wUHsNrG303hhxKIli10g3/Xkqnkv6+0WGaA2JkcVrzfSsZBgUHq8yaxj5TgtwTCdBWwm488J9NQJr0yDggySeqLcno/nIlU8qJ0g2nicJsIr5mJ6MdnnrHQArBvxzM8oSDMvgTYYBBgl7KUzthlDgmZue+pOzO4pDbYNCa+n0zvoh0pe80j7DLzay+cchEfZST+C8kBj/o27aDirytmqSfJdk8MYuERGQBoMduPiFBnAgCAoMwsdQDQBKI/IMmZYdgI8OD4YBT2Xom3tICBRBxEir6AkNkPUB6S+RxQqeP0G8gqy9ZlQ+JKsJ+hUBOgawwkF4CkArh/RVUFPswDIQUB0+u0+KAiMCFelghnhf1KENgCRCqc0Q25CLAYBEN0cYcgkNHO8nxTEAvvbUORWqnBFTqZfDUJijEAREnQREcMfEXtm2DHZWkmTmVXCKKjBo6ATkR8iji4BOOmcXPgCg+E2Tj2Bzf4Vjmr54hlgkQ8OUUkCrxJNZUk2CNs3dFHUlcgOSfqgGbEhg7mA8Lql4Hty2yPROcnSr43Vlz85xg4qafP0mhQLb699EcWvyBrVcbpi9cGrv0hr795xzXRca9Lb6ICFxMkR8U7hQUhMQ6TJf6b/zwH8SpZqskGcA3kTgyCIhzCPLDJpQZCWZqCkOgjN2yuMOG1CioffQCZ8Lyh/TQQRjKxmtRhp7A/GaNkJn1leFus6BffVJn0yyZ1A8HiBWlTgVp83Y/5kwMR7Atkeq+VHhwKx50yYWjM0uqWUvKFZhBZPUQdhRlF4U+ZhFDem1D2TLhqC3hdDrzGFZDh1ALGUOa7DIAMB5A9dT1utKbZWDsWE7fMhfByQaAy6mwHJPYWcHYt0lddM+mLXSUKp4uIYQriQECWWh5ARkVTrgQ07Fc22V3K4iBHKSFiRMG1EVrBNMzBdqSxuOwpCTKChKXszYgiIkD+g90AlHGWIPIA+LJSUoVuVFldOBp+lJx2/e6bOMTIH9yFMZEhZ1xxb84iIa4k7hgkhl54lcz4ukg2XPLDhviQrGHJTKoEoT9FkAD5lPnQkMD58nVcxSWNYHWL0eW+LgVhPsVm1HFkc9pqY2F7szSJFPbmVi15nv5+ZcgoWYjNYkhhWm4Kj+TGCHCrVIo3oZkg4v4lCidw4KkGaJLF4mzyUicJSQ7JKJnC5WiHECFrwj7dT3J+vLSUHzfq6g05rQ9ya02tlujAkZxQkDzAYSVyQwqQy6oQmsQEhqYhIJQDZU4q3o8uY4ZycMFDDhhTISq6VbgFlVExEaBwAFBx2CALgughINgB0X2QzCw+485hSGEiSOBa5/U4IvUBaUPzdJf2VSPQHtU1yRIMUl1e6DRnjTRRZUmTvOjDFNzxe4gKQBiPyLYjQosSKIFjhHmu8GihSFbDsx/kNFK8T1W0OipMaT8EFwiykG42f42NI5ejExvY05VtMTGjDU5vPxcxQTFlmC8cSstulrL6u+CucU12enEKEazZMhUQpkhcA81htf+c+DFnfiiVuoElX+Nah7ilSUMyhdwvYzWAwV+as8YjJLUXiy11aitYbSrXGNDaX/QSJ6FLj1h5Ayi8vKU1ryjrvQpMiuABOChAS4ZYs/ysuHAmUBkJYPSmSBScTPB6BJi+HmYsXwWLflY9f5ZwO4E6Kp1kctma4rPCcyxBOFSQfCsLwf5aJL7BifIKLVazpJ2K6mtLmnVGEeegMvnsRtJWS0xJyAVppirtgfIKVlmctRbJpX8rxVpUogG708CaSSAXo+lSGpsHh8leLK9oVxDEXhF76bGnycRtskaT7JVvfjZxvalKpNkfEJ2HmASiciQyJotjdTHuQ5kNE88rYN5Bw6EgGARALWPYT9UgRTNIqvgB8himXB4SPlceXSvtGv4dNHg3eKMolUcqVKZm1+dEQdWUAhpaahmL5jiTClAFVfagHXS6WVTc4ffBsUrkUVHFF4kciacbRUXDiEarWb2knVlprrq1k/VJfipBWEq4Nf48JiuORW4a6NdVAWiOoy0KkZom3I4bL0zgFiP8HpaAI3CgmXSW1NRNtTgunEr0u1mywhb2vU0kK8tKNXGYVto3s8O2sG6tSDNo41amJiM+rQRsa1FaVKBnVrQX3a0Cz1iaUwCP1ssBPrbywEt9WBIgnfqKZwFF5U4j5S0y98MGsjVDFULwaSJbisieKooleKEVNE/Inpu2woqlB6qkSJs1I0Eqo632i2nQVMGjRZJps+horwuHysZhbsrgD7BAi+F1Oyat0fO3wQxSckS7Vwu4RySa9wwEreTVVEZU0rDVGXf9vTvDJDUUAxoqZbQVQA+xTIOHP4GQCIAlRngAKTIgQXk65FMR1tZqWdtinFFUop/KJi0T1G2RLMoRCodfLGV2yDia8s4aZBEKkYaVKYgqYFwjIShfyqRTiAznCAQRCkLgb4BQA8CmQ3BZuHgAUHIAals+KCavkjGhD46w5dcnubEIYBLyoiiQJImCiKQtK1gnc5koZVgDCr70+uDoDEGTl2b9cFmtAMFLe6LytdDiGgL5TNHMBCQbs2uZ4R3keAAKZQclFCBhB+ykwUQTEDkSIbXEo9zAeIfZEV1QBSULLE2djrmH+RJAwcwpJPJKJR6RsSap1cnyH1SBu9MAJLUUkbFkAO+gsKAHpRAQXMmqi6JCINXR03QFSHG48VQDFDRUolyEOnUyqr7kiWCKckBKeCsAxAwAFbcigUEbBcQhQ1QZVqzrHZQRP2mrFYB8jpB/APwpWubJTAwCLZEgWTfMPxm/39Jjam3efalvazCyVc1NCVvQFlLuQcZPtB8Vwqh1JA0Dk7TIKgDOalbvxCO4XiOIq6DbN+tXXBTOPG1EktlQ6nZQjVS2tkptaZZdR2CtJKJiDHFTIOQY7b0LKDhS8usnmMCRIokTMZLP+MAmgIQJZAu7V+t0VPLf1Lyx5ABunxAqcJE5Z8hUFIBJ5dQUKv7TCoB08yGs3i6rZeEHlRKs5Bysrd7kMOvk0kX4jVgNQAB6pwDQPUD8MYjDWk9J4cOy8OXANAtQDQNKhAjKQbwXhyI3yg0Cw9bDFkJ0OnjmrOBMQ7xLAP6SDCoJoD33SgIxAXgDxgYHQLoCBHMK2hRut9fZVsMrqMRzK9h/EP6jzAUHYWrh4w2+Q2AorMqM9HKpAHiN+GAjnbJOAlX8Urgwjwx/1fFUSoT1BjzwaYz4sYhKAJA8iPkC0bgxT8L4Bh2PG4dMMKAgjKXYhCJCmORHTgicuGKcf8PhHagcZIiAdy4j8dxAc7SKIdR6AbSAMOoNQh6l1Cs9IlLR9Dh0DwRmRImOq/UWMqIikxmBWsUrqBmHhsom1A2scUNq34cKmDdtcMk9OQF9q/Q6xoeTEt4Oe5PDodewm8Q2RsQ7QPh6Y3QrFkMKssnRmkm+TdDNHol8NZmhseiXyACDzuI4yEY6Zkm7kBEKk7caiO0nz09J9Li+S6NpIWTAJtk81g5MEnuTJee0LEdoDWkSo5J4U0McSOnBxT3QSU4yZMMYA5TnJxw8KklrmnCTCTZUFSVw1T1XY/RiqZqbnDanKTQx/w/UANMAy8Jxp5k/bGtPsmrTypm5RvS7oksfYrp28EKY9PhGvTPpo03sZlO6gzTBJ4M/iYcM2mVuTuWYxMaSqIzoz7pjvF4cWMJm/pdJ3Y9KaZOynAz6ZxU5LTWPyn+g8gW0ys15PUdjjVxo2iW1jMlnRT+pisxKarNGGazqZ+2E2aDMNn5Ek50M22dzMnGezV0Ps/aC8M3GIjiZkc/sdNMTnMzmx2MuTP2Vj4jFsPPQwzJ6zwtpA/PGBrNROguLftiG9xXBNQ3WHgd6++LSPQfGwmgCL2NQMZyly4MB45AuavVXkkdsqqv4CKGIDOKfn2wIZdJRkvdqwRsllnafX+FHpZ5StCF/CfxIAFE8ilZ3ABdIMgCrozwBAxgqgBVS4qL0A8GJoFA/NORAC4XauhhY3ylaqqhvCqM4MSBVoNAmQP8yQ0EQQKRmi5OvIxbC4gEn8RuUrQLki5eAwYiYGJhvFgTr9kApFvOEgCYuSWKqkUlVA1WeoCBSlgsAwHsseMRwLuByIyGtz4AgkSL+oZ4kQFMgBJ52TnETE0pqKlHt2CyqAK3AfGnKVSqEmGauu1zAXzq9Jbi9haQtZLMZxA53KutCu14IrVaHC1HTwtC9kLMVoc4aYnKXmoYaVwCBLV8vth/LiYQK3FYESaWJLDnJ/Ild40OTqGyVkWBeHksxcQglV/2NpboDU6cBCwcqysG1ztX+M1V0zEblqtWCGriFuS9FwwhtXEAWl4a11cyspgxDsLXK9eakYTory6hn9U9teU3APlCPUDT8pR4QaMeti97cCtZIEh52HLKGBn3EBAcDgF+Z9t1R9D3nb85hrmZYbhWvn0NRgTDeAOw0r57rWfGgM9ZutdhUk9hGljlVVDUABglg9qZkHZZdgZplLZHfNcEzdA7CpWi9hoH2IvZvyCewg1FlujEg4u1RSAA0CaDaEqjHoXFaeEaIz0gWIMVFW7HyDnJVZSiGAzsD+DfBVQ1IJEEiBsDEhNokSHINAGFt/Dz11vewg0D5R77MS4EGIKeBzYPgdYM8FgN5BkzE2ZuiQRwSUUE0Y6zMbGUkJimN5FTRwVIB8IyvDAwjkCXCHQmAHlnirtZpbYIBTaUxU3XE9hWy8p3bjaFS2rbcaJ6w6KgNjiBrSXcukACYBDRvXVjqhwrTA3cvM6Aq5owtc6oD3BospDDtlfI3SFESCzxYQ9ghTJTflt76KYnoH0qrfTZjYB4gmwjnbB2C+zk7CYOu1ZF9kfG60RATfk3aWoHRk2Tw8OUbYfCe2sA5dtiB8WYhOjxaJS6SOHaYBezRCenfiAnwSA4crIDcm1klIanEw4xVdB9IkB9QU8Vw6icTCMO5MvZH90gLUWwnXi8VibPqNwi0CZg7BSbJOkrqbkzjTtb7uAWOzwAy2bT71pdr2y4mpTaElV8Yh9K+k5h5lAqa+uVJby9DoB97bxIocgCUgCJStT9yJC/bfuJ2MIv9mu7m26A3IBg9DMm9ndexKgtR9AVQhNE5EjdjV+3OQkWDn4XQgqmMzndBBmXkxmIy3Qwt7eLZdR5VIdv0MPx1iCzAY8kTPo9ZoBw3ibDG1HeSgNuM7U5Ntk27qDNtoALbIcA4EUKIZTYxwqoF0pA4PvoPbbdyU9hAlVY2lCc29HW+Q9EfHR3IKBiO7DfhvFxRSbEBBQTJy3NqUT9B1ZeibG2YnWD3Bk/ikbm14Gx6gChR+Q5qOT2sAFyqvCGX+kTlnLZ6u5NlkoAPWsb4NrJ29cRTzr9xKpMhC+GhkDF90JTxdQeLQB+AEg8k7x1gDieePEbWXLsY+sUM3bQJH6+7dtce2kAx8lwJxJ810N2L9DsLEOW5Heuk9Hz/28QVYf2UzbVTAXbFvtgbyjBtjkADJT3n9xQ2GcMGH6F2fqLrajxazocD+yom5PQbJAGwBZdMikxLzitN41whWDuJQ2OT2R1jdJYJ7pA9EDbgX0szXLwCLHb89s8QuOLzyZZLiLs+LLpduy5+ZxVZRzNfi/EJzkSD+w3DUVTMzg09gkt9sa4MId5tJP2HiXesVmSq/G5SESAqo3khobsyaf7BIcuoVL/JIXaOckAGX4YZl5nDZc0vpoxLpl+kHn3yhXOfAf9l9Bwg4xTq0FmIFwHjAwWdcuANUP2EI6zLm9Hxip9pwzZNWouClkgE5c5cYu+AcCoV2+TVfrTFs7Yt2Dan/aQQ4oCy5E/dOwV3TO1YTybTiem0I0irWeHk7f36vKvDc4Vuq4bwauTkE8+zxAN1aZITlpnPJHy6xaJMBvDj1HblyJDGv1WckVHY5/yYoAxuO4cb8/Qm9uhJvwXNrldQImYCyA0CD4zN2G/yVnlEXF5S/EngLeJAwYyAOiClfI3QviydoLYb64wKqm+rjAG53I7ucWWQ341/JWtZBsTv7nOnNt0td4l4T43qSDlBd2QLjwiT/LkgNQw+fwAvneTnwb86jDRv9c47rG4u/NZYyFQU1g10eaGBes6w9AT3H4nTdG0f2HyDsz9E/f9hBwL2O0IK9ejCug8DRovJHApdO4U3dLmt4uendZu4PJrgt9i6ttvu93tLj94ueFdjkEoL2BSojXVBrPvzXYw84FdHw0ywAsPIDYwIXxAtjrVi06wCug2XW6aD71q4NeYsVhZnHMp87CpfPLPctdPO2O+jZsYxmr01wos08gBCASgDIua1VaxsOChQycG5ALPUxxxStOb1bDGekjBhRQgBrqIekJB4ObATMaAISEltVI3CyGYENA35zBPLIdD4dhHF43Uu+p2Lmx+MTnANBLge+kWK2GjFT39ImnyBKHeu7HFnj2CRTx1YWvRMOPiYRILW67ZksS77Y0gYfztpPHRLKqEqFx86tuwXscnhT5jYBEkcsABmsQMt3xbeQ84GOSuyyxQLqc7a3loxGcjdgjuSv0aMr1jnacYRKjJFMckXTNqJerzBX+LxLXR4dfs8tTlUhW/HqILNcgkDtocUdvNfz0iTnI4csXJcJKv9IozY7F6h2gJvynwD2++6+xehrZ33ErbRubLPbEp3gEW6Hcs3deujzlfCknk89elPz3wjmc2XCsMNki+dQA9qfcvLpUOhy4AdZA0MeQWTH9gZBvOvQtJncOvnvO6xs/aPr8ziw4s5+tCeaJ11rDUis20OnIdbPamvLP23pPPtx725zLK5aBExeuAAsHRu3qmVSoV6P4yZtxrI6ZHJ7g4PE7nDGCuojG3vqb0Nvmz/eUfUoVItVkSaQ6wU1gFo7UGJAAJ3AYAMGqIB6A3elt/R4CKwZY4THOvGseGElVJE0HJwzTU0OSXDsmRAKUMbNjdmQBq06Y/838Cc2Rj3fKQaShKFtR3dExGiO7o/r2SvRIIbaAIvQ4NGQiQyE7e38nY74Zr++jMfn7c/q1KPjh5KVQm0Nr6EGL5njnP2qtT8TuhfDvsNU78cAu+3fJDQSqg4D+/Eg/tACP8OFzI7hfAFINiBy8hTQK/uobquSUDgOO/COgf2aD5Ix8+DHdFcx0IGB7+tm+/QCwf+X8I6xiLfakhnFe58E5/p/KlWKHP742XOaVsBltEv9QAr/4x1vBP+mu/nJ/H25+nzryXH+C/PH8DLgNM731mPLfPn20DfuXAgSEg944lwIgbQHHBFhVIL3xIYPfEIGr9IIP4HZAQEeezehWoR/xoAX/MzCFFsbQ/zf9O/Rx1gADHehiKFMgOMEgQVSEcjHdvnHwRCI+AOwh5x49AYFOgyAgXxQDbYV/3P099QcgoA8A5AJIACA6Gy30WCP/0oAAAsPz7Ys4KVSzwDCeez0hsWD/zUgN/JVjFZTnKAAkdOMQQ158PZOQPkdPHTOGL8sbAIlKZ/YC+F8dstFylv8i7D7BpYlpCWWigiwW3lU1eoer2cAmqAQIoAhAoLzYgsXF7EmIkIJfXPR4A+QFhNTcdyD2VjlT+C29lnD41kA/jO2jq8B4UmHmp0FAJxddhtN1whoPXHtS9d2DRZmpoeTHQJ8EhfH/m/EuA1hSyCTaHII0CSAAoNp8igoAQsQCFOGh857QXIKf8E9BpiaCaAN0GTYr1dAWW9cBEun4lqgogTB8KPS4DOBANWfGA16PFgROtEfM60BUJnc829w1rTAA/UHOElzMMcfL6zx9BPaiXfMBYXyBzhI2JJXiBZAc9BtB8kFojTxugb8C5h9YU7Xc45XFizpJpcJ7yfwhwGwCRA/gJmEJAfUd4O+ArAFoDrYG2VCHborvbjz5cObaSFUh7nOaVYtN+TA32Cq8GSyO4+AK9Fsx0DNAUZgYJPYOecqfZZXEhFkNTXIBNUagBeMoIO5hP0HLE2wpBTwWbS7hZXRTG8hpcd4M+Dvg34P+DM2bUTl9HgqvCs57XHCEddFmMbxoQ8VGgWNw84IuwEAwASOxyJl0HVxvQIQi0mhDCiJGHiBjeOAxeCxwLhx/tqoAPTRR+5UNUWIgkXK3zgxQLYB3o+AIyBNhKQEBEE5WlLZnODaiFfE44SQvb1gtLKU8D2UtvE2EcgzLB411AXrDlgd5mQWLnmF0OaHExREIASWYd4UXqDCUk3aoFvQJ4Rbg9h6OYywwVAnGrmCdGDUJ0eksveoN2UHjYizjBB1bgwG45vZGHlVG/deESAKuFcBCCs4CgEm4blM0Jn5t2F3Q51HuN3QC5t6eFm8gocN7mQgPubGw/M+LBQ2fUlDW7T6c1Dcj2eUPmKj1uAYfSYLA1pgjfAhYiAWAFY9UfC8wst3ENZmWpNrXj2hVNglDUolfrAigo5smaeFBDCvBEMuJBnU+HAs0LBKngBMQVSWLBbSRbFQt4QtZ2TZq4ekI6BIIQiwmhVLEixExZLST0fdYEWXDYklEdixRhOLPMhk81nVCySIecHg1XpaLXpUOJW+PZWIwRIRyDEhMnFcDbZHjGB2nZ2ANr2m8TEWbztJhwgWErc/wa8Pi9EPBt0asxvKGA1DFrHqyCsBEAayYjlPMK3rcYgCa0/J2I2a168xwAt2WtKzVax3DMAaanWZbILaxnDNDUfCh4wAaH1o9PlBQCOt4ffwD+VZgzcIWCj8Od3KCb3f50PDPrZDU8UpBN82XENPUmFcDwoLdxrJxZZwxMidwrgPMiSXdeE2gVgQBR8wnIuUIoB96WEPccKpIXzstXYQMO09yggp1etQoukku8JIkMD1cWrGIBAgZwZGG4R3wLpBCBslZ5wjhIoWT2+9+I8r3KdggKyA8cE9PQN7FzuXMB9AAaBGioibwHk0vMfQV+lwBEgAAHIQgbqJAhko37yxxypZoIGBhvCdWHNZInTjp8F3Cywlo02ZbAXYR3dqLlMxJXSH3lR3dqJYjhI7qJA9WwbqNShzIigAA9eguFk8izIuaNjoh3ds1acWg7Zzog4XIn1es7rOKP9CIbI2hG9Fg86PICDgbyMKtWLds0Gi4vM73ui2IiCM48yorHEqC8JUyJ+iaAP6NjpODJ3BSc4mRKwu4PooWimiEWLyMuipDLp3HCenFQynDIJFSN2sdDSCi0jDrOH0sV9I5jyg0zzXCW3Dpox8GqAsifpGRZsfNFlx8TwoHT+sSUB1kfB5OT1UU1G9b0lYIbQMQkuDIAa4OsIV4ShF04rBDiLCiNwLd1xDnYR9iTdpcFmO9QJdWgHijAwxACBCQwCWPaAVsMABljziagFgBTwTiLdD/I8Hm2cILU3WfDXw3JS/Dt2O2MrI6QqCwZCcIMCzxQkrSa3BiZrW2Op0uIPiJSj6AI3AC5QqLd3BdUhY3DhF7whwSIhzgyWJWxpY7ABuDt6Q5i4xnyYmAac30EBWFg0Af2Ivh2ojQGWAeomV2oBEAfqNtiQIXi0ICiICuKrickGuMAg6GBuJ2cq0UV3FdiQmMJUwUwqhAhNjQQIC4QjON2A0pbRQCBZcSoP6EEAZ2DvnCCbQAiISFZZYOD8s0yBdhewE4mWMAikTMrCWUsFZII7VUg3MO2VInPkkLD38HL3P5tlMsNoiIlUITHBHlHayGc/1L5kXCvlXSJpi2BDfCR85gi6y3Cvo6aIEE1ghDS5jjwmyLQ1cWOOnWkIlU3VvDkEVBFtjeA78M1iO2MGP1cIYyOMLcsYq83ATdBZY1uD+AYMS84hQI7yggf2RQnuINAOnRTBSAwOHIceiDLE8IgQuryIgOLQdmoc0I7k2gihOcaXDE6EunWrwxErpB2AKQXACZh7ianRc51oPgGdDB42MEngMQZ9yIoTYEMi8DEILcB6BkRIsAWED5eB2OJ7hR4UcCeOV4SOJTpXqFYSiwSEw+MhQSu0Ug6jNryHd1xTiHu9vOV/0hj/OZAHupkEkMEmgbCD2Oy9StbBPSjxvHxOrpV3JmIRYiEqGwj0LFeHAconYmqQC559UyyLCuuFcjCCSrPMDIRIgkyivgVUcigudj4uAQMAPyQMDXxbJfAGlEKJC0n/I9FVSNe0wAO40pjYfKYIR8AEwyIZicrHcIUkEYnKAfMoE6yMB1bIvmMTct4k4PzwmsY3FtjFYI2Ng5HwgsWf1t4hjySUjQHPE/CuE4SwpxRLP8J9iAIwfmUtgIhIDUswI2iyFCYEAGKK8XPEMHsTvOLiB/Y2iCy1dVc4AeEOJQMRMNDJG/bZ3AicExS16UDuDJKIgvuH7hYIV4luK3cCI3nDej/4NtkrDR/fnX7ilE0kJk5nyLhGHjwTfLmcBjEykDeJvQl5kyjWXYcEldwSKCC0SA1QGCDsmKEoxVxMjKig6kQwQqNU8dCGdjzAD7RKDIRHE741gjeYchw6sE2dWwzYJAZAAxwwAUtlHJ35L0JXBUcBEA00h/QFh7Dd0NFHtt3LKxTRCHKVsLYAnXI+LoNMw9tRCcjRNIOxMNxXEymTirUp0TAFvWNFmlugfpOmjBk3GLVDcqbWkuZUEqJM6oHYuCJilkI3ZPhDDideF5JVQfwEqiigHhTQShI/d0isxI0OMxleSHIELiKACNOCtxLYGPK9RrVSREi0oqTw4ivU7qyGDZwmHinw+UZ4G/idI6mPA0Zgljz6SCEqGHF0nWQfksiNgsZKWcdgtRDBhGxDSy9TkEx2LQs3w92MVE+01KXuDfYzjX+Srk4OPRCVFU5PCD1LKdMBSrw9oUzkCWelMGo2YwS2edVoVHD+EEgGIJZtcwf+ystiwLUUnTp4JNzYBh8JAGYB5EsSDRTvIJ5NeSGojQE6iD5TSEWwx4/1H8kv0guNmBrIbPVlk0cfADzAqjep2/IKoIiNctAYMSB5SvjYID1TRxJILRNswk1Ivi2DK+KHcaIpdVtSYk0BIRZG0yXXLIEcN5m2MH/SNN7To0nNLjSC0hNKyACAcjLTS0ErNMDjRI6dPzTI4wtI9IrtF9RR1enOKH6dSYj+JeUxnFIArTmBZcO6S0eXpPmDGY/DKvMr6NBRbTRkjxXGTYEpcWrBoPAdLWcQyNBOpY9kvtLCSg4pdI4yho1+LnTHjBdJnS5pUUFuT3k7dyipn0zjFe9jiCtg9Ewg4YFXSaSa+P4B14n0OkFXE+MOtS6IrHBYD3IrLDWtFMkOiTxXUxvSJCU3KNOzTY09jPEizMriJ25k4riBTcI4tLKjjtomNPCS801LIzSscbqwHlBMGLzwTUAfxIB85BEJK+SLQEjHGxtuGKSDSfMvCLNSj+ChTySzkqeGMshM95nUjNI8YLo8f4qtJXDpM2tNkzHUhFmI0hk9YJUznzU8IJ8lAosIHg1nY4M28xINBNh1Ykq8zmzcY/tL9Sk3IdJEsc4ICPQA18DRAuTAYHbN04w7RiCMy2MkzOKzrvAEUyjyIYmGZA5Q1jjYJ4U4iNLhmw9dLczvM3CL8zTOZZ0Cy/LYLNIwPzL8XrSGQSOSGSQ0sNPldjxdNLeyarJLMKyDXUzJKzJIujIWgk0xp3Rycs/HJDAWM+f2oyUs+NOMgEaXA3xIzmK/hG17pQKyqcIgqII0sgs8sNmTC8asL2VSYMSHnMwndxLJhiFdsIcpSLbROUJeoL5JuTeuUcIGdwfNSLeVxM75T0j/4ybPpjpshHKSJfyDmLmdFsgT2WyO0od28SqsvxJEwAuQJLqzh0r5Kezc03HNeywQq4hoB3YaQIHTnBWONzBOoClMswkEQgHbEfs/xKzZ3iXtKCSiOJ4R1hHcmByWAZ4pRHwZStKIEMSbQcJX4AmUKPPNURIYjXn1Y0ACGFSDIA402hiQQkD2VaUn4luV7Mh9NlyNcfUGTEjdUULgynE08Bglwgd3PRtYctvKr5zQG5AvhrEv/WsCMs9oWjDTMD5G4525LLjmVI2XMj0TeiA5HIiKAWdilxmne8DTICqL2mgyNgVFJHysUsEz01TIRyBtB8+eQGLzS8luPjoDkS52HZnjaJXzw0IIHPC96LPVT1yxZDvJw570nfNjDfzKgG/SoIUiLliicA9h8Yw9fAGD1owLCBAKAiAJH8gQC2HKJgQwpvL5S7cB2MSAsEb7Iy8OsZXQpUj/LAAt9aHefXu9KQBcFWCuAP6FXErEMUGepOs9BE/gEwEE0eMCkznOKTEmLKNdCB40kK0JSwJ7lEI1BDAo1TQ3PSAojD4pDOulT441IekCFdIPNTvXP0CwyUggiArd3xT8XCyzo6aJfzy8Dd1ahQ0ip1TSKrSjOxzjMiJLxzMcgnLpzWoYnMoBdCwN2Yz8s6nJezachGnMiJPJdN9yVCOHKULtkFQrWt1Cms15JzIkdVyiGQAPLAB2xFHJ0K8o7gGCL2xLuK9SOkcwuTSuAduMiKuYQPPi0YiyOJAgEodrE1ggyFXCFC486mE6dLtbp1fV+Mz9RJjmk3a1OAnEKfBSAnENXN/jq0npKmzgE4yIiydwxABc15syBKQ1VM9tJsNS3B8S8SMcl3PjClko7LO0QgAf1ZtXI/1Pp4QkmOLlRUc6GQSz9C1jJjyQ42jNMgLClNNJyRim8MpyZ3HHNwTcs7qznTwM67NAjAYAFIiTG7aCLu47Qn9hcydU6eGiA5o9rPByOIYnCIiMqQHIO42vWgwzDXXM+LwVTUvMJekfXbnNoi4C0LK8KOirosOyFgBrx1AqgBymty3YdrALFoYATCBTehZ4K9THnK3OVjbcs7PbAyckwopyIXQwqKzbYotNUihshovGypMmxSASUfNorrpxAPniLg1bImANy+PBZx5iJkgilkF6eFfD+weDVyInIyMPK3yAeSg4CQJGfF223p9A1n2vBdIdNkVxJYFNjlKaALgClShWWnR10fMX1hGIA2X2BexUuWxxKgcOPcEr8vAHCBF1TwUPJ1LoZfUvoY6dIJKwATS/1hEJE5XpFZ0PkG0uYA7SoXRF0AiMPB39iI5R330leHDiTBQyucBSAAUOnUSA4y+0uF05wIKRtZHCVMpTB0ykqCClXHefjy83TDUofQjA69Xn5pcc4PXAzM1tlLgyuWoPviMM/WBLxeSYRBdL8EvCWlLEWaEBdKeSVqGdLa7Lsp6wey7ktrseSElGCyhWBizGUV8eQq5RtUBaA7La7LgHVMNAKry6juRQ0t5QRYj5G9KKIM0u2EkIP0sNj/2YYQpTTWSAGeBzqdlEHLm2bNlXLIAdcs3LEgbcoKRt2C8u8h9ynaEEIfSvXViATy+A1MQE4uMueBLHCgFyERYuMp99hLL8t6g4ypxB9FPC0CpTAnEW8qMAZDSJCZg6S3a0uAqPCmJGztIiTMY9aYmtO1zWiuTKPweynAP7LlM3oqWzeYgigBt6JEn1oDSy3NgIhXI6XGgJSMTkum0HbXqHXKqNMXjtgYgC3AziRNBCGQh3bU108D2K+rSlShfKXgz85JcXzN5UhGzTzKEyyACzL87ITRcl05JX1NtVfcqFZ1tNI/UxBZAbej0h+EXXz0caACwV80z5O22j86RHlUU1L872BuQBEEciGls1PMCUrn/bWSHB1TETQrLugxEPlwdEj8vNxi1YQsBLkMhg1G00MyQuoL8w4T0vASaPGSCqE9VBiUQXyg70qpSDTGO7LOSqGFoqJy2OlpDsqwrTtAwq9OTdBEgdctSEyXaSsSAQgLw2rR6gMAFuA0AMAGUgcgMAA+A20DJBSBagKZAAASEIBAgckWNE+IWVMOJKrfTUcvKqQgSqpIctrfGOu1SiomIEzpwyouEy1I04DGDTFJcNIrNclkqMinyZMzHMMABbIYrjcpitjIRSlnyhMV8CKAEwPRVkwtMuIf03cNRjXAHGMiWTAnPJgaqTHmNezOGGq4pjL00zgvDXNBuNlNLVNkgPikjGwKYHYbE25TIf6QCRGCMhAVT2sIMpNh/qtiCp97AzeHbUpzRsuddRClDJSqJC7tXSqIS+43IBcKo6pOrjFIiqpiuksiuaKKK6FmSwPMHLmkCu+fj0CxfhEJEFL1CQFicw4sVzESxDAYWush1HNrESkQWMsWuIs8LTEqSdMdABIATgW4CcQ/AVQFEA0gWgBSA0AZ4HqABAPwFOALwAQD5RAgAQGeBngS4FoA/AWoD8AzgBBG6B3MPWtEAGAS4BIB+hWoBdr6gWgEaBaAZ4CUA0ARPluAGAeoFuBTgFIEuB3YFIDIQUgPlAYBaAJxHoB/aiAGvKRg04CTrIfOgFOASAZ4D8BpUD2vtrngH5nGqYeeoDQBg6gQHwqSAFOvrq/ABWx1rlagOSE1XNY6zLE9MAuqgAMcM1UoBSAMzVYdx5LWpWAdaw1C6QkAWwFVBrAugDyJqYGSkkj5JUvjiKukDhCZBaAVet6JbAc1Ayl/CRJByQkAJEDeJpIcyDaQd64oj3qckQMFoAbASKF2QGALX0QBBQNCHNRZ5Z+tfr36jAHcAJfX+p4YTqCKAvrckIBo/q45TwQwBwGsoH/qoG5+ueJm9aNAcBpAagjdRHUXJFrB4gJBvQwvSaNy4Bq0TpEXqjULpCjAMsf9GKREi0BvPQaG5Br3qqGnJEd8z63es6RckN9mWCg4N1D/kl4A20gBuo3wBGACAMYEKhthC6AoBuowjjvBNIYmBexvWGvSBF/UIDgoUVAXdLzhr4ChNvjNgJQA0A8Gqhq6QMIT0F4g7GRIs7B5wSqK81e4cimEaN0IAmQBRGxPi2d7OCrLFwuoBcBvhhEJjhkbM4NUoLynwORptBZ4e9Eo5dQKQEvCTQlpwCzjGthoQo3UBS2UwEmo1ByQzKy/DOJOGp+u4aukc5AmBlwJBroaSAN1ANtO0I1CmRWGuvDybs3VhxKa3UeRCDF64ZhrSbckDhsgarZWpt4bc8HcAEaB4JeFsDgxQjhcgYgfjDIolG1VjxE1ko5IZ0FACTljDvsqyuLAZobnmIZH06QRs0uYSQEuRMEUkICJ0ccIEilGCc6XOYaAmgAcqaSNpq6QkmxIpSbIIa5oybRAYsmybH6ykGqbckApsTUPAYpu3Y3UIZv4bOkKpoobam5hoabEiz+oTjoMQlNIBHmjpvl13m7po1xkRJ8Eaa9kBONeJlARzOQBTgXwzAoAAUlgMsIOcBgRiAtfB44KImaTpVoQDCCAdCOa2r8N6gfFqMaPm0xp3sLGh+q6Ri0QHlul0WjjW2ljiLbyGwkAZr1KIL4DWG3A7UGJsxaqAKKhpbpAIBxZbam25q6R7mnCEeavms3F+b6GrpAGFoRCpq7REkCP0vqCG3AFsAmm+OT6bEivwGeAE0WoDDrI6tAEtrjavlBSAGAFOtqBLgMOvDragd4CjqrOEgGDqvatAD5Q+UWgGlRHkVQGeA6i78lqA0AFQDuABAJxC9bjGnJFNbbARhtKbEi2oFuAk2vlD8AGAWoHywfmU4CLbC26NsuA/AF4BDaUgC2pSASADOu9rqZOOpOAI2wNtuBRAaOueBbgPlFuBXEG1AEBGgM4HrrU2vVo41oWrFu4CuCs3FJQs2mprYb4KGjMji23LgEob0mggExQPAW3CNIuUc1B99amoVuLRZwSFv5a92oFuqaykWFiIjbrGaPyd/sjlhXb52kxudQuYX/2adSGyACc0D299qPboQE9opSP2yCiobu0c6gMAZkXWsLrx6tgA0oqQZhvHkR6pLAg6oAQzDMT8kaUXnQqQOesVqjADJDTapEw0Bg5WxXwA1q9Y/us9klGDgHqBwO4WpQ6tYNDrhEqQBDr0AgAA== -->\n\n<!-- internal state end -->"},"request":{"retryCount":3,"retries":3,"retryAfter":16}},"response":{"url":"https://api.github.com/repos/OpenLake/Student_Database_COSA/issues/comments/3820089225","status":500,"headers":{"access-control-allow-origin":"*","access-control-expose-headers":"ETag, Link, Location, Retry-After, X-GitHub-OTP, X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Used, X-RateLimit-Resource, X-RateLimit-Reset, X-OAuth-Scopes, X-Accepted-OAuth-Scopes, X-Poll-Interval, X-GitHub-Media-Type, X-GitHub-SSO, X-GitHub-Request-Id, Deprecation, Sunset","content-length":"0","content-security-policy":"default-src 'none'","content-type":"application/json; charset=utf-8","date":"Mon, 09 Feb 2026 16:23:32 GMT","referrer-policy":"origin-when-cross-origin, strict-origin-when-cross-origin","server":"github.com","strict-transport-security":"max-age=31536000; includeSubdomains; preload","vary":"Accept-Encoding, Accept, X-Requested-With","x-accepted-github-permissions":"issues=write; pull_requests=write","x-content-type-options":"nosniff","x-frame-options":"deny","x-github-api-version-selected":"2022-11-28","x-github-media-type":"github.v3; format=json","x-github-request-id":"A03C:51096:EAFCFE:3EFD3C7:698A0A03","x-ratelimit-limit":"8650","x-ratelimit-remaining":"8587","x-ratelimit-reset":"1770657425","x-ratelimit-resource":"core","x-ratelimit-used":"63","x-xss-protection":"0"},"data":""}}

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: 17

Note

Due to the large number of review comments, Critical, Major 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 (9)
backend/routes/onboarding.js (2)

11-11: ⚠️ Potential issue | 🟠 Major

Avoid logging the full req.user object — it may contain PII.

console.log(req.user) can emit emails, names, and other personal information into logs. Replace with a scoped log (e.g., only log req.user._id) or remove it entirely.

Proposed fix
-    console.log(req.user);
+    console.log("Onboarding request for user:", req.user._id);

33-36: ⚠️ Potential issue | 🟠 Major

Raw error object is leaked to the client.

Passing the caught error directly in the JSON response ({ message: "Onboarding failed", error }) can expose stack traces and internal details. Return only a safe message and log the full error server-side.

Proposed fix
     console.error("Onboarding failed:", error);
-    res.status(500).json({ message: "Onboarding failed", error });
+    res.status(500).json({ message: "Onboarding failed" });
backend/routes/feedbackRoutes.js (1)

52-52: ⚠️ Potential issue | 🟡 Minor

Pre-existing typo: "ture" should be "true".

is_anonymous === "ture" will never match a client sending the string "true". This appears to be a pre-existing bug, but since this area is being touched, it's worth fixing now.

Proposed fix
-      is_anonymous: is_anonymous === "ture" || is_anonymous === true,
+      is_anonymous: is_anonymous === "true" || is_anonymous === true,
backend/routes/profile.js (1)

110-111: ⚠️ Potential issue | 🟠 Major

Remove or redact debug logging of user-supplied data.

console.log("Received userId:", userId) and console.log("Received updatedDetails:", updatedDetails) can leak PII (names, emails, phone numbers, dates of birth, social links) to server logs in production.

🛡️ Proposed fix
-    console.log("Received userId:", userId);
-    console.log("Received updatedDetails:", updatedDetails);
backend/routes/auth.js (1)

232-242: ⚠️ Potential issue | 🔴 Critical

user.setPassword() is a passport-local-mongoose method that was removed in this PR.

This PR removed passport-local-mongoose from the project and switched to manual bcrypt-based authentication. The setPassword method no longer exists on the User model, so the password-reset route will throw a TypeError at runtime. Replace it with bcrypt hashing and a direct save.

🐛 Proposed fix
     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; // pre-save hook will hash it
+    await user.save();
+    return res
+      .status(200)
+      .json({ message: "Password has been reset successfully" });
backend/routes/announcements.js (2)

103-105: ⚠️ Potential issue | 🟠 Major

User-supplied search is passed directly to new RegExp() — ReDoS risk.

An attacker can craft a search string with pathological regex patterns (e.g., (a+)+$) that cause catastrophic backtracking. Escape special regex characters before constructing the RegExp, or use MongoDB's $text index for search.

🛡️ Proposed fix — escape regex special characters
+    const escapeRegex = (str) => str.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
     if (search) {
-      const regex = new RegExp(search, "i");
+      const regex = new RegExp(escapeRegex(search), "i");
       filter.$or = [{ title: regex }, { content: regex }];
     }

4-9: ⚠️ Potential issue | 🔴 Critical

Fix broken imports: Event, OrganizationalUnit, and Position are not exported from schema.js and will be undefined at runtime.

These models were moved to separate files but the imports here were not updated. The findTargetId function will crash on line 18, 25, or 32 when calling .findOne() on undefined models.

Fix: Update imports to use the standalone model files
-const {
-  Announcement,
-  Event,
-  OrganizationalUnit,
-  Position,
-} = require("../models/schema");
+const { Announcement } = require("../models/schema");
+const Event = require("../models/eventSchema");
+const OrganizationalUnit = require("../models/organizationSchema");
+const Position = require("../models/positionSchema");
backend/index.js (2)

68-70: ⚠️ Potential issue | 🟠 Major

Duplicate route mount for announcements.

/api/announcements is mounted twice (lines 68 and 70), which means announcementRoutes handlers will be registered on the same path twice. This can cause handlers to fire twice or produce unexpected behavior.

Proposed fix
 app.use("/api/announcements", announcementRoutes);
 app.use("/api/dashboard", dashboardRoutes);
-app.use("/api/announcements", announcementRoutes);
 app.use("/api/analytics", analyticsRoutes);

40-40: ⚠️ Potential issue | 🟠 Major

Hardcoded session secret.

"keyboard cat" is a well-known placeholder. This should be loaded from an environment variable (e.g. process.env.SESSION_SECRET) to avoid session forgery in production.

🤖 Fix all issues with AI agents
In `@backend/controllers/certificateController.js`:
- Around line 10-148: The createBatch handler currently has no try/catch so any
DB or runtime error will crash the request; wrap the entire async body of
createBatch (all awaits like User.findById, PositionHolder.findOne,
OrganizationalUnit.findById, Promise.all user checks, CertificateBatch.create,
etc.) in a try/catch (or use your existing async error-wrapper middleware) and
in the catch log the error and return res.status(500).json({ message: "Internal
server error", error: /* brief error message or id */ }) to ensure unhandled
rejections are converted to proper HTTP 500 responses.
- Around line 116-128: The validation logic inside the users.map is wrong
because zodObjectId.safeParse(uid) returns an object, so change the check to
inspect validation.success; in the block that builds userChecks (where users.map
calls zodObjectId.safeParse), replace the condition that uses !validation with
!validation.success so invalid IDs return { uid, ok: false, reason: "Invalid ID"
} and subsequent lookups in User.findById only run for successful validations.
- Around line 44-48: The code logs position._id before verifying the result of
Position.findById, which can cause a TypeError if position is null; update the
handler in certificateController.js so you first check the returned value from
Position.findById(positionHolder.position_id) (i.e., if (!position) return
res.status(403).json(...)) and only after that call console.log(position._id) or
otherwise safely access properties of position.
- Around line 18-20: The authorization guard currently lets users with no role
pass because it checks user.role && user.role !== "CLUB_COORDINATOR"; change it
to explicitly deny when role is missing or not the coordinator, e.g. replace the
condition with if (!user.role || user.role !== "CLUB_COORDINATOR") and keep the
existing response using res.status(403).json({ message: "Not authorized to
perform the task" }); so only users whose user.role === "CLUB_COORDINATOR" are
allowed.
- Around line 88-112: The code dereferences query results before null-checking,
causing crashes: after calling User.findOne for gensecObj, Position.findOne for
presidentPosition, PositionHolder.findOne for presidentHolder, and User.findById
for presidentObj, add immediate null/undefined checks and return the 500
response if any are missing (use the same error messages), and remove or move
any console.log calls that access properties like ._id or .user_id until after
those checks; specifically update the logic around gensecObj, presidentPosition,
presidentHolder, and presidentObj to validate each result right after its query
(and only then access ._id or .user_id).

In `@backend/index.js`:
- Around line 29-31: The current connectDB() call silently continues on
failures; update either the connectDB implementation in config/db.js to re-throw
the caught error or call process.exit(1) inside its catch block, or change the
startup flow in backend/index.js to await connectDB() and wrap it in a try/catch
that logs the error and exits (e.g., catch(err) { processLogger.error(..., err);
process.exit(1); }). Ensure you reference the connectDB function (connectDB and
the connectDB() call) and the catch block in config/db.js so the process fails
fast when DB connection fails.

In `@backend/middlewares/isAuthenticated.js`:
- Around line 39-42: The routes import is receiving the entire exported object
instead of the middleware function: update the require in auth.js to pull the
named isAuthenticated export (so the value passed to router.get("/fetchAuth",
isAuthenticated, ...) is the middleware function, not the object) and remove the
large commented-out block in isAuthenticated.js; ensure the module still exports
the named symbols isAuthenticated and jwtIsAuthenticated so other files can
destructure them.

In `@backend/models/certificateSchema.js`:
- Around line 6-9: The ref string on the unit_id field in the certificate schema
is misspelled as "Oraganizational_Unit" causing populate('unit_id') to fail;
update the unit_id schema definition (the ref value) in certificateSchema.js to
the correct registered model name "Organizational_Unit" so it matches the model
created in organizationSchema.js.
- Around line 62-68: The schema-level unique on certificateId causes MongoDB to
block multiple documents with null/undefined (so only one Pending/Rejected
certificate is allowed); remove unique: true from the certificateId field in the
schema and rely on the existing partial index that enforces uniqueness only for
approved certificates (or if you prefer the alternative, replace unique: true
with { unique: true, sparse: true }); update/verify the partial index creation
logic (the index that targets certificateId when status === "Approved") to
ensure uniqueness is enforced at the index level instead of the schema.

In `@backend/models/schema.js`:
- Around line 110-114: schema.js now only exports UserSkill, Skill, Announcement
which breaks many modules that still destructure models like User, Event,
OrganizationalUnit, Position, PositionHolder, Achievement, Feedback; update each
affected import (e.g., backend/routes/announcements.js, profile.js, orgUnit.js,
events.js, onboarding.js, feedbackRoutes.js, positionRoutes.js and controllers
dashboardController.js, certificateController.js, analyticsController.js,
eventControllers.js, middleware isEventContact.js) to import the missing models
directly from their actual schema modules (e.g., userSchema.js for User,
eventSchema.js for Event, organizationSchema.js for OrganizationalUnit,
positionSchema.js for Position/PositionHolder, feedbackSchema.js for Feedback,
achievementSchema.js for Achievement) and adjust the destructuring names
accordingly, or alternatively re-export those models from schema.js if you
intend a single entrypoint; ensure no file destructures undefined symbols after
the change.

In `@backend/package.json`:
- Around line 34-37: The package.json contains unresolved Git merge conflict
markers around the body-parser dependency; remove the conflict markers (<<<<<<<,
=======, >>>>>>>) and delete the "body-parser" entry so package.json becomes
valid JSON; ensure the remaining JSON is properly comma-separated and valid (no
trailing commas) and that nothing else in package.json references body-parser
(index.js uses express.json()), so run npm install to verify.

In `@backend/routes/analytics.js`:
- Around line 25-30: The route registers middleware in the wrong order so
authorizeRole runs before isAuthenticated; update the router.get call that uses
router.get("/club-coordinator", authorizeRole(["CLUB_COORDINATOR"]),
isAuthenticated, controller.getClubCoordinatorAnalytics) to call isAuthenticated
before authorizeRole so req.user is populated (i.e., use
router.get("/club-coordinator", isAuthenticated,
authorizeRole(["CLUB_COORDINATOR"]), controller.getClubCoordinatorAnalytics));
keep controller.getClubCoordinatorAnalytics unchanged.

In `@backend/routes/auth.js`:
- Line 13: The import in backend/routes/auth.js is grabbing the whole exports
object instead of the middleware function; replace the default-style require
with a destructured import so isAuthenticated refers to the function exported by
the module (e.g., change the require to const { isAuthenticated } =
require("../middlewares/isAuthenticated")); ensure any other references to
jwtIsAuthenticated are similarly imported via destructuring if needed so Express
receives actual middleware functions when mounting routes like /fetchAuth.
- Line 64: The jwt.sign call uses an undefined secretKey which will throw a
ReferenceError at runtime; declare and read the secret from an environment
variable (e.g., process.env.JWT_SECRET) near the top of the file and use that
value for secretKey in the token creation (update the jwt.sign call that uses
secretKey). Also add a small guard in the auth initialization (or before issuing
tokens) to throw/log a clear error if the JWT secret is missing so the login
route fails fast with a helpful message.

In `@backend/routes/feedbackRoutes.js`:
- Around line 188-211: The route handler in feedbackRoutes.js is taking
resolved_by from req.body and logging req.body; change it to derive the resolver
from the authenticated user (use req.user._id) and remove or restrict the
console.log of the full body to avoid sensitive data exposure. Specifically, in
the async handler where feedbackId, actions_taken, and resolved_by are read and
where you set feedback.resolved_by, stop reading resolved_by from req.body and
instead set feedback.resolved_by = req.user._id (ensure req.user exists and
return 401 if not authenticated); also remove the console.log(req.body) (or
replace with a scoped debug log that excludes sensitive fields) and keep the
check for actions_taken as-is.

In `@backend/routes/profile.js`:
- Line 87: Replace the incorrect reference to the uninitialized local binding by
calling the imported model: change the call using user.findOne(...) to
User.findOne(...), and ensure you don't shadow the imported User with a local
variable named user (e.g., keep const user = await User.findOne({ user_id: ID_No
}); so the imported symbol User is used to query and the local variable user
holds the result).

In `@backend/utils/batchValidate.js`:
- Line 3: zodObjectId currently uses a regex that allows non-hex characters;
update the pattern used in the zodObjectId constant (zodObjectId) to only allow
24 hex characters by replacing the character class with the correct hex range
and keep the same error message or adjust it to "Invalid ObjectId" — i.e., use a
regex that enforces /^[0-9a-fA-F]{24}$/ so validations only accept valid MongoDB
ObjectId hex strings.
🟡 Minor comments (5)
backend/middlewares/isAuthenticated.js-29-36 (1)

29-36: ⚠️ Potential issue | 🟡 Minor

jwtIsAuthenticated does not propagate the error reason on token verification failure.

The catch block swallows the actual JWT error (e.g., TokenExpiredError vs JsonWebTokenError). Consider logging err.name or err.message at debug level for observability.

Also, note that req.user is set to the raw decoded payload. If downstream code expects a Mongoose document (e.g., req.user._id as an ObjectId), this could cause subtle mismatches since jwt.verify returns a plain object with string fields.

backend/utils/batchValidate.js-6-6 (1)

6-6: ⚠️ Potential issue | 🟡 Minor

Misleading error message for min(5).

min(5) enforces a minimum length of 5, but the message says "Title is required," which better suits an emptiness check. Consider something like "Title must be at least 5 characters".

backend/utils/batchValidate.js-10-10 (1)

10-10: ⚠️ Potential issue | 🟡 Minor

Typo: "Atleast" → "At least".

✏️ Fix
-  users: zod.array(zodObjectId).min(1, "Atleast 1 user must be associated."),
+  users: zod.array(zodObjectId).min(1, "At least 1 user must be associated."),
backend/models/organizationSchema.js-15-19 (1)

15-19: ⚠️ Potential issue | 🟡 Minor

Inconsistent casing in type enum values.

"Council", "Club", "Committee" use PascalCase while "independent_position" uses snake_case. This can cause subtle bugs when comparing/filtering by type. Consider normalizing to a single convention.

backend/controllers/certificateController.js-15-15 (1)

15-15: ⚠️ Potential issue | 🟡 Minor

Typo: "messge""message".

Proposed fix
-    return res.status(404).json({ messge: "Invalid data (User not found)" });
+    return res.status(404).json({ message: "Invalid data (User not found)" });
🧹 Nitpick comments (8)
backend/middlewares/isAuthenticated.js (1)

14-22: Remove commented-out code.

There are three blocks of dead commented-out code: the Bearer token alternative (Lines 15–22), the debug log (Line 32), and a large block of leftover controller logic (Lines 44–84). These add noise and should be removed — version control preserves history.

Also applies to: 32-32, 44-84

backend/routes/events.js (1)

442-465: Verbose debug logging should not ship to production.

Lines 442–465 log the full event object (before and after update) with JSON.stringify. This can leak sensitive data to logs and degrades performance on large documents. These appear to be temporary debug aids — consider removing them or gating behind a debug flag/log level.

package.json (1)

1-6: Remove unused dependencies from root package.json.

The root package.json contains only passport-local and passport-local-mongoose, which are unused. There are no root-level JavaScript files, no monorepo workspaces, and no scripts that reference these packages. Since the backend uses JWT/bcrypt for auth (not Passport-Local), these dependencies are unnecessary and should be removed. Either delete the root package.json entirely if it serves no other purpose, or confirm its intended use if this is part of a planned monorepo setup.

backend/utils/authValidate.js (1)

3-6: Extract shared email regex to a constant to reduce duplication.

The IIT Bhilai email regex is duplicated across loginValidate and registerValidate. Extract it to a shared constant.

Proposed refactor
 const zod = require("zod");
+
+const iitbhilaiEmailRegex = /^[a-zA-Z0-9._%+-]+@iitbhilai\.ac\.in$/i;
 
 const loginValidate = zod.object({
-  username: zod.string().regex(/^[a-zA-Z0-9._%+-]+@iitbhilai\.ac\.in$/i),
+  username: zod.string().regex(iitbhilaiEmailRegex),
   password: zod.string().min(8),
 });
 
 const registerValidate = zod.object({
-  username: zod.string().regex(/^[a-zA-Z0-9._%+-]+@iitbhilai\.ac\.in$/i),
+  username: zod.string().regex(iitbhilaiEmailRegex),
   password: zod.string().min(8),

Also applies to: 8-10

backend/package.json (1)

56-57: nodemon and npm should not be runtime dependencies.

nodemon (line 56) is a dev tool and belongs in devDependencies. npm (line 57) is the package manager itself and generally should not be listed as a dependency at all. These inflate the production install unnecessarily.

Proposed fix
     "morgan": "^1.10.0",
     "multer": "^2.0.1",
     "nodemailer": "^7.0.3",
-    "nodemon": "^3.1.10",
-    "npm": "^10.2.0",
     "passport": "^0.7.0",

Then add nodemon under devDependencies:

   "devDependencies": {
     "cookie": "^0.5.0",
     "eslint": "^8.56.0",
     "eslint-plugin-node": "^11.1.0",
     "husky": "^8.0.3",
     "lint-staged": "^15.2.0",
+    "nodemon": "^3.1.10",
     "prettier": "^3.1.1"
   }
backend/routes/announcements.js (1)

226-226: Manual updatedAt assignment is redundant — schema uses timestamps: true.

The announcementSchema in schema.js (line 104) now includes { timestamps: true }, which means Mongoose automatically updates updatedAt on every .save(). This manual assignment is unnecessary and could interfere with Mongoose's timestamp management.

♻️ Proposed fix
-      announcement.updatedAt = Date.now();
       await announcement.save();
backend/controllers/certificateController.js (2)

11-11: Remove debug console.log statements before merging.

There are ~8 console.log calls (some commented out) that leak internal IDs to stdout. These should be removed or replaced with a proper logger at debug level.

Also applies to: 45-45, 68-68, 76-76, 89-89, 100-100, 106-106, 109-109


147-147: Return 201 Created for successful resource creation.

res.json(...) defaults to status 200. Since a new batch is being created, res.status(201).json(...) is more semantically correct.

Comment on lines +188 to +211
async (req, res) => {
const feedbackId = req.params.id;
const { actions_taken, resolved_by } = req.body;
console.log(req.body);
console.log("User resolving feedback:", resolved_by);

if (!actions_taken || actions_taken.trim() === "") {
return res.status(400).json({ error: "Resolution comment is required." });
}

if (feedback.is_resolved) {
return res.status(400).json({ error: "Feedback is already resolved." });
}
try {
const feedback = await Feedback.findById(feedbackId);
if (!feedback) {
return res.status(404).json({ error: "Feedback not found" });
}

feedback.is_resolved = true;
feedback.resolved_at = new Date();
feedback.actions_taken = actions_taken;
feedback.resolved_by = resolved_by;
if (feedback.is_resolved) {
return res.status(400).json({ error: "Feedback is already resolved." });
}

await feedback.save();
feedback.is_resolved = true;
feedback.resolved_at = new Date();
feedback.actions_taken = actions_taken;
feedback.resolved_by = resolved_by;
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 | 🟠 Major

resolved_by should come from the authenticated user, not from req.body.

Taking resolved_by from the request body (line 190, 211) allows a client to impersonate any user as the resolver. Since this route already requires isAuthenticated, use req.user._id instead.

Additionally, console.log(req.body) on line 191 may log sensitive data — remove or scope down.

Proposed fix
   async (req, res) => {
     const feedbackId = req.params.id;
-    const { actions_taken, resolved_by } = req.body;
-    console.log(req.body);
-    console.log("User resolving feedback:", resolved_by);
+    const { actions_taken } = req.body;
 
     if (!actions_taken || actions_taken.trim() === "") {
       return res.status(400).json({ error: "Resolution comment is required." });
     }
 
     try {
       const feedback = await Feedback.findById(feedbackId);
       if (!feedback) {
         return res.status(404).json({ error: "Feedback not found" });
       }
 
       if (feedback.is_resolved) {
         return res.status(400).json({ error: "Feedback is already resolved." });
       }
 
       feedback.is_resolved = true;
       feedback.resolved_at = new Date();
       feedback.actions_taken = actions_taken;
-      feedback.resolved_by = resolved_by;
+      feedback.resolved_by = req.user._id;
🤖 Prompt for AI Agents
In `@backend/routes/feedbackRoutes.js` around lines 188 - 211, The route handler
in feedbackRoutes.js is taking resolved_by from req.body and logging req.body;
change it to derive the resolver from the authenticated user (use req.user._id)
and remove or restrict the console.log of the full body to avoid sensitive data
exposure. Specifically, in the async handler where feedbackId, actions_taken,
and resolved_by are read and where you set feedback.resolved_by, stop reading
resolved_by from req.body and instead set feedback.resolved_by = req.user._id
(ensure req.user exists and return 401 if not authenticated); also remove the
console.log(req.body) (or replace with a scoped debug log that excludes
sensitive fields) and keep the check for actions_taken as-is.

@@ -0,0 +1,16 @@
const zod = require("zod");

const zodObjectId = zod.string().regex(/^[0-9a-zA-Z]{24}$/, "Invalid ObjectId");
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

ObjectId regex accepts invalid characters.

MongoDB ObjectIds are 24-character hex strings. The current regex [0-9a-zA-Z] allows non-hex characters (e.g., GZ, gz), which would pass validation but fail on database lookup.

🐛 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");
📝 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 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
In `@backend/utils/batchValidate.js` at line 3, zodObjectId currently uses a regex
that allows non-hex characters; update the pattern used in the zodObjectId
constant (zodObjectId) to only allow 24 hex characters by replacing the
character class with the correct hex range and keep the same error message or
adjust it to "Invalid ObjectId" — i.e., use a regex that enforces
/^[0-9a-fA-F]{24}$/ so validations only accept valid MongoDB ObjectId hex
strings.

Switched to session-based authentication and configured persistent session storage with connect-mongo.
…ession-based authentication and configured persistent session storage with connect-mongo.
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: 6

Caution

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

⚠️ Outside diff range comments (8)
backend/routes/auth.js (3)

196-212: ⚠️ Potential issue | 🟡 Minor

Password reset GET route lacks error handling for DB query.

Line 200 (await User.findOne({ _id: id })) is not wrapped in a try-catch — if id is not a valid ObjectId, Mongoose will throw a CastError that becomes an unhandled promise rejection.

Proposed fix
 router.get("/reset-password/:id/:token", async (req, res) => {
   const { id, token } = req.params;
-  console.log(req.params);
-  const user = await User.findOne({ _id: id });
-  if (!user) {
-    return res.status(404).json({ message: "User not found" });
-  }
-  const secret = user._id + process.env.JWT_SECRET_TOKEN;
   try {
+    const user = await User.findOne({ _id: id });
+    if (!user) {
+      return res.status(404).json({ message: "User not found" });
+    }
+    const secret = user._id + process.env.JWT_SECRET_TOKEN;
     jwt.verify(token, secret);
     return res.status(200).json({ message: "Token verified successfully" });
   } catch (error) {
     console.log(error);
     return res.status(400).json({ message: "Invalid or expired token" });
   }
 });
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@backend/routes/auth.js` around lines 196 - 212, The GET route handler
router.get("/reset-password/:id/:token", async (req, res) => { ... }) currently
calls User.findOne({ _id: id }) without guarding against invalid ObjectId or DB
errors; wrap the DB lookup in a try-catch (or pre-validate id with
mongoose.Types.ObjectId.isValid) so a malformed id doesn't throw an unhandled
CastError—if id is invalid return a 400 with a clear message, and if the DB
lookup throws return a 500 and log the error; keep the existing jwt.verify block
but ensure errors from User.findOne are handled before calling jwt.verify.

189-189: ⚠️ Potential issue | 🟠 Major

Password reset link logged to console — leaks reset tokens in production logs.

console.log(link) outputs the full password reset URL (including the JWT token) to server logs. An attacker with log access could use these to reset any user's password.

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

In `@backend/routes/auth.js` at line 189, Remove the unsafe console.log(link) that
emits the full password reset URL (including JWT) to logs; instead, either stop
logging the reset link entirely or conditionally log only in non-production
(check NODE_ENV) and sanitize the output by removing or masking the token
portion of the URL before logging. Locate the console.log call referencing the
variable "link" in the password reset flow and replace it with a secure logging
approach (e.g., omit token, log only the route/hostname or a masked token) or
use the application logger to emit a non-sensitive message.

224-232: ⚠️ Potential issue | 🔴 Critical

user.setPassword() will crash — this method doesn't exist.

The passport-local-mongoose plugin is not registered on the User schema. The code at line 224 calls user.setPassword(), which is a method provided by that plugin, and will throw "user.setPassword is not a function" at runtime.

The User schema instead uses a pre-save hook (lines 102–106 in backend/models/userSchema.js) to hash passwords with bcrypt. Replace this call with direct password assignment:

Suggested fix
     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; // pre-save hook will hash it
+    await user.save();
+    return res
+      .status(200)
+      .json({ message: "Password has been reset successfully" });
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@backend/routes/auth.js` around lines 224 - 232, The code calls
user.setPassword(), which doesn't exist because passport-local-mongoose isn't
used; replace that call by assigning the new plaintext password to the User
model field that the pre-save bcrypt hook expects (e.g., user.password =
password), then await user.save() and handle errors: on save failure return
res.status(500).json({ message: "Error resetting password" }) otherwise return
the 200 success JSON; remove the user.setPassword callback and rely on the
schema's pre-save hashing logic so the password is hashed before persisting.
backend/index.js (1)

76-78: ⚠️ Potential issue | 🟠 Major

Duplicate route mount for /api/announcements.

Lines 76 and 78 both mount announcementRoutes on the same path. This causes announcement route handlers to execute twice per request.

Proposed fix — remove the duplicate
 app.use("/api/announcements", announcementRoutes);
 app.use("/api/dashboard", dashboardRoutes);
-app.use("/api/announcements", announcementRoutes);
 app.use("/api/analytics", analyticsRoutes);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@backend/index.js` around lines 76 - 78, There's a duplicate mount of
announcementRoutes via app.use("/api/announcements", announcementRoutes) causing
handlers to run twice; remove the redundant call so announcementRoutes is only
mounted once (keep a single app.use("/api/announcements", announcementRoutes)
and delete the extra occurrence) and verify no other duplicate mounts exist for
announcementRoutes.
backend/routes/onboarding.js (1)

32-34: ⚠️ Potential issue | 🟡 Minor

Raw error object leaked in the 500 response.

Passing the Mongoose/Node error object directly in the JSON response can expose internal details (stack traces, field names, schema structure) to the client.

Proposed fix
   } catch (error) {
     console.error("Onboarding failed:", error);
-    res.status(500).json({ message: "Onboarding failed", error });
+    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 32 - 34, The catch block currently
returns the raw error object to the client (see the catch(error) handler,
console.error and res.status(500).json call). Log the full error on the server
(keep console.error(error) or use your app logger) but change the JSON response
to avoid leaking internals: replace res.status(500).json({ message: "Onboarding
failed", error }) with a sanitized response such as res.status(500).json({
message: "Onboarding failed" }) or, if you must expose something, only include a
non-sensitive string like error.message conditionally in non-production (e.g.
process.env.NODE_ENV !== 'production' ? { error: error.message } : {}).
frontend/src/services/auth.js (1)

13-25: ⚠️ Potential issue | 🔴 Critical

Field names in registerUser don't match the backend's expected payload — registration will silently fail.

The backend /auth/register route destructures { username, password, user_id, name } from req.body (line 71 of backend/routes/auth.js), and validates against registerValidate schema which requires these exact fields. This function sends { name, ID, email, password } — so ID arrives as an unrecognized key (backend expects user_id), and email is ignored (backend expects username). Zod validation will reject the request since username and user_id will be undefined, returning a 400 error.

🐛 Proposed fix
-export async function registerUser(name, ID, email, password) {
+export async function registerUser(name, user_id, username, password) {
   try {
     const response = await api.post(`/auth/register`, {
       name,
-      ID,
-      email,
+      user_id,
+      username,
       password,
     });
     return response.data.user || null;

Update all call sites to pass arguments in the new order: registerUser(name, user_id, username, password).

🤖 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 13 - 25, The registerUser
function is sending the wrong payload keys so the backend's registerValidate
(and the /auth/register handler that destructures { username, password, user_id,
name }) rejects the request; change registerUser to post { name, user_id,
username, password } instead of { name, ID, email, password } and update all
call sites to the new signature/order registerUser(name, user_id, username,
password) so the backend receives the expected fields (refer to function
registerUser and the /auth/register handler).
frontend/src/Components/Auth/UserOnboarding.jsx (2)

41-51: ⚠️ Potential issue | 🟡 Minor

Admission year validation has no upper bound.

Line 46 checks userData.add_year < 2016 but does not enforce an upper bound. A user could enter a far-future year (e.g., 9999). Consider adding userData.add_year > new Date().getFullYear() as an additional check.

Proposed fix
-    if (!userData.add_year || userData.add_year < 2016)
+    if (!userData.add_year || userData.add_year < 2016 || userData.add_year > new Date().getFullYear())
       newErrors.add_year = "Invalid admission year";
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@frontend/src/Components/Auth/UserOnboarding.jsx` around lines 41 - 51, The
validate function currently only checks that userData.add_year is at least 2016
but allows future years; update validate (in UserOnboarding.jsx) to also check
that userData.add_year is not greater than the current year (new
Date().getFullYear()) and set newErrors.add_year to an appropriate message when
it exceeds the current year; keep the existing lower-bound check so the
admission year must be between 2016 and the current calendar year.

128-137: ⚠️ Potential issue | 🟠 Major

Read-only ID_No field can create a UX dead end if fetch fails.

The Student ID field is now readOnly (line 131), but validation at line 43 still requires ID_No to be non-empty. If fetchCredentials fails or returns no user_id, the user is stuck — they can't edit the field and can't submit the form. Consider either showing an error state that directs the user to retry/contact support, or falling back to an editable field when the value is empty.

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

In `@frontend/src/Components/Auth/UserOnboarding.jsx` around lines 128 - 137, The
read-only Student ID (userData.ID_No) can lock the user out if fetchCredentials
fails; update the UI to allow input when no ID is present and provide a retry
path: change the input to be editable when userData.ID_No is falsy (e.g., set
readOnly based on userData.ID_No) and add a visible error state tied to
errors.ID_No with a "Retry" action that calls fetchCredentials (or a "Contact
support" hint) so users can either re-fetch or manually enter their ID;
reference userData.ID_No, errors.ID_No, and fetchCredentials to locate and
implement these changes.
🧹 Nitpick comments (10)
frontend/src/Components/Dashboard/Dashboard.jsx (1)

19-32: Loading guard could be more precise.

When isUserLoggedIn is null (auth still resolving), this shows "Loading Dashboard....." — which is misleading since the dashboard isn't loading, auth state is. Consider checking isLoading from the context (as RoleRedirect.jsx does) for a clearer UX, or redirecting unauthenticated users to /login instead of showing a loading spinner.

That said, the useAdminContext() migration itself is correct.

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

In `@frontend/src/Components/Dashboard/Dashboard.jsx` around lines 19 - 32, The
loading guard currently uses isUserLoggedIn and isOnboardingComplete which
treats a null/undefined auth state as "Loading Dashboard....."; update the
Dashboard component to read isLoading from useAdminContext (same source used in
RoleRedirect.jsx) and show the loading UI only when isLoading === true, and when
isLoading === false and isUserLoggedIn !== true perform a redirect to /login (or
render the unauthenticated view) instead of the spinner; adjust checks around
isOnboardingComplete to only gate the dashboard once auth finished (isLoading
false) and onboarding is explicitly true.
frontend/src/services/auth.js (2)

29-31: Clean up debug artifacts.

Empty line and commented-out console.log are leftover debug artifacts.

Proposed fix
   try {
-    
-    const res = await api.post("/auth/login", { username, password });
-    //console.log("Response is: ", res);
+    const res = await api.post("/auth/login", { username, password });
     return res.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 29 - 31, Remove the leftover
debug artifacts in the auth login call: delete the extra blank line and the
commented-out console.log following the const res = await
api.post("/auth/login", { username, password }); statement (in the login
function that calls api.post("/auth/login")). Ensure no debug console statements
remain in frontend/src/services/auth.js.

1-1: Remove commented-out import.

Dead code — axios is no longer used directly in this file.

Proposed fix
-//import axios from "axios";
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@frontend/src/services/auth.js` at line 1, Remove the dead commented import
for axios in frontend/src/services/auth.js by deleting the line "//import axios
from \"axios\";" so the file contains no unused/commented imports; this cleans
up the codebase and avoids misleading readers about unused dependencies.
backend/routes/auth.js (1)

23-26: Response key message carries user data — misleading contract.

The fetchAuth endpoint returns user data under the message key. This is confusing since message conventionally holds a human-readable string. The frontend (useAuth.js line 22) reads response.message to get the user object, creating a fragile coupling. Consider using data or user as the key.

Proposed fix (backend)
-  res.json({message: {personal_info, role, onboardingComplete, user_id}, success: true});
+  res.json({user: {personal_info, role, onboardingComplete, user_id}, success: true});

Then update frontend/src/hooks/useAuth.js accordingly:

-        const user = response.message;
+        const user = response.user;
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@backend/routes/auth.js` around lines 23 - 26, The fetchAuth route currently
returns the authenticated user fields under a misleading key `message`; change
the JSON response in the router.get("/fetchAuth", isAuthenticated, ...) handler
so it returns the user object under a clear key such as `user` or `data` (e.g.,
include { user: { personal_info, role, onboardingComplete, user_id } , success:
true } instead of `message`), and update the frontend consumer (useAuth.js) to
read the new key (replace response.message usages with response.user or
response.data) to keep the API contract explicit and avoid fragile coupling.
frontend/src/hooks/useAuth.js (1)

21-25: The backend uses message to carry user data — fragile coupling.

response.message contains the actual user object (from backend/routes/auth.js line 25: res.json({message: {personal_info, role, onboardingComplete, user_id}, success: true})). The field name message is conventionally used for human-readable strings, making this contract confusing and easy to break during future refactors. Consider renaming to user or data on the backend response.

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

In `@frontend/src/hooks/useAuth.js` around lines 21 - 25, The frontend currently
reads the user from response.message (in useAuth.js around fetchCredentials and
handleLogin), which is fragile; update the API contract and usage so the backend
returns a clearly named field (e.g., user or data) instead of message, then
update the frontend to read that field (replace response.message with
response.user or response.data in the fetchCredentials consumer and pass that
object into handleLogin). Ensure both the backend response creation (in auth.js)
and the frontend usage (fetchCredentials handling in useAuth.js) are changed
together to keep the contract consistent.
frontend/src/Components/Auth/UserOnboarding.jsx (2)

1-1: useContext is still imported but no longer used.

Same issue as in Login.jsx — the switch to useAdminContext makes the useContext import dead code.

Proposed fix
-import { useState, useEffect, useContext } from "react";
+import { useState, useEffect } from "react";
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@frontend/src/Components/Auth/UserOnboarding.jsx` at line 1, The import list
in UserOnboarding.jsx still includes useContext even though the component now
uses useAdminContext; remove the dead import by deleting useContext from the
import statement (the import line containing useState, useEffect, useContext) so
only the actually used hooks (useState, useEffect) remain, and ensure there are
no other references to useContext in the UserOnboarding component.

60-60: Remove console.log debug statement.

This will emit validation errors to the browser console on every submit attempt in production.

Proposed fix
    const validationErrors = validate();
-   console.log("Validation errors: ",validationErrors);
    if (Object.keys(validationErrors).length > 0) {
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@frontend/src/Components/Auth/UserOnboarding.jsx` at line 60, Remove the
development console logging in the UserOnboarding component: delete the
console.log("Validation errors: ", validationErrors) statement (or guard it
behind a NODE_ENV === "development" check) so validationErrors is no longer
emitted to the browser console on submit; locate this call in the
UserOnboarding.jsx submit/validation handler where validationErrors is
referenced and remove or conditionally wrap it.
frontend/src/App.jsx (2)

23-25: Remove commented-out debug/dead code.

Multiple commented-out blocks (console.log, role/navItems variables, SidebarProvider wrapper) add noise. If these are planned for future work, track them in an issue rather than leaving them commented out in the codebase.

Also applies to: 35-35, 64-64

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

In `@frontend/src/App.jsx` around lines 23 - 25, Remove the commented-out
debug/dead code in frontend/src/App.jsx: delete the commented console.log("User
data is: ", authData), the commented role and navItems variable lines (role,
navItems), and any commented SidebarProvider wrapper references noted in the
diff (and similar comments at lines 35 and 64). If this functionality is needed
later, open an issue or feature ticket rather than keeping commented code;
ensure no behavior changes to functions/components that reference authData,
role, navItems, or SidebarProvider.

17-18: Unused imports: NavbarConfig and SidebarProvider.

These imports are only referenced in commented-out code (lines 24–25, 35, 64). If the sidebar integration is planned for a future PR, consider removing these imports and the associated commented blocks to keep the file clean. They can be re-added when the feature is ready.

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

In `@frontend/src/App.jsx` around lines 17 - 18, Remove the unused imports
NavbarConfig and SidebarProvider from App.jsx and delete the associated
commented-out blocks that reference them (the commented lines around the
navbar/sidebar usage) to keep the file clean; if sidebar/navbar will be
implemented soon, instead add a short TODO comment referencing NavbarConfig and
SidebarProvider and leave no unused imports in the module.
frontend/src/Components/Auth/Login.jsx (1)

1-1: useContext is still imported but no longer used.

Line 2 switches to useAdminContext, making the useContext import on line 1 dead code.

Proposed fix
-import React, { useState, useContext } from "react";
+import React, { useState } from "react";
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@frontend/src/Components/Auth/Login.jsx` at line 1, The import list in
Login.jsx still includes the unused symbol useContext; remove useContext from
the React import so only React and useState (and any other used hooks) are
imported, since the component now uses useAdminContext instead; update the
import line to eliminate the dead import and ensure no other references to
useContext remain in the file (e.g., check for useContext(...) usages and
replace them with useAdminContext where appropriate).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@backend/config/passportConfig.js`:
- Around line 59-92: The LocalStrategy in passportConfig.js is rejecting
non-@iitbhilai.ac.in usernames because loginValidate (in
backend/utils/authValidate.js) uses a strict domain regex; update the validation
or strategy to avoid locking out existing users: either relax or change
loginValidate to accept legacy usernames/emails (e.g., allow plain usernames or
any email) or modify the LocalStrategy to accept the raw username first and only
enforce the IIT Bhilai domain for new registrations (e.g., perform domain check
in the signup flow instead of in loginValidate), and add a short DB
audit/migration plan to handle existing non-IIT users; refer to the
LocalStrategy declaration (passport.use(new LocalStrategy(...))) and the
loginValidate schema to locate where to change validation and where to add
fallback logic.

In `@backend/routes/onboarding.js`:
- Around line 7-9: The onboarding PUT handler (router.put("/", isAuthenticated,
async (req, res) => {...})) currently destructures Program, discipline,
add_year, mobile_no from req.body and later calls Program.trim(), which will
throw if missing; add Zod validation (or at minimum a guard) for the onboarding
payload mirroring the auth routes: define a Zod schema for { Program:
string().min(1), discipline: string().min(1), add_year:
number().int().optional(), mobile_no: string().optional() } and parse req.body
at the start of the handler (or check Program/discipline existence before
calling trim), then use the validated values instead of raw req.body variables
to prevent TypeError and ensure consistent input validation.

In `@frontend/src/Components/Auth/Login.jsx`:
- Line 26: Remove the trailing space from the toast message in the Login
component: update the toast.success call inside Login.jsx (the one that
currently reads toast.success("Login successful ")) to use "Login successful"
instead; ensure any other similar toast messages in the Login component (or
surrounding functions like the submit/login handler) do not contain unintended
trailing whitespace.
- Around line 22-27: The Login.jsx code assumes loginUser(email, password)
always returns an object but it can return null; add a null guard before
accessing response.success: after calling loginUser, check if response === null
(or !response) and handle it by showing an error toast (e.g., toast.error with a
clear message), avoid calling handleLogin/navigate in that case, and return
early; otherwise continue to check response.success and proceed to call
handleLogin(response.data), toast.success, and navigate. Ensure this change
references the call to loginUser and the subsequent use of
response.success/handleLogin so you prevent the TypeError and surface the real
network/API error to the user.

In `@frontend/src/Components/Auth/UserOnboarding.jsx`:
- Around line 26-33: The code assumes response.message exists; guard against a
missing or undefined message by checking response?.message before using it:
obtain user via a safe access (e.g., const user = response?.message || {}) or
early-return if missing, and update setUserData using user?.personal_info?.name,
user?.personal_info?.email, and user?.user_id (or sensible defaults) so
accessing personal_info cannot throw; locate the block where fetchCredentials()
is called and the setUserData update is performed to apply this fix.

In `@frontend/src/hooks/useAuth.js`:
- Line 23: Remove the console.log that prints user data in useAuth (the line
logging "User is:", user). Either delete the console.log entirely or replace it
with a non-production-only debug/log call (e.g., use a debug/logger and guard
with NODE_ENV !== 'production') so PII is not emitted in production; ensure the
change targets the useAuth hook where the user variable is logged.

---

Outside diff comments:
In `@backend/index.js`:
- Around line 76-78: There's a duplicate mount of announcementRoutes via
app.use("/api/announcements", announcementRoutes) causing handlers to run twice;
remove the redundant call so announcementRoutes is only mounted once (keep a
single app.use("/api/announcements", announcementRoutes) and delete the extra
occurrence) and verify no other duplicate mounts exist for announcementRoutes.

In `@backend/routes/auth.js`:
- Around line 196-212: The GET route handler
router.get("/reset-password/:id/:token", async (req, res) => { ... }) currently
calls User.findOne({ _id: id }) without guarding against invalid ObjectId or DB
errors; wrap the DB lookup in a try-catch (or pre-validate id with
mongoose.Types.ObjectId.isValid) so a malformed id doesn't throw an unhandled
CastError—if id is invalid return a 400 with a clear message, and if the DB
lookup throws return a 500 and log the error; keep the existing jwt.verify block
but ensure errors from User.findOne are handled before calling jwt.verify.
- Line 189: Remove the unsafe console.log(link) that emits the full password
reset URL (including JWT) to logs; instead, either stop logging the reset link
entirely or conditionally log only in non-production (check NODE_ENV) and
sanitize the output by removing or masking the token portion of the URL before
logging. Locate the console.log call referencing the variable "link" in the
password reset flow and replace it with a secure logging approach (e.g., omit
token, log only the route/hostname or a masked token) or use the application
logger to emit a non-sensitive message.
- Around line 224-232: The code calls user.setPassword(), which doesn't exist
because passport-local-mongoose isn't used; replace that call by assigning the
new plaintext password to the User model field that the pre-save bcrypt hook
expects (e.g., user.password = password), then await user.save() and handle
errors: on save failure return res.status(500).json({ message: "Error resetting
password" }) otherwise return the 200 success JSON; remove the user.setPassword
callback and rely on the schema's pre-save hashing logic so the password is
hashed before persisting.

In `@backend/routes/onboarding.js`:
- Around line 32-34: The catch block currently returns the raw error object to
the client (see the catch(error) handler, console.error and res.status(500).json
call). Log the full error on the server (keep console.error(error) or use your
app logger) but change the JSON response to avoid leaking internals: replace
res.status(500).json({ message: "Onboarding failed", error }) with a sanitized
response such as res.status(500).json({ message: "Onboarding failed" }) or, if
you must expose something, only include a non-sensitive string like
error.message conditionally in non-production (e.g. process.env.NODE_ENV !==
'production' ? { error: error.message } : {}).

In `@frontend/src/Components/Auth/UserOnboarding.jsx`:
- Around line 41-51: The validate function currently only checks that
userData.add_year is at least 2016 but allows future years; update validate (in
UserOnboarding.jsx) to also check that userData.add_year is not greater than the
current year (new Date().getFullYear()) and set newErrors.add_year to an
appropriate message when it exceeds the current year; keep the existing
lower-bound check so the admission year must be between 2016 and the current
calendar year.
- Around line 128-137: The read-only Student ID (userData.ID_No) can lock the
user out if fetchCredentials fails; update the UI to allow input when no ID is
present and provide a retry path: change the input to be editable when
userData.ID_No is falsy (e.g., set readOnly based on userData.ID_No) and add a
visible error state tied to errors.ID_No with a "Retry" action that calls
fetchCredentials (or a "Contact support" hint) so users can either re-fetch or
manually enter their ID; reference userData.ID_No, errors.ID_No, and
fetchCredentials to locate and implement these changes.

In `@frontend/src/services/auth.js`:
- Around line 13-25: The registerUser function is sending the wrong payload keys
so the backend's registerValidate (and the /auth/register handler that
destructures { username, password, user_id, name }) rejects the request; change
registerUser to post { name, user_id, username, password } instead of { name,
ID, email, password } and update all call sites to the new signature/order
registerUser(name, user_id, username, password) so the backend receives the
expected fields (refer to function registerUser and the /auth/register handler).

---

Duplicate comments:
In `@backend/config/passportConfig.js`:
- Around line 18-27: The code extracts email into the variable `email` but then
inconsistently uses `profile.emails[0].value` when checking `isIITBhilaiEmail`
and logging; update those references to use the already-defensive `email`
variable instead. Specifically, in the Google OAuth callback block where `const
email = profile.emails?.[0]?.value` and `if (!email) return done(...)` are
present, replace `profile.emails[0].value` with `email` in the
`isIITBhilaiEmail(email)` call and in the `console.log` so the validation and
logging use the validated value.

In `@backend/index.js`:
- Around line 84-90: connectDB() is currently swallowing errors so await
connectDB() never rejects and the server (app.listen) starts even when DB
connection failed; either make connectDB (in config/db.js) rethrow the caught
error or handle it here by wrapping await connectDB() in a try/catch and exit
the process on failure (e.g., log the error via console.error/processLogger and
call process.exit(1)) before calling app.listen; reference connectDB() and
app.listen to locate the code to change.
- Around line 48-52: The MongoStore.create call is passing ttl in milliseconds
(60*60*1000) but connect-mongo expects seconds, so change the ttl to seconds to
match the cookie maxAge; either set ttl: 60*60 (1 hour) or compute it from the
session cookie (e.g., Math.floor(sessionOptions.cookie.maxAge / 1000)) so
MongoStore.ttl and the cookie maxAge remain consistent; update the ttl value in
the MongoStore.create(...) options.

---

Nitpick comments:
In `@backend/routes/auth.js`:
- Around line 23-26: The fetchAuth route currently returns the authenticated
user fields under a misleading key `message`; change the JSON response in the
router.get("/fetchAuth", isAuthenticated, ...) handler so it returns the user
object under a clear key such as `user` or `data` (e.g., include { user: {
personal_info, role, onboardingComplete, user_id } , success: true } instead of
`message`), and update the frontend consumer (useAuth.js) to read the new key
(replace response.message usages with response.user or response.data) to keep
the API contract explicit and avoid fragile coupling.

In `@frontend/src/App.jsx`:
- Around line 23-25: Remove the commented-out debug/dead code in
frontend/src/App.jsx: delete the commented console.log("User data is: ",
authData), the commented role and navItems variable lines (role, navItems), and
any commented SidebarProvider wrapper references noted in the diff (and similar
comments at lines 35 and 64). If this functionality is needed later, open an
issue or feature ticket rather than keeping commented code; ensure no behavior
changes to functions/components that reference authData, role, navItems, or
SidebarProvider.
- Around line 17-18: Remove the unused imports NavbarConfig and SidebarProvider
from App.jsx and delete the associated commented-out blocks that reference them
(the commented lines around the navbar/sidebar usage) to keep the file clean; if
sidebar/navbar will be implemented soon, instead add a short TODO comment
referencing NavbarConfig and SidebarProvider and leave no unused imports in the
module.

In `@frontend/src/Components/Auth/Login.jsx`:
- Line 1: The import list in Login.jsx still includes the unused symbol
useContext; remove useContext from the React import so only React and useState
(and any other used hooks) are imported, since the component now uses
useAdminContext instead; update the import line to eliminate the dead import and
ensure no other references to useContext remain in the file (e.g., check for
useContext(...) usages and replace them with useAdminContext where appropriate).

In `@frontend/src/Components/Auth/UserOnboarding.jsx`:
- Line 1: The import list in UserOnboarding.jsx still includes useContext even
though the component now uses useAdminContext; remove the dead import by
deleting useContext from the import statement (the import line containing
useState, useEffect, useContext) so only the actually used hooks (useState,
useEffect) remain, and ensure there are no other references to useContext in the
UserOnboarding component.
- Line 60: Remove the development console logging in the UserOnboarding
component: delete the console.log("Validation errors: ", validationErrors)
statement (or guard it behind a NODE_ENV === "development" check) so
validationErrors is no longer emitted to the browser console on submit; locate
this call in the UserOnboarding.jsx submit/validation handler where
validationErrors is referenced and remove or conditionally wrap it.

In `@frontend/src/Components/Dashboard/Dashboard.jsx`:
- Around line 19-32: The loading guard currently uses isUserLoggedIn and
isOnboardingComplete which treats a null/undefined auth state as "Loading
Dashboard....."; update the Dashboard component to read isLoading from
useAdminContext (same source used in RoleRedirect.jsx) and show the loading UI
only when isLoading === true, and when isLoading === false and isUserLoggedIn
!== true perform a redirect to /login (or render the unauthenticated view)
instead of the spinner; adjust checks around isOnboardingComplete to only gate
the dashboard once auth finished (isLoading false) and onboarding is explicitly
true.

In `@frontend/src/hooks/useAuth.js`:
- Around line 21-25: The frontend currently reads the user from response.message
(in useAuth.js around fetchCredentials and handleLogin), which is fragile;
update the API contract and usage so the backend returns a clearly named field
(e.g., user or data) instead of message, then update the frontend to read that
field (replace response.message with response.user or response.data in the
fetchCredentials consumer and pass that object into handleLogin). Ensure both
the backend response creation (in auth.js) and the frontend usage
(fetchCredentials handling in useAuth.js) are changed together to keep the
contract consistent.

In `@frontend/src/services/auth.js`:
- Around line 29-31: Remove the leftover debug artifacts in the auth login call:
delete the extra blank line and the commented-out console.log following the
const res = await api.post("/auth/login", { username, password }); statement (in
the login function that calls api.post("/auth/login")). Ensure no debug console
statements remain in frontend/src/services/auth.js.
- Line 1: Remove the dead commented import for axios in
frontend/src/services/auth.js by deleting the line "//import axios from
\"axios\";" so the file contains no unused/commented imports; this cleans up the
codebase and avoids misleading readers about unused dependencies.

Comment on lines 59 to 92
//Local Strategy
passport.use(new LocalStrategy(async (username, password, done) => {

const result = loginValidate.safeParse({ username, password });

if (!result.success) {
let errors = result.error.issues.map((issue) => issue.message);
return done(null, false, {message: errors});
}

try{

const user = await User.findOne({ username });
if (!user) {
return done(null, false, {message: "Invalid user credentials"});
}


if (user.strategy !== "local" || !user.password) {
return done(null, false, { message: "Invalid login method" });
}

const isValid = await bcrypt.compare(password, user.password);
if (!isValid) {
return done(null, false, { message: "Invalid user credentials" });
}


return done(null, user);
}catch(err){
return done(err);
}

}));
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 | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

cat backend/utils/authValidate.js

Repository: OpenLake/Student_Database_COSA

Length of output: 548


LocalStrategy enforces IIT Bhilai email-only access; existing non-IIT users will be blocked.

The loginValidate schema in backend/utils/authValidate.js enforces a strict regex pattern (^[a-zA-Z0-9._%+-]+@iitbhilai\.ac\.in$) that rejects any username lacking the @iitbhilai.ac.in domain. This means any existing user account not using an IIT Bhilai email will be permanently locked out. Before deploying this change, audit the database for non-IIT Bhilai usernames and have a migration strategy ready (or disable this validation if it's unintended).

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

In `@backend/config/passportConfig.js` around lines 59 - 92, The LocalStrategy in
passportConfig.js is rejecting non-@iitbhilai.ac.in usernames because
loginValidate (in backend/utils/authValidate.js) uses a strict domain regex;
update the validation or strategy to avoid locking out existing users: either
relax or change loginValidate to accept legacy usernames/emails (e.g., allow
plain usernames or any email) or modify the LocalStrategy to accept the raw
username first and only enforce the IIT Bhilai domain for new registrations
(e.g., perform domain check in the signup flow instead of in loginValidate), and
add a short DB audit/migration plan to handle existing non-IIT users; refer to
the LocalStrategy declaration (passport.use(new LocalStrategy(...))) and the
loginValidate schema to locate where to change validation and where to add
fallback logic.

Comment on lines +7 to 9
router.put("/", isAuthenticated, async (req, res) => {
const { add_year, Program, discipline, mobile_no } = req.body;

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 | 🟠 Major

No input validation — Program.trim() will throw if the field is missing.

Unlike the auth routes which use Zod validation, onboarding accepts raw req.body without validation. If Program, discipline, or add_year are undefined, line 20 (Program.trim()) will throw a TypeError, and the other fields will silently store undefined.

Consider adding Zod validation for the onboarding payload, consistent with the pattern used in auth routes.

🛡️ Proposed fix — add a guard or validation
 router.put("/", isAuthenticated, async (req, res) => {
   const { add_year, Program, discipline, mobile_no } = req.body;
+
+  if (!add_year || !Program || !discipline) {
+    return res.status(400).json({ message: "add_year, Program, and discipline are required." });
+  }

   try {
📝 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.put("/", isAuthenticated, async (req, res) => {
const { add_year, Program, discipline, mobile_no } = req.body;
router.put("/", isAuthenticated, async (req, res) => {
const { add_year, Program, discipline, mobile_no } = req.body;
if (!add_year || !Program || !discipline) {
return res.status(400).json({ message: "add_year, Program, and discipline are required." });
}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@backend/routes/onboarding.js` around lines 7 - 9, The onboarding PUT handler
(router.put("/", isAuthenticated, async (req, res) => {...})) currently
destructures Program, discipline, add_year, mobile_no from req.body and later
calls Program.trim(), which will throw if missing; add Zod validation (or at
minimum a guard) for the onboarding payload mirroring the auth routes: define a
Zod schema for { Program: string().min(1), discipline: string().min(1),
add_year: number().int().optional(), mobile_no: string().optional() } and parse
req.body at the start of the handler (or check Program/discipline existence
before calling trim), then use the validated values instead of raw req.body
variables to prevent TypeError and ensure consistent input validation.

Comment on lines 22 to 27
const response = await loginUser(email, password);
//console.log(response);
if (response.success) {
handleLogin(response.data);
toast.success("Login successful ");
navigate("/onboarding", { replace: 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 | 🟠 Major

Null-pointer risk: loginUser returns null on network/API errors.

Per frontend/src/services/auth.js (lines 26–36), loginUser returns null when the request fails. Accessing response.success on null at line 24 will throw a TypeError. While the outer catch block will handle it, the error logged will be a misleading TypeError rather than the actual failure reason, and the control flow is fragile.

Add a null guard before accessing response.success:

Proposed fix
      const response = await loginUser(email, password);
-     //console.log(response);
-     if (response.success) {
+     if (response?.success) {
        handleLogin(response.data);
        toast.success("Login successful");  
        navigate("/onboarding", { replace: true });
📝 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 response = await loginUser(email, password);
//console.log(response);
if (response.success) {
handleLogin(response.data);
toast.success("Login successful ");
navigate("/onboarding", { replace: true });
const response = await loginUser(email, password);
if (response?.success) {
handleLogin(response.data);
toast.success("Login successful");
navigate("/onboarding", { replace: true });
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@frontend/src/Components/Auth/Login.jsx` around lines 22 - 27, The Login.jsx
code assumes loginUser(email, password) always returns an object but it can
return null; add a null guard before accessing response.success: after calling
loginUser, check if response === null (or !response) and handle it by showing an
error toast (e.g., toast.error with a clear message), avoid calling
handleLogin/navigate in that case, and return early; otherwise continue to check
response.success and proceed to call handleLogin(response.data), toast.success,
and navigate. Ensure this change references the call to loginUser and the
subsequent use of response.success/handleLogin so you prevent the TypeError and
surface the real network/API error to the user.

//console.log(response);
if (response.success) {
handleLogin(response.data);
toast.success("Login successful ");
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 | 🟡 Minor

Trailing space in toast message.

"Login successful " has a trailing space. Minor text quality nit.

Proposed fix
-        toast.success("Login successful ");  
+        toast.success("Login successful");  
📝 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
toast.success("Login successful ");
toast.success("Login successful");
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@frontend/src/Components/Auth/Login.jsx` at line 26, Remove the trailing space
from the toast message in the Login component: update the toast.success call
inside Login.jsx (the one that currently reads toast.success("Login successful
")) to use "Login successful" instead; ensure any other similar toast messages
in the Login component (or surrounding functions like the submit/login handler)
do not contain unintended trailing whitespace.

Comment on lines 26 to 33
const response = await fetchCredentials();
const user = response.message;
setUserData((prev) => ({
...prev,
name: user.personal_info.name,
email: user.personal_info.email,
name: user.personal_info?.name,
email: user.personal_info?.email,
ID_No: user.user_id,
}));
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 | 🟠 Major

Null-safety gap: response.message may be undefined, causing user.personal_info to throw.

If fetchCredentials() returns an unexpected shape (e.g., the API response lacks a message field), user will be undefined and user.personal_info?.name at line 30 will throw a TypeError. The optional chaining only guards personal_info, not user itself.

Proposed fix
        const response = await fetchCredentials();
        const user = response.message;
+       if (!user) return;
        setUserData((prev) => ({
          ...prev,
          name: user.personal_info?.name,
          email: user.personal_info?.email,
          ID_No: user.user_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 response = await fetchCredentials();
const user = response.message;
setUserData((prev) => ({
...prev,
name: user.personal_info.name,
email: user.personal_info.email,
name: user.personal_info?.name,
email: user.personal_info?.email,
ID_No: user.user_id,
}));
const response = await fetchCredentials();
const user = response.message;
if (!user) return;
setUserData((prev) => ({
...prev,
name: user.personal_info?.name,
email: user.personal_info?.email,
ID_No: user.user_id,
}));
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@frontend/src/Components/Auth/UserOnboarding.jsx` around lines 26 - 33, The
code assumes response.message exists; guard against a missing or undefined
message by checking response?.message before using it: obtain user via a safe
access (e.g., const user = response?.message || {}) or early-return if missing,
and update setUserData using user?.personal_info?.name,
user?.personal_info?.email, and user?.user_id (or sensible defaults) so
accessing personal_info cannot throw; locate the block where fetchCredentials()
is called and the setUserData update is performed to apply this fix.

console.log("Onboarding complete:", user.onboardingComplete);
const response = await fetchCredentials();
const user = response.message;
console.log("User is:", user);
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 | 🟡 Minor

Remove console.log from production code path.

This runs on every app initialization and logs user data (potentially including PII) to the browser console.

Proposed fix
         const user = response.message;
-        console.log("User is:", user);
         if (response?.success) {
📝 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
console.log("User is:", user);
const user = response.message;
if (response?.success) {
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@frontend/src/hooks/useAuth.js` at line 23, Remove the console.log that prints
user data in useAuth (the line logging "User is:", user). Either delete the
console.log entirely or replace it with a non-production-only debug/log call
(e.g., use a debug/logger and guard with NODE_ENV !== 'production') so PII is
not emitted in production; ensure the change targets the useAuth hook where the
user variable is logged.

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: 12

Caution

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

⚠️ Outside diff range comments (4)
backend/routes/auth.js (1)

237-260: ⚠️ Potential issue | 🔴 Critical

user.setPassword does not exist — the entire password-reset POST route will crash.

setPassword is injected by the passport-local-mongoose plugin. The new userSchema.js does not use that plugin (module.exports = User with only bcrypt and no .plugin() call), so user.setPassword is undefined. Calling it throws TypeError: user.setPassword is not a function, making password resets completely non-functional.

The fix is to directly assign to user.password and call save(). The pre-save hook in userSchema.js will hash it automatically.

Additionally, the User.findOne call at line 240 and the entire route body are outside a try/catch, so any DB error also propagates as an unhandled rejection.

🐛 Proposed fix
 router.post("/reset-password/:id/:token", async (req, res) => {
   const { id, token } = req.params;
   const { password } = req.body;
-  const user = await User.findOne({ _id: id });
-  if (!user) {
-    return res.status(404).json({ message: "User not found" });
-  }
-  const secret = user._id + process.env.JWT_SECRET_TOKEN;
   try {
+    const user = await User.findOne({ _id: id });
+    if (!user) {
+      return res.status(404).json({ message: "User not found" });
+    }
+    const secret = user._id + process.env.JWT_SECRET_TOKEN;
     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; // pre-save hook will hash it
+    await user.save();
+    return res.status(200).json({ message: "Password has been reset successfully" });
   } catch (error) {
     console.log(error);
     return res.status(400).json({ message: "Invalid or expired token" });
   }
 });
🤖 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 POST /reset-password
handler calls user.setPassword (in the User.findOne result) which no longer
exists because passport-local-mongoose was removed, causing a TypeError; fix by
replacing the setPassword call with direct assignment user.password = password
and await user.save() (the userSchema pre-save hook will hash the password), and
wrap the DB operations (User.findOne, user.save) and jwt.verify in a try/catch
to properly handle and return DB or verification errors instead of letting
unhandled rejections crash the route.
backend/routes/profile.js (3)

109-111: ⚠️ Potential issue | 🟠 Major

console.log statements log updatedDetails which can contain PII.

updatedDetails may include personal_info fields (name, email, phone, date of birth). Logging this to the server console is a privacy concern and should be removed before production.

Proposed fix
-    console.log("Received userId:", userId);
-    console.log("Received updatedDetails:", updatedDetails);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@backend/routes/profile.js` around lines 109 - 111, Remove the debug
console.log statements that print sensitive request data: do not log
req.body.updatedDetails or req.body.userId in production; instead either delete
the two console.log lines or replace them with a non-sensitive, structured log
(e.g., log only the request trace ID or userId hash) inside the route handler
that processes updatedDetails (refer to the variables userId and updatedDetails
in the profile update handler). Ensure no personal_info fields (name, email,
phone, dob) are ever output to logs.

220-220: ⚠️ Potential issue | 🟡 Minor

user.updated_at is not a schema field — this line is dead code.

The schema uses timestamps: true, which creates and manages updatedAt (camelCase) automatically on every save(). updated_at (snake_case) is not defined in the schema and is silently discarded. Remove this line.

Proposed fix
-    user.updated_at = new Date();
-
     // Save changes
     await user.save();
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@backend/routes/profile.js` at line 220, Remove the dead assignment to
user.updated_at (snake_case) — the Mongoose schema uses timestamps: true and
manages updatedAt (camelCase) automatically, so the line setting user.updated_at
should be deleted; locate the code around the user object update in the route
handler (the line with user.updated_at = new Date()) and remove it, relying on
the model's save() or update call to set updatedAt instead.

225-229: ⚠️ Potential issue | 🟠 Major

updatedStudent: user returns the hashed password in the API response.

The full Mongoose document (including password) is serialized into the response. Strip the password before returning:

🐛 Proposed fix
+    const userSafe = user.toObject();
+    delete userSafe.password;
     return res.status(200).json({
       success: true,
       message: "Student profile updated successfully",
-      updatedStudent: user,
+      updatedStudent: userSafe,
     });
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@backend/routes/profile.js` around lines 225 - 229, The response currently
returns the full Mongoose document as updatedStudent (variable user), which
includes the hashed password; convert the document to a plain object (e.g., via
user.toObject() or user.toJSON()) or ensure you fetched it with
.select('-password'), remove or unset the password property on that plain object
(delete obj.password or set it to undefined), and return the sanitized object as
updatedStudent in the res.status(200).json(...) response so no password is
leaked.
🧹 Nitpick comments (5)
backend/config/passportConfig.js (1)

24-29: Lines 24–25 still bypass the extracted email variable.

The guard on lines 18–22 already ensures email is non-null, but the subsequent calls revert to profile.emails[0].value, making the earlier extraction partially dead and the intent unclear.

♻️ Proposed fix
-      if (!isIITBhilaiEmail(profile.emails[0].value)) {
-        console.log("Google OAuth blocked for: ", profile.emails[0].value);
+      if (!isIITBhilaiEmail(email)) {
+        console.log("Google OAuth blocked for: ", email);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@backend/config/passportConfig.js` around lines 24 - 29, The code still uses
profile.emails[0].value instead of the previously extracted email variable;
update the Google OAuth guard to use the extracted email (email) everywhere for
consistency and clarity—specifically replace any remaining
profile.emails[0].value usages in the block that calls isIITBhilaiEmail and the
console.log/done message so isIITBhilaiEmail(email) and console.log("Google
OAuth blocked for:", email) / done(null, false, { message: ... , email }) (or
include email in the message) are used, keeping the existing guard that ensures
email is non-null.
frontend/src/Components/Auth/RoleRedirect.jsx (1)

1-1: useContext is imported but no longer used.

useAdminContext() on line 7 wraps useContext internally; the direct import is now dead.

♻️ Proposed fix
-import React, { useContext } from "react";
+import React from "react";
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@frontend/src/Components/Auth/RoleRedirect.jsx` at line 1, The import of
useContext in RoleRedirect.jsx is dead; remove useContext from the import
statement so the line reads only import React (or import React, { ... } if other
hooks remain), since useAdminContext() (used in this file) wraps useContext
internally; update the import of React accordingly and ensure no other
references to useContext remain in the file (useAdminContext is the single
context access point).
backend/routes/events.js (1)

444-477: Development debug logs should be removed before merging.

The PUT /:eventId handler emits full JSON-stringified event documents and structural metadata via console.log. These are clearly left from development and will spam production logs with potentially sensitive event data.

♻️ 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");
-
-    // Fetch the event BEFORE update to compare
-    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
console.log debug block from the PUT '/:eventId' route handler in
backend/routes/events.js (the section that logs eventId, updates, eventBefore,
event AFTER update, etc.), and replace it with minimal, non-sensitive logging:
log only the operation outcome and the eventId (or use the existing app logger
at debug level) rather than JSON-stringifying full documents or request bodies;
keep the Event.findByIdAndUpdate(..., updates, { new: true, runValidators: true
}) logic and error handling unchanged, and ensure any logging does not include
sensitive fields from the "event" or "updates" objects.
frontend/src/Components/Auth/Login.jsx (1)

1-9: Combine duplicate react-router-dom imports and remove unused useContext.

useNavigate (line 4) and Link (line 9) are both from react-router-dom and can be merged into a single import. useContext is imported on line 1 but unused since the refactor to useAdminContext.

♻️ Proposed fix
-import React, { useState, useContext } from "react";
+import React, { useState } from "react";
 import { useAdminContext } from "../../context/AdminContext";
 import { loginUser } from "../../services/auth";
-import { useNavigate } from "react-router-dom";
+import { useNavigate, Link } from "react-router-dom";
 import GoogleIcon from "@mui/icons-material/Google";
 import cosa from "../../assets/COSA.png";
 import backgroundImage from "../../assets/iitbh.jpg";
 import { toast } from "react-toastify";
-import {Link} from "react-router-dom"
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@frontend/src/Components/Auth/Login.jsx` around lines 1 - 9, The import list
in Login.jsx has duplicate imports from react-router-dom and an unused
useContext; update the top imports by removing the unused useContext from the
React import and merging Link into the existing react-router-dom import that
already brings in useNavigate (i.e., import { useNavigate, Link } from
"react-router-dom"), leaving useAdminContext and other imports unchanged so
there are no duplicate or unused imports.
frontend/src/Components/Auth/Register.jsx (1)

158-171: Remove commented-out dead code.

Lines 158–171 contain a fully commented-out <Link>-wrapped button block that was replaced by the active button on line 172. This should be deleted to keep the file clean.

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

In `@frontend/src/Components/Auth/Register.jsx` around lines 158 - 171, Remove the
dead commented-out Google sign-up block in the Register component (the commented
<Link> ... <button> ... <GoogleIcon /> block) so the file is clean; also remove
any now-unused imports related to Link or GoogleIcon in
frontend/src/Components/Auth/Register.jsx (e.g., React Router's Link or
GoogleIcon) to avoid unused-import warnings.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@backend/controllers/certificateController.js`:
- Line 15: The JSON response in certificateController.js uses a misspelled key
"messge" in the error return (the res.status(404).json call); change the key to
"message" so the error payload is { message: "Invalid data (User not found)" }
to ensure consumers receive the standard "message" field.

In `@backend/controllers/eventControllers.js`:
- Around line 11-13: The 404 guard is using `if (!latestEvents)` but
`Event.find()` always returns an array, so replace that check with a length
check (e.g., `if (latestEvents.length === 0)`) in the controller handling the
`Event.find()` result (look for `latestEvents` and the `Event.find()` call in
the event controller function) so an empty result returns the intended 404 JSON
response instead of 200 with an empty array.
- Around line 6-9: The sort call on Event.find uses the wrong field name
'updated_at' so it doesn't sort; update the sort to use the Mongoose timestamps
field 'updatedAt' (e.g., change .sort({updated_at: -1}) to .sort({updatedAt:
-1}) for latestEvents) and scan for any other .sort calls or selects in this
controller that reference 'updated_at' to similarly switch them to 'updatedAt'
to ensure sorting works as intended.

In `@backend/index.js`:
- Around line 48-52: The ttl passed to MongoStore.create is using milliseconds
(60*60*1000) but connect-mongo expects seconds, causing sessions to live ~41
days; update the ttl to be in seconds (e.g., 60*60) or compute it from the
session cookie maxAge (Math.floor(session.cookie.maxAge / 1000) or use the same
constant) so ttl and cookie maxAge match; locate the MongoStore.create call and
replace the ttl expression accordingly to ensure sessions expire from the DB
when the cookie does.
- Line 53: The session middleware in backend/index.js sets the cookie name to
"token", which collides with jwtIsAuthenticated in
backend/middlewares/isAuthenticated.js that expects req.cookies.token to be a
JWT; rename the session cookie (e.g., to "sid" or "sessionId") in the session
configuration (the object containing name: "token") and update any server code
that reads that cookie name accordingly so jwtIsAuthenticated continues to read
req.cookies.token for JWTs while the session middleware writes the new cookie
name; ensure no other logic assumes the old "token" cookie name.

In `@backend/routes/auth.js`:
- Line 223: Remove the sensitive logging of the password-reset token by deleting
the console.log(req.params) line (or replace it with a safe log that omits
req.params.token); specifically locate the route handler that accesses
req.params.token and remove any console.log calls that print req.params, or if
you must log, log only non-sensitive keys or use your logger to redact/omit
token before logging.

In `@backend/routes/profile.js`:
- Line 7: The require in profile routes is destructuring a default export so
User becomes undefined; replace the destructuring import "const { User } =
require('../models/userSchema')" with a default import "const User =
require('../models/userSchema')" so that calls like User.findOne(...) and other
User.* usages reference the exported model correctly.

In `@frontend/src/Components/Auth/Login.jsx`:
- Line 136: The Login.jsx page currently displays the text "Sign up with Google"
inside the Google auth button (the span within the Login component); update that
label to "Sign in with Google" so the action matches the Login page—locate the
span/button in the Login component and replace the displayed string accordingly.

In `@frontend/src/Components/Auth/Register.jsx`:
- Line 16: The code incorrectly calls useNavigate with an argument — change the
call to const navigate = useNavigate(); (in Register.jsx) and, where a default
redirect is intended, call navigate('/') explicitly at the appropriate place
(e.g., after successful registration in the submit/handleRegister function)
rather than passing "/" into useNavigate; ensure any existing tests or usages
reference navigate('/') and not the hook argument.
- Around line 40-54: The toast shows empty text when the caught error has no
HTTP response (network failure) because the existing logic only inspects
response/response.data; update the error-extraction in Register.jsx (the block
using response, respData, errorMessage and calling toast.error) to handle the
case where response is undefined by falling back to the original error.message
(or a generic "Network error" string) returned by registerUser in
frontend/src/services/auth.js so toast.error is always given a non-empty,
user-facing message.

In `@frontend/src/Components/Auth/RoleRedirect.jsx`:
- Around line 10-12: The current check Object.keys(isUserLoggedIn) crashes when
isUserLoggedIn is null; update the guard in RoleRedirect.jsx to handle
null/undefined (e.g., use Object.keys(isUserLoggedIn ?? {}).length === 0 or
simply if (!isUserLoggedIn) return <Navigate to="/login" replace />) and ensure
you consider the isLoading flag from useAuth.js so you only redirect after
loading completes.

In `@frontend/src/services/auth.js`:
- Around line 4-7: fetchCredentials lacks a try/catch and can cause an unhandled
rejection; wrap the api.get call in a try/catch inside fetchCredentials, catch
any network/error, log the error (e.g., console.error or existing logger) and
return the same fallback value the other functions in this module use (e.g.,
null or an empty object) so callers won’t receive an unhandled rejection —
update the function name fetchCredentials accordingly and keep the returned
shape consistent with other auth service functions.

---

Outside diff comments:
In `@backend/routes/auth.js`:
- Around line 237-260: The POST /reset-password handler calls user.setPassword
(in the User.findOne result) which no longer exists because
passport-local-mongoose was removed, causing a TypeError; fix by replacing the
setPassword call with direct assignment user.password = password and await
user.save() (the userSchema pre-save hook will hash the password), and wrap the
DB operations (User.findOne, user.save) and jwt.verify in a try/catch to
properly handle and return DB or verification errors instead of letting
unhandled rejections crash the route.

In `@backend/routes/profile.js`:
- Around line 109-111: Remove the debug console.log statements that print
sensitive request data: do not log req.body.updatedDetails or req.body.userId in
production; instead either delete the two console.log lines or replace them with
a non-sensitive, structured log (e.g., log only the request trace ID or userId
hash) inside the route handler that processes updatedDetails (refer to the
variables userId and updatedDetails in the profile update handler). Ensure no
personal_info fields (name, email, phone, dob) are ever output to logs.
- Line 220: Remove the dead assignment to user.updated_at (snake_case) — the
Mongoose schema uses timestamps: true and manages updatedAt (camelCase)
automatically, so the line setting user.updated_at should be deleted; locate the
code around the user object update in the route handler (the line with
user.updated_at = new Date()) and remove it, relying on the model's save() or
update call to set updatedAt instead.
- Around line 225-229: The response currently returns the full Mongoose document
as updatedStudent (variable user), which includes the hashed password; convert
the document to a plain object (e.g., via user.toObject() or user.toJSON()) or
ensure you fetched it with .select('-password'), remove or unset the password
property on that plain object (delete obj.password or set it to undefined), and
return the sanitized object as updatedStudent in the res.status(200).json(...)
response so no password is leaked.

---

Duplicate comments:
In `@backend/config/passportConfig.js`:
- Around line 65-95: The LocalStrategy callback uses loginValidate which
currently enforces an `@iitbhilai.ac.in` domain and therefore blocks
legacy/non-IIT usernames; update the Zod schema referenced by loginValidate in
backend/utils/authValidate.js to permit existing non-IIT usernames (either
remove the domain regex, allow other domains, or accept plain usernames) and
re-run validation so passport.use(new LocalStrategy(...)) won't reject those
accounts; also add a one-time DB audit script to find User records where
username does not match /@iitbhilai\.ac\.in$/ so you can review and migrate/flag
legacy accounts before deploying.

In `@backend/controllers/certificateController.js`:
- Around line 44-48: The code calls Position.findById(...) and immediately
accesses position._id before verifying the result, which can cause an NPE;
update the certificateController.js code around Position.findById and the
console.log so you first check if (position) (or if (!position) return
res.status(403)...), and only then access or log position._id; alternatively
move or remove the console.log(position._id) to after the null-check to ensure
position is not null before dereferencing.
- Around line 105-115: The code accesses presidentHolder.user_id and
presidentObj._id without null checks; update the president resolution flow
(around PositionHolder.findOne and User.findById) to first check that
presidentHolder is non-null before reading .user_id (only compute presidentId if
presidentHolder exists and return/res.status(404|500) early if not), then call
User.findById(presidentId) and only access or log presidentObj._id after
verifying presidentObj is non-null (move or remove the console.log of
presidentObj._id until after the null check); reference PositionHolder.findOne,
presidentHolder, presidentId, User.findById, and presidentObj when making these
changes.
- Around line 121-124: zodObjectId.safeParse(uid) returns an object with a
success boolean, so checking `if (!validation)` never detects invalid IDs;
update the code in the certificateController to inspect `validation.success` (or
use `validation.success === false`) and return `{ uid, ok: false, reason:
"Invalid ID" }` when false, or use the validated value from `validation.data`
when success before calling `User.findById`, so invalid IDs don't fall through
and cause a CastError.

In `@backend/index.js`:
- Around line 83-89: The connectDB function currently swallows connection
failures in its catch block (it only console.error's and returns), so modify the
catch in connectDB to terminate the process after logging the error — e.g.,
after the existing console.error call in the catch of connectDB, add
process.exit(1) so the IIFE awaiting connectDB() fails fast and the server does
not start without a DB connection.

In `@backend/routes/onboarding.js`:
- Around line 7-9: The handler for router.put currently destructures Program
from req.body and then calls Program.trim(), which will throw if Program is
undefined; update the router.put async handler to validate and guard these
inputs before using them: check that Program (and other required fields like
add_year, discipline, mobile_no if needed) exists and is a string (or coerce via
String(Program) or use (Program || "").trim()), and return a 4xx response when a
required field is missing; locate the router.put(...) function and the code that
calls Program.trim() and replace the direct trim call with a safe guarded trim
or explicit presence check and error response.

In `@frontend/src/Components/Auth/Login.jsx`:
- Line 26: In Login.jsx inside the Login component where toast.success("Login
successful ") is called, remove the trailing space from the message so it
becomes "Login successful"; update the toast.success invocation accordingly to
avoid the accidental extra whitespace in the UI notification.
- Around line 22-27: The code assumes loginUser(...) always returns an object,
but frontend/src/services/auth.js can return null on errors, so guard against a
null response before accessing response.success; in the Login component wrap the
await loginUser(email, password) result with a null check (e.g., if (!response)
{ toast.error(...) ; return; }) or treat response as falsy and handle
error/logging appropriately, only calling handleLogin(response.data),
toast.success, and navigate when response && response.success are true; ensure
you reference the loginUser call, the response variable, and the
handleLogin/navigate usage when applying the fix.

---

Nitpick comments:
In `@backend/config/passportConfig.js`:
- Around line 24-29: The code still uses profile.emails[0].value instead of the
previously extracted email variable; update the Google OAuth guard to use the
extracted email (email) everywhere for consistency and clarity—specifically
replace any remaining profile.emails[0].value usages in the block that calls
isIITBhilaiEmail and the console.log/done message so isIITBhilaiEmail(email) and
console.log("Google OAuth blocked for:", email) / done(null, false, { message:
... , email }) (or include email in the message) are used, keeping the existing
guard that ensures email is non-null.

In `@backend/routes/events.js`:
- Around line 444-477: Remove the development console.log debug block from the
PUT '/:eventId' route handler in backend/routes/events.js (the section that logs
eventId, updates, eventBefore, event AFTER update, etc.), and replace it with
minimal, non-sensitive logging: log only the operation outcome and the eventId
(or use the existing app logger at debug level) rather than JSON-stringifying
full documents or request bodies; keep the Event.findByIdAndUpdate(..., updates,
{ new: true, runValidators: true }) logic and error handling unchanged, and
ensure any logging does not include sensitive fields from the "event" or
"updates" objects.

In `@frontend/src/Components/Auth/Login.jsx`:
- Around line 1-9: The import list in Login.jsx has duplicate imports from
react-router-dom and an unused useContext; update the top imports by removing
the unused useContext from the React import and merging Link into the existing
react-router-dom import that already brings in useNavigate (i.e., import {
useNavigate, Link } from "react-router-dom"), leaving useAdminContext and other
imports unchanged so there are no duplicate or unused imports.

In `@frontend/src/Components/Auth/Register.jsx`:
- Around line 158-171: Remove the dead commented-out Google sign-up block in the
Register component (the commented <Link> ... <button> ... <GoogleIcon /> block)
so the file is clean; also remove any now-unused imports related to Link or
GoogleIcon in frontend/src/Components/Auth/Register.jsx (e.g., React Router's
Link or GoogleIcon) to avoid unused-import warnings.

In `@frontend/src/Components/Auth/RoleRedirect.jsx`:
- Line 1: The import of useContext in RoleRedirect.jsx is dead; remove
useContext from the import statement so the line reads only import React (or
import React, { ... } if other hooks remain), since useAdminContext() (used in
this file) wraps useContext internally; update the import of React accordingly
and ensure no other references to useContext remain in the file (useAdminContext
is the single context access point).

const id = req.user.id;
const user = await User.findById(id);
if (!user) {
return res.status(404).json({ messge: "Invalid data (User 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 | 🟡 Minor

Typo in response key: "messge""message".

🐛 Proposed fix
-      return res.status(404).json({ messge: "Invalid data (User not found)" });
+      return res.status(404).json({ message: "Invalid data (User not found)" });
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@backend/controllers/certificateController.js` at line 15, The JSON response
in certificateController.js uses a misspelled key "messge" in the error return
(the res.status(404).json call); change the key to "message" so the error
payload is { message: "Invalid data (User not found)" } to ensure consumers
receive the standard "message" field.

Comment on lines 6 to +9
const latestEvents = await Event.find({})
.sort({updated_at: -1})
.limit(4)
.select('title updated_at schedule.venue status');
.select('title updatedAt schedule.venue status');
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 | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

# Search for Event schema and model files
fd -t f -e js | grep -iE "(event|schema)" | head -20

Repository: OpenLake/Student_Database_COSA

Length of output: 541


🏁 Script executed:

# Search for the Event model definition and timestamp configuration
rg -n "timestamps|updated_at|updatedAt" --type js -A2 -B2

Repository: OpenLake/Student_Database_COSA

Length of output: 4841


🏁 Script executed:

# Get more context from eventControllers.js around the lines mentioned
fd -t f -name "*eventController*" -o -name "*eventcontroller*"

Repository: OpenLake/Student_Database_COSA

Length of output: 304


🏁 Script executed:

cat -n backend/models/eventSchema.js | tail -20

Repository: OpenLake/Student_Database_COSA

Length of output: 629


🏁 Script executed:

cat -n backend/controllers/eventControllers.js | head -30

Repository: OpenLake/Student_Database_COSA

Length of output: 1243


Sort field updated_at doesn't match the Mongoose timestamp field updatedAt.

Lines 9 and 17 both reference updatedAt (Mongoose timestamps field), but the sort on line 7 uses the snake_case form updated_at, which doesn't exist on the document. The sort is silently a no-op.

🐛 Proposed fix
-         .sort({updated_at: -1})
+         .sort({updatedAt: -1})
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const latestEvents = await Event.find({})
.sort({updated_at: -1})
.limit(4)
.select('title updated_at schedule.venue status');
.select('title updatedAt schedule.venue status');
const latestEvents = await Event.find({})
.sort({updatedAt: -1})
.limit(4)
.select('title updatedAt schedule.venue status');
🤖 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 sort call on
Event.find uses the wrong field name 'updated_at' so it doesn't sort; update the
sort to use the Mongoose timestamps field 'updatedAt' (e.g., change
.sort({updated_at: -1}) to .sort({updatedAt: -1}) for latestEvents) and scan for
any other .sort calls or selects in this controller that reference 'updated_at'
to similarly switch them to 'updatedAt' to ensure sorting works as intended.

Comment on lines +11 to +13
if(!latestEvents){
return res.status(404).json({message: "No events are created"});
}
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 | 🟠 Major

Event.find() always returns an array — this 404 guard is dead code.

Mongoose's .find() resolves to [] when no documents exist, never null or undefined. !latestEvents is always false after a successful query, so a request with no events returns 200 [] instead of the intended 404.

🐛 Proposed fix
-         if(!latestEvents){
-            return res.status(404).json({message: "No events are created"});
-         }
+         if(!latestEvents || latestEvents.length === 0){
+            return res.status(404).json({message: "No events are created"});
+         }
📝 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
if(!latestEvents){
return res.status(404).json({message: "No events are created"});
}
if(!latestEvents || latestEvents.length === 0){
return res.status(404).json({message: "No events are created"});
}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@backend/controllers/eventControllers.js` around lines 11 - 13, The 404 guard
is using `if (!latestEvents)` but `Event.find()` always returns an array, so
replace that check with a length check (e.g., `if (latestEvents.length === 0)`)
in the controller handling the `Event.find()` result (look for `latestEvents`
and the `Event.find()` call in the event controller function) so an empty result
returns the intended 404 JSON response instead of 200 with an empty array.

Comment on lines +48 to +52
store: MongoStore.create({
mongoUrl: process.env.MONGODB_URI,
ttl: 60*60*1000,
collectionName: "sessions"
}),
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 | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

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

Repository: OpenLake/Student_Database_COSA

Length of output: 825


🌐 Web query:

connect-mongo ttl parameter unit seconds documentation

💡 Result:

In connect-mongo, the ttl option is specified in seconds (default 1209600, i.e., 14 days). The docs describe it as “maximum lifetime (in seconds)” and examples set it as 14 * 24 * 60 * 60. [1], [2]


ttl is in seconds but is set to a millisecond value — sessions will persist for ~41.7 days.

The connect-mongo ttl parameter expects seconds. With ttl: 60*60*1000 (3,600,000 seconds), sessions persist for approximately 41.7 days in MongoDB, while the cookie's maxAge is 3,600,000 milliseconds (1 hour). This mismatch causes expired session cookies to remain in the database, wasting storage and keeping stale session data accessible.

Proposed fix
     store: MongoStore.create({
       mongoUrl: process.env.MONGODB_URI,
-      ttl: 60*60*1000,
+      ttl: 60 * 60, // 3600 seconds = 1 hour (matches cookie maxAge)
       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"
}),
store: MongoStore.create({
mongoUrl: process.env.MONGODB_URI,
ttl: 60 * 60, // 3600 seconds = 1 hour (matches cookie maxAge)
collectionName: "sessions"
}),
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@backend/index.js` around lines 48 - 52, The ttl passed to MongoStore.create
is using milliseconds (60*60*1000) but connect-mongo expects seconds, causing
sessions to live ~41 days; update the ttl to be in seconds (e.g., 60*60) or
compute it from the session cookie maxAge (Math.floor(session.cookie.maxAge /
1000) or use the same constant) so ttl and cookie maxAge match; locate the
MongoStore.create call and replace the ttl expression accordingly to ensure
sessions expire from the DB when the cookie does.

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 | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# First, let's check the structure of the backend directory
echo "=== Backend directory structure ==="
find backend -type f -name "*.js" | head -20

Repository: OpenLake/Student_Database_COSA

Length of output: 797


🏁 Script executed:

#!/bin/bash
# Run the provided verification script
rg -n "cookies\|token\|req\.cookies" backend/middlewares/isAuthenticated.js -A2 -B2

Repository: OpenLake/Student_Database_COSA

Length of output: 56


🏁 Script executed:

#!/bin/bash
# Check the context around line 53 in backend/index.js
echo "=== Context around line 53 in backend/index.js ==="
sed -n '40,70p' backend/index.js

Repository: OpenLake/Student_Database_COSA

Length of output: 1025


🏁 Script executed:

#!/bin/bash
# Search for where jwtIsAuthenticated is used
echo "=== Usage of jwtIsAuthenticated ==="
rg "jwtIsAuthenticated" backend -n -A1 -B1

Repository: OpenLake/Student_Database_COSA

Length of output: 789


🏁 Script executed:

#!/bin/bash
# Read the entire isAuthenticated.js file to see the implementation
cat -n backend/middlewares/isAuthenticated.js

Repository: OpenLake/Student_Database_COSA

Length of output: 2903


🏁 Script executed:

#!/bin/bash
# Check the certificate routes to understand the flow
cat -n backend/routes/certificateRoutes.js

Repository: OpenLake/Student_Database_COSA

Length of output: 399


Session cookie named "token" will conflict with JWT-based middleware.

The session middleware is configured with name: "token", which means it sets a cookie called "token" containing a session ID. However, jwtIsAuthenticated in the certificate routes reads req.cookies.token (line 24 of backend/middlewares/isAuthenticated.js) and attempts to verify it as a JWT. When jwt.verify() is called on the session ID string, verification will fail, causing all certificate endpoints to return a 401 Unauthorized error.

Rename the session cookie to avoid the collision:

🐛 Proposed fix
-    name: "token"
+    name: "sid"
📝 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
name: "token"
name: "sid"
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@backend/index.js` at line 53, The session middleware in backend/index.js sets
the cookie name to "token", which collides with jwtIsAuthenticated in
backend/middlewares/isAuthenticated.js that expects req.cookies.token to be a
JWT; rename the session cookie (e.g., to "sid" or "sessionId") in the session
configuration (the object containing name: "token") and update any server code
that reads that cookie name accordingly so jwtIsAuthenticated continues to read
req.cookies.token for JWTs while the session middleware writes the new cookie
name; ensure no other logic assumes the old "token" cookie name.

className="w-full bg-[#23659C] text-white py-2 rounded-md flex items-center justify-center space-x-2 hover:opacity-90 font-medium"
>
<span>Sign in with Google</span>
<span>Sign up with Google</span>
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 | 🟡 Minor

"Sign up with Google" button label is wrong on the Login page.

This is the Login page, so the action is sign-in, not sign-up. The label should read "Sign in with Google" to avoid confusing users.

Proposed fix
-              <span>Sign up with Google</span>
+              <span>Sign in with Google</span>
📝 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
<span>Sign up with Google</span>
<span>Sign in with Google</span>
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@frontend/src/Components/Auth/Login.jsx` at line 136, The Login.jsx page
currently displays the text "Sign up with Google" inside the Google auth button
(the span within the Login component); update that label to "Sign in with
Google" so the action matches the Login page—locate the span/button in the Login
component and replace the displayed string accordingly.

name: ""
})
const [loading, setLoading] = useState(false);
const navigate = useNavigate("/");
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 | 🟡 Minor

useNavigate does not accept arguments.

useNavigate() in react-router-dom v6 takes no parameters; the "/" argument is silently ignored. The returned navigate function still works, but this is misleading — it looks like a default path is being configured.

Proposed fix
-  const navigate = useNavigate("/");
+  const navigate = useNavigate();
📝 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 navigate = useNavigate("/");
const navigate = useNavigate();
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@frontend/src/Components/Auth/Register.jsx` at line 16, The code incorrectly
calls useNavigate with an argument — change the call to const navigate =
useNavigate(); (in Register.jsx) and, where a default redirect is intended, call
navigate('/') explicitly at the appropriate place (e.g., after successful
registration in the submit/handleRegister function) rather than passing "/" into
useNavigate; ensure any existing tests or usages reference navigate('/') and not
the hook argument.

Comment on lines +40 to +54
let errorMessage = "";
const respData = response && response.data;
if (respData) {
const msg = respData.message;
if (Array.isArray(msg)) {
errorMessage = msg.join(". ");
} else if (typeof msg === "string") {
errorMessage = msg;
} else if (msg && msg.message) {
errorMessage = msg.message;
}
} else if (response && response.status) {
errorMessage = response.statusText;
}
toast.error(errorMessage);
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 | 🟡 Minor

Empty error toast when response is undefined (network error).

registerUser in frontend/src/services/auth.js (line 30) returns error.response, which is undefined for network-level failures (no HTTP response). In that case, neither branch of the error-extraction logic is entered, errorMessage stays "", and toast.error("") fires silently with no visible message.

🐛 Proposed fix
+      if (!response) {
+        toast.error("Registration failed. Network error, please try again.");
+        return;
+      }
       // success response is the full axios response
       if (response && response.status === 200 && response.data && response.data.success) {
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@frontend/src/Components/Auth/Register.jsx` around lines 40 - 54, The toast
shows empty text when the caught error has no HTTP response (network failure)
because the existing logic only inspects response/response.data; update the
error-extraction in Register.jsx (the block using response, respData,
errorMessage and calling toast.error) to handle the case where response is
undefined by falling back to the original error.message (or a generic "Network
error" string) returned by registerUser in frontend/src/services/auth.js so
toast.error is always given a non-empty, user-facing message.

Comment on lines +10 to 12
if (Object.keys(isUserLoggedIn).length === 0) {
return <Navigate to="/login" replace />;
}
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

Object.keys(null) throws a TypeError when isUserLoggedIn is null.

isUserLoggedIn is initialized as null in useAuth.js (line 4), so once isLoading flips to false for an unauthenticated user, this call crashes the component before the redirect can happen.

🐛 Proposed fix
-  if (Object.keys(isUserLoggedIn).length === 0) {
+  if (!isUserLoggedIn || Object.keys(isUserLoggedIn).length === 0) {
     return <Navigate to="/login" replace />;
   }
📝 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
if (Object.keys(isUserLoggedIn).length === 0) {
return <Navigate to="/login" replace />;
}
if (!isUserLoggedIn || Object.keys(isUserLoggedIn).length === 0) {
return <Navigate to="/login" replace />;
}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@frontend/src/Components/Auth/RoleRedirect.jsx` around lines 10 - 12, The
current check Object.keys(isUserLoggedIn) crashes when isUserLoggedIn is null;
update the guard in RoleRedirect.jsx to handle null/undefined (e.g., use
Object.keys(isUserLoggedIn ?? {}).length === 0 or simply if (!isUserLoggedIn)
return <Navigate to="/login" replace />) and ensure you consider the isLoading
flag from useAuth.js so you only redirect after loading completes.

Comment on lines 4 to 7
export async function fetchCredentials() {
const response = await api.get(`/auth/fetchAuth`);
const response = await api.get("/auth/fetchAuth");
return response.data;
}
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 | 🟠 Major

fetchCredentials has no error handling — unhandled rejection on failure.

Every other function in this file wraps the API call in try/catch. fetchCredentials is likely called during app bootstrap (e.g., to hydrate auth state); an uncaught network error here will propagate as an unhandled promise rejection and can break the entire initialization flow.

🐛 Proposed fix
 export async function fetchCredentials() {
-  const response = await api.get("/auth/fetchAuth");
-  return response.data;
+  try {
+    const response = await api.get("/auth/fetchAuth");
+    return response.data;
+  } catch (error) {
+    console.error("Failed to fetch credentials:", error.response?.data || error.message);
+    return null;
+  }
}
🤖 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 4 - 7, fetchCredentials lacks a
try/catch and can cause an unhandled rejection; wrap the api.get call in a
try/catch inside fetchCredentials, catch any network/error, log the error (e.g.,
console.error or existing logger) and return the same fallback value the other
functions in this module use (e.g., null or an empty object) so callers won’t
receive an unhandled rejection — update the function name fetchCredentials
accordingly and keep the returned shape consistent with other auth service
functions.

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