From 07c41af5e268506ff584315d461e739e3ed2f860 Mon Sep 17 00:00:00 2001 From: Goodnessmbakara Date: Sat, 7 Jun 2025 00:30:04 +0100 Subject: [PATCH] feat(auth): add student sign-in endpoint with JWT authentication and Swagger docs --- src/controllers/studentController.ts | 168 ++++++++++++++++++++++++++- src/routes/studentRoutes.ts | 136 +++++++++++++++++++++- 2 files changed, 295 insertions(+), 9 deletions(-) diff --git a/src/controllers/studentController.ts b/src/controllers/studentController.ts index 9a6e58c..6bac4d5 100644 --- a/src/controllers/studentController.ts +++ b/src/controllers/studentController.ts @@ -4,9 +4,78 @@ import bcrypt from 'bcryptjs'; import jwt from 'jsonwebtoken'; import { JWT_SECRET } from '../config/config'; +/** + * @swagger + * /api/students/signup: + * post: + * summary: Register a new student + * tags: [Students] + * requestBody: + * required: true + * content: + * multipart/form-data: + * schema: + * type: object + * required: + * - name + * - dob + * - email + * - country + * - password + * properties: + * name: + * type: string + * description: Student's full name + * dob: + * type: string + * format: date + * description: Date of birth + * email: + * type: string + * format: email + * description: Student's email address + * country: + * type: string + * description: Student's country of residence + * password: + * type: string + * format: password + * description: Student's password + * profilePhoto: + * type: string + * format: binary + * description: Student's profile photo + * responses: + * 201: + * description: Student successfully registered + * content: + * application/json: + * schema: + * type: object + * properties: + * token: + * type: string + * description: JWT token for authentication + * student: + * type: object + * properties: + * id: + * type: string + * name: + * type: string + * email: + * type: string + * 400: + * description: Missing required fields + * 409: + * description: Email already registered + * 500: + * description: Server error + */ export const registerStudent = async (req: Request, res: Response): Promise => { try { const { name, dob, email, country, password } = req.body; + const profilePhoto = req.file?.filename; if (!name || !dob || !email || !country || !password) { res.status(400).json({ message: 'All fields are required' }); @@ -20,9 +89,6 @@ export const registerStudent = async (req: Request, res: Response): Promise => { + try { + const { email, password } = req.body; + + if (!email || !password) { + res.status(400).json({ message: 'Email and password are required' }); + return; + } + + const student = await Student.findOne({ email }); + if (!student) { + res.status(401).json({ message: 'Invalid credentials' }); + return; + } + + const isValidPassword = await bcrypt.compare(password, student.password); + if (!isValidPassword) { + res.status(401).json({ message: 'Invalid credentials' }); + return; + } + + const token = jwt.sign({ id: student._id, email: student.email }, JWT_SECRET, { + expiresIn: '1h', + }); + + res.status(200).json({ + token, + student: { + id: String(student._id), + name: student.name, + email: student.email, + }, + }); + } catch (error) { + console.error('Signin error:', error); + res.status(500).json({ message: 'Server error' }); + } +}; + diff --git a/src/routes/studentRoutes.ts b/src/routes/studentRoutes.ts index d4ca079..eb5546d 100644 --- a/src/routes/studentRoutes.ts +++ b/src/routes/studentRoutes.ts @@ -1,13 +1,79 @@ import express from 'express'; -import { registerStudent } from '../controllers/studentController'; -import { upload } from '../middleware/upload'; +import { registerStudent, signinStudent } from '../controllers/studentController'; +import { uploadProfilePhoto } from '../middleware/upload'; const router = express.Router(); -// Simple route handler for signup -router.post('/signup', upload.single('profilePhoto'), async (req, res, next) => { +/** + * @swagger + * /api/students/signup: + * post: + * summary: Register a new student + * tags: [Students] + * requestBody: + * required: true + * content: + * multipart/form-data: + * schema: + * type: object + * required: + * - name + * - dob + * - email + * - country + * - password + * properties: + * name: + * type: string + * description: Student's full name + * dob: + * type: string + * format: date + * description: Date of birth + * email: + * type: string + * format: email + * description: Student's email address + * country: + * type: string + * description: Student's country of residence + * password: + * type: string + * format: password + * description: Student's password + * profilePhoto: + * type: string + * format: binary + * description: Student's profile photo + * responses: + * 201: + * description: Student successfully registered + * content: + * application/json: + * schema: + * type: object + * properties: + * token: + * type: string + * description: JWT token for authentication + * student: + * type: object + * properties: + * id: + * type: string + * name: + * type: string + * email: + * type: string + * 400: + * description: Missing required fields + * 409: + * description: Email already registered + * 500: + * description: Server error + */ +router.post('/signup', uploadProfilePhoto.single('profilePhoto'), async (req, res, next) => { try { - console.log('Processing signup request'); await registerStudent(req, res); } catch (error) { console.error('Signup error:', error); @@ -15,5 +81,65 @@ router.post('/signup', upload.single('profilePhoto'), async (req, res, next) => } }); +/** + * @swagger + * /api/students/signin: + * post: + * summary: Sign in a student + * tags: [Students] + * requestBody: + * required: true + * content: + * application/json: + * schema: + * type: object + * required: + * - email + * - password + * properties: + * email: + * type: string + * format: email + * description: Student's email address + * password: + * type: string + * format: password + * description: Student's password + * responses: + * 200: + * description: Student successfully signed in + * content: + * application/json: + * schema: + * type: object + * properties: + * token: + * type: string + * description: JWT token for authentication + * student: + * type: object + * properties: + * id: + * type: string + * name: + * type: string + * email: + * type: string + * 400: + * description: Missing required fields + * 401: + * description: Invalid credentials + * 500: + * description: Server error + */ +router.post('/signin', async (req, res, next) => { + try { + await signinStudent(req, res); + } catch (error) { + console.error('Signin error:', error); + next(error); + } +}); + export default router;