Phase 2 done

This commit is contained in:
Mosiur Rahman
2026-04-22 16:10:04 +06:00
parent 6888405b59
commit bd2c58f9ef
7 changed files with 199 additions and 0 deletions

View File

@@ -0,0 +1,57 @@
import { Request, Response } from "express";
import { User } from "../models/User";
import { generateToken } from "../utils/generateToken";
export const register = async (req: Request, res: Response): Promise<void> => {
try {
const {name, email, password} = req.body
if (!name || !email || !password) {
res.status(400).json({message: 'All fields are required'});
return;
}
const existingUser = await User.findOne({email});
if (existingUser) {
res.status(409).json({message:'Email already registered'});
return;
}
const user = await User.create({ name, email, password });
const token = generateToken(user._id, user.email);
res.status(201).json({
token,
user,
})
} catch (error) {
console.error('Register error:', error)
res.status(500).json({ message: 'Server error during registration' })
}
};
export const login = async (req: Request, res: Response ): Promise<void> => {
try {
const {email, password} = req.body;
if (!email || !password) {
res.status(400).json({
message: 'Email and password are required'
});
return;
}
const user = await User.findOne({ email });
if (!user) {
res.status(401).json({message:'Invalid Email or password'})
return;
}
const token = generateToken(user._id, user.email)
res.json({token, user})
} catch(error) {
console.error('Login error:', error)
res.status(500).json({ message: 'Server error during login' });
}
}

View File

@@ -0,0 +1,36 @@
import { Request, Response, NextFunction } from 'express';
import jwt from 'jsonwebtoken';
import { User } from '../models/User';
import { ENV } from '../config/env';
interface JwtPayload {
id: string;
email: string;
}
export const protect = async (
req: Request,
res: Response,
next: NextFunction
): Promise<void> => {
try {
const authHeader = req.headers.authorization;
if (!authHeader || !authHeader.startsWith('Bearer ')) {
res.status(401).json({ message: 'No token Provided' });
return;
}
const token = authHeader.split(' ')[1];
const decode =jwt.verify(token, ENV.JWT_SECRET) as JwtPayload;
const user = await User.findById(decode.id)
if (!user) {
res.status(401).json({ message: 'User no longer exists' })
return;
}
req.user = user;
next()
} catch(error) {
res.status(401).json({ message: 'Invalid or expired Token' })
}
}

61
server/src/models/User.ts Normal file
View File

@@ -0,0 +1,61 @@
import { Document, model, Schema, Types } from "mongoose";
import bcrypt from 'bcryptjs';
export interface IUser {
name: string;
email: string;
password: string;
createdAt?: Date;
}
export interface IUserDocument extends IUser, Document {
_id: Types.ObjectId
comparePassword(candidatePassword: string): Promise<boolean>;
}
const userSchema = new Schema<IUserDocument>({
name: {
type: String,
required: [true, 'Name is required'],
trim: true,
minLength: [2, 'Name must be at least 2 characters'],
},
email: {
type: String,
required: [true, 'Email is required'],
unique: true,
lowercase: true, // always store as lowercase
trim: true,
match: [/^\S+@\S+\.\S+$/, 'Please enter a valid email'],
},
password: {
type: String,
required: [true, 'Password is required'],
minlength: [6, 'Password must be at least 6 characters'],
},
},
{ timestamps: true } // auto-adds createdAt and updatedAt
);
userSchema.pre('save', async function () {
if (!this.isModified('password')) return ;
const salt = await bcrypt.genSalt(12);
this.password = await bcrypt.hash(this.password, salt);
});
userSchema.methods.comparePassword = async function (
candidatePassword: string
) : Promise<boolean> {
return bcrypt.compare(candidatePassword, this.password)
}
userSchema.set('toJSON', {
transform: (_doc, ret) => {
delete ret.password;
return ret
}
})
export const User = model<IUserDocument>('User', userSchema);

View File

@@ -0,0 +1,10 @@
import { Router } from 'express';
import { register, login } from '../controllers/authController';
const router = Router();
router.post('/register', register);
router.post ('/login', login)
export default router;

View File

@@ -2,6 +2,7 @@ import express, {Application, Request, Response} from 'express';
import cors from 'cors'; import cors from 'cors';
import {ENV} from './config/env'; import {ENV} from './config/env';
import {connectDB, readyState} from './config/db'; import {connectDB, readyState} from './config/db';
import authRoutes from './routes/authRoutes';
const app: Application = express() const app: Application = express()
@@ -27,6 +28,10 @@ app.get('/health/db', (req: Request, res: Response)=> {
}); });
//api routes
app.use('/api/auth', authRoutes);
//start Server //start Server
const start = async (): Promise<void> => { const start = async (): Promise<void> => {

9
server/src/types/express.d.ts vendored Normal file
View File

@@ -0,0 +1,9 @@
import { IUserDocument } from '../model/User';
declare global {
namespace Express {
interface Request {
user?: IUserDocument
}
}
}

View File

@@ -0,0 +1,21 @@
import jwt from 'jsonwebtoken';
import { Types } from 'mongoose';
import { ENV } from '../config/env';
interface TokenPayload {
id: string;
email: string;
}
export const generateToken = (
userId: Types.ObjectId,
email: string,
): string => {
const payload: TokenPayload = {
id: userId.toString(),
email,
};
return jwt.sign(payload, ENV.JWT_SECRET, {
expiresIn: '7d'
});
};