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

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions server/config/db.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import mongoose from "mongoose";

const connectDB = async () => {
try {
console.log("Connecting to MongoDB...");
await mongoose.connect(process.env.MONGODB_URI!);
console.log("✅ MongoDB Connected");
} catch (error) {
console.error(error);
}
};

export default connectDB;
Comment on lines +1 to +13

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🩺 Stability & Availability | 🔴 Critical | ⚡ Quick win

Connection failures are swallowed — server will start without a DB.

catch only logs; it never exits or rethrows, so await connectDB() in server.ts always resolves and the HTTP listener starts regardless of connection outcome. Combined with the non-null assertion on MONGODB_URI (no validation that it's actually set), a misconfigured environment fails silently and the app serves traffic that will error on every DB-touching request instead of failing fast at startup.

🐛 Suggested fix
 import mongoose from "mongoose";

 const connectDB = async () => {
+  const uri = process.env.MONGODB_URI;
+  if (!uri) {
+    throw new Error("MONGODB_URI is not defined");
+  }
   try {
     console.log("Connecting to MongoDB...");
-    await mongoose.connect(process.env.MONGODB_URI!);
+    await mongoose.connect(uri);
     console.log("✅ MongoDB Connected");
   } catch (error) {
     console.error(error);
+    process.exit(1);
   }
 };

 export default connectDB;
📝 Committable suggestion

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

Suggested change
import mongoose from "mongoose";
const connectDB = async () => {
try {
console.log("Connecting to MongoDB...");
await mongoose.connect(process.env.MONGODB_URI!);
console.log("✅ MongoDB Connected");
} catch (error) {
console.error(error);
}
};
export default connectDB;
import mongoose from "mongoose";
const connectDB = async () => {
const uri = process.env.MONGODB_URI;
if (!uri) {
throw new Error("MONGODB_URI is not defined");
}
try {
console.log("Connecting to MongoDB...");
await mongoose.connect(uri);
console.log("✅ MongoDB Connected");
} catch (error) {
console.error(error);
process.exit(1);
}
};
export default connectDB;
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@server/config/db.ts` around lines 1 - 13, Update connectDB to validate that
MONGODB_URI is defined before calling mongoose.connect, and make connection
failures propagate instead of being swallowed: log the error with context, then
rethrow it (or terminate startup explicitly) so the await in server.ts prevents
the HTTP listener from starting.

124 changes: 124 additions & 0 deletions server/controllers/authControllers.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
import { Request, Response } from "express";
import jwt from 'jsonwebtoken'
import { User } from "../models/User.js";
import bcrypt from "bcrypt";
import { AuthRequest } from "../middlewares/auth.js"

// Helper to generate JWT Token
const generateToken = (id: string)=>{
return jwt.sign({id}, process.env.JWT_SECRET as string, {expiresIn: "30d"} )
Comment on lines +8 to +9

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

rg -n -C3 'JWT_SECRET|dotenv|config\(' server --glob '*.ts'

Repository: prepwave/QuickDine

Length of output: 1221


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '\n== server/controllers/authControllers.ts ==\n'
cat -n server/controllers/authControllers.ts | sed -n '1,220p'

printf '\n== server/server.ts ==\n'
cat -n server/server.ts | sed -n '1,120p'

printf '\n== server/middlewares/auth.ts ==\n'
cat -n server/middlewares/auth.ts | sed -n '1,160p'

Repository: prepwave/QuickDine

Length of output: 8305


Validate JWT_SECRET during bootstrap. as string/! only hides the type error; if the secret is missing, signup/login will fail at jwt.sign(), protected routes will fail at jwt.verify(), and the app will keep running in a broken state. Move the check to server/server.ts right after env loading and fail fast.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@server/controllers/authControllers.ts` around lines 8 - 9, Move JWT_SECRET
validation out of generateToken and into server/server.ts immediately after
environment loading. Check that the value is present, fail fast during bootstrap
with an appropriate startup error, and then use the validated secret without
relying on an unchecked type assertion; leave generateToken focused on signing
tokens.

}

// Resister a new User
// Post /api/uath/register
export const registerUser = async (req: Request, res: Response): Promise<void> => {
try{
const { name, email, password, phone, role } = req.body;

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🔒 Security & Privacy | 🔴 Critical | ⚡ Quick win

Do not let public registration assign privileged roles.

A caller can submit role: "admin" or "owner" and immediately receive that privilege. Ignore this field here; privileged-role assignment must be an authorized server-side operation.

Proposed fix
-        const { name, email, password, phone, role } = req.body;
+        const { name, email, password, phone } = req.body;
...
-            role,

Also applies to: 33-39

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@server/controllers/authControllers.ts` at line 16, Update the registration
flow in authControllers.ts to stop destructuring or using role from req.body;
public registration must create users with the existing non-privileged default
role. Leave privileged-role assignment to authorized server-side operations and
preserve handling of the other registration fields.


if(!name || !email || !password){
res.status(400).json({message: "Please Enter all required Fieds"})
return;
}
// Check if user exists
const userExists = await User.findOne({email})
if(userExists){ res.status(400).json({message: "User already exists "})
return;
}

// Hash password
const salt = await bcrypt.genSalt(10)
const hashedPassword = await bcrypt.hash(password, salt)

// creat user
const user = await User.create({
name,
email,
password: hashedPassword,
phone,
role,
})

if(user){
res.status(201).json({
_id: user._id,
name: user.name,
email: user.email,
phone: user.phone,
role: user.role,
token: generateToken(user._id.toString())
})
}else{
res.status(400).json({message: "Invailid User data"});
}
} catch (error : any){
console.error(error);
res.status(400).json({message: error.message});
Comment on lines +53 to +55

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🔒 Security & Privacy | 🟠 Major | ⚡ Quick win

Do not return raw internal errors to clients.

These catch-all blocks expose database/JWT implementation details via error.message and misclassify unexpected failures as 400. Log the error server-side; return fixed client-safe messages and reserve 5xx for unexpected failures.

Also applies to: 94-96

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@server/controllers/authControllers.ts` around lines 53 - 55, Update the catch
blocks in the authentication controllers, including the blocks near the shown
response and the corresponding block near the later location, to log errors
server-side while returning fixed client-safe messages instead of error.message.
Classify expected authentication or validation failures as 400-level responses,
and return an appropriate 5xx status with a generic message for unexpected
failures.


}
}

// Authentication a User & get token
// Post /api/uath/register
export const loginUser = async (req: Request, res: Response): Promise<void> => {
try{
const { email, password} = req.body;

if( !email || !password){
res.status(400).json({message: "Please provide email and password"})
return;
}
// Check for user
const user = await User.findOne({email})
if(!user){
res.status(400).json({message: "Invailid email or password"});
return;

}

// Check if password matches (useeer.password isnot undefined because we queried it )
const isMatch = await bcrypt.compare(password, user.password || "" )
if(!isMatch){
res.status(400).json({message: "Invailid email or password"});
return;
}
res.json({
_id: user._id,
name: user.name,
email: user.email,
phone: user.phone,
role: user.role,
token: generateToken(user._id.toString())
})


} catch (error: any){
console.error(error);
res.status(400).json({message: error.message});

}
}
// get user profile
// GET /api/uath/me
// access Private
export const getMe = async (req: AuthRequest, res: Response): Promise<void> => {
try{
if (!req.user){
res.status(401).json({message: "Not Authorized"})
return;

}
res.json(req.user)
} catch (error) {
console.error(error);

if (error instanceof Error) {
res.status(400).json({
message: error.message
});
} else {
res.status(400).json({
message: "Unknown error"
});
}
}
}
74 changes: 74 additions & 0 deletions server/middlewares/auth.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
import { Request, Response, NextFunction } from "express";
import jwt from "jsonwebtoken";
import { User, IUser } from "../models/User.js";

export interface AuthRequest extends Request {
user?: IUser;
}

export const protect = async (
req: AuthRequest,
res: Response,
next: NextFunction
): Promise<void> => {

let token;

if (
req.headers.authorization &&
req.headers.authorization.startsWith("Bearer")
) {
try {

token = req.headers.authorization.split(" ")[1];

const decoded = jwt.verify(
token,
process.env.JWT_SECRET!
) as { id: string };

const user = await User.findById(decoded.id).select("-password");

if (!user) {
res.status(401).json({
message: "Not authorized, user not found"
});
return;
}

req.user = user;

next();

} catch (error) {
console.error("Auth Middleware Error:", error);

res.status(401).json({
message: "Not authorized, token failed"
});
return;
}
}

if (!token) {
res.status(401).json({
message: "Not authorized, no token"
});
return;
}
}
export const adminOnly = (req: AuthRequest, res: Response, next: NextFunction ): void=> {
if(req.user &&req.user.role==="admin"){
next()
}else{
res.status(403).json({message:" Access denied, admin role required"});
}
}

export const ownerOnly = (req: AuthRequest, res: Response, next: NextFunction ): void=> {
if(req.user &&(req.user.role==="owner"|| req.user.role==="admin")){
next()
}else{
res.status(403).json({message:" Access denied, admin role required"});
}
}
33 changes: 33 additions & 0 deletions server/models/User.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import {Document, model, Schema } from "mongoose"

export interface IUser extends Document{
name: string;
email: string;
password?: string;
phone?: string;
role: "user" | "admin" | "owner";
createdAt: Date;
updatedAt: Date;
}

const UserSchema = new Schema<IUser>(
{
name: {type: String, required: true, trim: true},
email: {type: String, required: true, unique: true, trim: true, lowercase: true},
password: {type: String, required: true, minlength: 6},
phone: {type: String, trim: true, minlength: 6 },
role: {type: String, enum: ["user" , "admin" , "owner"], default: "user"},

},
{timestamps: true}


)
// Remove password when converting to JSON
UserSchema.set("toJSON", {
transform: (doc, ret)=>{
delete ret.password;
return ret;
}
})
export const User = model<IUser>("User", UserSchema)
Loading