-
Notifications
You must be signed in to change notification settings - Fork 401
Fix: Corrected routing for 'Login as Vigy' button (Issue #2638) #2658
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
WalkthroughAdds a Vigy feature (model, controllers, routes, frontend pages), updates server configuration (CORS, sessions, port, DB error handling), introduces env/setup and test-user scripts, adjusts OAuth checks and redirects, and contains several unresolved merge-conflict markers in middleware/routes. Changes
Sequence Diagram(s)sequenceDiagram
autonumber
actor U as User (Browser)
participant FE as Frontend (VigyLogin/VigyForm)
participant BE as Backend (/api/vigy)
participant DB as MongoDB
rect rgba(224,240,255,0.35)
note over U,FE: Login flow
U->>FE: submit credentials
FE->>BE: POST /api/vigy/login {email,password}
BE->>DB: find user, verify password
DB-->>BE: user doc / not found
BE-->>FE: 200 {token, publicProfile} or 401/403
FE-->>U: store token, redirect /vigy-dashboard
end
rect rgba(230,255,230,0.35)
note over U,FE: Registration flow
U->>FE: submit registration form
FE->>BE: POST /api/vigy/register {profile + password}
BE->>DB: validate uniqueness, create user
DB-->>BE: saved doc or duplicate/error
BE-->>FE: 201 or 400/500
FE-->>U: show success / error
end
sequenceDiagram
autonumber
actor U as User (Browser)
participant FE as Frontend
participant BE as Backend (/auth/google)
participant G as Google OAuth
rect rgba(255,245,230,0.35)
U->>FE: click "Continue with Google"
FE->>BE: GET /auth/google
BE->>BE: validate GOOGLE_CLIENT_ID/SECRET & resolved callback URL
BE-->>U: redirect to Google or 500 if misconfigured
U->>G: consent
G-->>BE: callback -> /auth/google/callback
BE-->>FE: redirect to FRONTEND_URL with success/error query
end
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes Possibly related issues
Possibly related PRs
Suggested reviewers
Poem
Pre-merge checks and finishing touches❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing touches
🧪 Generate unit tests
Tip 👮 Agentic pre-merge checks are now available in preview!Pro plan users can now enable pre-merge checks in their settings to enforce checklists before merging PRs.
Please see the documentation for more information. Example: reviews:
pre_merge_checks:
custom_checks:
- name: "Undocumented Breaking Changes"
mode: "warning"
instructions: |
Pass/fail criteria: All breaking changes to public APIs, CLI flags, environment variables, configuration keys, database schemas, or HTTP/GraphQL endpoints must be documented in the "Breaking Change" section of the PR description and in CHANGELOG.md. Exclude purely internal or private changes (e.g., code not exported from package entry points or explicitly marked as internal). Please share your feedback with us on this Discord post. 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. Comment |
@ajaykuraparthi is attempting to deploy a commit to the Vivek Prajapati's projects Team on Vercel. A member of the Team first needs to authorize it. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 32
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (5)
src/Admin/Pages/VigyForm.jsx (4)
141-143
: JSX pattern escapes are broken; use [0-9] or escape backslashes.Current "\d{12}" becomes "d{12}" in JSX.
- pattern="\d{12}" + pattern="[0-9]{12}"
169-171
: Fix phone pattern escape.- pattern="\d{10}" + pattern="[0-9]{10}"
201-205
: Fix bank account number pattern escape.- pattern="\d{8,18}" + pattern="[0-9]{8,18}"
245-251
: Removerequired
from file inputs or support multipart uploads
ThepanCard
(and the other file input at lines 253–260) aren’t included in the JSON payload you currently send, so they’re never uploaded. Either switch your submission to useFormData
(and a backend endpoint that accepts multipart) or drop therequired
prop:- required
backend/createTestUser.js (1)
1-62
: Prevent accidental prod seeding; ensure clean connect/disconnect flow.
- Add a production guard to avoid creating approved test users in live data.
- Prefer
await mongoose.connect(...)
andawait mongoose.disconnect()
to ensure deterministic teardown.-const mongoose = require('mongoose'); -const Vigy = require('./models/Vigy'); -const config = require('./config/config'); +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)); +async function connect() { + await mongoose.connect(config.mongoURI); + console.log('Connected to MongoDB'); +} // Test user data const testUser = { @@ isVerified: true }; async function createTestUser() { try { + if (process.env.NODE_ENV === 'production') { + console.error('Refusing to seed test user in production.'); + return; + } + await connect(); // Check if user already exists const existingUser = await Vigy.findOne({ email: testUser.email }); @@ } catch (error) { console.error('Error creating test user:', error); } finally { - mongoose.connection.close(); + await mongoose.disconnect(); } } createTestUser();
🧹 Nitpick comments (21)
src/Admin/Pages/VigyDashboard.jsx (2)
15-16
: Use history-replacing redirects to avoid back-stack loops.Replace pushes to /vigy-login with replace: true.
- navigate('/vigy-login'); + navigate('/vigy-login', { replace: true }); @@ - navigate('/vigy-login'); + navigate('/vigy-login', { replace: true }); @@ - navigate('/vigy-login'); + navigate('/vigy-login', { replace: true });Also applies to: 23-24, 30-31
9-25
: Optionally validate token on mount.Consider a lightweight /api/vigy/profile ping; if 401, logout and redirect.
src/Admin/Pages/VigyForm.jsx (4)
299-312
: Dead code: FormData is built but not used.Remove to avoid confusion, or switch to multipart and use it.
- const formDataToSend = new FormData(); - - // Append all form fields to formDataToSend - Object.keys(formData).forEach(tab => { - Object.keys(formData[tab]).forEach(field => { - if (field === 'panCard' || field === 'addressProof' || field === 'profilePicture') { - formDataToSend.append(field, formData[tab][field], formData[tab][field].name); - } else { - formDataToSend.append(field, formData[tab][field]); - } - }); - });
334-339
: Centralize API base and avoid hardcoding env switch in components.Use a single config (e.g., import { API_BASE } from config) and append /api/vigy/register.
341-345
: Redirect after successful registration.Provide a clear next step.
alert('Registration submitted successfully! Please wait for approval.'); - // You can redirect to login page or show success message + // Redirect to Vigy login + window.location.replace('/vigy-login');
240-268
: 50KB limit for documents may be too strict.Confirm backend constraints; typical KYC scans exceed 50KB.
backend/models/Vigy.js (5)
10-16
: Tighten input validation (email/phone/IFSC/DOB).Add format checks and a simple age gate.
email: { type: String, required: true, unique: true, trim: true, - lowercase: true + lowercase: true, + match: [/^\S+@\S+\.\S+$/, 'Invalid email'], }, phoneNumber: { type: String, required: true, - trim: true + trim: true, + match: [/^\d{10}$/, 'Phone number must be 10 digits'], }, ifscCode: { type: String, required: true, - trim: true + trim: true, + match: [/^[A-Z]{4}0[A-Z0-9]{6}$/, 'Invalid IFSC code'], } dob: { type: Date, - required: true + required: true, + validate: { + validator: v => { + const now = new Date(); + const min = new Date(now.getFullYear() - 18, now.getMonth(), now.getDate()); + return v <= min; + }, + message: 'User must be at least 18 years old', + } },Also applies to: 22-26, 69-73, 44-47
109-116
: Prefer Mongoose-managed timestamps.
updatedAt
won’t auto-update as written. Usetimestamps: true
and drop manual date fields.-const vigySchema = new mongoose.Schema({ +const vigySchema = new mongoose.Schema({ ... - createdAt: { - type: Date, - default: Date.now - }, - updatedAt: { - type: Date, - default: Date.now - } -}); +}, { timestamps: true });Also applies to: 4-4, 117-117
120-130
: Hash on updates, not just creates.If passwords can be updated via
findOneAndUpdate
, add a hook to hash the new value.// Add alongside the pre('save') hook vigySchema.pre('findOneAndUpdate', async function (next) { const update = this.getUpdate() || {}; const pwd = update.password || (update.$set && update.$set.password); if (!pwd) return next(); try { const salt = await bcrypt.genSalt(10); const hashed = await bcrypt.hash(pwd, salt); if (update.$set && update.$set.password) update.$set.password = hashed; else update.password = hashed; this.setUpdate(update); next(); } catch (err) { next(err); } });
138-145
: Enforce safe serialization by default.Add a toJSON/toObject transform so sensitive fields are never leaked even if callers forget
getPublicProfile()
.vigySchema.set('toJSON', { transform: (_doc, ret) => { delete ret.password; delete ret.aadhaarNumber; delete ret.bankDetails; delete ret.documents; delete ret.__v; return ret; } }); vigySchema.set('toObject', { transform: vigySchema.get('toJSON').transform });
10-16
: Ensure unique indexes exist in the database.
unique: true
adds a unique index; confirm indexes are built in prod and backfill duplicates before enabling.You can run a one-time migration to create indexes and resolve duplicates on
aadhaarNumber
.Also applies to: 32-38
src/User/pages/UserAuth/UserAuth.jsx (1)
338-344
: Expose “Login as Vigy” on mobile too.Left panel is hidden on small screens; add a mobile-visible Vigy link next to the Admin one to keep parity.
<p className="sm:hidden text-sm text-center pb-0 mt-2"> <Link to="/admin-verification" className="bg-[#2d3e40] text-green-500 px-6 rounded-full hover:bg-opacity-90 transition duration-300" > Login as Admin </Link> </p> + <p className="sm:hidden text-sm text-center mt-2"> + <Link + to="/vigy-login" + className="bg-[#f9efe4] text-[#2d3e40] px-6 rounded-full border border-[#2d3e40] hover:bg-[#2d3e40] hover:text-white transition duration-300" + > + Login as Vigy + </Link> + </p>(Repeat for the signup side if desired.)
Also applies to: 481-488
backend/app.js (2)
17-20
: CORS: allowlist and previews.Consider a dynamic origin function to allow the configured FRONTEND_URL, localhost, and Vercel preview URLs. Keeps credentials working without overexposing.
-app.use(cors({ - origin: process.env.FRONTEND_URL || "http://localhost:3000", - credentials: true -})); +const allowlist = [ + process.env.FRONTEND_URL, + 'http://localhost:3000', + 'https://vigy-bag.vercel.app' +].filter(Boolean); +app.use(cors({ + origin(origin, cb) { + if (!origin || allowlist.some(o => (o instanceof RegExp ? o.test(origin) : o === origin))) return cb(null, true); + cb(new Error(`CORS blocked: ${origin}`)); + }, + credentials: true +}));
37-49
: Test endpoints are helpful; minor consistency nit.Expose
resolvedCallbackUrl
via the test route to mirror middleware calculations and avoid confusion with a hardcoded fallback.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"; + const callbackUrl = process.env.GOOGLE_CALLBACK_URL || "http://localhost:5000/auth/google/callback"; // mirror middleware resolution 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" } }); });Also applies to: 60-66, 69-84
src/Admin/Pages/VigyLogin.jsx (2)
35-38
: Avoid hard-coded backend URLs; centralize via env-driven base URL.This eases deploys across environments and prevents surprises in previews.
Apply:
- const apiUrl = process.env.NODE_ENV === 'production' - ? 'https://vigybag-backend.onrender.com/api/vigy/login' - : 'http://localhost:5000/api/vigy/login'; + const apiBase = + (typeof import.meta !== 'undefined' && import.meta.env?.VITE_API_BASE_URL) || + process.env.REACT_APP_API_BASE_URL || + (process.env.NODE_ENV === 'production' + ? 'https://vigybag-backend.onrender.com' + : 'http://localhost:5000'); + const apiUrl = `${apiBase}/api/vigy/login`;
28-69
: Add request timeout and (optional) credential mode for cookie-based auth.Prevents hanging UIs and prepares for HttpOnly-cookie auth.
- const response = await axios.post(apiUrl, formData); + const response = await axios.post(apiUrl, formData, { + timeout: 10000, + withCredentials: true + });backend/routes/vigyRoutes.js (2)
126-141
: Protected routes aren’t protected.Add auth middleware (JWT verify) or remove these endpoints from the dev router.
I can wire a minimal
authenticateJWT
middleware if desired.
1-144
: Mount correct implementation per environment.To avoid conflicts with DB-backed routes, mount conditionally in app.js.
Add in backend/app.js:
// Pseudocode if (process.env.NODE_ENV === 'production') { app.use('/api/vigy', require('./routes/vigyDbRoutes')); // real DB-backed routes } else { app.use('/api/vigy', require('./routes/vigyRoutes')); // in-memory dev routes }backend/routes/authRoutes.js (1)
23-26
: Propagate flow context to Google (state) so callback can branch.Wrap authenticate to pass a dynamic “state” from the query (default to “user”).
- passport.authenticate("google", { - scope: ["profile", "email"], - }) + (req, res, next) => { + const state = req.query.flow || 'user'; + return passport.authenticate("google", { + scope: ["profile", "email"], + state + })(req, res, next); + }Note: If your Google strategy isn’t already set with
state: true
, enable it.backend/controllers/vigyController.js (2)
126-132
: Prefer status constants/enumsReplace string literals with a shared enum to avoid typos and allow centralized changes.
1-227
: Operational hardening recommendations
- Add rate limiting and incremental lockouts on /login.
- Store Aadhaar/bank details encrypted at rest; consider field-level encryption.
- Consider issuing JWT in an HttpOnly, Secure, SameSite=Strict cookie for web flows.
- Centralize validation (e.g., celebrate/express-validator) and reuse schemas for register/update.
- Ensure CORS and cookie settings are strict in production.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
💡 Knowledge Base configuration:
- MCP integration is disabled by default for public repositories
- Jira integration is disabled by default for public repositories
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
⛔ Files ignored due to path filters (1)
backend/package-lock.json
is excluded by!**/package-lock.json
📒 Files selected for processing (16)
backend/app.js
(1 hunks)backend/config/config.js
(1 hunks)backend/config/db.js
(1 hunks)backend/controllers/vigyController.js
(1 hunks)backend/createTestUser.js
(1 hunks)backend/middlewares/Passport.js
(1 hunks)backend/models/Vigy.js
(1 hunks)backend/package.json
(3 hunks)backend/routes/authRoutes.js
(1 hunks)backend/routes/vigyRoutes.js
(1 hunks)backend/setup-env.js
(1 hunks)src/Admin/Pages/VigyDashboard.jsx
(1 hunks)src/Admin/Pages/VigyForm.jsx
(1 hunks)src/Admin/Pages/VigyLogin.jsx
(1 hunks)src/App.jsx
(2 hunks)src/User/pages/UserAuth/UserAuth.jsx
(1 hunks)
🧰 Additional context used
🧬 Code graph analysis (11)
src/Admin/Pages/VigyLogin.jsx (2)
src/Admin/Pages/VigyForm.jsx (3)
formData
(11-17)handleInputChange
(47-74)handleSubmit
(295-350)src/Admin/Pages/AdminLogin.jsx (2)
event
(13-28)
backend/config/config.js (1)
backend/server.js (1)
console
(11-13)
backend/controllers/vigyController.js (2)
backend/createTestUser.js (1)
Vigy
(2-2)backend/routes/adminRegistrationRoutes.js (1)
req
(14-82)
backend/createTestUser.js (3)
backend/models/Vigy.js (1)
mongoose
(1-1)backend/controllers/vigyController.js (5)
Vigy
(1-1)vigy
(49-67)vigy
(107-107)vigy
(159-159)vigy
(193-197)backend/config/config.js (1)
config
(6-14)
src/Admin/Pages/VigyForm.jsx (2)
src/User/pages/AdditionalDetails.jsx (2)
e
(10-36)backend/routes/adminRegistrationRoutes.js (1)
req
(14-82)
src/App.jsx (3)
src/Admin/Pages/VigyLogin.jsx (1)
VigyLogin
(8-207)src/Admin/Pages/VigyForm.jsx (1)
VigyForm
(9-432)src/Admin/Pages/VigyDashboard.jsx (1)
VigyDashboard
(5-174)
backend/middlewares/Passport.js (2)
backend/app.js (1)
passport
(5-5)backend/routes/authRoutes.js (1)
passport
(3-3)
backend/app.js (3)
backend/server.js (1)
app
(2-2)backend/routes/vigyRoutes.js (2)
express
(1-1)req
(78-78)backend/controllers/vigyController.js (2)
req
(16-32)req
(96-96)
backend/models/Vigy.js (2)
backend/controllers/authController.js (1)
req
(10-59)backend/routes/adminRegistrationRoutes.js (1)
req
(14-82)
backend/routes/authRoutes.js (2)
backend/app.js (1)
passport
(5-5)backend/middlewares/Passport.js (1)
passport
(1-1)
backend/routes/vigyRoutes.js (2)
backend/controllers/vigyController.js (2)
req
(16-32)req
(96-96)backend/routes/adminRegistrationRoutes.js (1)
req
(14-82)
🔇 Additional comments (10)
backend/package.json (1)
37-37
: Retainmongoose-findorcreate
dependency
Used inbackend/models/User.js
viauserSchema.plugin(findOrCreate)
.Likely an incorrect or invalid review comment.
src/Admin/Pages/VigyDashboard.jsx (1)
14-16
: Verified/vigy-login
route and its usage
The route is registered in App.jsx and the “Login as Vigy” link in UserAuth.jsx correctly points to/vigy-login
.backend/models/Vigy.js (1)
133-135
: LGTM: password comparison method is correct.src/User/pages/UserAuth/UserAuth.jsx (2)
212-216
: Correct route: /vigy-login fixes the 404.Change aligns the button with the new Vigy login route.
212-212
:/vigy-login
route exists and is accessible. App.jsx defines<Route path="/vigy-login" element={<VigyLogin/>}/>
(line 273) at root level outside any auth guards, so casing and availability are correct; no redirect-loop risk.src/App.jsx (1)
51-52
: Imports for Vigy pages look correct.These resolve to existing components and enable the new routes.
backend/app.js (1)
57-57
: Mounting Vigy routes looks good.This aligns frontend calls (e.g., /api/vigy/login and /api/vigy/register) with the router.
src/Admin/Pages/VigyLogin.jsx (1)
78-206
: LGTM on UX and routing intent.Form UX, loading state, error surfacing, and navigate('/vigy-dashboard') align with the PR goal.
backend/routes/authRoutes.js (2)
8-22
: Preflight config check is good.Helpful errors for misconfiguration.
1-46
: End-to-end verification complete. FRONTEND_URL is configured in CORS and auth redirects, and the Google OAuth flow now lands on/vigy-dashboard
as expected.
secret: process.env.SESSION_SECRET || "Our little secret.", | ||
resave: false, |
There was a problem hiding this comment.
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.
|
||
const config = { | ||
port: process.env.PORT || 3000, | ||
port: process.env.PORT || 5000, |
There was a problem hiding this comment.
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.
console.log("⚠️ MongoDB not available. Using in-memory fallback for development."); | ||
// Don't exit for development - let the app continue | ||
} |
There was a problem hiding this comment.
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.
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.
const generateToken = (vigyId) => { | ||
return jwt.sign({ vigyId }, process.env.JWT_SECRET || 'mysecretkey', { | ||
expiresIn: '7d' | ||
}); | ||
}; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
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'.
const { | ||
fullname, | ||
email, | ||
password, | ||
phoneNumber, | ||
address, | ||
aadhaarNumber, | ||
gender, | ||
dob, | ||
bankAccountName, | ||
bankAccountNumber, | ||
bankName, | ||
bankBranch, | ||
ifscCode, | ||
referralCode, | ||
promotionalCode | ||
} = req.body; | ||
|
There was a problem hiding this comment.
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.
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).
password: 'defaultPassword123', // You might want to add a password field to the form | ||
phoneNumber: formData.contact.phoneNumber || '', |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Remove hardcoded default password from payload.
This is a security risk and creates identical credentials.
- password: 'defaultPassword123', // You might want to add a password field to the form
Outside this hunk, either:
- add password/confirm fields with strength rules, or
- switch to OTP/set-password flow post-approval.
📝 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.
password: 'defaultPassword123', // You might want to add a password field to the form | |
phoneNumber: formData.contact.phoneNumber || '', | |
phoneNumber: formData.contact.phoneNumber || '', |
🤖 Prompt for AI Agents
In src/Admin/Pages/VigyForm.jsx around lines 319-320 the payload includes a
hardcoded password ('defaultPassword123'), which is a security risk; remove this
hardcoded value and instead either add password and confirm fields to the form
with client-side strength validation and include the user-supplied password in
the payload, or remove password from the creation payload entirely and implement
an OTP / set-password post-approval flow (send invite/OTP email and create
account only after password is set). Ensure validation is enforced and no
default or predictable password is ever sent in the request payload.
const response = await axios.post(apiUrl, formData); | ||
|
||
if (response.data.success) { | ||
// Store token or user data | ||
localStorage.setItem('vigyToken', response.data.token); | ||
localStorage.setItem('vigyUser', JSON.stringify(response.data.user)); | ||
|
||
// Redirect to Vigy dashboard or appropriate page | ||
navigate('/vigy-dashboard'); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Token in localStorage is vulnerable to XSS; wire “Remember me” and prefer HttpOnly cookies.
- Best: issue an HttpOnly, Secure, SameSite=Lax cookie from the backend and remove localStorage usage.
- If you must keep client storage, use sessionStorage by default and only persist to localStorage when “Remember me” is checked.
Minimal client-side wiring:
- localStorage.setItem('vigyToken', response.data.token);
- localStorage.setItem('vigyUser', JSON.stringify(response.data.user));
+ const storage = rememberMe ? localStorage : sessionStorage;
+ storage.setItem('vigyToken', response.data.token);
+ storage.setItem('vigyUser', JSON.stringify({
+ id: response.data.user.id,
+ email: response.data.user.email,
+ fullname: response.data.user.fullname
+ }));
And capture the checkbox:
- <input
+ <input
type="checkbox"
className="mr-2 h-4 w-4 rounded border-gray-300 text-[#4a7c59] focus:ring-[#4a7c59]"
+ onChange={(e) => setRememberMe(e.target.checked)}
/>
Add state at the top:
- const [loading, setLoading] = useState(false);
+ const [loading, setLoading] = useState(false);
+ const [rememberMe, setRememberMe] = useState(false);
Server-side (recommended): set a cookie and remove token storage; I can draft that if you want.
Also applies to: 134-142
🤖 Prompt for AI Agents
In src/Admin/Pages/VigyLogin.jsx around lines 39-48 and also apply same change
at 134-142: storing the auth token in localStorage is vulnerable to XSS; update
the login flow to stop writing tokens to localStorage and instead rely on the
backend to set an HttpOnly, Secure, SameSite=Lax cookie for authentication,
removing localStorage reads/writes; if a client-side persistence option is
required, add a "remember me" checkbox state at the top of the component and, on
successful login, store the token in sessionStorage by default and only copy it
to localStorage when the "remember me" checkbox is checked (leave server-set
HttpOnly cookie as the primary auth mechanism).
const handleGoogleLogin = () => { | ||
const googleAuthUrl = process.env.NODE_ENV === 'production' | ||
? 'https://vigybag-backend.onrender.com/auth/google' | ||
: 'http://localhost:5000/auth/google'; | ||
window.location.href = googleAuthUrl; | ||
}; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Google login flow may land on the wrong dashboard; pass flow context.
You redirect to “/vigy-dashboard” after form login, but Google callback currently redirects to “/dashboard”. Propagate a “vigy” flow signal and handle it in the backend.
- const googleAuthUrl = process.env.NODE_ENV === 'production'
- ? 'https://vigybag-backend.onrender.com/auth/google'
- : 'http://localhost:5000/auth/google';
+ const base =
+ (typeof import.meta !== 'undefined' && import.meta.env?.VITE_API_BASE_URL) ||
+ process.env.REACT_APP_API_BASE_URL ||
+ (process.env.NODE_ENV === 'production'
+ ? 'https://vigybag-backend.onrender.com'
+ : 'http://localhost:5000');
+ const googleAuthUrl = `${base}/auth/google?flow=vigy`;
Pair with backend changes proposed in authRoutes.js to read state/flow and redirect to “/vigy-dashboard”.
📝 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.
const handleGoogleLogin = () => { | |
const googleAuthUrl = process.env.NODE_ENV === 'production' | |
? 'https://vigybag-backend.onrender.com/auth/google' | |
: 'http://localhost:5000/auth/google'; | |
window.location.href = googleAuthUrl; | |
}; | |
const handleGoogleLogin = () => { | |
const base = | |
(typeof import.meta !== 'undefined' && import.meta.env?.VITE_API_BASE_URL) || | |
process.env.REACT_APP_API_BASE_URL || | |
(process.env.NODE_ENV === 'production' | |
? 'https://vigybag-backend.onrender.com' | |
: 'http://localhost:5000'); | |
const googleAuthUrl = `${base}/auth/google?flow=vigy`; | |
window.location.href = googleAuthUrl; | |
}; |
🤖 Prompt for AI Agents
In src/Admin/Pages/VigyLogin.jsx around lines 71 to 76, the Google OAuth
redirect does not include any flow context so the callback can’t distinguish
Vigy logins and ends up sending users to the wrong dashboard; update the
googleAuthUrl construction to append a state (or flow) query parameter (e.g.
state=vigy) in both production and development URLs (URL-encode the value or use
URLSearchParams) so the backend can read req.query.state (or req.query.flow) and
redirect to /vigy-dashboard accordingly.
to="/forgot-password" | ||
className="text-sm text-[#4a7c59] hover:underline" | ||
> | ||
Forgot password? | ||
</Link> | ||
</div> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
💡 Verification agent
🧩 Analysis chain
Verify target exists for “/forgot-password”.
If this route is missing, users will hit another 404.
Run:
🏁 Script executed:
#!/bin/bash
rg -nP --type=ts --type=tsx --type=js --type=jsx -C2 '\bRoute\b.*path=.*["'\'']/forgot-password' src || true
Length of output: 137
🏁 Script executed:
#!/bin/bash
# Search for any Route definitions matching /forgot-password
rg -n -C2 '<Route[^>]*path=[\"\047]/forgot-password' -g '*.js' -g '*.jsx' -g '*.ts' -g '*.tsx' src || true
# Fallback: search for all occurrences of the string
rg -n '/forgot-password' -g '*.js' -g '*.jsx' -g '*.ts' -g '*.tsx' src || true
Length of output: 481
Ensure “/forgot-password” client route is registered
The login page at src/Admin/Pages/VigyLogin.jsx links to “/forgot-password”, but no matching <Route path="/forgot-password">
was found—add a route mapping this path to the existing ForgotPasswordForm component (src/User/pages/ForgotPasswordForm.jsx) to prevent a 404.
🤖 Prompt for AI Agents
In src/Admin/Pages/VigyLogin.jsx around lines 144 to 149, the link points to
"/forgot-password" but no route exists; add a Route for path="/forgot-password"
that renders the existing ForgotPasswordForm component
(src/User/pages/ForgotPasswordForm.jsx). Update your router (e.g., in
src/App.jsx or wherever routes are declared) to import ForgotPasswordForm and
add a <Route path="/forgot-password" element={<ForgotPasswordForm/>} /> (or the
equivalent for your router version), and ensure any auth/layout wrappers match
other auth routes so the page renders instead of 404.
<Route path="/vigy-login" element={<VigyLogin />} /> | ||
<Route path="/vigy-form" element={<VigyForm />} /> | ||
<Route path="/vigy-dashboard" element={<VigyDashboard />} /> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
💡 Verification agent
🧩 Analysis chain
Routes added fix the 404; consider a legacy redirect and confirm intentional duplicate for /vigy-form.
- Good: adding /vigy-login addresses Issue [Bug] "Login as Vigy" button redirects to 404 page #2638.
- Add a redirect for stale links pointing to /vigy to harden UX.
- You now expose VigyForm both at /admin/vigy-form (guarded area) and /vigy-form (public). If that’s intentional, ignore; otherwise, dedupe.
Apply this minimal redirect:
<Route path="/vigy-login" element={<VigyLogin />} />
<Route path="/vigy-form" element={<VigyForm />} />
<Route path="/vigy-dashboard" element={<VigyDashboard />} />
+ <Route path="/vigy" element={<Navigate to="/vigy-login" replace />} />
Run to verify no stale links remain:
🏁 Script executed:
#!/bin/bash
# Find any remaining references to old Vigy URLs
rg -nP -C2 '(vigy[-_/ ]?login|vigy[-_/ ]?form|/vigy\b)' --glob '!dist/**' --glob '!build/**' src
Length of output: 5820
Add missing redirect, fix stale AdminPanel link, and confirm duplicate route
- Insert legacy redirect in src/App.jsx below the dashboard route:
<Route path="/vigy-dashboard" element={<VigyDashboard />} /> + <Route path="/vigy" element={<Navigate to="/vigy-login" replace />} />
- Correct broken link in src/Admin/Pages/AdminPanel.jsx (line 58):
- <Link to="/admin/vigy_form" > + <Link to="/admin/vigy-form" >
- Confirm whether exposing VigyForm at both /admin/vigy-form and /vigy-form is intentional; if not, remove or guard the public route.
📝 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.
<Route path="/vigy-login" element={<VigyLogin />} /> | |
<Route path="/vigy-form" element={<VigyForm />} /> | |
<Route path="/vigy-dashboard" element={<VigyDashboard />} /> | |
// File: src/App.jsx (around lines 273–275) | |
<Route path="/vigy-login" element={<VigyLogin />} /> | |
<Route path="/vigy-form" element={<VigyForm />} /> | |
<Route path="/vigy-dashboard" element={<VigyDashboard />} /> | |
<Route path="/vigy" element={<Navigate to="/vigy-login" replace />} /> |
<Route path="/vigy-login" element={<VigyLogin />} /> | |
<Route path="/vigy-form" element={<VigyForm />} /> | |
<Route path="/vigy-dashboard" element={<VigyDashboard />} /> | |
// File: src/Admin/Pages/AdminPanel.jsx (around line 58) | |
<Link to="/admin/vigy-form" > |
🤖 Prompt for AI Agents
In src/App.jsx around lines 273-275, add the legacy redirect route immediately
after the /vigy-dashboard Route so users hitting the old path are forwarded to
the new dashboard; update src/Admin/Pages/AdminPanel.jsx at line 58 to correct
the broken link (replace the stale href/import with the current route path or
component reference used by routing); and verify whether VigyForm is
intentionally exposed both at /admin/vigy-form and /vigy-form — if not, remove
the public /vigy-form Route or wrap it with an auth guard so only authorized
users can access it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 5
♻️ Duplicate comments (1)
backend/app.js (1)
25-33
: Cross-site sessions: set trust proxy and sameSite, and use a production session store.Without these, cookies may be dropped behind proxies and MemoryStore is not production-safe.
+if (process.env.NODE_ENV === 'production') { + app.set('trust proxy', 1); +} app.use( session({ secret: process.env.SESSION_SECRET || "Our little secret.", resave: false, saveUninitialized: false, cookie: { secure: process.env.NODE_ENV === 'production', httpOnly: true, + sameSite: process.env.NODE_ENV === 'production' ? 'none' : 'lax', maxAge: 24 * 60 * 60 * 1000 // 24 hours } }) );If possible, switch to a persistent store (e.g., connect-mongo/redis):
+const MongoStore = require("connect-mongo"); @@ session({ @@ + store: process.env.MONGODB_URI + ? MongoStore.create({ mongoUrl: process.env.MONGODB_URI }) + : undefined,
🧹 Nitpick comments (6)
backend/routes/subscribe.js (3)
24-26
: Plan for idempotency and duplicates.When you persist, enforce a unique constraint on email and treat duplicate subscriptions as 200 OK (idempotent), avoiding user enumeration.
27-31
: Avoid echoing PII in success response.No need to return the subscriber’s email; reduces exposure and log surface.
- res.status(200).json({ + return res.status(200).json({ success: true, - message: "Successfully subscribed to newsletter", - email: email + message: "Successfully subscribed to newsletter" });
33-39
: Return after sending the error response.Make control flow explicit.
- res.status(500).json({ + return res.status(500).json({ success: false, message: "Internal server error" });backend/app.js (3)
19-22
: Harden CORS with an allowlist and support multiple origins (local + prod + previews).Current single-origin string can break previews and is easier to misconfigure.
-app.use(cors({ - origin: process.env.FRONTEND_URL || "http://localhost:3000", - credentials: true -})); +const allowedOrigins = [ + process.env.FRONTEND_URL, + "http://localhost:3000", + "http://localhost:5173" +].filter(Boolean); +app.use(cors({ + origin(origin, cb) { + if (!origin || allowedOrigins.includes(origin)) return cb(null, true); + return cb(new Error("CORS: Origin not allowed")); + }, + credentials: true +})); +app.options("*", cors());
62-69
: Gate test routes in non-production.Avoid exposing internal diagnostics.
-app.get("/api/vigy/test", (req, res) => { +if (process.env.NODE_ENV !== "production") app.get("/api/vigy/test", (req, res) => { res.json({ message: "Vigy API is working! ✅", status: "success", endpoint: "/api/vigy/test" }); -}); +});
17-22
: Optional: set JSON body size limit.Prevents large payloads hitting all routes.
-app.use(express.json()); +app.use(express.json({ limit: "100kb" }));
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
💡 Knowledge Base configuration:
- MCP integration is disabled by default for public repositories
- Jira integration is disabled by default for public repositories
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
⛔ Files ignored due to path filters (1)
backend/package-lock.json
is excluded by!**/package-lock.json
📒 Files selected for processing (3)
backend/app.js
(1 hunks)backend/package.json
(2 hunks)backend/routes/subscribe.js
(1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
- backend/package.json
🧰 Additional context used
🧬 Code graph analysis (2)
backend/routes/subscribe.js (1)
src/User/pages/Home/Home.jsx (1)
e
(80-134)
backend/app.js (6)
backend/server.js (1)
app
(2-2)backend/routes/subscribe.js (2)
express
(1-1)req
(8-8)backend/routes/authRoutes.js (3)
express
(1-1)passport
(3-3)req
(16-22)backend/routes/vigyRoutes.js (2)
express
(1-1)req
(78-78)backend/middlewares/Passport.js (1)
passport
(1-1)backend/controllers/vigyController.js (2)
req
(16-32)req
(96-96)
// Route setup | ||
app.use("/api/subscribe", subscribeRoute); // POST /api/subscribe | ||
app.use("/auth", authRoutes); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Rate-limit the public subscribe endpoint.
Prevents abuse and mail-bombing.
+const rateLimit = require("express-rate-limit");
+const subscribeLimiter = rateLimit({
+ windowMs: 60 * 1000,
+ max: 10,
+ standardHeaders: true,
+ legacyHeaders: false
+});
-app.use("/api/subscribe", subscribeRoute); // POST /api/subscribe
+app.use("/api/subscribe", subscribeLimiter, subscribeRoute); // POST /api/subscribe
📝 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.
// Route setup | |
app.use("/api/subscribe", subscribeRoute); // POST /api/subscribe | |
app.use("/auth", authRoutes); | |
// At the top of backend/app.js, alongside your other requires: | |
const express = require("express"); | |
const rateLimit = require("express-rate-limit"); | |
const subscribeRoute = require("./routes/subscribe"); | |
const authRoutes = require("./routes/auth"); | |
// … any other requires | |
// Define a rate limiter specifically for the public subscribe endpoint | |
const subscribeLimiter = rateLimit({ | |
windowMs: 60 * 1000, // 1 minute | |
max: 10, // limit each IP to 10 requests per window | |
standardHeaders: true, // return rate limit info in the `RateLimit-*` headers | |
legacyHeaders: false, // disable the `X-RateLimit-*` headers | |
}); | |
// … your app setup (middleware, etc.) | |
// Route setup | |
app.use("/api/subscribe", subscribeLimiter, subscribeRoute); // POST /api/subscribe | |
app.use("/auth", authRoutes); |
🤖 Prompt for AI Agents
In backend/app.js around lines 53 to 55, the public POST /api/subscribe endpoint
is not rate-limited which allows abuse; add a rate-limiting middleware (e.g.,
express-rate-limit) and apply it specifically to the /api/subscribe route:
import/configure an Express rateLimit instance with a sensible window and max
(for example short window and low max per IP), attach any needed headers or
handlers for blocked requests, and use app.use("/api/subscribe",
rateLimitMiddleware, subscribeRoute) so the limiter protects only that endpoint.
backend/app.js
Outdated
// Route setup | ||
app.use("/api/subscribe", subscribeRoute); // POST /api/subscribe | ||
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); | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
💡 Verification agent
🧩 Analysis chain
Likely typo: '/vpi' should be '/api' for user routes.
This would orphan user routes and cause 404s.
-app.use("/vpi", userRoutes);
+app.use("/api", userRoutes);
Run to confirm no other '/vpi' mounts exist:
🏁 Script executed:
#!/bin/bash
rg -n -C2 -P 'app\.use\(\s*["\']/vpi["\']' backend
Length of output: 116
🏁 Script executed:
#!/bin/bash
# Verify no other mounts of "/vpi" exist
rg -n -C2 -e "app.use('/vpi'" -e 'app.use("/vpi"' backend
Length of output: 168
🏁 Script executed:
#!/bin/bash
# Search for any occurrences of "/vpi" in the backend directory
rg -n "/vpi" -C2 backend
Length of output: 285
Fix mount path for userRoutes in backend/app.js
Change at line 58 to avoid orphaning endpoints:
-app.use("/vpi", userRoutes);
+app.use("/api", userRoutes);
📝 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.
// Route setup | |
app.use("/api/subscribe", subscribeRoute); // POST /api/subscribe | |
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); | |
// Route setup | |
app.use("/api/subscribe", subscribeRoute); // POST /api/subscribe | |
app.use("/auth", authRoutes); | |
app.use("/api", routes); | |
app.use("/api", passwordResetRoutes); | |
app.use("/api", userRoutes); | |
app.use("/api/v1", adminRegistrationRoutes); | |
app.use("/api/vigy", vigyRoutes); |
🤖 Prompt for AI Agents
In backend/app.js around lines 53 to 61, the userRoutes are mounted at "/vpi"
which orphaned those endpoints; change the mount path to "/api/vpi" so they live
under the API namespace (replace app.use("/vpi", userRoutes) with
app.use("/api/vpi", userRoutes)); ensure this line remains before any catch-all
or conflicting middleware so the routes are reachable.
app.use("/api/vigy", vigyRoutes); | ||
|
There was a problem hiding this comment.
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.
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" | ||
} | ||
}); | ||
}); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
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.
backend/routes/subscribe.js
Outdated
const { email } = req.body; | ||
|
||
if (!email) { | ||
return res.status(400).json({ | ||
success: false, | ||
message: "Email is required" | ||
}); | ||
} | ||
|
||
// Validate email format | ||
if (!validator.isEmail(email)) { | ||
return res.status(400).json({ | ||
success: false, | ||
message: "Please provide a valid email address" | ||
}); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Normalize and validate email on the server (don’t trust client-side).
Guard type, trim, and canonicalize before validation to prevent bypasses and ensure consistent storage.
- const { email } = req.body;
-
- if (!email) {
+ const rawEmail = req.body?.email;
+ if (typeof rawEmail !== "string" || !rawEmail.trim()) {
return res.status(400).json({
success: false,
message: "Email is required"
});
}
- // Validate email format
- if (!validator.isEmail(email)) {
+ // Normalize and validate email format
+ const email = validator.normalizeEmail(rawEmail) || rawEmail.trim().toLowerCase();
+ if (!validator.isEmail(email)) {
return res.status(400).json({
success: false,
message: "Please provide a valid email address"
});
}
📝 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.
const { email } = req.body; | |
if (!email) { | |
return res.status(400).json({ | |
success: false, | |
message: "Email is required" | |
}); | |
} | |
// Validate email format | |
if (!validator.isEmail(email)) { | |
return res.status(400).json({ | |
success: false, | |
message: "Please provide a valid email address" | |
}); | |
} | |
const rawEmail = req.body?.email; | |
if (typeof rawEmail !== "string" || !rawEmail.trim()) { | |
return res.status(400).json({ | |
success: false, | |
message: "Email is required" | |
}); | |
} | |
// Normalize and validate email format | |
const email = validator.normalizeEmail(rawEmail) || rawEmail.trim().toLowerCase(); | |
if (!validator.isEmail(email)) { | |
return res.status(400).json({ | |
success: false, | |
message: "Please provide a valid email address" | |
}); | |
} |
🤖 Prompt for AI Agents
In backend/routes/subscribe.js around lines 8 to 23, the server currently trusts
req.body.email as-is; you must first guard the type and canonicalize the value
before validating. Ensure you check that req.body.email is a string, then trim
whitespace and convert to lowercase (and optionally apply provider-specific
canonicalization like removing dots/plus for Gmail) before calling
validator.isEmail; use the normalized value for both validation and
storage/response so validation cannot be bypassed and emails are stored
consistently.
kindly fix the merge conflict |
kindly fix the merge conflict then commit it again |
Merge conflicts resolved. Ready for review/merge. |
- Resolved merge conflicts in app.js by combining both branches' routes - Added missing subscribe route from main branch - Added body-parser and validator dependencies - Updated package-lock.json with new dependencies - Created routes/subscribe.js with email validation - Preserved all vigy functionality from fix-vigy-login branch
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 7
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
src/App.jsx (1)
207-209
: Duplicate exposure of VigyForm — remove or guard the public routeVigyForm is mounted multiple times in src/App.jsx (lines 207, 265, 275 — line 275 is a top-level "/vigy-form") and there’s a link to "/vigy-form" in src/Admin/Pages/VigyLogin.jsx:186. Confirm intent; if unintended remove the public "/vigy-form" route or protect it with the appropriate PrivateRoute/AdminRoute and update links.
♻️ Duplicate comments (31)
backend/setup-env.js (3)
15-20
: Don’t overwrite an existing .env; set restrictive perms when writing.Skip creation if the file exists, and write with mode 600.
-try { - fs.writeFileSync(envPath, envContent); +try { + if (fs.existsSync(envPath)) { + console.log('ℹ️ .env already exists, not overwriting.'); + console.log('📁 Location:', envPath); + process.exit(0); + } + fs.writeFileSync(envPath, envContent, { mode: 0o600 }); console.log('✅ .env file created successfully!'); console.log('📁 Location:', envPath); console.log('\n⚠️ IMPORTANT: Update GOOGLE_CLIENT_ID and GOOGLE_CLIENT_SECRET with your actual values');
13-13
: Ensure backend/.env is ignored and loaded via dotenv with the same path.Add
.env
(orbackend/.env
) to.gitignore
and configuredotenv.config({ path: <backend>/.env })
where env is consumed.#!/bin/bash # Verify .env is gitignored rg -nP '(^|/)\\.env(\\b|$)' .gitignore || echo "No .env ignore rule found" # Verify dotenv loads the backend env path rg -nP "dotenv\\.config\\(([^)]*)\\)" backend -C2 || true
4-4
: Port mismatch: use 5000 to align with frontend/dev config.Frontend and OAuth dev callback target 5000; keep
.env
in sync.-const envContent = `PORT=3001 +const envContent = `PORT=5000 MONGO_URI=mongodb://localhost:27017/vigybag JWT_SECRET=vigybag_jwt_secret_key_2024 SESSION_SECRET=vigybag_session_secret_2024 GOOGLE_CLIENT_ID=your_google_client_id_here GOOGLE_CLIENT_SECRET=your_google_client_secret_here NODE_ENV=development `;backend/middlewares/Passport.js (1)
5-5
: Load the same .env that setup script generates.Point dotenv to
backend/.env
so local runs pick up the generated file.-const dotenv = require("dotenv"); -dotenv.config(); +const dotenv = require("dotenv"); +const path = require("path"); +dotenv.config({ path: path.join(__dirname, "../.env") });backend/routes/subscribe.js (1)
10-12
: Normalize email safely before validate/store.Use
validator.normalizeEmail
and guard type; your currenttoString()
+lowercase is okay but not canonical.- try { - const emailRaw = (req.body?.email ?? "").toString(); - const email = emailRaw.trim().toLowerCase(); + try { + const rawEmail = req.body?.email; + if (typeof rawEmail !== "string" || !rawEmail.trim()) { + return res.status(400).json({ success: false, message: "⚠️ A valid email is required." }); + } + const email = validator.normalizeEmail(rawEmail) || rawEmail.trim().toLowerCase();backend/models/Vigy.js (3)
48-74
: Make bankDetails/documents required via sub-schemas + IFSC validationPrevents bypassing nested requireds and validates IFSC.
- bankDetails: { - accountName: { type: String, required: true, trim: true }, - accountNumber:{ type: String, required: true, trim: true }, - bankName: { type: String, required: true, trim: true }, - branch: { type: String, required: true, trim: true }, - ifscCode: { type: String, required: true, trim: true } - }, + bankDetails: { + type: new mongoose.Schema({ + accountName: { type: String, required: true, trim: true }, + accountNumber:{ type: String, required: true, trim: true }, + bankName: { type: String, required: true, trim: true }, + branch: { type: String, required: true, trim: true }, + ifscCode: { type: String, required: true, trim: true, match: [/^[A-Z]{4}0[A-Z0-9]{6}$/, 'Invalid IFSC code'] } + }, { _id: false }), + required: true, + }, - documents: { - panCard: { type: String, required: true }, - addressProof: { type: String, required: true }, - profilePicture:{ type: String, required: false } - }, + documents: { + type: new mongoose.Schema({ + panCard: { type: String, required: true }, + addressProof: { type: String, required: true }, + profilePicture:{ type: String } + }, { _id: false }), + required: true, + },Also applies to: 75-88
32-38
: Encrypt high‑risk PII at rest (Aadhaar, bank details, docs)Store ciphertext and a deterministic hash for lookups/uniqueness.
I can add AES‑GCM field‑level encryption hooks and a hashed lookup field (HMAC) for Aadhaar; say the word.
Also applies to: 48-74, 75-88
32-38
: Fix invalid Mongoose option; enforce Aadhaar formatReplace ineffective length with a regex validator.
aadhaarNumber: { type: String, required: true, unique: true, trim: true, - length: 12 + match: [/^\d{12}$/, 'Aadhaar number must be exactly 12 digits'], },src/Admin/Pages/VigyLogin.jsx (2)
35-39
: Avoid localStorage; wire “Remember me” and prefer HttpOnly cookiesDefault to sessionStorage; only persist when “Remember me” is checked. If the backend sets HttpOnly cookies, switch axios to withCredentials and remove token storage entirely.
- const apiUrl = process.env.NODE_ENV === 'production' - ? 'https://vigybag-backend.onrender.com/api/vigy/login' - : 'http://localhost:5000/api/vigy/login'; + const base = + (typeof import.meta !== 'undefined' && import.meta.env?.VITE_API_BASE_URL) || + process.env.REACT_APP_API_BASE_URL || + (process.env.NODE_ENV === 'production' + ? 'https://vigybag-backend.onrender.com' + : 'http://localhost:5000'); + const apiUrl = `${base}/api/vigy/login`; - const response = await axios.post(apiUrl, formData); + const response = await axios.post(apiUrl, formData/*, { withCredentials: true }*/); if (response.data.success) { - // Store token or user data - localStorage.setItem('vigyToken', response.data.token); - localStorage.setItem('vigyUser', JSON.stringify(response.data.user)); + const storage = rememberMe ? localStorage : sessionStorage; + storage.setItem('vigyToken', response.data.token); + storage.setItem('vigyUser', JSON.stringify({ + id: response.data.user.id, + email: response.data.user.email, + fullname: response.data.user.fullname + }));Add state + checkbox wiring (outside this hunk):
- const [loading, setLoading] = useState(false); + const [loading, setLoading] = useState(false); + const [rememberMe, setRememberMe] = useState(false);- <input + <input type="checkbox" className="mr-2 h-4 w-4 rounded border-gray-300 text-[#4a7c59] focus:ring-[#4a7c59]" + onChange={(e) => setRememberMe(e.target.checked)} />Also applies to: 41-48
71-76
: Ensure Google OAuth redirects to the Vigy dashboardPass a flow hint so the backend can route back to “/vigy-dashboard”.
- const handleGoogleLogin = () => { - const googleAuthUrl = process.env.NODE_ENV === 'production' - ? 'https://vigybag-backend.onrender.com/auth/google' - : 'http://localhost:5000/auth/google'; - window.location.href = googleAuthUrl; - }; + const handleGoogleLogin = () => { + const base = + (typeof import.meta !== 'undefined' && import.meta.env?.VITE_API_BASE_URL) || + process.env.REACT_APP_API_BASE_URL || + (process.env.NODE_ENV === 'production' + ? 'https://vigybag-backend.onrender.com' + : 'http://localhost:5000'); + const googleAuthUrl = `${base}/auth/google?flow=vigy`; + window.location.href = googleAuthUrl; + };src/Admin/Pages/VigyForm.jsx (1)
315-320
: Remove hardcoded default password; collect from user with confirm + strengthHardcoding a default password is a security risk and will create identical credentials.
- password: 'defaultPassword123', // You might want to add a password field to the form
Add password + confirm inputs (example in the “contact” tab) and reference them here:
+ password: formData.contact.password || '',
Example inputs to add under the “contact” case (outside this hunk):
<InputField label="Password" name="password" type="password" required minLength={8} onChange={handleInputChange}/> <InputField label="Confirm Password" name="confirmPassword" type="password" required onChange={handleInputChange}/>backend/config/config.js (1)
7-7
: Centralize derived URLs to kill hardcoded localhost elsewhere.Export frontendUrl and googleCallbackUrl here so routes can consume consistent values.
const config = { - port: process.env.PORT || 5000, + port: process.env.PORT || 5000, + frontendUrl: process.env.FRONTEND_URL || 'http://localhost:3000', + googleCallbackUrl: + process.env.GOOGLE_CALLBACK_URL || + `http://localhost:${process.env.PORT || 5000}/auth/google/callback`, mongoURI: process.env.MONGO_URI || 'mongodb://localhost:27017/myapp', jwtSecret: process.env.JWT_SECRET || 'mysecretkey', sessionSecret: process.env.SESSION_SECRET || 'defaultsessionsecret', googleClientID: process.env.GOOGLE_CLIENT_ID || '', googleClientSecret: process.env.GOOGLE_CLIENT_SECRET || '', };backend/routes/authRoutes.js (2)
30-31
: Avoid “undefined/auth…” when FRONTEND_URL is unset.Define a single FRONTEND_URL fallback and reuse for both success and failure redirects.
-router.get( - "/google/callback", - passport.authenticate("google", { failureRedirect: process.env.FRONTEND_URL + "/auth?error=google_auth_failed" }), +const FRONTEND_URL = process.env.FRONTEND_URL || "http://localhost:3000"; +router.get( + "/google/callback", + passport.authenticate("google", { failureRedirect: `${FRONTEND_URL}/auth?error=google_auth_failed` }),
31-37
: Stop leaking PII in redirect; support Vigy flow via state.Don’t put email/username in query params; redirect using state to select destination.
- (req, res) => { - const email = req.user.email; - const username = req.user.username; - const frontendUrl = process.env.FRONTEND_URL || "http://localhost:3000"; - res.redirect( - `${frontendUrl}/dashboard?email=${encodeURIComponent(email)}&username=${encodeURIComponent(username)}` - ); - } + (req, res) => { + const flow = req.query.state; // e.g., 'vigy' + const targetPath = flow === 'vigy' ? '/vigy-dashboard' : '/dashboard'; + res.redirect(`${FRONTEND_URL}${targetPath}`); + }backend/app.js (4)
26-37
: Sessions: set trust proxy and sameSite for cross-site auth.Without these, cookies may be dropped behind proxies or in OAuth flows.
-const app = express(); +const app = express(); +if (process.env.NODE_ENV === 'production') { + app.set('trust proxy', 1); +} @@ 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 + secure: process.env.NODE_ENV === "production", + httpOnly: true, + sameSite: process.env.NODE_ENV === "production" ? "none" : "lax", + maxAge: 24 * 60 * 60 * 1000, // 24 hours }, }) );
55-57
: Rate‑limit public subscribe endpoint.Add a small limiter to reduce abuse.
+const rateLimit = require("express-rate-limit"); +const subscribeLimiter = rateLimit({ windowMs: 60 * 1000, max: 10, standardHeaders: true, legacyHeaders: false }); @@ -app.use("/api/subscribe", subscribeRoute); // POST /api/subscribe +app.use("/api/subscribe", subscribeLimiter, subscribeRoute); // POST /api/subscribe
60-60
: Typo: mount user routes under /api, not /vpi.This causes 404s for user endpoints.
-app.use("/vpi", userRoutes); // /vpi +app.use("/api", userRoutes); // /api
74-91
: Gate the Google OAuth config test route in non‑prod.Avoid leaking config in production.
-app.get("/auth/google/test", (req, res) => { +if (process.env.NODE_ENV !== "production") app.get("/auth/google/test", (req, res) => { @@ -}); +});backend/routes/vigyRoutes.js (3)
33-74
: Add basic request validation to /register.Validate required fields and normalize email before storing.
+const { body, validationResult } = require('express-validator'); -router.post('/register', (req, res) => { +router.post( + '/register', + [ + body('email').isEmail().normalizeEmail(), + body('password').isLength({ min: 8, max: 128 }), + body('fullname').isLength({ min: 2, max: 100 }).trim(), + ], + (req, res) => { try { + const errors = validationResult(req); + if (!errors.isEmpty()) { + return res.status(400).json({ success: false, errors: errors.array() }); + } const userData = req.body; @@ - res.json({ + res.json({ success: true, message: "Registration successful! Please wait for approval.", user: { id: newUser.id, email: newUser.email, status: newUser.status } }); } catch (error) { @@ - } -}); + } +});
4-30
: In‑memory users and plaintext passwords must be dev‑only.Block this router in production to avoid accidental exposure.
-// In-memory storage for development +// In-memory storage for development only +router.use((req, res, next) => { + if (process.env.NODE_ENV === 'production') { + return res.status(503).json({ success: false, message: 'Vigy dev routes disabled in production' }); + } + next(); +});
76-117
: Use hashed passwords and real JWTs or delegate to DB‑backed controllers.Plaintext compare and “test_token_…” aren’t acceptable beyond mock dev flows.
Consider replacing this router with the DB‑backed handlers in controllers/vigyController.js to avoid duplication and security drift.
backend/controllers/vigyController.js (9)
16-33
: Validate and normalize registration input up‑front.Add basic presence/format checks and normalization to reduce bad data and operator payloads.
try { - const { + const { fullname, email, password, phoneNumber, address, aadhaarNumber, gender, dob, bankAccountName, bankAccountNumber, bankName, bankBranch, ifscCode, referralCode, promotionalCode } = req.body; + const emailNorm = typeof email === 'string' ? email.trim().toLowerCase() : ''; + const aadhaarNorm = typeof aadhaarNumber === 'string' ? aadhaarNumber.trim() : ''; + if (!fullname || !emailNorm || !password) { + 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' }); + }
35-37
: Harden uniqueness query against NoSQL operators.Use normalized strings in the query.
- const existingVigy = await Vigy.findOne({ - $or: [{ email }, { aadhaarNumber }] - }); + const existingVigy = await Vigy.findOne({ + $or: [{ email: emailNorm }, { aadhaarNumber: aadhaarNorm }] + });
49-67
: Persist normalized values.Store canonical email/aadhaar to match lookups/indexes.
const vigy = new Vigy({ fullname, - email, + email: emailNorm, password, phoneNumber, address, - aadhaarNumber, + aadhaarNumber: aadhaarNorm, gender, dob,
81-88
: Handle duplicate key (E11000) explicitly; avoid echoing internals.Return deterministic 400 without leaking error.message.
- } 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' });
98-104
: Type‑check and normalize login inputs.Prevents operator payloads and case mismatches.
- // Validate input - if (!email || !password) { + // Validate input + if (typeof email !== 'string' || typeof password !== 'string') { return res.status(400).json({ success: false, - message: 'Please provide both email and password' + 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' }); + }
106-108
: Use normalized email in the lookup.- const vigy = await Vigy.findOne({ email }); + const vigy = await Vigy.findOne({ email: emailNorm });
5-9
: No default JWT secret; fail fast if unset.Remove insecure fallback to 'mysecretkey'.
-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' }); +};
154-172
: JWT auth middleware is missing for req.vigyId.Implement middleware that verifies token and sets req.vigyId; apply on protected routes.
#!/bin/bash # Verify presence of Vigy auth middleware and its usage on /api/vigy/profile rg -nP -C2 'authenticateVigy|verifyVigy|req\.vigyId' backend
186-197
: Prevent mass‑assignment on profile update.Whitelist allowed fields instead of spreading req.body.
- 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 } + );backend/config/db.js (1)
12-13
: Don’t continue without a DB in non‑dev; implement a real dev fallback.Right now we only log and keep going. Gate by NODE_ENV and wire an actual in‑memory Mongo for development; exit in prod/stage to avoid partial-start bugs.
Apply:
+const { MongoMemoryServer } = require("mongodb-memory-server"); const connectDB = async () => { try { await mongoose.connect(config.mongoURI); console.log("Connected to MongoDB"); } 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 + const env = process.env.NODE_ENV || "development"; + if (env !== "development" && env !== "test") { + console.error("MongoDB unavailable. Exiting (non-dev)."); + process.exit(1); + } + console.warn("⚠️ MongoDB not available. Falling back to in-memory MongoDB for dev."); + const mem = await MongoMemoryServer.create(); + const uri = mem.getUri(); + await mongoose.connect(uri); + console.log("Connected to in-memory MongoDB"); + const shutdown = async () => { + await mongoose.disconnect().catch(() => {}); + await mem.stop().catch(() => {}); + process.exit(0); + }; + process.on("SIGINT", shutdown); + process.on("SIGTERM", shutdown); } };
🧹 Nitpick comments (14)
src/User/pages/UserAuth/UserAuth.jsx (1)
337-345
: Mobile parity: expose “Login as Vigy” on small screens.On mobile the left column is hidden (
md:block
), so the Vigy login button is inaccessible. Add a mobile-visible link next to the existing Admin links.@@ <p className="sm:hidden text-sm text-center pb-0 mt-2"> <Link to="/admin-verification" className="bg-[#2d3e40] text-green-500 px-6 rounded-full hover:bg-opacity-90 transition duration-300" > Login as Admin </Link> </p> + <p className="sm:hidden text-sm text-center mt-2"> + <Link + to="/vigy-login" + className="bg-[#f9efe4] text-[#2d3e40] px-6 rounded-full border border-[#2d3e40] hover:bg-[#2d3e40] hover:text-white transition duration-300" + > + Login as Vigy + </Link> + </p> @@ <p className="sm:hidden text-sm text-center pb-0 mt-2"> <Link to="/admin-verification" className="bg-[#2d3e40] text-green-500 px-6 rounded-full hover:bg-opacity-90 transition duration-300" > Login as Admin </Link> </p> + <p className="sm:hidden text-sm text-center mt-2"> + <Link + to="/vigy-login" + className="bg-[#f9efe4] text-[#2d3e40] px-6 rounded-full border border-[#2d3e40] hover:bg-[#2d3e40] hover:text-white transition duration-300" + > + Login as Vigy + </Link> + </p>Also applies to: 481-488
backend/setup-env.js (1)
6-7
: Generate non‑guessable secrets instead of hardcoded defaults.Avoid shipping fixed JWT/session secrets; create strong random values at generation time.
+const crypto = require('crypto'); -const envContent = `PORT=5000 -MONGO_URI=mongodb://localhost:27017/vigybag -JWT_SECRET=vigybag_jwt_secret_key_2024 -SESSION_SECRET=vigybag_session_secret_2024 -GOOGLE_CLIENT_ID=your_google_client_id_here -GOOGLE_CLIENT_SECRET=your_google_client_secret_here -NODE_ENV=development -`; +const jwtSecret = crypto.randomBytes(32).toString('hex'); +const sessionSecret = crypto.randomBytes(32).toString('hex'); +const envContent = `PORT=5000 +MONGO_URI=mongodb://localhost:27017/vigybag +JWT_SECRET=${jwtSecret} +SESSION_SECRET=${sessionSecret} +GOOGLE_CLIENT_ID=your_google_client_id_here +GOOGLE_CLIENT_SECRET=your_google_client_secret_here +# Optional but recommended for OAuth: +# GOOGLE_CALLBACK_URL=http://localhost:5000/auth/google/callback +NODE_ENV=development +`;backend/routes/subscribe.js (2)
33-37
: API message style: consider dropping emojis for machine consumers.If this endpoint feeds non-UI clients, remove emojis from messages.
- message: "🎉 Subscribed successfully!", + message: "Subscribed successfully.",
1-6
: Minor: remove redundant import/line.Line 1 already imports express; delete line 2 redeclaration.
-const express = require("express");
src/Admin/Pages/VigyDashboard.jsx (1)
11-16
: Prefer sessionStorage/HttpOnly cookies; clear both stores on logoutUse sessionStorage by default (mitigates XSS persistence) and clear both storages on logout. If you later move to HttpOnly cookies, this guard can fall back to a profile ping.
- const token = localStorage.getItem('vigyToken'); - const user = localStorage.getItem('vigyUser'); + const token = sessionStorage.getItem('vigyToken') ?? localStorage.getItem('vigyToken'); + const user = sessionStorage.getItem('vigyUser') ?? localStorage.getItem('vigyUser');const handleLogout = () => { - localStorage.removeItem('vigyToken'); - localStorage.removeItem('vigyUser'); + sessionStorage.removeItem('vigyToken'); + sessionStorage.removeItem('vigyUser'); + localStorage.removeItem('vigyToken'); + localStorage.removeItem('vigyUser'); navigate('/vigy-login'); };Also applies to: 27-31
src/Admin/Pages/VigyForm.jsx (4)
299-311
: Dead code: formDataToSend is built but never usedEither remove the block or actually submit multipart if files are required.
- const formDataToSend = new FormData(); - // Append all form fields to formDataToSend - Object.keys(formData).forEach(tab => { - Object.keys(formData[tab]).forEach(field => { - if (field === 'panCard' || field === 'addressProof' || field === 'profilePicture') { - formDataToSend.append(field, formData[tab][field], formData[tab][field].name); - } else { - formDataToSend.append(field, formData[tab][field]); - } - }); - }); + // NOTE: Files are collected but not submitted in this JSON flow. + // If backend requires documents here, switch to FormData and submit multipart.
141-143
: Tighten client-side patterns (anchor regex)Anchor patterns so partial matches don’t pass.
- pattern="\d{12}" + pattern="^\d{12}$"- pattern="\d{10}" + pattern="^\d{10}$"- pattern="\d{8,18}" + pattern="^\d{8,18}$"(IFSC is already anchored.)
Also applies to: 169-171, 201-205, 231-233
339-349
: Navigate to login on successful registrationCloses the loop for users and aligns with new route.
- if (response.data.success) { - console.log('Registration successful!'); - alert('Registration submitted successfully! Please wait for approval.'); - // You can redirect to login page or show success message - } + if (response.data.success) { + alert('Registration submitted! Please wait for approval.'); + navigate('/vigy-login'); + }
295-299
: Centralize API base URLAvoid duplicating env logic across files.
- // Use local backend for development, change to production URL when deploying - const apiUrl = process.env.NODE_ENV === 'production' - ? 'https://vigybag-backend.onrender.com/api/vigy/register' - : 'http://localhost:5000/api/vigy/register'; + const base = + (typeof import.meta !== 'undefined' && import.meta.env?.VITE_API_BASE_URL) || + process.env.REACT_APP_API_BASE_URL || + (process.env.NODE_ENV === 'production' + ? 'https://vigybag-backend.onrender.com' + : 'http://localhost:5000'); + const apiUrl = `${base}/api/vigy/register`;backend/models/Vigy.js (1)
4-4
: Use Mongoose timestamps instead of manual createdAt/updatedAtManual timestamps won’t auto-update on writes; use timestamps: true and drop fields.
-const vigySchema = new mongoose.Schema({ +const vigySchema = new mongoose.Schema({ // fields... - createdAt: { type: Date, default: Date.now }, - updatedAt: { type: Date, default: Date.now } -}); +}, { timestamps: true });Also applies to: 109-116
src/App.jsx (2)
274-277
: Add legacy redirect for stale “/vigy” linksPrevents 404s from old bookmarks and fixes Issue #2638 hard.
<Route path="/vigy-login" element={<VigyLogin />} /> <Route path="/vigy-form" element={<VigyForm />} /> - <Route path="/vigy-dashboard" element={<VigyDashboard />} /> + <Route path="/vigy-dashboard" element={<VigyDashboard />} /> + <Route path="/vigy" element={<Navigate to="/vigy-login" replace />} />
274-276
: Consider guarding /vigy-dashboardIf Vigy isn’t authenticated, redirect to /vigy-login.
- <Route path="/vigy-dashboard" element={<VigyDashboard />} /> + <Route + path="/vigy-dashboard" + element={ + (sessionStorage.getItem('vigyToken') || localStorage.getItem('vigyToken')) + ? <VigyDashboard /> + : <Navigate to="/vigy-login" replace /> + } + />backend/createTestUser.js (2)
5-8
: Await DB connection; guard against prod execution.Use await for connection and bail out in production to avoid accidental seeding.
-// Connect to MongoDB -mongoose.connect(config.mongoURI) - .then(() => console.log('Connected to MongoDB')) - .catch(err => console.error('MongoDB connection error:', err)); +// Connect to MongoDB +if (process.env.NODE_ENV === 'production') { + console.error('Refusing to run seeder in production.'); + process.exit(1); +} +async function connect() { + try { + await mongoose.connect(config.mongoURI); + console.log('Connected to MongoDB'); + } catch (err) { + console.error('MongoDB connection error:', err); + process.exit(1); + } +}
61-61
: Ensure connection before seeding and avoid logging passwords.Call connect() first; don’t print plaintext passwords even in dev logs.
-createTestUser(); +(async () => { + await connect(); + await createTestUser(); +})();
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (1)
backend/package-lock.json
is excluded by!**/package-lock.json
📒 Files selected for processing (17)
backend/app.js
(1 hunks)backend/config/config.js
(1 hunks)backend/config/db.js
(1 hunks)backend/controllers/vigyController.js
(1 hunks)backend/createTestUser.js
(1 hunks)backend/middlewares/Passport.js
(1 hunks)backend/models/Vigy.js
(1 hunks)backend/package.json
(3 hunks)backend/routes/authRoutes.js
(1 hunks)backend/routes/subscribe.js
(1 hunks)backend/routes/vigyRoutes.js
(1 hunks)backend/setup-env.js
(1 hunks)src/Admin/Pages/VigyDashboard.jsx
(1 hunks)src/Admin/Pages/VigyForm.jsx
(1 hunks)src/Admin/Pages/VigyLogin.jsx
(1 hunks)src/App.jsx
(2 hunks)src/User/pages/UserAuth/UserAuth.jsx
(1 hunks)
🧰 Additional context used
🧬 Code graph analysis (11)
src/Admin/Pages/VigyDashboard.jsx (2)
src/Admin/Pages/VigyLogin.jsx (2)
navigate
(16-16)error
(15-15)src/User/pages/UserAuth/UserAuth.jsx (1)
navigate
(24-24)
backend/createTestUser.js (3)
backend/models/Vigy.js (1)
mongoose
(1-1)backend/controllers/vigyController.js (5)
Vigy
(1-1)vigy
(49-67)vigy
(107-107)vigy
(159-159)vigy
(193-197)backend/config/config.js (1)
config
(6-14)
backend/routes/authRoutes.js (2)
backend/app.js (1)
passport
(5-5)backend/middlewares/Passport.js (1)
passport
(1-1)
src/Admin/Pages/VigyLogin.jsx (2)
src/Admin/Pages/VigyForm.jsx (3)
formData
(11-17)handleInputChange
(47-74)handleSubmit
(295-350)src/User/pages/UserAuth/UserAuth.jsx (2)
loading
(23-23)navigate
(24-24)
backend/app.js (4)
backend/routes/authRoutes.js (2)
express
(1-1)passport
(3-3)backend/routes/vigyRoutes.js (2)
express
(1-1)req
(78-78)backend/middlewares/Passport.js (1)
passport
(1-1)backend/controllers/vigyController.js (2)
req
(16-32)req
(96-96)
src/App.jsx (3)
src/Admin/Pages/VigyLogin.jsx (1)
VigyLogin
(8-207)src/Admin/Pages/VigyForm.jsx (1)
VigyForm
(9-432)src/Admin/Pages/VigyDashboard.jsx (1)
VigyDashboard
(5-174)
backend/routes/vigyRoutes.js (3)
backend/routes/authRoutes.js (3)
express
(1-1)router
(2-2)backend/controllers/vigyController.js (2)
req
(16-32)req
(96-96)backend/createTestUser.js (1)
existingUser
(36-36)
backend/controllers/vigyController.js (2)
backend/createTestUser.js (2)
Vigy
(2-2)vigy
(46-46)backend/routes/vigyRoutes.js (1)
req
(78-78)
backend/routes/subscribe.js (2)
backend/app.js (1)
express
(2-2)backend/routes/index.js (3)
express
(1-1)require
(2-2)router
(3-3)
backend/middlewares/Passport.js (2)
backend/app.js (1)
passport
(5-5)backend/routes/authRoutes.js (1)
passport
(3-3)
src/Admin/Pages/VigyForm.jsx (1)
src/Admin/Pages/VigyLogin.jsx (2)
formData
(9-12)error
(15-15)
🪛 Gitleaks (8.27.2)
backend/setup-env.js
[high] 6-6: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.
(generic-api-key)
🪛 Biome (2.1.2)
backend/routes/subscribe.js
[error] 2-2: Shouldn't redeclare 'express'. Consider to delete it or rename it.
'express' is defined here:
(lint/suspicious/noRedeclare)
🔇 Additional comments (5)
backend/package.json (1)
6-16
: No action required — backend/package.json scripts are valid (no nested "scripts").
backend/package.json contains a top-level "scripts" object with start, dev, prod, lint, lint:fix, and test; the nested "scripts" issue reported earlier is not present.Likely an incorrect or invalid review comment.
src/User/pages/UserAuth/UserAuth.jsx (1)
212-216
: Verify /vigy-login route exists and deployment rewritesAutomated search returned no results — confirm <Route path="/vigy-login" ... /> is registered (likely in src/App.jsx) and that hosting deploy rewrites allow deep-linking.
src/Admin/Pages/VigyLogin.jsx (1)
143-149
: Forgot-password route: confirmedLink in src/Admin/Pages/VigyLogin.jsx (line 144) points to "/forgot-password" and matches the Route in src/App.jsx (line 179): <Route path="forgot-password" element={} />. No action needed.
backend/routes/authRoutes.js (1)
8-22
: Precheck for Google OAuth config is good.Validation before invoking passport is a solid safeguard.
backend/app.js (1)
58-63
: No change required — vigy routes are correct.
Login links point to /vigy-login and /vigy-form is the registration route; no stale /vigy-form references need replacing.
app.use( | ||
session({ | ||
secret: "Our little secret.", | ||
secret: process.env.SESSION_SECRET || "Our little secret.", | ||
resave: false, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
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.
backend/app.js
Outdated
app.use("/api/vigy", vigyRoutes); // /api/vigy | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Don’t mount dev-only in‑memory Vigy routes in production.
Guard with NODE_ENV or a feature flag.
-app.use("/api/vigy", vigyRoutes); // /api/vigy
+if (process.env.NODE_ENV !== "production") {
+ app.use("/api/vigy", vigyRoutes); // /api/vigy
+}
📝 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.
app.use("/api/vigy", vigyRoutes); // /api/vigy | |
if (process.env.NODE_ENV !== "production") { | |
app.use("/api/vigy", vigyRoutes); // /api/vigy | |
} |
🤖 Prompt for AI Agents
In backend/app.js around lines 62-63, the dev-only in-memory Vigy routes are
being mounted unconditionally; wrap the app.use("/api/vigy", vigyRoutes) call in
a runtime guard so these routes are only registered in non-production or when an
explicit feature flag is enabled (e.g., check process.env.NODE_ENV !==
'production' OR a boolean env var like process.env.ENABLE_VIGY === 'true');
ensure the condition is clear, default-off in production, and add a short log
message when the routes are mounted or skipped for observability.
// Check if Google OAuth credentials are configured | ||
if (!process.env.GOOGLE_CLIENT_ID || !process.env.GOOGLE_CLIENT_SECRET) { | ||
console.warn("⚠️ Google OAuth credentials not found. Google login will not work."); | ||
console.warn(" Please set GOOGLE_CLIENT_ID and GOOGLE_CLIENT_SECRET in your environment variables."); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Guard Google strategy init; don’t default to an incorrect prod callback URL.
Currently it can initialize with missing creds or a wrong callback (vigybag.com
), breaking OAuth. In production, require GOOGLE_CALLBACK_URL and skip/fail fast if missing.
-// Check if Google OAuth credentials are configured
-if (!process.env.GOOGLE_CLIENT_ID || !process.env.GOOGLE_CLIENT_SECRET) {
- console.warn("⚠️ Google OAuth credentials not found. Google login will not work.");
- console.warn(" Please set GOOGLE_CLIENT_ID and GOOGLE_CLIENT_SECRET in your environment variables.");
-}
-
-// Resolve callback URL safely (never default to localhost in production)
-const resolvedCallbackUrl = (() => {
- const fromEnv = process.env.GOOGLE_CALLBACK_URL;
- if (fromEnv && fromEnv.trim().length > 0) return fromEnv.trim();
-
- if (process.env.NODE_ENV === "development") {
- return "http://localhost:5000/auth/google/callback";
- }
-
- // Optional: Production fallback
- return "https://vigybag.com/auth/google/callback";
-})();
+// Resolve Google OAuth config
+const missingCreds = !process.env.GOOGLE_CLIENT_ID || !process.env.GOOGLE_CLIENT_SECRET;
+const isDev = process.env.NODE_ENV !== "production";
+const resolvedCallbackUrl = (() => {
+ const fromEnv = process.env.GOOGLE_CALLBACK_URL;
+ if (fromEnv && fromEnv.trim()) return fromEnv.trim();
+ return isDev ? "http://localhost:5000/auth/google/callback" : undefined;
+})();
+const canInitGoogle = !missingCreds && !!resolvedCallbackUrl;
+if (!canInitGoogle) {
+ const msg = "Google OAuth config incomplete. Set GOOGLE_CLIENT_ID, GOOGLE_CLIENT_SECRET and GOOGLE_CALLBACK_URL (in production).";
+ if (isDev) {
+ console.warn("⚠️", msg, "Skipping GoogleStrategy initialization.");
+ } else {
+ console.error("❌", msg);
+ // Fail fast or skip strategy; choose one. Here we skip init to keep server up.
+ }
+}
-passport.use(
- new GoogleStrategy(
- {
- clientID: process.env.GOOGLE_CLIENT_ID,
- clientSecret: process.env.GOOGLE_CLIENT_SECRET,
- callbackURL: resolvedCallbackUrl,
- },
- async (accessToken, refreshToken, profile, done) => {
+if (canInitGoogle) {
+ passport.use(new GoogleStrategy(
+ {
+ clientID: process.env.GOOGLE_CLIENT_ID,
+ clientSecret: process.env.GOOGLE_CLIENT_SECRET,
+ callbackURL: resolvedCallbackUrl,
+ },
+ async (accessToken, refreshToken, profile, done) => {
// existing verify logic unchanged
- }
- )
-);
+ }
+ ));
+}
Also applies to: 13-25, 29-31
🤖 Prompt for AI Agents
In backend/middlewares/Passport.js around lines 7-11 (also address lines 13-25
and 29-31): the code currently warns only when GOOGLE_CLIENT_ID/SECRET or
callback URL are missing and may initialize the Google strategy with incorrect
defaults (e.g., vigybag.com). Change this to require GOOGLE_CLIENT_ID,
GOOGLE_CLIENT_SECRET and in production require GOOGLE_CALLBACK_URL; if any
required value is missing, do not initialize the Google strategy and either
throw or exit fast (or explicitly skip strategy registration with a clear error
log). Ensure you remove any hardcoded/default callback URL usage and read
callback URL only from env, validating it before using.
// Prepare data for the new API | ||
const registrationData = { | ||
fullname: formData.personal.fullname || '', | ||
email: formData.contact.email || '', | ||
password: 'defaultPassword123', // You might want to add a password field to the form | ||
phoneNumber: formData.contact.phoneNumber || '', | ||
address: formData.contact.address || '', | ||
aadhaarNumber: formData.personal.aadhaarNumber || '', | ||
gender: formData.personal.gender || '', | ||
dob: formData.personal.dob || '', | ||
bankAccountName: formData.banking.bankAccountName || '', | ||
bankAccountNumber: formData.banking.bankAccountNumber || '', | ||
bankName: formData.banking.bankName || '', | ||
bankBranch: formData.banking.bankBranch || '', | ||
ifscCode: formData.banking.ifscCode || '', | ||
referralCode: formData.additional.referralCode || '', | ||
promotionalCode: formData.additional.promotionalCode || '' | ||
}; | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧩 Analysis chain
Repo sweep for default password remnants
🏁 Script executed:
#!/bin/bash
# Find any default password leftovers
rg -n -C2 'defaultPassword|defaultPassword123' -g '!**/dist/**' -g '!**/build/**'
Length of output: 148
🏁 Script executed:
#!/bin/bash
set -euo pipefail
# Robust search for default password patterns across the repo, excluding common large dirs.
# Use ripgrep when available; fallback to git grep.
PATTERN='defaultPassword123|defaultPassword'
PATTERN_PHRASE='default password'
if command -v rg >/dev/null 2>&1; then
rg -n -S -C2 -uu --hidden --glob '!.git/**' --glob '!node_modules/**' --glob '!dist/**' --glob '!build/**' "$PATTERN" || true
rg -n -S -C2 -uu --hidden --glob '!.git/**' --glob '!node_modules/**' --glob '!dist/**' --glob '!build/**' "$PATTERN_PHRASE" || true
else
git grep -n -e "defaultPassword123" -e "defaultPassword" || true
git grep -n -e "default password" || true
fi
Length of output: 297
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "=== pwd ==="
pwd
echo
echo "=== top-level listing ==="
ls -la | sed -n '1,200p'
echo
echo "=== detect git repo ==="
if git rev-parse --is-inside-work-tree >/dev/null 2>&1; then
echo "Inside git repo: yes"
git rev-parse --abbrev-ref HEAD || true
else
echo "Inside git repo: no"
fi
echo
echo "=== grep search for default password patterns (fallback to grep instead of rg) ==="
grep -RIn --binary-files=without-match --exclude-dir=.git --exclude-dir=node_modules --exclude-dir=dist --exclude-dir=build -E 'defaultPassword123|defaultPassword' . || true
echo
echo "=== grep search for phrase 'default password' ==="
grep -RIn --binary-files=without-match --exclude-dir=.git --exclude-dir=node_modules --exclude-dir=dist --exclude-dir=build -E 'default password' . || true
echo
echo "=== locate VigyForm.jsx ==="
if command -v fd >/dev/null 2>&1; then
fd -HI --hidden -e jsx VigyForm || true
else
find . -type f -iname 'VigyForm.jsx' -print || true
fi
echo
echo "=== show snippet of src/Admin/Pages/VigyForm.jsx (if present) ==="
if [ -f src/Admin/Pages/VigyForm.jsx ]; then
sed -n '1,480p' src/Admin/Pages/VigyForm.jsx || true
else
echo "src/Admin/Pages/VigyForm.jsx not found"
fi
Length of output: 17235
Remove hard-coded default password from client (critical)
Hard-coded 'defaultPassword123' exists in src/Admin/Pages/VigyForm.jsx (line ~319). Sending a plaintext default password from the client is a security vulnerability — remove it and stop including passwords in client payloads. Fix options:
- Remove the literal and do not send password from the client.
- Collect a user-chosen password in the form (with validation) OR delegate password creation to the backend (generate a secure password + send set-password link / OTP).
- Rotate/invalidate any accounts created with this default.
const registrationData = { | ||
fullname: formData.personal.fullname || '', | ||
email: formData.contact.email || '', | ||
password: 'defaultPassword123', // You might want to add a password field to the form | ||
phoneNumber: formData.contact.phoneNumber || '', | ||
address: formData.contact.address || '', | ||
aadhaarNumber: formData.personal.aadhaarNumber || '', | ||
gender: formData.personal.gender || '', | ||
dob: formData.personal.dob || '', | ||
bankAccountName: formData.banking.bankAccountName || '', | ||
bankAccountNumber: formData.banking.bankAccountNumber || '', | ||
bankName: formData.banking.bankName || '', | ||
bankBranch: formData.banking.bankBranch || '', | ||
ifscCode: formData.banking.ifscCode || '', | ||
referralCode: formData.additional.referralCode || '', | ||
promotionalCode: formData.additional.promotionalCode || '' | ||
}; | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Align payload with backend model: nest bankDetails (and later documents)
Current payload sends flattened bank fields; the model/controller expect bankDetails.{...}. Prevents silent data loss and mismatches.
- bankAccountName: formData.banking.bankAccountName || '',
- bankAccountNumber: formData.banking.bankAccountNumber || '',
- bankName: formData.banking.bankName || '',
- bankBranch: formData.banking.bankBranch || '',
- ifscCode: formData.banking.ifscCode || '',
+ bankDetails: {
+ accountName: formData.banking.bankAccountName || '',
+ accountNumber:formData.banking.bankAccountNumber|| '',
+ bankName: formData.banking.bankName || '',
+ branch: formData.banking.bankBranch || '',
+ ifscCode: formData.banking.ifscCode || ''
+ },
Follow-up: if documents are required in the model, either (a) switch this submit to multipart and send files, or (b) relax model requirements and add a separate upload step. I can draft either path.
📝 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.
const registrationData = { | |
fullname: formData.personal.fullname || '', | |
email: formData.contact.email || '', | |
password: 'defaultPassword123', // You might want to add a password field to the form | |
phoneNumber: formData.contact.phoneNumber || '', | |
address: formData.contact.address || '', | |
aadhaarNumber: formData.personal.aadhaarNumber || '', | |
gender: formData.personal.gender || '', | |
dob: formData.personal.dob || '', | |
bankAccountName: formData.banking.bankAccountName || '', | |
bankAccountNumber: formData.banking.bankAccountNumber || '', | |
bankName: formData.banking.bankName || '', | |
bankBranch: formData.banking.bankBranch || '', | |
ifscCode: formData.banking.ifscCode || '', | |
referralCode: formData.additional.referralCode || '', | |
promotionalCode: formData.additional.promotionalCode || '' | |
}; | |
const registrationData = { | |
fullname: formData.personal.fullname || '', | |
email: formData.contact.email || '', | |
password: 'defaultPassword123', // You might want to add a password field to the form | |
phoneNumber: formData.contact.phoneNumber || '', | |
address: formData.contact.address || '', | |
aadhaarNumber: formData.personal.aadhaarNumber || '', | |
gender: formData.personal.gender || '', | |
dob: formData.personal.dob || '', | |
bankDetails: { | |
accountName: formData.banking.bankAccountName || '', | |
accountNumber: formData.banking.bankAccountNumber || '', | |
bankName: formData.banking.bankName || '', | |
branch: formData.banking.bankBranch || '', | |
ifscCode: formData.banking.ifscCode || '' | |
}, | |
referralCode: formData.additional.referralCode || '', | |
promotionalCode: formData.additional.promotionalCode || '' | |
}; |
🤖 Prompt for AI Agents
In src/Admin/Pages/VigyForm.jsx around lines 316 to 333, the registration
payload currently flattens banking fields which the backend expects under
bankDetails; change the payload so banking values are nested as bankDetails: {
bankAccountName, bankAccountNumber, bankName, bankBranch, ifscCode } instead of
top-level keys, and similarly prepare for documents by either converting the
submission to multipart/form-data and attaching files or removing documents from
this payload and performing a separate file-upload step; ensure keys match the
backend model exactly.
3226bb1
to
28944e3
Compare
Hi, I’ve rebased my branch and fixed the conflicts. The Vercel check failed due to authorization, which I believe needs maintainer access. Please let me know if any further changes are needed |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 3
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
backend/routes/subscribe.js (1)
1-87
: Unresolved merge conflict and duplicate declarations make the route unusable.
Resolve conflicts and standardize validation, dedupe imports/exports.-const express = require("express"); -<<<<<<< HEAD -const router = express.Router(); -const validator = require("validator"); -const Subscriber = require("../models/subscriber"); -// POST /api/subscribe -router.post("/", async (req, res) => { - const emailRaw = (req.body?.email ?? "").toString(); - const email = emailRaw.trim().toLowerCase(); - // Check if email is provided - if (!email) { - return res.status(400).json({ message: "⚠️ A valid email is required." }); - } - // Validate email format - if (!validator.isEmail(email)) { - return res - .status(400) - .json({ message: "⚠️ Please provide a valid email address." }); - } - try { - const subscriber = new Subscriber({ email }); - await subscriber.save(); - return res.status(201).json({ message: "🎉 Subscribed successfully!" }); - } catch (err) { - console.error("Subscribe error:", err); - // Duplicate email - if (err.code === 11000) { - return res.status(409).json({ message: "This email is already subscribed." }); - } - // Schema validation error - if (err.name === "ValidationError") { - return res.status(400).json({ message: err.message }); - } - // Server error - return res.status(500).json({ message: "❌ Server error. Try again later." }); -======= -const validator = require("validator"); -const router = express.Router(); -// POST /api/subscribe -router.post("/", async (req, res) => { - try { - const { email } = req.body; - if (!email) { - return res.status(400).json({ - success: false, - message: "Email is required" - }); - } - // Validate email format - if (!validator.isEmail(email)) { - return res.status(400).json({ - success: false, - message: "Please provide a valid email address" - }); - } - // TODO: Add email to database or mailing list service - // TODO: Send confirmation email - res.status(200).json({ - success: true, - message: "Successfully subscribed to newsletter", - email: email - }); - } catch (error) { - console.error("Subscribe error:", error); - res.status(500).json({ - success: false, - message: "Internal server error" - }); ->>>>>>> 0163c432 (resolve: merge conflicts between fix-vigy-login and main branches) - } -}); - -module.exports = router; +const express = require("express"); +const validator = require("validator"); +const Subscriber = require("../models/subscriber"); +const router = express.Router(); + +// POST /api/subscribe +router.post("/", async (req, res) => { + try { + const rawEmail = req.body?.email; + if (typeof rawEmail !== "string" || !rawEmail.trim()) { + return res.status(400).json({ success: false, message: "Email is required" }); + } + const email = validator.normalizeEmail(rawEmail) || rawEmail.trim().toLowerCase(); + if (!validator.isEmail(email)) { + return res.status(400).json({ success: false, message: "Please provide a valid email address" }); + } + const subscriber = new Subscriber({ email }); + await subscriber.save(); + return res.status(201).json({ success: true, message: "Successfully subscribed to newsletter", email }); + } catch (err) { + console.error("Subscribe error:", err); + if (err?.code === 11000) { + return res.status(409).json({ success: false, message: "This email is already subscribed." }); + } + if (err?.name === "ValidationError") { + return res.status(400).json({ success: false, message: err.message }); + } + return res.status(500).json({ success: false, message: "Internal server error" }); + } +}); + +module.exports = router;
♻️ Duplicate comments (22)
backend/setup-env.js (3)
4-4
: Port mismatch with dev backend (should be 5000).
Frontend and backend configs default to 5000 elsewhere; align the generated PORT.-const envContent = `PORT=3001 +const envContent = `PORT=5000 MONGO_URI=mongodb://localhost:27017/vigybag JWT_SECRET=vigybag_jwt_secret_key_2024 SESSION_SECRET=vigybag_session_secret_2024 GOOGLE_CLIENT_ID=your_google_client_id_here GOOGLE_CLIENT_SECRET=your_google_client_secret_here NODE_ENV=development `;
15-23
: Don’t overwrite existing .env; create-once with a clear message.
Prevents clobbering local secrets.-try { - fs.writeFileSync(envPath, envContent); +try { + if (fs.existsSync(envPath)) { + console.log('ℹ️ .env already exists, not overwriting.'); + console.log('📁 Location:', envPath); + process.exit(0); + } + fs.writeFileSync(envPath, envContent); console.log('✅ .env file created successfully!'); console.log('📁 Location:', envPath); console.log('\n⚠️ IMPORTANT: Update GOOGLE_CLIENT_ID and GOOGLE_CLIENT_SECRET with your actual values'); } catch (error) {
13-20
: Ensure .env is gitignored and loaded from backend path.
Add backend/.env to .gitignore and call dotenv.config({ path: backend/.env }) in backend entry points to ensure the generated file is used.backend/routes/subscribe.js (1)
1-1
: Normalize/validate email (server-side) — good direction.
Change keeps us safe even if client validation is bypassed.src/Admin/Pages/VigyLogin.jsx (2)
144-149
: Ensure client route exists for “/forgot-password”.
Prevent a 404 from this link.
41-48
: Avoid storing tokens in localStorage; wire “Remember me” and default to sessionStorage.
Mitigates XSS token theft; best is HttpOnly cookies from backend.- if (response.data.success) { - // Store token or user data - localStorage.setItem('vigyToken', response.data.token); - localStorage.setItem('vigyUser', JSON.stringify(response.data.user)); - - // Redirect to Vigy dashboard or appropriate page - navigate('/vigy-dashboard'); - } + if (response.data.success) { + const storage = rememberMe ? localStorage : sessionStorage; + storage.setItem('vigyToken', response.data.token); + storage.setItem('vigyUser', JSON.stringify({ + id: response.data.user?.id, + email: response.data.user?.email, + fullname: response.data.user?.fullname + })); + navigate('/vigy-dashboard'); + }And add state + checkbox handler:
- const [loading, setLoading] = useState(false); + const [loading, setLoading] = useState(false); + const [rememberMe, setRememberMe] = useState(false); @@ - <input + <input type="checkbox" className="mr-2 h-4 w-4 rounded border-gray-300 text-[#4a7c59] focus:ring-[#4a7c59]" + onChange={(e) => setRememberMe(e.target.checked)} />backend/app.js (3)
28-43
: Harden session cookies for cross-site flows and require SESSION_SECRET in prod.
Prevents cookie drop and weak secrets in production.
90-106
: Don’t expose OAuth config in production.
Gate the /auth/google/test route behind NODE_ENV !== 'production'.
73-79
: Mount path “/vpi” likely a typo; switch to “/api”.
Prevents orphaned user routes.-app.use("/vpi", userRoutes); +app.use("/api", userRoutes);backend/models/Vigy.js (4)
48-74
: MakebankDetails
a required sub-schema and validate IFSC.Plain nested objects let callers omit the parent field and bypass nested requireds. Use a sub-schema and add an IFSC format check.
- bankDetails: { - accountName: { - type: String, - required: true, - trim: true - }, - accountNumber: { - type: String, - required: true, - trim: true - }, - bankName: { - type: String, - required: true, - trim: true - }, - branch: { - type: String, - required: true, - trim: true - }, - ifscCode: { - type: String, - required: true, - trim: true - } - }, + bankDetails: { + type: new mongoose.Schema( + { + accountName: { type: String, required: true, trim: true }, + accountNumber: { type: String, required: true, trim: true }, + bankName: { type: String, required: true, trim: true }, + branch: { type: String, required: true, trim: true }, + ifscCode: { + type: String, + required: true, + trim: true, + match: [/^[A-Z]{4}0[A-Z0-9]{6}$/, 'Invalid IFSC code'], + }, + }, + { _id: false } + ), + required: true, + },
75-88
: Makedocuments
a required sub-schema.Ensure presence of the whole object, not only nested keys.
- documents: { - panCard: { - type: String, // File path or URL - required: true - }, - addressProof: { - type: String, // File path or URL - required: true - }, - profilePicture: { - type: String, // File path or URL - required: false - } - }, + documents: { + type: new mongoose.Schema( + { + panCard: { type: String, required: true }, // file path or URL + addressProof: { type: String, required: true }, // file path or URL + profilePicture: { type: String, required: false } + }, + { _id: false } + ), + required: true, + },
32-38
: Replace invalidlength
with a proper validator for Aadhaar.
length: 12
is ignored by Mongoose. Enforce exactly 12 digits with amatch
regex.aadhaarNumber: { type: String, required: true, unique: true, trim: true, - length: 12 + match: [/^\d{12}$/, 'Aadhaar number must be exactly 12 digits'], },
32-38
: Encrypt high‑risk PII at rest (aadhaar, bank account numbers, documents).Persisting plaintext PII is a major risk. Encrypt fields with AES‑GCM and index a deterministic HMAC for Aadhaar uniqueness.
Minimal plugin-based example (illustrative):
+const { fieldEncryption } = require('mongoose-field-encryption'); +const ENC_KEY = process.env.PII_ENC_KEY; // 32-byte base64 +const HMAC_KEY = process.env.PII_HMAC_KEY; // for deterministic hash/index ... const vigySchema = new mongoose.Schema({ ... aadhaarNumber: { ... }, bankDetails: { ... }, documents: { ... }, ... -}); +}); + +vigySchema.plugin(fieldEncryption, { + fields: [ + 'aadhaarNumber', + 'bankDetails.accountNumber', + 'documents.panCard', + 'documents.addressProof', + ], + secret: ENC_KEY, + // consider saltGenerator for rotation +}); + +// Store deterministic Aadhaar hash for uniqueness +vigySchema.add({ aadhaarHash: { type: String, unique: true, index: true } }); +vigySchema.pre('save', function(next) { + if (this.isModified('aadhaarNumber')) { + this.aadhaarHash = require('crypto') + .createHmac('sha256', HMAC_KEY) + .update(this.aadhaarNumber) + .digest('hex'); + } + next(); +});Confirm library acceptance, key management, and add tests for encrypt/decrypt and uniqueness collisions.
Also applies to: 48-88
backend/controllers/vigyController.js (9)
16-33
: Validate and normalize registration input up front.Type-check, trim, and canonicalize values before use.
const registerVigy = async (req, res) => { try { - const { + let { fullname, email, password, phoneNumber, address, aadhaarNumber, gender, dob, bankAccountName, bankAccountNumber, bankName, bankBranch, ifscCode, referralCode, promotionalCode } = req.body; + + if (typeof email !== 'string' || typeof password !== 'string') { + return res.status(400).json({ success: false, message: 'Invalid payload' }); + } + const emailNorm = email.trim().toLowerCase(); + const aadhaarNorm = String(aadhaarNumber || '').trim(); + if (!fullname || !emailNorm || !password || !phoneNumber || !address || !aadhaarNorm || !dob) { + 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' }); + }
48-67
: Store normalized email/Aadhaar.Align stored values with query forms.
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 });
81-88
: Handle duplicate key errors deterministically (E11000).Return 400 with a stable message; don’t leak internals.
} 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' }); }
5-9
: Remove insecure JWT secret fallback. Fail fast if unset.Hard-coded default weakens security.
-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' }); +};
34-38
: Harden uniqueness check against operator payloads.Query with normalized strings.
- const existingVigy = await Vigy.findOne({ - $or: [{ email }, { aadhaarNumber }] - }); + const existingVigy = await Vigy.findOne({ + $or: [{ email: emailNorm }, { aadhaarNumber: aadhaarNorm }] + });
96-105
: Type-check and normalize login payload.Block NoSQL operator payloads; trim/lowercase email.
- const { email, password } = req.body; + const { email, password } = req.body; + if (typeof email !== 'string' || typeof password !== 'string') { + return res.status(400).json({ success: false, message: 'Invalid payload' }); + } + const emailNorm = email.trim().toLowerCase(); ... - if (!email || !password) { + if (!emailNorm || !password) { return res.status(400).json({ success: false, message: 'Please provide both email and password' }); }
106-118
: Use normalized email and select password for comparison.If you mark
password
asselect: false
, you must project it explicitly.- const vigy = await Vigy.findOne({ email }); + const vigy = await Vigy.findOne({ email: emailNorm }).select('+password');
154-172
: JWT auth middleware contract is unclear (req.vigyId).Ensure a middleware verifies the token and sets
req.vigyId
; protect this route accordingly.Quick verification script:
#!/bin/bash # Expect an auth middleware that sets req.vigyId and is used on /api/vigy/profile rg -nP -C2 "req\\.vigyId|authenticateVigy|Authorization|jwt\\.verify" backend/middlewares backend/routes
186-197
: Prevent mass assignment on profile updates.Whitelist fields; avoid spreading
req.body
into updates.- 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 } + );
🧹 Nitpick comments (8)
src/Admin/Pages/VigyLogin.jsx (1)
71-76
: Pass flow context to Google OAuth to land on the correct dashboard.
Backend can read it to route Vigy logins to /vigy-dashboard.- const handleGoogleLogin = () => { - const googleAuthUrl = process.env.NODE_ENV === 'production' - ? 'https://vigybag-backend.onrender.com/auth/google' - : 'http://localhost:5000/auth/google'; - window.location.href = googleAuthUrl; - }; + const handleGoogleLogin = () => { + const base = process.env.NODE_ENV === 'production' + ? 'https://vigybag-backend.onrender.com' + : 'http://localhost:5000'; + const url = new URL('/auth/google', base); + url.searchParams.set('flow', 'vigy'); + window.location.href = url.toString(); + };backend/models/Vigy.js (4)
22-26
: ValidatephoneNumber
format.Add a basic E.164 check to prevent malformed data.
phoneNumber: { type: String, required: true, - trim: true + trim: true, + match: [/^\+?[1-9]\d{7,14}$/, 'Invalid phone number'], },
4-4
: Use Mongoose timestamps instead of manualcreatedAt/updatedAt
.Removes duplication and ensures automatic updates.
-const vigySchema = new mongoose.Schema({ +const vigySchema = new mongoose.Schema({ ... - createdAt: { - type: Date, - default: Date.now - }, - updatedAt: { - type: Date, - default: Date.now - } -}); +}, { timestamps: true });Also applies to: 109-116
17-21
: Considerselect: false
for password (pair with controller.select('+password')
).Prevents accidental exposure in queries; adjust login query accordingly.
password: { type: String, required: true, - minlength: 6 + minlength: 6, + select: false },
137-145
: Extend redaction or centralize viatoJSON
transform.Optional: move redaction to a schema transform so all JSON responses are safe by default.
Example:
+vigySchema.set('toJSON', { + transform: (_doc, ret) => { + delete ret.password; + delete ret.aadhaarNumber; + delete ret.bankDetails; + delete ret.documents; + return ret; + }, +});backend/controllers/vigyController.js (3)
71-79
: Consider withholding tokens until verification/approval.You mint tokens at registration while status defaults to 'pending'. Confirm this aligns with access policy for protected routes.
Also applies to: 137-143
144-151
: Avoid echoing raw error messages to clients.Return generic messages; keep full details in server logs.
- res.status(500).json({ - success: false, - message: 'Server error during login', - error: error.message - }); + res.status(500).json({ success: false, message: 'Server error during login' });
1-228
: Add basic abuse mitigations for auth endpoints.Apply rate limiting and incremental backoff on login to deter brute force; add audit logs for registration and suspicious activity.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (1)
backend/package-lock.json
is excluded by!**/package-lock.json
📒 Files selected for processing (17)
backend/app.js
(1 hunks)backend/config/config.js
(1 hunks)backend/config/db.js
(1 hunks)backend/controllers/vigyController.js
(1 hunks)backend/createTestUser.js
(1 hunks)backend/middlewares/Passport.js
(1 hunks)backend/models/Vigy.js
(1 hunks)backend/package.json
(1 hunks)backend/routes/authRoutes.js
(1 hunks)backend/routes/subscribe.js
(2 hunks)backend/routes/vigyRoutes.js
(1 hunks)backend/setup-env.js
(1 hunks)src/Admin/Pages/VigyDashboard.jsx
(1 hunks)src/Admin/Pages/VigyForm.jsx
(1 hunks)src/Admin/Pages/VigyLogin.jsx
(1 hunks)src/App.jsx
(2 hunks)src/User/pages/UserAuth/UserAuth.jsx
(1 hunks)
✅ Files skipped from review due to trivial changes (1)
- backend/package.json
🚧 Files skipped from review as they are similar to previous changes (9)
- backend/config/db.js
- src/Admin/Pages/VigyDashboard.jsx
- src/Admin/Pages/VigyForm.jsx
- src/User/pages/UserAuth/UserAuth.jsx
- backend/routes/authRoutes.js
- backend/createTestUser.js
- src/App.jsx
- backend/routes/vigyRoutes.js
- backend/config/config.js
🧰 Additional context used
🧬 Code graph analysis (6)
backend/models/Vigy.js (2)
backend/config/db.js (1)
mongoose
(3-3)backend/createTestUser.js (1)
mongoose
(1-1)
backend/middlewares/Passport.js (2)
backend/app.js (1)
passport
(5-5)backend/routes/authRoutes.js (1)
passport
(3-3)
backend/controllers/vigyController.js (2)
backend/createTestUser.js (2)
Vigy
(2-2)vigy
(46-46)backend/routes/vigyRoutes.js (1)
req
(78-78)
src/Admin/Pages/VigyLogin.jsx (1)
src/Admin/Pages/VigyForm.jsx (2)
formData
(11-17)handleSubmit
(295-350)
backend/app.js (3)
backend/routes/vigyRoutes.js (2)
express
(1-1)req
(78-78)backend/middlewares/Passport.js (1)
passport
(1-1)backend/controllers/vigyController.js (2)
req
(16-32)req
(96-96)
backend/routes/subscribe.js (3)
backend/routes/index.js (3)
require
(2-2)router
(3-3)express
(1-1)backend/app.js (1)
express
(2-2)src/User/pages/Home/Home.jsx (1)
🪛 Biome (2.1.2)
backend/middlewares/Passport.js
[error] 27-27: Expected a property, a shorthand property, a getter, a setter, or a method but instead found '<<'.
Expected a property, a shorthand property, a getter, a setter, or a method here.
(parse)
[error] 27-27: Expected an expression but instead found '<<'.
Expected an expression here.
(parse)
[error] 27-27: Expected an expression but instead found '<<'.
Expected an expression here.
(parse)
[error] 28-28: Expected a JSX attribute but instead found '.'.
Expected a JSX attribute here.
(parse)
[error] 28-28: Expected a JSX attribute but instead found '.'.
Expected a JSX attribute here.
(parse)
[error] 29-29: Expected a JSX attribute but instead found ','.
Expected a JSX attribute here.
(parse)
[error] 29-29: Expected a JSX attribute but instead found '.'.
Expected a JSX attribute here.
(parse)
[error] 29-29: Expected a JSX attribute but instead found '.'.
Expected a JSX attribute here.
(parse)
[error] 30-30: Expected a JSX attribute but instead found ','.
Expected a JSX attribute here.
(parse)
[error] 30-30: Expected a JSX attribute but instead found '.'.
Expected a JSX attribute here.
(parse)
[error] 30-30: Expected a JSX attribute but instead found '.'.
Expected a JSX attribute here.
(parse)
[error] 30-32: Expected a JSX attribute but instead found '|| "http://vigybag.com/auth/google/callback",
======='.
Expected a JSX attribute here.
(parse)
[error] 32-32: Expected a JSX attribute but instead found '.'.
Expected a JSX attribute here.
(parse)
[error] 32-32: Expected a JSX attribute but instead found '.'.
Expected a JSX attribute here.
(parse)
[error] 33-33: Expected a JSX attribute but instead found ','.
Expected a JSX attribute here.
(parse)
[error] 33-33: Expected a JSX attribute but instead found '.'.
Expected a JSX attribute here.
(parse)
[error] 33-33: Expected a JSX attribute but instead found '.'.
Expected a JSX attribute here.
(parse)
[error] 34-34: Expected a JSX attribute but instead found ','.
Expected a JSX attribute here.
(parse)
[error] 35-35: Expected a JSX attribute but instead found ','.
Expected a JSX attribute here.
(parse)
[error] 35-35: Unexpected token. Did you mean {'>'}
or >
?
(parse)
[error] 35-35: Unexpected token. Did you mean {'>'}
or >
?
(parse)
[error] 35-35: Unexpected token. Did you mean {'>'}
or >
?
(parse)
[error] 35-35: Unexpected token. Did you mean {'>'}
or >
?
(parse)
[error] 35-35: Unexpected token. Did you mean {'>'}
or >
?
(parse)
[error] 35-35: Unexpected token. Did you mean {'>'}
or >
?
(parse)
backend/app.js
[error] 19-20: Expected a statement but instead found '======='.
Expected a statement here.
(parse)
[error] 31-32: Expected a statement but instead found '>>>>>>> b4f6540 (Fix: Corrected routing'.
Expected a statement here.
(parse)
[error] 32-32: expected (
but instead found 'Login as Vigy'
Remove 'Login as Vigy'
(parse)
[error] 32-32: expected ;
but instead found button
Remove button
(parse)
[error] 32-32: expected ,
but instead found #
Remove #
(parse)
[error] 32-32: Expected an identifier but instead found '2638'.
Expected an identifier here.
(parse)
[error] 32-32: Private names are only allowed on the left side of a 'in' expression
(parse)
[error] 32-32: expected ,
but instead found 2638
Remove 2638
(parse)
[error] 32-32: expected ;
but instead found )
Remove )
(parse)
[error] 47-50: Expected a statement but instead found '<<<<<<< HEAD
// Routes
app.use("/api/subscribe", subscribeRoute)'.
Expected a statement here.
(parse)
[error] 55-59: Expected a statement but instead found '=======
// Root route for testing
app.get("/", (req, res) =>'.
Expected a statement here.
(parse)
[error] 70-70: Expected a statement but instead found ')'.
Expected a statement here.
(parse)
[error] 109-109: Expected a statement but instead found '>>>>>>> b4f6540 (Fix: Corrected routing'.
Expected a statement here.
(parse)
[error] 109-109: expected (
but instead found 'Login as Vigy'
Remove 'Login as Vigy'
(parse)
[error] 109-109: expected ;
but instead found button
Remove button
(parse)
[error] 109-109: expected ,
but instead found #
Remove #
(parse)
[error] 109-109: Private names are only allowed on the left side of a 'in' expression
(parse)
[error] 17-18: This variable is used before its declaration.
The variable is declared here:
(lint/correctness/noInvalidUseBeforeDeclaration)
[error] 18-19: This variable is used before its declaration.
The variable is declared here:
(lint/correctness/noInvalidUseBeforeDeclaration)
[error] 22-22: Shouldn't redeclare 'subscribeRoute'. Consider to delete it or rename it.
'subscribeRoute' is defined here:
(lint/suspicious/noRedeclare)
[error] 24-24: Shouldn't redeclare 'app'. Consider to delete it or rename it.
'app' is defined here:
(lint/suspicious/noRedeclare)
backend/routes/subscribe.js
[error] 1-2: Expected a statement but instead found '<<<<<<< HEAD'.
Expected a statement here.
(parse)
[error] 45-45: Expected a statement but instead found '======='.
Expected a statement here.
(parse)
[error] 82-82: numbers cannot be followed by identifiers directly after
an identifier cannot appear here
(parse)
🪛 Gitleaks (8.27.2)
backend/setup-env.js
[high] 6-6: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.
(generic-api-key)
🔇 Additional comments (3)
backend/middlewares/Passport.js (2)
5-5
: Load the same env file path that setup-env.js creates.
Avoid surprises across cwd differences.-const dotenv = require("dotenv"); -dotenv.config(); +const dotenv = require("dotenv"); +const path = require("path"); +dotenv.config({ path: path.join(__dirname, "../.env") });
13-22
: Good: safe callback URL resolution in dev; keep prod strict.
No change needed once conflicts are resolved.backend/controllers/vigyController.js (1)
126-133
: Confirm allowed statuses for login.Verify that only 'approved' and 'active' are intended; document or centralize as a constant/enumeration.
passport.use( | ||
new GoogleStrategy( | ||
{ | ||
<<<<<<< HEAD | ||
clientID: process.env.GOOGLE_CLIENT_ID, | ||
clientSecret: process.env.GOOGLE_CLIENT_SECRET, | ||
callbackURL: process.env.GOOGLE_CALLBACK_URL || "http://vigybag.com/auth/google/callback", | ||
======= | ||
clientID: process.env.GOOGLE_CLIENT_ID, | ||
clientSecret: process.env.GOOGLE_CLIENT_SECRET, | ||
callbackURL: resolvedCallbackUrl, | ||
>>>>>>> b4f65404 (Fix: Corrected routing for 'Login as Vigy' button (Issue #2638)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Unresolved merge conflict breaks build; also risk of initializing GoogleStrategy with invalid callbackURL.
Clean conflict markers and guard strategy init; fail fast in prod if config is incomplete.
passport.use(
- new GoogleStrategy(
- {
-<<<<<<< HEAD
- clientID: process.env.GOOGLE_CLIENT_ID,
- clientSecret: process.env.GOOGLE_CLIENT_SECRET,
- callbackURL: process.env.GOOGLE_CALLBACK_URL || "http://vigybag.com/auth/google/callback",
-=======
- clientID: process.env.GOOGLE_CLIENT_ID,
- clientSecret: process.env.GOOGLE_CLIENT_SECRET,
- callbackURL: resolvedCallbackUrl,
->>>>>>> b4f65404 (Fix: Corrected routing for 'Login as Vigy' button (Issue #2638))
- },
- async (accessToken, refreshToken, profile, done) => {
+ // Guard config before initializing strategy
+ // In dev, allow localhost default; in prod, require explicit GOOGLE_CALLBACK_URL
+ () => {
+ const missingCreds = !process.env.GOOGLE_CLIENT_ID || !process.env.GOOGLE_CLIENT_SECRET;
+ const isProd = process.env.NODE_ENV === 'production';
+ const canInitGoogle = !missingCreds && !!resolvedCallbackUrl;
+ if (!canInitGoogle) {
+ const msg = "Google OAuth config incomplete. Set GOOGLE_CLIENT_ID, GOOGLE_CLIENT_SECRET, and GOOGLE_CALLBACK_URL (in production).";
+ if (isProd) throw new Error(msg);
+ console.warn("⚠️", msg, "Skipping GoogleStrategy initialization in development.");
+ return;
+ }
+ passport.use(new GoogleStrategy(
+ {
+ clientID: process.env.GOOGLE_CLIENT_ID,
+ clientSecret: process.env.GOOGLE_CLIENT_SECRET,
+ callbackURL: resolvedCallbackUrl,
+ },
+ async (accessToken, refreshToken, profile, done) => {
// existing verify logic unchanged
- }
- )
+ }
+ ));
+ }
);
Committable suggestion skipped: line range outside the PR's diff.
🧰 Tools
🪛 Biome (2.1.2)
[error] 27-27: Expected a property, a shorthand property, a getter, a setter, or a method but instead found '<<'.
Expected a property, a shorthand property, a getter, a setter, or a method here.
(parse)
[error] 27-27: Expected an expression but instead found '<<'.
Expected an expression here.
(parse)
[error] 27-27: Expected an expression but instead found '<<'.
Expected an expression here.
(parse)
[error] 28-28: Expected a JSX attribute but instead found '.'.
Expected a JSX attribute here.
(parse)
[error] 28-28: Expected a JSX attribute but instead found '.'.
Expected a JSX attribute here.
(parse)
[error] 29-29: Expected a JSX attribute but instead found ','.
Expected a JSX attribute here.
(parse)
[error] 29-29: Expected a JSX attribute but instead found '.'.
Expected a JSX attribute here.
(parse)
[error] 29-29: Expected a JSX attribute but instead found '.'.
Expected a JSX attribute here.
(parse)
[error] 30-30: Expected a JSX attribute but instead found ','.
Expected a JSX attribute here.
(parse)
[error] 30-30: Expected a JSX attribute but instead found '.'.
Expected a JSX attribute here.
(parse)
[error] 30-30: Expected a JSX attribute but instead found '.'.
Expected a JSX attribute here.
(parse)
[error] 30-32: Expected a JSX attribute but instead found '|| "http://vigybag.com/auth/google/callback",
======='.
Expected a JSX attribute here.
(parse)
[error] 32-32: Expected a JSX attribute but instead found '.'.
Expected a JSX attribute here.
(parse)
[error] 32-32: Expected a JSX attribute but instead found '.'.
Expected a JSX attribute here.
(parse)
[error] 33-33: Expected a JSX attribute but instead found ','.
Expected a JSX attribute here.
(parse)
[error] 33-33: Expected a JSX attribute but instead found '.'.
Expected a JSX attribute here.
(parse)
[error] 33-33: Expected a JSX attribute but instead found '.'.
Expected a JSX attribute here.
(parse)
[error] 34-34: Expected a JSX attribute but instead found ','.
Expected a JSX attribute here.
(parse)
[error] 35-35: Expected a JSX attribute but instead found ','.
Expected a JSX attribute here.
(parse)
[error] 35-35: Unexpected token. Did you mean {'>'}
or >
?
(parse)
[error] 35-35: Unexpected token. Did you mean {'>'}
or >
?
(parse)
[error] 35-35: Unexpected token. Did you mean {'>'}
or >
?
(parse)
[error] 35-35: Unexpected token. Did you mean {'>'}
or >
?
(parse)
[error] 35-35: Unexpected token. Did you mean {'>'}
or >
?
(parse)
[error] 35-35: Unexpected token. Did you mean {'>'}
or >
?
(parse)
🤖 Prompt for AI Agents
In backend/middlewares/Passport.js around lines 24 to 35, remove the Git merge
conflict markers and the duplicated client config, use the resolvedCallbackUrl
value (or compute it consistently) for callbackURL, and add a guard that
validates process.env.GOOGLE_CLIENT_ID, process.env.GOOGLE_CLIENT_SECRET and
resolvedCallbackUrl before calling passport.use; if any are missing, log an
explicit error and exit in production (or skip initialization in non-prod) so
the GoogleStrategy is never initialized with invalid config.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 2
♻️ Duplicate comments (4)
backend/setup-env.js (3)
17-17
: Confirm.env
load path and gitignore.This writes to
backend/.env
. Ensure runtime usesdotenv.config({ path: path.join(__dirname, ".env") })
(or equivalent) and that.env
is gitignored.Run:
#!/bin/bash # .env should be ignored rg -nP '(^|/)\\.env(\\b|$)' .gitignore -n || echo "No .env ignore rule found" # Verify dotenv is loading backend/.env explicitly rg -nP "dotenv\\.config\\(([^)]*)\\)" backend -C2 # Check for other hard-coded ports; ensure 5000 is consistent rg -nP 'PORT=\\d+|process\\.env\\.PORT|:\\s*\\d{4,5}\\b' -g '!**/node_modules/**' -n -C2
8-8
: Fix port: set PORT=5000 (currently 3001).Frontend/back-end expect 5000; keep local flow consistent. The AI summary also claims 5000, but code has 3001.
Apply this diff:
-const envContent = `PORT=3001 +const envContent = `PORT=5000 MONGO_URI=mongodb://localhost:27017/vigybag JWT_SECRET=${jwtSecret} SESSION_SECRET=${sessionSecret} GOOGLE_CLIENT_ID= GOOGLE_CLIENT_SECRET= NODE_ENV=development `;
19-23
: Do not overwrite existing .env; preserve secrets/sessions and tighten perms.Overwriting rotates secrets and invalidates sessions; write once with 0600 perms.
Apply this diff:
- fs.writeFileSync(envPath, envContent, { encoding: 'utf8', flag: 'w' }); - console.log('✅ .env file created successfully!'); - console.log('📁 Location:', envPath); + if (fs.existsSync(envPath)) { + console.log('ℹ️ .env already exists, not overwriting.'); + console.log('📁 Location:', envPath); + process.exit(0); + } + fs.writeFileSync(envPath, envContent, { encoding: 'utf8', flag: 'wx', mode: 0o600 }); + console.log('✅ .env file created successfully!'); + console.log('📁 Location:', envPath);backend/routes/subscribe.js (1)
8-16
: Normalize email with validator and add strict type guard (avoid .toString()).Use validator.normalizeEmail and a proper string check to prevent edge-case bypasses and ensure consistent storage.
- const emailRaw = (req.body?.email ?? "").toString(); - const email = emailRaw.trim().toLowerCase(); - - if (!email) { - return res.status(400).json({ message: "⚠️ A valid email is required." }); - } - - if (!validator.isEmail(email)) { - return res.status(400).json({ message: "⚠️ Please provide a valid email address." }); - } + const rawEmail = req.body?.email; + if (typeof rawEmail !== "string" || !rawEmail.trim()) { + return res.status(400).json({ message: "⚠️ A valid email is required." }); + } + const email = + validator.normalizeEmail(rawEmail, { + gmail_remove_subaddress: true, + gmail_remove_dots: false, + }) || rawEmail.trim().toLowerCase(); + if (!validator.isEmail(email)) { + return res.status(400).json({ message: "⚠️ Please provide a valid email address." }); + }
🧹 Nitpick comments (1)
backend/routes/subscribe.js (1)
30-33
: Avoid leaking internal validation details in API responses.Return a generic message to clients; log the detailed err.message server-side.
- if (err.name === "ValidationError") { - return res.status(400).json({ message: err.message }); - } + if (err.name === "ValidationError") { + console.warn("Subscribe validation error:", err.message); + return res.status(400).json({ message: "⚠️ Please provide a valid email address." }); + }
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (3)
backend/app.js
(1 hunks)backend/routes/subscribe.js
(1 hunks)backend/setup-env.js
(1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
- backend/app.js
🔇 Additional comments (2)
backend/setup-env.js (1)
5-6
: LGTM: Strong random secrets (256-bit) for JWT/Session.Good call using
crypto.randomBytes(32)
for both secrets.backend/routes/subscribe.js (1)
4-4
: LGTM: single router init and export look correct.Duplicate imports/exports from earlier feedback are resolved. Thanks.
const subscriber = new Subscriber({ email }); | ||
await subscriber.save(); | ||
|
||
return res.status(201).json({ message: "🎉 Subscribed successfully!" }); | ||
} catch (err) { | ||
console.error("Subscribe error:", err); | ||
|
||
// Duplicate email | ||
if (err.code === 11000) { | ||
return res.status(409).json({ message: "This email is already subscribed." }); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧩 Analysis chain
Enforce case-insensitive email uniqueness at the DB level (index + backfill).
Lowercasing at write-time helps, but without a case-insensitive unique index, legacy mixed‑case rows can allow duplicates.
- Ensure schema sets: unique: true, lowercase: true, trim: true.
- Add an index with collation strength 2 or backfill all existing emails to lowercase before creating a standard unique index.
Example (in the Subscriber schema file):
email: { type: String, required: true, unique: true, lowercase: true, trim: true }
// Ensure case-insensitive uniqueness (MongoDB >=3.4):
SubscriberSchema.index({ email: 1 }, { unique: true, collation: { locale: "en", strength: 2 } });
Run to verify schema/index hints:
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "Locate Subscriber model file(s):"
fd -a -g "models/**/subscriber.*" backend || true
echo -e "\nScan for email schema options and indexes:"
rg -n -C2 -S 'email.*(unique|lowercase|trim|index|collation)' backend || true
rg -n -C3 -S 'Schema\\(|new Schema|index\\(\\{\\s*email' backend || true
Length of output: 542
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "Searching repo for Subscriber model, schema definitions, and email/index settings (using fixed-string searches to avoid regex errors)..."
echo -e "\n1) Occurrences of 'Subscriber':"
rg -nF --hidden 'Subscriber' || true
echo -e "\n2) Mongoose/model/schema patterns (literal):"
rg -nF --hidden 'mongoose.model' || true
rg -nF --hidden 'new Schema' || true
rg -nF --hidden 'Schema(' || true
echo -e "\n3) Email field options and index hints (literal):"
rg -nF --hidden 'email:' || true
rg -nF --hidden 'lowercase: true' || true
rg -nF --hidden 'unique: true' || true
rg -nF --hidden 'index(' || true
rg -nF --hidden 'collation' || true
echo -e "\n4) Candidate model files and paths:"
fd -a -g "*subscriber*" || true
fd -a -g "*Subscriber*" || true
fd -a -g "models/**" || true
Length of output: 3885
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "== backend/models/subscriber.js =="
sed -n '1,240p' backend/models/subscriber.js || true
echo -e "\n== Search for 'index' and 'collation' across backend/models =="
rg -n --hidden 'index' backend/models || true
rg -n --hidden 'collation' backend || true
echo -e "\n== Check for 'trim: true' and 'lowercase: true' occurrences in subscriber model =="
rg -n --hidden -n --fixed-strings 'trim: true' backend/models/subscriber.js || true
rg -n --hidden -n --fixed-strings 'lowercase: true' backend/models/subscriber.js || true
Length of output: 579
Enforce case‑insensitive email uniqueness at the DB level
backend/models/subscriber.js already has email: { unique: true, lowercase: true, trim: true } — but a plain unique index is case‑sensitive and legacy mixed‑case rows can still create duplicates.
- Add a case‑insensitive unique index, e.g.
subscriberSchema.index({ email: 1 }, { unique: true, collation: { locale: "en", strength: 2 } });
and ensure the index is created on the production DB (migration/createIndexes). - Alternatively, backfill existing documents to lowercase and deduplicate, then create the unique index.
🤖 Prompt for AI Agents
In backend/routes/subscribe.js around lines 20 to 28, the code relies on a plain
unique index for email which is case‑sensitive and allows legacy mixed‑case
duplicates; add a case‑insensitive unique index on the subscriber schema (e.g.
index on email with collation { locale: "en", strength: 2 }) and ensure it is
created in production by adding a migration or createIndexes routine; before
creating the index, backfill existing subscriber documents to lowercase emails
and deduplicate any conflicting entries (or remove duplicates) so the unique
index can be created without errors.
} catch (error) { | ||
console.error('❌ Error creating .env file:', error.message); | ||
console.log('\n📝 Please create a .env file manually with this content:'); | ||
console.log(envContent); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Stop printing secrets on error; redact and exit non‑zero.
envContent
includes real secrets; avoid leaking to CI logs.
Apply this diff:
- console.error('❌ Error creating .env file:', error.message);
- console.log('\n📝 Please create a .env file manually with this content:');
- console.log(envContent);
+ console.error('❌ Error creating .env file:', error.message);
+ const redacted = envContent
+ .replace(/^JWT_SECRET=.*$/m, 'JWT_SECRET=<redacted>')
+ .replace(/^SESSION_SECRET=.*$/m, 'SESSION_SECRET=<redacted>');
+ console.log('\n📝 Please create a .env file manually. Template:');
+ console.log(redacted);
+ process.exit(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.
} catch (error) { | |
console.error('❌ Error creating .env file:', error.message); | |
console.log('\n📝 Please create a .env file manually with this content:'); | |
console.log(envContent); | |
} catch (error) { | |
console.error('❌ Error creating .env file:', error.message); | |
const redacted = envContent | |
.replace(/^JWT_SECRET=.*$/m, 'JWT_SECRET=<redacted>') | |
.replace(/^SESSION_SECRET=.*$/m, 'SESSION_SECRET=<redacted>'); | |
console.log('\n📝 Please create a .env file manually. Template:'); | |
console.log(redacted); | |
process.exit(1); | |
} |
🤖 Prompt for AI Agents
In backend/setup-env.js around lines 24 to 27, the catch block prints the full
envContent (which contains secrets) to logs; replace that behavior so you do not
leak secrets: log a concise, non‑secret error message (e.g., "Error creating
.env file; secrets redacted"), optionally include non‑secret error context, do
NOT print envContent, and then exit with a non‑zero status (process.exit(1)) to
signal failure.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 1
♻️ Duplicate comments (3)
backend/setup-env.js (3)
8-15
: Align PORT and harden localhost resolution for Windows.Switch PORT to 5000 (frontend expects 5000) and prefer 127.0.0.1 to avoid IPv6 localhost quirks on Windows.
Apply this diff:
-const envContent = `PORT=3001 -MONGO_URI=mongodb://localhost:27017/vigybag +const envContent = `PORT=5000 +MONGO_URI=mongodb://127.0.0.1:27017/vigybag JWT_SECRET=${jwtSecret} SESSION_SECRET=${sessionSecret} GOOGLE_CLIENT_ID= GOOGLE_CLIENT_SECRET= NODE_ENV=development `;To verify the expected dev port, run:
#!/bin/bash # Look for frontend API base URLs/ports rg -nP -C2 '(http://(localhost|127\.0\.0\.1|0\.0\.0\.0):\d+)' --type js --type ts --type tsx --type jsx
17-18
: Ensure dotenv loads the same path and .env is ignored.Confirm backend loads this exact file (backend/.env) and that it’s gitignored.
Run:
#!/bin/bash # .env should be ignored rg -nP '(^|/)(backend/)?\.env(\b|$)' .gitignore -n || echo "No .env ignore rule found" # Verify dotenv loads backend/.env (or project root) consistently rg -nP -C2 "dotenv\.config\(([^)]*)\)" backend || echo "No explicit dotenv.config found in backend/"
19-24
: Don’t overwrite existing .env; set secure perms.Current code will rotate secrets if re-run, breaking sessions/tokens. Guard if file exists; create with mode 0600 and fail if present.
Apply this diff:
try { - fs.writeFileSync(envPath, envContent, { encoding: 'utf8', flag: 'w' }); + if (fs.existsSync(envPath)) { + console.log('ℹ️ .env already exists, not overwriting.'); + console.log('📁 Location:', envPath); + process.exit(0); + } + fs.writeFileSync(envPath, envContent, { encoding: 'utf8', flag: 'wx', mode: 0o600 }); console.log('✅ .env file created successfully!'); console.log('📁 Location:', envPath); console.log('\n⚠️ IMPORTANT: Update GOOGLE_CLIENT_ID and GOOGLE_CLIENT_SECRET with your actual values'); } catch (error) {
🧹 Nitpick comments (1)
backend/setup-env.js (1)
1-1
: Optional: make the script directly runnable.Add shebang and strict mode.
+#!/usr/bin/env node +'use strict'; const fs = require('fs');
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (3)
backend/app.js
(1 hunks)backend/routes/subscribe.js
(1 hunks)backend/setup-env.js
(1 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
- backend/app.js
- backend/routes/subscribe.js
🔇 Additional comments (1)
backend/setup-env.js (1)
5-6
: Good: cryptographically strong secrets.32-byte random secrets are appropriate for JWT/session keys.
console.error('❌ Error creating .env file:', error.message); | ||
console.log('\n📝 Please create a .env file manually with this content:'); | ||
console.log(envContent); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Avoid printing secrets on failure.
Dumping envContent leaks JWT/SESSION secrets into logs/CI.
Apply this diff:
- console.error('❌ Error creating .env file:', error.message);
- console.log('\n📝 Please create a .env file manually with this content:');
- console.log(envContent);
+ console.error('❌ Error creating .env file:', error.message);
+ console.log('\n📝 Please create a .env file manually. Example:');
+ console.log(`PORT=5000
+MONGO_URI=mongodb://127.0.0.1:27017/vigybag
+JWT_SECRET=<64-hex-secret>
+SESSION_SECRET=<64-hex-secret>
+GOOGLE_CLIENT_ID=
+GOOGLE_CLIENT_SECRET=
+NODE_ENV=development`);
📝 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.
console.error('❌ Error creating .env file:', error.message); | |
console.log('\n📝 Please create a .env file manually with this content:'); | |
console.log(envContent); | |
console.error('❌ Error creating .env file:', error.message); | |
console.log('\n📝 Please create a .env file manually. Example:'); | |
console.log(`PORT=5000 | |
MONGO_URI=mongodb://127.0.0.1:27017/vigybag | |
JWT_SECRET=<64-hex-secret> | |
SESSION_SECRET=<64-hex-secret> | |
GOOGLE_CLIENT_ID= | |
GOOGLE_CLIENT_SECRET= | |
NODE_ENV=development`); |
🤖 Prompt for AI Agents
In backend/setup-env.js around lines 25 to 27, the failure path currently logs
the full envContent (including secrets) to stdout/CI; remove printing of raw
envContent and instead either log a safe template with secret placeholders (e.g.
JWT_SECRET=<<REDACTED>>) or instruct the user to create the .env manually and
show only non-sensitive keys/paths, or write the content to a local file with
restrictive permissions and log the filename; implement one of these approaches
so secrets are never printed to logs.
The latest updates on your projects. Learn more about Vercel for GitHub.
|
All checks have passed and the changes look good. Please approve deployment to Vercel. 🚀 |
Summary
This PR fixes the "Login as Vigy" button which previously redirected to a 404 page.
Changes
Related Issue
Fixes #2638
Screenshots
(Before: 404 error, After: working Vigy login form)
Notes
Tested on Chrome and Edge (Windows 11). Working as expected.
Summary by CodeRabbit
New Features
Improvements
Chores