import jwt from 'jsonwebtoken'; import bcrypt from 'bcryptjs'; import User from '../models/User.js'; import dotenv from 'dotenv'; dotenv.config(); const SECRET_KEY = process.env.JWT_SECRET_KEY || 'your-super-secret-key-change-in-production'; const ALGORITHM = 'HS256'; export const ACCESS_TOKEN_EXPIRE_MINUTES = 60 * 24; // 24 hours class AuthService { /** * Verify a password against its hash */ verifyPassword(plainPassword, hashedPassword) { return bcrypt.compareSync(plainPassword, hashedPassword); } /** * Hash a password */ getPasswordHash(password) { const salt = bcrypt.genSaltSync(10); return bcrypt.hashSync(password, salt); } /** * Create a JWT access token */ createAccessToken(data, expiresInMinutes = ACCESS_TOKEN_EXPIRE_MINUTES) { const payload = { ...data, exp: Math.floor(Date.now() / 1000) + (expiresInMinutes * 60) }; return jwt.sign(payload, SECRET_KEY, { algorithm: ALGORITHM }); } /** * Decode and validate a JWT token */ decodeToken(token) { try { const payload = jwt.verify(token, SECRET_KEY, { algorithms: [ALGORITHM] }); const userId = payload.sub; const email = payload.email; const role = payload.role; if (!userId) { return null; } return { userId, email, role }; } catch (error) { return null; } } /** * Get a user by email */ async getUserByEmail(email) { return await User.findByEmail(email); } /** * Get a user by ID */ async getUserById(userId) { try { return await User.findById(userId); } catch (error) { return null; } } /** * Create a new user */ async createUser(userData) { // Check if user already exists const existingUser = await this.getUserByEmail(userData.email); if (existingUser) { throw new Error('User with this email already exists'); } // Create user with Mongoose model const user = new User({ email: userData.email, name: userData.name, role: userData.role || UserRole.DOCTOR, hashed_password: userData.password, // Will be hashed by pre-save hook is_active: true }); await user.save(); return user; } /** * Authenticate a user by email and password */ async authenticateUser(email, password) { const user = await this.getUserByEmail(email); if (!user) { return null; } const isValid = await user.comparePassword(password); if (!isValid) { return null; } return user; } /** * Convert a user document to a response object */ userToResponse(user) { if (user.toResponse) { return user.toResponse(); } return { id: user._id.toString(), email: user.email, name: user.name, role: user.role, created_at: user.created_at, is_active: user.is_active !== false }; } /** * Update a user's information */ async updateUser(userId, updateData) { try { const user = await User.findByIdAndUpdate( userId, { $set: updateData }, { new: true, runValidators: true } ); return user; } catch (error) { return null; } } /** * Delete a user by ID */ async deleteUser(userId) { try { const result = await User.findByIdAndDelete(userId); return result !== null; } catch (error) { return false; } } /** * Get all users */ async getAllUsers() { return await User.find().sort({ created_at: -1 }); } /** * Get all doctors */ async getDoctors() { return await User.findActiveDoctors(); } /** * Get all admins */ async getAdmins() { return await User.findActiveAdmins(); } } // Singleton instance export const authService = new AuthService();