Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
69 changes: 60 additions & 9 deletions backend/app.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,29 +9,80 @@ const authRoutes = require("./routes/authRoutes");
const userRoutes = require("./routes/userRoutes");
const passwordResetRoutes = require("./routes/passwordResetRoutes");
const adminRegistrationRoutes = require("./routes/adminRegistrationRoutes");
const vigyRoutes = require("./routes/vigyRoutes");
const subscribeRoute = require("./routes/subscribe");

const app = express();

// Middleware
app.use(cors());
// Middleware setup
app.use(express.json());
app.use(cors({
origin: process.env.FRONTEND_URL || "http://localhost:3000",
credentials: true
}));
app.use(
session({
secret: "Our little secret.",
secret: process.env.SESSION_SECRET || "Our little secret.",
resave: false,
Comment on lines +25 to 26
Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Sessions over cross-site: set trust proxy and sameSite.

Without app.set('trust proxy', 1) and sameSite: 'none' in production, secure cookies may be dropped in cross-site flows (e.g., OAuth).

 const app = express();
@@
 app.use(
   session({
     secret: process.env.SESSION_SECRET || "Our little secret.",
     resave: false,
     saveUninitialized: false,
     cookie: {
       secure: process.env.NODE_ENV === 'production',
       httpOnly: true,
-      maxAge: 24 * 60 * 60 * 1000 // 24 hours
+      sameSite: process.env.NODE_ENV === 'production' ? 'none' : 'lax',
+      maxAge: 24 * 60 * 60 * 1000 // 24 hours
     }
   })
 );
+if (process.env.NODE_ENV === 'production') {
+  app.set('trust proxy', 1);
+}

Also applies to: 26-30

🤖 Prompt for AI Agents
In backend/app.js around lines 23-24 (and similarly lines 26-30) the Express app
is not configured to trust a proxy nor are session cookie options set for
cross-site flows; in production add app.set('trust proxy', 1) before mounting
the session middleware and update the session cookie options to include secure:
true and sameSite: 'none' (guard these settings behind NODE_ENV === 'production'
or equivalent so local/dev stays unchanged), ensuring the session secret remains
used from process.env.SESSION_SECRET.

Comment on lines 23 to 26
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

Require SESSION_SECRET in production.

Avoid weak default secrets in prod.

 app.use(
   session({
-    secret: process.env.SESSION_SECRET || "Our little secret.",
+    secret: (() => {
+      if (process.env.NODE_ENV === "production" && !process.env.SESSION_SECRET) {
+        throw new Error("SESSION_SECRET must be set in production");
+      }
+      return process.env.SESSION_SECRET || "Our little secret.";
+    })(),
📝 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
app.use(
session({
secret: "Our little secret.",
secret: process.env.SESSION_SECRET || "Our little secret.",
resave: false,
app.use(
session({
secret: (() => {
if (process.env.NODE_ENV === "production" && !process.env.SESSION_SECRET) {
throw new Error("SESSION_SECRET must be set in production");
}
return process.env.SESSION_SECRET || "Our little secret.";
})(),
resave: false,
🤖 Prompt for AI Agents
In backend/app.js around lines 26 to 29, the session middleware is falling back
to a weak default secret; update it to require process.env.SESSION_SECRET when
running in production (NODE_ENV === 'production') and refuse to start or throw a
clear error if the env var is missing, otherwise allow the current behavior in
non-production (development/test); ensure the session secret is only set from
the env and remove the hardcoded fallback.

saveUninitialized: false,
cookie: {
secure: process.env.NODE_ENV === 'production',
httpOnly: true,
maxAge: 24 * 60 * 60 * 1000 // 24 hours
}
})
);
app.use(passport.initialize());
app.use(passport.session());

// Routes
// Root route for testing
app.get("/", (req, res) => {
res.json({
message: "VigyBag Backend Server is Running! 🚀",
status: "success",
timestamp: new Date().toISOString(),
endpoints: {
vigy: "/api/vigy",
auth: "/auth",
admin: "/api/v1"
}
});
});

// Route setup
app.use("/api/subscribe", subscribeRoute); // POST /api/subscribe
app.use("/auth", authRoutes); // /auth
app.use("/api", routes); // general /api routes (e.g. products)
app.use("/api", passwordResetRoutes); // /api/password-reset
app.use("/vpi", userRoutes); // /vpi
app.use("/api/v1", adminRegistrationRoutes); // /api/v1/admin
app.use("/auth", authRoutes);
app.use("/api", routes);
app.use("/api", passwordResetRoutes);
app.use("/vpi", userRoutes);
app.use("/api/v1", adminRegistrationRoutes);
app.use("/api/vigy", vigyRoutes);

Comment on lines +59 to +60
Copy link
Contributor

Choose a reason for hiding this comment

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

💡 Verification agent

🧩 Analysis chain

Quick sanity check for the PR goal (frontend route).

Ensure the “Login as Vigy” link points to the intended path (e.g., /vigy-login) and no stale /vigy-form links remain.


🏁 Script executed:

#!/bin/bash
# Search frontend for Vigy login routes/links
rg -n -C2 -S --glob '!**/node_modules/**' --type-add 'jsx:*.{js,jsx,ts,tsx}' --type jsx \
  -e 'vigy[-_/ ]login' -e 'vigy-login' -e '/vigy-login' -e '/vigy-form' -e 'Login as Vigy'

Length of output: 3362


Login link correct; remove stale “/vigy-form” references

  • The “Login as Vigy” link correctly points to /vigy-login (UserAuth.jsx:212–216).
  • Remove or rename all remaining /vigy-form routes and links, e.g.:
    • src/App.jsx:273–274
    • src/User/pages/UserAuth/UserAuth.jsx:212–216
    • src/Admin/Pages/VigyLogin.jsx:186–188
🤖 Prompt for AI Agents
In backend/app.js around lines 60 to 61, the app is mounting vigy routes at
/api/vigy but there are stale references to the old /vigy-form path elsewhere;
find and remove or rename any routes, route handlers, or exported endpoints that
still expose or reference /vigy-form so all server-side routes match the new
/vigy-login or /api/vigy conventions. Update any Express route declarations,
middleware mounts, and route exports to use the new path names, and search the
repo for literal "/vigy-form" occurrences to either delete or replace with the
correct route, ensuring route handlers and client-side links remain consistent.

// Test route for Vigy API
app.get("/api/vigy/test", (req, res) => {
res.json({
message: "Vigy API is working! ✅",
status: "success",
endpoint: "/api/vigy/test"
});
});

// Test route for Google OAuth configuration
app.get("/auth/google/test", (req, res) => {
const hasClientId = !!process.env.GOOGLE_CLIENT_ID;
const hasClientSecret = !!process.env.GOOGLE_CLIENT_SECRET;
const callbackUrl = process.env.GOOGLE_CALLBACK_URL || "http://localhost:5000/auth/google/callback";

res.json({
message: "Google OAuth Configuration Test",
status: hasClientId && hasClientSecret ? "configured" : "missing_credentials",
configuration: {
hasClientId,
hasClientSecret,
callbackUrl,
frontendUrl: process.env.FRONTEND_URL || "http://localhost:3000"
}
});
});
Comment on lines +71 to +86
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

Info disclosure risk: Google OAuth config test should not be public in prod.

Leaking presence/URLs of OAuth config aids reconnaissance.

-app.get("/auth/google/test", (req, res) => {
+if (process.env.NODE_ENV !== "production") app.get("/auth/google/test", (req, res) => {
   const hasClientId = !!process.env.GOOGLE_CLIENT_ID;
   const hasClientSecret = !!process.env.GOOGLE_CLIENT_SECRET;
   const callbackUrl = process.env.GOOGLE_CALLBACK_URL || "http://localhost:5000/auth/google/callback";
   
   res.json({
     message: "Google OAuth Configuration Test",
     status: hasClientId && hasClientSecret ? "configured" : "missing_credentials",
     configuration: {
       hasClientId,
       hasClientSecret,
       callbackUrl,
       frontendUrl: process.env.FRONTEND_URL || "http://localhost:3000"
     }
   });
-});
+});
📝 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
app.get("/auth/google/test", (req, res) => {
const hasClientId = !!process.env.GOOGLE_CLIENT_ID;
const hasClientSecret = !!process.env.GOOGLE_CLIENT_SECRET;
const callbackUrl = process.env.GOOGLE_CALLBACK_URL || "http://localhost:5000/auth/google/callback";
res.json({
message: "Google OAuth Configuration Test",
status: hasClientId && hasClientSecret ? "configured" : "missing_credentials",
configuration: {
hasClientId,
hasClientSecret,
callbackUrl,
frontendUrl: process.env.FRONTEND_URL || "http://localhost:3000"
}
});
});
// Only enable the Google OAuth configuration test endpoint in non-production environments
if (process.env.NODE_ENV !== "production") app.get("/auth/google/test", (req, res) => {
const hasClientId = !!process.env.GOOGLE_CLIENT_ID;
const hasClientSecret = !!process.env.GOOGLE_CLIENT_SECRET;
const callbackUrl = process.env.GOOGLE_CALLBACK_URL || "http://localhost:5000/auth/google/callback";
res.json({
message: "Google OAuth Configuration Test",
status: hasClientId && hasClientSecret ? "configured" : "missing_credentials",
configuration: {
hasClientId,
hasClientSecret,
callbackUrl,
frontendUrl: process.env.FRONTEND_URL || "http://localhost:3000"
}
});
});
🤖 Prompt for AI Agents
In backend/app.js around lines 72-87, the public /auth/google/test endpoint
discloses OAuth presence and URLs which is sensitive in production; remove or
gate it so it cannot be hit in prod: either register the route only when
NODE_ENV !== 'production' (or another explicit dev flag) or protect it with an
authenticated/admin-only middleware, and change the response in production to a
generic non-revealing success/failure message (no client IDs, secrets, or
callback/frontend URLs). Ensure the fix prevents any sensitive configuration
from being returned in production while keeping the endpoint available for
local/dev testing.


module.exports = app;
2 changes: 1 addition & 1 deletion backend/config/config.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
require('dotenv').config();

const config = {
port: process.env.PORT || 3000,
port: process.env.PORT || 5000,
Copy link
Contributor

Choose a reason for hiding this comment

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

💡 Verification agent

🧩 Analysis chain

Port default changed to 5000 — verify server wiring and callbacks.

Ensure the HTTP server actually uses this config value and update any hardcoded localhost ports (OAuth callbacks, CORS, frontend API base URLs).


🏁 Script executed:

#!/bin/bash
# Is config.port used by the server?
rg -nP --glob '!**/node_modules/**' '\b(config\.port|process\.env\.PORT)\b' backend -C2

# Find hardcoded localhost ports (3000/5000) across repo
rg -nP --glob '!**/node_modules/**' 'localhost:(3000|5000)' -C2

# Common Google OAuth callback strings to verify
rg -nP --glob '!**/node_modules/**' 'google.*callback|/auth/google/callback' backend -C3

Length of output: 5660


Unify port defaults—remove hardcoded localhost URLs

• backend/routes/ContactRoute.js (line 7): import config and use config.port instead of process.env.PORT || 3001
• backend/app.js (lines 18 & 81): replace "http://localhost:3000" with process.env.FRONTEND_URL
• backend/app.js (line 72): replace default "http://localhost:5000/auth/google/callback" with process.env.GOOGLE_CALLBACK_URL or build from config.port
• backend/middlewares/Passport.js (line 18): use process.env.GOOGLE_CALLBACK_URL instead of hardcoded
• Frontend (src/Admin/Pages/VigyLogin.jsx lines 37 & 74; src/Admin/Pages/VigyForm.jsx line 337): use an env-based API base URL instead of http://localhost:5000

🤖 Prompt for AI Agents
In backend/config/config.js around line 7, the config currently uses a hardcoded
port fallback and other parts of the app expect env-driven URLs; update this
file to centralize env defaults by exporting port (process.env.PORT || 5000),
frontendUrl (process.env.FRONTEND_URL || 'http://localhost:3000'),
googleCallbackUrl (process.env.GOOGLE_CALLBACK_URL ||
`http://localhost:${port}/auth/google/callback`) and any API_BASE_URL if needed,
then ensure module.exports includes these values so other files
(ContactRoute.js, app.js, Passport.js, frontend callers) can import config.port,
config.frontendUrl, and config.googleCallbackUrl instead of using hardcoded
localhost strings or duplicating env logic.

mongoURI: process.env.MONGO_URI || 'mongodb://localhost:27017/myapp',
jwtSecret: process.env.JWT_SECRET || 'mysecretkey',
sessionSecret: process.env.SESSION_SECRET || 'defaultsessionsecret',
Expand Down
3 changes: 2 additions & 1 deletion backend/config/db.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@ const connectDB = async () => {
console.log("Connected to MongoDB");
} catch (err) {
console.error("Error connecting to MongoDB:", err.message);
process.exit(1);
console.log("⚠️ MongoDB not available. Using in-memory fallback for development.");
// Don't exit for development - let the app continue
}
Comment on lines +12 to 14
Copy link
Contributor

Choose a reason for hiding this comment

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

💡 Verification agent

🧩 Analysis chain

Confirm an actual in-memory fallback exists and is wired.

This file only logs; ensure routes use a real store in dev.


🏁 Script executed:

#!/bin/bash
# Look for any in-memory store used by Vigy routes
rg -nP "(in-?memory|memory store|fallback).*vigy" backend -C3
rg -nP "router\\.|app\\.use\\(.*/api/vigy" -C2 backend

Length of output: 4643


Implement actual in-memory MongoDB fallback in development (backend/config/db.js:12–14)
Logging the fallback alone is insufficient—no in-memory store is wired up. Integrate and initialize a real in-memory database (e.g. MongoMemoryServer) and ensure all routes (including Vigy) use it when MongoDB is unavailable.

🤖 Prompt for AI Agents
In backend/config/db.js around lines 12–14, the code only logs that MongoDB is
unavailable but does not wire up a real in-memory fallback; implement a
MongoMemoryServer-based fallback for development: start MongoMemoryServer when
the real Mongo URI fails to connect (only in NODE_ENV !== 'production'), obtain
its URI and reconnect mongoose to that URI, ensure the returned connection
promise resolves before the app continues so all routes (including Vigy) use the
in-memory DB, surface the original connection error in the log, and add
shutdown/cleanup handlers to stop the MongoMemoryServer on process exit.

🛠️ Refactor suggestion

Don't silently continue on DB failure in non-dev. Gate by NODE_ENV.

Exiting in prod prevents partial-start bugs.

   } catch (err) {
     console.error("Error connecting to MongoDB:", err.message);
-    console.log("⚠️  MongoDB not available. Using in-memory fallback for development.");
-    // Don't exit for development - let the app continue
+    if (process.env.NODE_ENV !== "development" && process.env.NODE_ENV !== "test") {
+      console.error("MongoDB unavailable. Exiting (non-dev).");
+      process.exit(1);
+    }
+    console.warn("⚠️  MongoDB not available. Using in-memory fallback for development.");
+    // Keep the app running only in dev/test
   }
📝 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("⚠️ MongoDB not available. Using in-memory fallback for development.");
// Don't exit for development - let the app continue
}
} catch (err) {
console.error("Error connecting to MongoDB:", err.message);
if (process.env.NODE_ENV !== "development" && process.env.NODE_ENV !== "test") {
console.error("MongoDB unavailable. Exiting (non-dev).");
process.exit(1);
}
console.warn("⚠️ MongoDB not available. Using in-memory fallback for development.");
// Keep the app running only in dev/test
}
🤖 Prompt for AI Agents
In backend/config/db.js around lines 12 to 14, the code currently logs a warning
and silently continues when MongoDB is unavailable; change this so the fallback
is only used in development by checking process.env.NODE_ENV === 'development',
and if NODE_ENV is anything else log the error (including any caught error
details) and exit the process with a non-zero code (e.g., process.exit(1));
ensure the fallback branch is guarded by the dev check so production/staging
never continues silently on DB failure.

};

Expand Down
227 changes: 227 additions & 0 deletions backend/controllers/vigyController.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,227 @@
const Vigy = require('../models/Vigy');
const jwt = require('jsonwebtoken');

// Generate JWT Token
const generateToken = (vigyId) => {
return jwt.sign({ vigyId }, process.env.JWT_SECRET || 'mysecretkey', {
expiresIn: '7d'
});
};
Comment on lines +5 to +9
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

Eliminate insecure JWT secret fallback

Hard-coding a default ('mysecretkey') is unsafe; fail fast if JWT_SECRET is missing.

-const generateToken = (vigyId) => {
-  return jwt.sign({ vigyId }, process.env.JWT_SECRET || 'mysecretkey', {
-    expiresIn: '7d'
-  });
-};
+const generateToken = (vigyId) => {
+  const secret = process.env.JWT_SECRET;
+  if (!secret) {
+    throw new Error('JWT_SECRET is not configured');
+  }
+  return jwt.sign({ vigyId }, secret, { expiresIn: '7d' });
+};
📝 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 generateToken = (vigyId) => {
return jwt.sign({ vigyId }, process.env.JWT_SECRET || 'mysecretkey', {
expiresIn: '7d'
});
};
const generateToken = (vigyId) => {
const secret = process.env.JWT_SECRET;
if (!secret) {
throw new Error('JWT_SECRET is not configured');
}
return jwt.sign({ vigyId }, secret, { expiresIn: '7d' });
};
🤖 Prompt for AI Agents
In backend/controllers/vigyController.js around lines 5 to 9, the generateToken
function currently falls back to a hard-coded JWT secret which is insecure;
change it to require a configured secret by reading process.env.JWT_SECRET once
and throwing an explicit error (or returning a rejected promise) if it's
undefined/empty, then use that secret in jwt.sign so the app fails fast during
startup or token generation rather than using 'mysecretkey'.


// @desc Register new Vigy user
// @route POST /api/vigy/register
// @access Public
const registerVigy = async (req, res) => {
try {
const {
fullname,
email,
password,
phoneNumber,
address,
aadhaarNumber,
gender,
dob,
bankAccountName,
bankAccountNumber,
bankName,
bankBranch,
ifscCode,
referralCode,
promotionalCode
} = req.body;

Comment on lines +16 to +33
Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Validate and normalize registration input up-front

Add presence/type checks and sanitize to prevent malformed data and operator payloads.

 const registerVigy = async (req, res) => {
   try {
     const {
       fullname,
       email,
       password,
       phoneNumber,
       address,
       aadhaarNumber,
       gender,
       dob,
       bankAccountName,
       bankAccountNumber,
       bankName,
       bankBranch,
       ifscCode,
       referralCode,
       promotionalCode
     } = req.body;
+
+    // Basic type/format validation + normalization
+    const emailNorm = typeof email === 'string' ? email.trim().toLowerCase() : '';
+    const aadhaarNorm = typeof aadhaarNumber === 'string' ? aadhaarNumber.trim() : '';
+    if ([fullname, emailNorm, password, phoneNumber, address, aadhaarNorm, gender, dob].some(v => !v)) {
+      return res.status(400).json({ success: false, message: 'Missing required fields' });
+    }
+    if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(emailNorm)) {
+      return res.status(400).json({ success: false, message: 'Invalid email' });
+    }
+    if (!/^\d{12}$/.test(aadhaarNorm)) {
+      return res.status(400).json({ success: false, message: 'Invalid Aadhaar number' });
+    }
📝 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 {
fullname,
email,
password,
phoneNumber,
address,
aadhaarNumber,
gender,
dob,
bankAccountName,
bankAccountNumber,
bankName,
bankBranch,
ifscCode,
referralCode,
promotionalCode
} = req.body;
const {
fullname,
email,
password,
phoneNumber,
address,
aadhaarNumber,
gender,
dob,
bankAccountName,
bankAccountNumber,
bankName,
bankBranch,
ifscCode,
referralCode,
promotionalCode
} = req.body;
// Basic type/format validation + normalization
const emailNorm = typeof email === 'string' ? email.trim().toLowerCase() : '';
const aadhaarNorm = typeof aadhaarNumber === 'string' ? aadhaarNumber.trim() : '';
if ([fullname, emailNorm, password, phoneNumber, address, aadhaarNorm, gender, dob].some(v => !v)) {
return res.status(400).json({ success: false, message: 'Missing required fields' });
}
if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(emailNorm)) {
return res.status(400).json({ success: false, message: 'Invalid email' });
}
if (!/^\d{12}$/.test(aadhaarNorm)) {
return res.status(400).json({ success: false, message: 'Invalid Aadhaar number' });
}
🤖 Prompt for AI Agents
In backend/controllers/vigyController.js around lines 16 to 33, the controller
currently destructures req.body without validating or normalizing inputs; add
upfront validation and sanitization: ensure required fields (fullname, email,
password) are present and of expected types, trim strings, lowercase and
validate email, strip non-digits from phoneNumber and validate length,
parse/validate dob, enforce Aadhaar and bankAccountNumber are numeric with
expected lengths, uppercase and validate IFSC format, normalize bankName/branch
strings, and treat referralCode/promotionalCode as optional; additionally reject
or ignore unexpected keys and return a 400 with clear messages on validation
failures (implement via a validation library like Joi/express-validator or a
small custom sanitizer before using values).

// Check if Vigy already exists
const existingVigy = await Vigy.findOne({
$or: [{ email }, { aadhaarNumber }]
});
Comment on lines +35 to +37
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

Fix NoSQL injection risk in uniqueness check

Do not pass unvalidated values directly into the query. Use normalized strings.

-    const existingVigy = await Vigy.findOne({ 
-      $or: [{ email }, { aadhaarNumber }] 
-    });
+    const existingVigy = await Vigy.findOne({
+      $or: [{ email: emailNorm }, { aadhaarNumber: aadhaarNorm }]
+    });

Committable suggestion skipped: line range outside the PR's diff.

🤖 Prompt for AI Agents
In backend/controllers/vigyController.js around lines 35-37, the uniqueness
query uses raw input values which allows NoSQL injection; normalize and validate
inputs before constructing the query: coerce email to a trimmed lowercase string
and validate its format, coerce aadhaarNumber to a trimmed string of digits
(strip non-digits) and validate its length/format, bail out or return a 400
error if validation fails, then use the normalizedEmail and normalizedAadhaar in
the Vigy.findOne query (do not interpolate raw req values). Also ensure you only
add each field to the $or array if the normalized value exists to avoid
injecting unexpected types.


if (existingVigy) {
return res.status(400).json({
success: false,
message: existingVigy.email === email
? 'Email already registered'
: 'Aadhaar number already registered'
});
}

// Create new Vigy user
const vigy = new Vigy({
fullname,
email,
password,
phoneNumber,
address,
aadhaarNumber,
gender,
dob,
bankDetails: {
accountName: bankAccountName,
accountNumber: bankAccountNumber,
bankName,
branch: bankBranch,
ifscCode
},
referralCode,
promotionalCode
});
Comment on lines +48 to +67
Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Use normalized values on create

Ensure stored email/aadhaar are normalized to match queries and indexes.

     const vigy = new Vigy({
       fullname,
-      email,
+      email: emailNorm,
       password,
       phoneNumber,
       address,
-      aadhaarNumber,
+      aadhaarNumber: aadhaarNorm,
       gender,
       dob,
       bankDetails: {
         accountName: bankAccountName,
         accountNumber: bankAccountNumber,
         bankName,
         branch: bankBranch,
         ifscCode
       },
       referralCode,
       promotionalCode
     });
📝 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
// Create new Vigy user
const vigy = new Vigy({
fullname,
email,
password,
phoneNumber,
address,
aadhaarNumber,
gender,
dob,
bankDetails: {
accountName: bankAccountName,
accountNumber: bankAccountNumber,
bankName,
branch: bankBranch,
ifscCode
},
referralCode,
promotionalCode
});
// Create new Vigy user
const vigy = new Vigy({
fullname,
email: emailNorm,
password,
phoneNumber,
address,
aadhaarNumber: aadhaarNorm,
gender,
dob,
bankDetails: {
accountName: bankAccountName,
accountNumber: bankAccountNumber,
bankName,
branch: bankBranch,
ifscCode
},
referralCode,
promotionalCode
});
🤖 Prompt for AI Agents
In backend/controllers/vigyController.js around lines 48-67, the Vigy document
is created using raw email and aadhaarNumber; normalize and store canonical
values instead: derive a normalizedEmail = email.trim().toLowerCase() and a
normalizedAadhaar = aadhaarNumber.replace(/\D/g) (strip non-digits) before
constructing the Vigy object, use those normalized fields in the object (e.g.,
email: normalizedEmail, aadhaarNumber: normalizedAadhaar), and ensure any
related query/indexes use the same normalized form.


await vigy.save();

// Generate token
const token = generateToken(vigy._id);

res.status(201).json({
success: true,
message: 'Vigy registration successful',
token,
user: vigy.getPublicProfile()
});

} catch (error) {
console.error('Vigy registration error:', error);
res.status(500).json({
success: false,
message: 'Server error during registration',
error: error.message
});
}
Comment on lines +81 to +88
Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Handle duplicate key (E11000) explicitly

Avoid leaking internals and return deterministic 400s for unique constraint violations.

   } catch (error) {
-    console.error('Vigy registration error:', error);
-    res.status(500).json({
-      success: false,
-      message: 'Server error during registration',
-      error: error.message
-    });
+    if (error && error.code === 11000) {
+      const key = Object.keys(error.keyPattern || {})[0] || 'field';
+      const message =
+        key === 'email'
+          ? 'Email already registered'
+          : key === 'aadhaarNumber'
+          ? 'Aadhaar number already registered'
+          : 'Duplicate value';
+      return res.status(400).json({ success: false, message });
+    }
+    console.error('Vigy registration error:', error);
+    res.status(500).json({ success: false, message: 'Server error during registration' });
   }
📝 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
} catch (error) {
console.error('Vigy registration error:', error);
res.status(500).json({
success: false,
message: 'Server error during registration',
error: error.message
});
}
} catch (error) {
if (error && error.code === 11000) {
const key = Object.keys(error.keyPattern || {})[0] || 'field';
const message =
key === 'email'
? 'Email already registered'
: key === 'aadhaarNumber'
? 'Aadhaar number already registered'
: 'Duplicate value';
return res.status(400).json({ success: false, message });
}
console.error('Vigy registration error:', error);
res.status(500).json({ success: false, message: 'Server error during registration' });
}
🤖 Prompt for AI Agents
In backend/controllers/vigyController.js around lines 81 to 88, the catch block
currently logs the full error and returns the error.message to the client;
change it to explicitly detect Mongo duplicate key errors (error.code === 11000
or error.name === 'MongoError' / contains E11000) and respond with a
deterministic 400 and a brief message like "Resource already exists" (optionally
include which field from error.keyValue), while logging the full error
server-side without sending internals to the client; all other errors should
continue to return a 500 with a generic message.

};

// @desc Login Vigy user
// @route POST /api/vigy/login
// @access Public
const loginVigy = async (req, res) => {
try {
const { email, password } = req.body;

// Validate input
if (!email || !password) {
return res.status(400).json({
success: false,
message: 'Please provide both email and password'
});
}
Comment on lines +96 to +104
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

Harden login input to prevent NoSQL operator payloads

Type-check and normalize before use.

-    // Validate input
-    if (!email || !password) {
-      return res.status(400).json({
-        success: false,
-        message: 'Please provide both email and password'
-      });
-    }
+    // Validate input + normalize
+    if (typeof email !== 'string' || typeof password !== 'string') {
+      return res.status(400).json({ success: false, message: 'Invalid payload' });
+    }
+    const emailNorm = email.trim().toLowerCase();
+    if (!emailNorm || !password) {
+      return res.status(400).json({ success: false, message: 'Please provide both email and password' });
+    }
📝 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 { email, password } = req.body;
// Validate input
if (!email || !password) {
return res.status(400).json({
success: false,
message: 'Please provide both email and password'
});
}
const { email, password } = req.body;
// Validate input + normalize
if (typeof email !== 'string' || typeof password !== 'string') {
return res.status(400).json({ success: false, message: 'Invalid payload' });
}
const emailNorm = email.trim().toLowerCase();
if (!emailNorm || !password) {
return res.status(400).json({ success: false, message: 'Please provide both email and password' });
}
🤖 Prompt for AI Agents
In backend/controllers/vigyController.js around lines 96 to 104, the login input
is used directly and can accept non-string NoSQL operator payloads; ensure you
type-check and normalize both fields before use by verifying req.body.email and
req.body.password are primitive strings (reject if they are objects/arrays/other
types), coerce/convert to string safely, trim whitespace, enforce sensible
length limits, and validate email shape (or restrict allowed characters) for
email; explicitly reject inputs that start with "$" or contain characters that
could represent operators or nested paths, and return 400 if validation fails so
only safe, normalized strings are passed to authentication logic.


// Find Vigy user
const vigy = await Vigy.findOne({ email });

Comment on lines +106 to +108
Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Use normalized email in the login query

Avoid case-mismatch and ensure exact match.

-    const vigy = await Vigy.findOne({ email });
+    const vigy = await Vigy.findOne({ email: emailNorm });
📝 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
// Find Vigy user
const vigy = await Vigy.findOne({ email });
// Find Vigy user
const vigy = await Vigy.findOne({ email: emailNorm });
🤖 Prompt for AI Agents
In backend/controllers/vigyController.js around lines 106-108, the login lookup
uses the raw email which can fail on case or whitespace differences; normalize
the incoming email (trim and toLowerCase) before querying (e.g., compute
normalizedEmail = email.trim().toLowerCase() and use Vigy.findOne({ email:
normalizedEmail })), and ensure user creation also stores emails normalized (and
ideally enforce a unique lowercase index in the DB) so lookups are consistent.

if (!vigy) {
return res.status(401).json({
success: false,
message: 'Invalid credentials'
});
}

// Check password
const isPasswordValid = await vigy.comparePassword(password);

if (!isPasswordValid) {
return res.status(401).json({
success: false,
message: 'Invalid credentials'
});
}

// Check if account is approved
if (vigy.status !== 'approved' && vigy.status !== 'active') {
return res.status(403).json({
success: false,
message: `Account status: ${vigy.status}. Please wait for approval.`
});
}

// Generate token
const token = generateToken(vigy._id);

res.json({
success: true,
message: 'Login successful',
token,
user: vigy.getPublicProfile()
});

} catch (error) {
console.error('Vigy login error:', error);
res.status(500).json({
success: false,
message: 'Server error during login',
error: error.message
});
}
};

// @desc Get Vigy profile
// @route GET /api/vigy/profile
// @access Private
const getVigyProfile = async (req, res) => {
try {
const vigy = await Vigy.findById(req.vigyId);

if (!vigy) {
return res.status(404).json({
success: false,
message: 'Vigy user not found'
});
}

res.json({
success: true,
user: vigy.getPublicProfile()
});

Comment on lines +154 to +172
Copy link
Contributor

Choose a reason for hiding this comment

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

💡 Verification agent

🧩 Analysis chain

Confirm auth middleware contract for req.vigyId

Ensure JWT auth middleware validates the token, handles expiry, and sets req.vigyId; return 401 if absent.


🏁 Script executed:

#!/bin/bash
# Locate Vigy auth middleware and its contract
rg -nP -C2 'req\.vigyId|vigy.*auth|authenticateVigy|verifyVigy' --type=js

Length of output: 4211


I requested the contents of backend/middlewares to locate the authentication middleware implementation.


🏁 Script executed:

#!/bin/bash
# List files in backend/middlewares
echo "Files in backend/middlewares:"
ls backend/middlewares

echo -e "\nSearch for jsonwebtoken import in middlewares:"
rg -nP "require\(['\"]jsonwebtoken['\"]\)" -g "backend/middlewares/*.js"

echo -e "\nSearch for req.vigyId assignment in middlewares:"
rg -nP "req\.vigyId" -g "backend/middlewares/*.js"

echo -e "\nSearch for auth middleware exports in middlewares:"
rg -nP "module\.exports.*auth" -g "backend/middlewares/*.js"

Length of output: 639


🏁 Script executed:

#!/bin/bash
# Print first 200 lines of the Passport middleware
sed -n '1,200p' backend/middlewares/Passport.js

Length of output: 2408


🏁 Script executed:

#!/bin/bash
# Locate and inspect the Vigy routes file for getVigyProfile
route_file=$(rg -l "getVigyProfile" -g "backend/routes/*.js")
echo "Route file: $route_file"

echo -e "\nRoute definition and applied middleware:"
sed -n '1,200p' "$route_file"

Length of output: 257


🏁 Script executed:

#!/bin/bash
# List top-level files and folders in backend
echo "backend directory listing:"
ls -1 backend

Length of output: 234


🏁 Script executed:

#!/bin/bash
# Search for jsonwebtoken usage across the codebase
rg -n "require.*jsonwebtoken" -n .

echo "Middlewares in backend/middlewares:"
ls -1 backend/middlewares

echo "Auth-related files in backend:"
find backend -type f -name "*auth*.js"

echo "Route files in backend/routes:"
ls -1 backend/routes

echo "Route files referencing vigyController:"
rg -l "vigyController" -g "backend/routes/*.js"
for f in $(rg -l "vigyController" -g "backend/routes/*.js"); do
  echo "==== $f ===="
  sed -n '1,200p' "$f"
done

Length of output: 838


🏁 Script executed:

#!/bin/bash
# Display vigyRoutes.js to inspect applied middleware
sed -n '1,200p' backend/routes/vigyRoutes.js

Length of output: 3454


Implement and apply JWT auth middleware for Vigy routes

  • No middleware currently validates JWTs or populates req.vigyId in backend/middlewares. Create (e.g. authenticateVigy) that calls jsonwebtoken.verify, handles token expiry, sets req.vigyId = decoded.id, and responds 401 on missing/invalid tokens.
  • In backend/routes/vigyRoutes.js, apply this middleware to the /profile (and any other protected) routes before invoking getVigyProfile.
🤖 Prompt for AI Agents
In backend/controllers/vigyController.js around lines 154 to 172, the controller
expects req.vigyId but no JWT auth middleware exists; create a new middleware
backend/middlewares/authenticateVigy.js that reads the Authorization header,
verifies the token with jsonwebtoken.verify (using your JWT secret), on success
sets req.vigyId = decoded.id and calls next(), and on missing/invalid/expired
token responds with 401 and a clear JSON error; then import and apply this
authenticateVigy middleware in backend/routes/vigyRoutes.js to the /profile
route (and any other protected Vigy routes) before getVigyProfile so req.vigyId
is populated.

} catch (error) {
console.error('Get Vigy profile error:', error);
res.status(500).json({
success: false,
message: 'Server error while fetching profile',
error: error.message
});
}
};

// @desc Update Vigy profile
// @route PUT /api/vigy/profile
// @access Private
const updateVigyProfile = async (req, res) => {
try {
const updates = req.body;
delete updates.password; // Don't allow password update through this route
delete updates.email; // Don't allow email update through this route
delete updates.aadhaarNumber; // Don't allow Aadhaar update through this route

const vigy = await Vigy.findByIdAndUpdate(
req.vigyId,
{ ...updates, updatedAt: Date.now() },
{ new: true, runValidators: true }
);
Comment on lines +186 to +197
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

Prevent mass-assignment in profile update

Current spread allows hostile fields (e.g., role, status) to be updated. Whitelist allowed keys and rely on timestamps.

-    const updates = req.body;
-    delete updates.password; // Don't allow password update through this route
-    delete updates.email; // Don't allow email update through this route
-    delete updates.aadhaarNumber; // Don't allow Aadhaar update through this route
-
-    const vigy = await Vigy.findByIdAndUpdate(
-      req.vigyId,
-      { ...updates, updatedAt: Date.now() },
-      { new: true, runValidators: true }
-    );
+    const { password, email, aadhaarNumber, ...rest } = req.body;
+    const allowed = ['fullname', 'phoneNumber', 'address', 'gender', 'dob', 'bankDetails', 'referralCode', 'promotionalCode'];
+    const safeUpdates = {};
+    for (const key of allowed) {
+      if (Object.prototype.hasOwnProperty.call(rest, key)) {
+        safeUpdates[key] = rest[key];
+      }
+    }
+    const vigy = await Vigy.findByIdAndUpdate(
+      req.vigyId,
+      { $set: safeUpdates },
+      { new: true, runValidators: 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 updateVigyProfile = async (req, res) => {
try {
const updates = req.body;
delete updates.password; // Don't allow password update through this route
delete updates.email; // Don't allow email update through this route
delete updates.aadhaarNumber; // Don't allow Aadhaar update through this route
const vigy = await Vigy.findByIdAndUpdate(
req.vigyId,
{ ...updates, updatedAt: Date.now() },
{ new: true, runValidators: true }
);
const updateVigyProfile = async (req, res) => {
try {
const { password, email, aadhaarNumber, ...rest } = req.body;
const allowed = [
'fullname',
'phoneNumber',
'address',
'gender',
'dob',
'bankDetails',
'referralCode',
'promotionalCode'
];
const safeUpdates = {};
for (const key of allowed) {
if (Object.prototype.hasOwnProperty.call(rest, key)) {
safeUpdates[key] = rest[key];
}
}
const vigy = await Vigy.findByIdAndUpdate(
req.vigyId,
{ $set: safeUpdates },
{ new: true, runValidators: true }
);


if (!vigy) {
return res.status(404).json({
success: false,
message: 'Vigy user not found'
});
}

res.json({
success: true,
message: 'Profile updated successfully',
user: vigy.getPublicProfile()
});

} catch (error) {
console.error('Update Vigy profile error:', error);
res.status(500).json({
success: false,
message: 'Server error while updating profile',
error: error.message
});
}
};

module.exports = {
registerVigy,
loginVigy,
getVigyProfile,
updateVigyProfile
};
61 changes: 61 additions & 0 deletions backend/createTestUser.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
const mongoose = require('mongoose');
const Vigy = require('./models/Vigy');
const config = require('./config/config');

// Connect to MongoDB
mongoose.connect(config.mongoURI)
.then(() => console.log('Connected to MongoDB'))
.catch(err => console.error('MongoDB connection error:', err));

// Test user data
const testUser = {
fullname: 'Test Vigy User',
email: '[email protected]',
password: 'test123',
phoneNumber: '1234567890',
address: '123 Test Street, Test City, Test State 123456',
aadhaarNumber: '123456789012',
gender: 'other',
dob: '1990-01-01',
bankDetails: {
accountName: 'TEST USER',
accountNumber: '1234567890',
bankName: 'SBI',
branch: 'TEST BRANCH',
ifscCode: 'SBIN0001234'
},
referralCode: 'TEST123',
promotionalCode: 'PROMO123',
status: 'approved', // Set to approved so user can login
isVerified: true
};

async function createTestUser() {
try {
// Check if user already exists
const existingUser = await Vigy.findOne({ email: testUser.email });

if (existingUser) {
console.log('Test user already exists');
console.log('Email:', existingUser.email);
console.log('Status:', existingUser.status);
return;
}

// Create new test user
const vigy = new Vigy(testUser);
await vigy.save();

console.log('Test user created successfully!');
console.log('Email:', vigy.email);
console.log('Password: test123');
console.log('Status:', vigy.status);

} catch (error) {
console.error('Error creating test user:', error);
} finally {
mongoose.connection.close();
}
}

createTestUser();
Loading