Spaces:
Sleeping
Sleeping
| 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(); | |