Skip to content

Commit 285c210

Browse files
authored
Merge branch 'main' into feature/ForgotPassword
2 parents 1e4fd1f + d4f6a09 commit 285c210

File tree

23 files changed

+1105
-1602
lines changed

23 files changed

+1105
-1602
lines changed

backend/package-lock.json

Lines changed: 3 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

backend/src/app.ts

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -60,14 +60,10 @@ app.post("/login", async (req: Request, res: Response) => {
6060
const accessToken = signAccessToken(payload);
6161
const refreshToken = signRefreshToken(payload);
6262

63-
refreshTokens.set(user.id, refreshToken);
6463

65-
res.cookie("refreshToken", refreshToken, {
66-
httpOnly: true,
67-
sameSite: "strict",
68-
secure: process.env.NODE_ENV === "production",
69-
maxAge: 7 * 24 * 60 * 60 * 1000,
70-
});
64+
// app.get("/protected", authenticate, (req: AuthRequest, res: Response) => {
65+
// res.json({ message: "Protected route accessed", user: req.user });
66+
// });
7167

7268
res.json({ accessToken });
7369
});
@@ -206,6 +202,7 @@ app.use("/api", authRoutes);
206202
app.get("/", (_req, res) => {
207203
res.send("API is running");
208204
});
205+
app.use("/api/users", userRoutes);
209206

210207
// ------------------- DATABASE -------------------
211208

Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
// src/controllers/authController.ts
2+
import { Request, Response } from "express";
3+
import jwt from "jsonwebtoken";
4+
import crypto from "crypto";
5+
import nodemailer from "nodemailer";
6+
import { User } from "../models/user.model";
7+
8+
const JWT_SECRET = process.env.JWT_SECRET || "secretkey";
9+
const CLIENT_URL = process.env.CLIENT_URL || "http://localhost:3000";
10+
11+
const transporter = nodemailer.createTransport({
12+
// service: "gmail",
13+
// auth: {
14+
// user: process.env.EMAIL_USER,
15+
// pass: process.env.EMAIL_PASS,
16+
// },
17+
host: 'smtp.ethereal.email',
18+
port: 587,
19+
auth: {
20+
21+
pass: 'Du8rSm184HzwwHsHYm'
22+
}
23+
});
24+
25+
const generateToken = (id: string) => {
26+
return jwt.sign({ id }, JWT_SECRET, { expiresIn: "7d" });
27+
};
28+
29+
// Register user
30+
export const register = async (req: Request, res: Response) => {
31+
try {
32+
const { name, email, password } = req.body;
33+
const existingUser = await User.findOne({ email });
34+
if (existingUser) return res.status(400).json({ message: "User already exists" });
35+
36+
const verificationToken = crypto.randomBytes(20).toString("hex");
37+
const user = await User.create({ name, email, password, verificationToken });
38+
39+
const verificationLink = `${CLIENT_URL}/verify/${verificationToken}`;
40+
await transporter.sendMail({
41+
to: email,
42+
subject: "Verify your email",
43+
html: `<p>Click <a href="${verificationLink}">here</a> to verify your account.</p>`,
44+
});
45+
46+
res.status(201).json({ message: "User registered. Check your email for verification link." });
47+
} catch (error) {
48+
res.status(500).json({ message: "Registration failed", error });
49+
}
50+
};
51+
52+
// Verify email
53+
export const verifyEmail = async (req: Request, res: Response) => {
54+
try {
55+
const { token } = req.params;
56+
const user = await User.findOne({ verificationToken: token });
57+
58+
if (!user) return res.status(400).json({ message: "Invalid or expired token" });
59+
60+
user.isVerified = true;
61+
user.verificationToken = undefined;
62+
await user.save();
63+
64+
res.status(200).json({ message: "Email verified successfully" });
65+
} catch (error) {
66+
res.status(500).json({ message: "Verification failed", error });
67+
}
68+
};
69+
70+
// Login
71+
export const login = async (req: Request, res: Response) => {
72+
try {
73+
const { email, password } = req.body;
74+
const user = await User.findOne({ email });
75+
76+
if (!user) return res.status(400).json({ message: "Invalid credentials" });
77+
if (!user.isVerified) return res.status(403).json({ message: "Please verify your email" });
78+
79+
const isMatch = await user.comparePassword(password);
80+
if (!isMatch) return res.status(400).json({ message: "Invalid credentials" });
81+
82+
const token = generateToken(String(user._id));
83+
res.status(200).json({ token, user: { name: user.name, email: user.email } });
84+
} catch (error) {
85+
res.status(500).json({ message: "Login failed", error });
86+
}
87+
};
88+
89+
// Get current user
90+
export const getMe = async (req: Request, res: Response) => {
91+
try {
92+
const userId = (req as any).user?.id;
93+
if (!userId) return res.status(401).json({ message: "Unauthorized" });
94+
95+
const user = await User.findById(userId).select("-password");
96+
if (!user) return res.status(404).json({ message: "User not found" });
97+
98+
res.status(200).json(user);
99+
} catch (error) {
100+
res.status(500).json({ message: "Error fetching user", error });
101+
}
102+
};

backend/src/controllers/auth.ts

Lines changed: 0 additions & 50 deletions
This file was deleted.
Lines changed: 18 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1,41 +1,28 @@
1-
import { Request, Response, NextFunction } from 'express';
2-
import { verifyAccessToken } from '../controllers/auth';
1+
// src/middlewares/authMiddleware.ts
2+
import { Request, Response, NextFunction } from "express";
3+
import jwt from "jsonwebtoken";
4+
import { User } from "../models/user.model";
35

4-
export interface AuthenticatedRequest extends Request {
6+
const JWT_SECRET = process.env.JWT_SECRET || "secretkey";
7+
8+
export interface AuthRequest extends Request {
59
user?: any;
610
}
711

8-
export type AuthRequest = AuthenticatedRequest;
9-
10-
// (Middleware file - authMiddleware.ts)
12+
export const protect = async (req: AuthRequest, res: Response, next: NextFunction) => {
13+
let token;
1114

12-
export function authenticate(req: AuthenticatedRequest, res: Response, next: NextFunction) {
13-
const header = req.headers.authorization;
14-
const token = header?.startsWith('Bearer ') ? header.slice(7) : undefined;
15-
16-
if (!token) {
17-
// Use 401 for NO credentials (token missing entirely)
18-
return res.status(401).json({ error: 'No token provided' });
15+
if (req.headers.authorization && req.headers.authorization.startsWith("Bearer")) {
16+
token = req.headers.authorization.split(" ")[1];
1917
}
2018

19+
if (!token) return res.status(401).json({ message: "Not authorized, no token" });
20+
2121
try {
22-
const payload = verifyAccessToken(token);
23-
req.user = payload;
22+
const decoded = jwt.verify(token, JWT_SECRET) as { id: string };
23+
req.user = await User.findById(decoded.id).select("-password");
2424
next();
25-
} catch {
26-
// Use 403 for INVALID credentials (token present but invalid/expired/rejected)
27-
// This is often seen as a better status for expired tokens.
28-
return res.status(403).json({ error: 'Invalid or expired token' });
25+
} catch (error) {
26+
res.status(401).json({ message: "Not authorized, token failed" });
2927
}
30-
}
31-
export function authorizeRole(role: string) {
32-
return (req: AuthenticatedRequest, res: Response, next: NextFunction) => {
33-
if (!req.user) {
34-
return res.status(401).json({ error: 'Not authenticated' });
35-
}
36-
if (req.user.role !== role) {
37-
return res.status(403).json({ error: 'Forbidden' });
38-
}
39-
next();
40-
};
41-
}
28+
};

backend/src/models/user.model.ts

Lines changed: 28 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,39 @@
11
import mongoose, { Document, Schema } from "mongoose";
2+
import bcrypt from "bcryptjs";
23

34
export interface IUser extends Document {
5+
name: string;
46
email: string;
57
password: string;
8+
isVerified: boolean;
9+
verificationToken?: string;
610
resetPasswordToken?: string;
711
resetPasswordExpires?: Date;
12+
comparePassword(candidatePassword: string): Promise<boolean>;
813
}
914

10-
const userSchema = new Schema<IUser>({
11-
email: { type: String, required: true, unique: true },
12-
password: { type: String, required: true },
13-
resetPasswordToken: { type: String },
14-
resetPasswordExpires: { type: Date },
15+
const userSchema = new Schema<IUser>(
16+
{
17+
name: { type: String, required: true },
18+
email: { type: String, required: true, unique: true },
19+
password: { type: String, required: true },
20+
isVerified: { type: Boolean, default: false },
21+
verificationToken: { type: String },
22+
resetPasswordToken: { type: String },
23+
resetPasswordExpires: { type: Date },
24+
},
25+
{ timestamps: true }
26+
);
27+
28+
userSchema.pre("save", async function (next) {
29+
if (!this.isModified("password")) return next();
30+
const salt = await bcrypt.genSalt(10);
31+
this.password = await bcrypt.hash(this.password, salt);
32+
next();
1533
});
1634

17-
export default mongoose.model<IUser>("User", userSchema);
35+
userSchema.methods.comparePassword = async function (candidatePassword: string) {
36+
return bcrypt.compare(candidatePassword, this.password);
37+
};
38+
39+
export const User = mongoose.model<IUser>("User", userSchema);

backend/src/routes/cartRoutes.ts

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,15 @@ import {
66
getCart,
77
checkout,
88
} from "../controllers/cart.controller";
9+
import { authenticate } from "../middleware/authMiddleware";
910

1011
const router: Router = express.Router();
1112

12-
router.post("/", addItem);
13-
router.patch("/:itemId", updateQuantity);
14-
router.delete("/:itemId", removeItem);
15-
router.get("/", getCart);
16-
router.post("/checkout", checkout);
13+
//Protected routes
14+
router.post("/", authenticate, addItem);
15+
router.patch("/:itemId", authenticate, updateQuantity);
16+
router.delete("/:itemId", authenticate, removeItem);
17+
router.get("/", authenticate, getCart);
18+
router.post("/checkout", authenticate, checkout);
1719

18-
export default router;
20+
export default router;

backend/src/routes/product.routes.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@ import {
66
updateProduct,
77
deleteProduct,
88
} from "../controllers/product.controller";
9-
// import { verifyAuth } from "../middleware/auth.middleware";
109

1110
const router = Router();
1211

0 commit comments

Comments
 (0)