const bcrypt = require('bcrypt'); const { Admin, LoginHistory } = require('../models'); const { generateToken, generateRefreshToken, verifyToken } = require('../utils/jwt'); const logger = require('../config/logger'); /** * Admin Authentication Service * Handles admin login and authentication logic */ /** * Authenticate admin and generate token * @param {string} username - Admin username * @param {string} password - Admin password * @param {Object} loginInfo - Login metadata (IP, user agent, device info) * @returns {Object} Admin data and token */ const loginAdmin = async (username, password, loginInfo = {}) => { try { // Find admin by username const admin = await Admin.findOne({ where: { username }, }); if (!admin) { throw new Error('Invalid username or password'); } // Check if admin is active if (admin.status === 'inactive') { throw new Error('Admin account is inactive'); } // Verify password const isPasswordValid = await bcrypt.compare(password, admin.password); if (!isPasswordValid) { throw new Error('Invalid username or password'); } // Generate JWT token and refresh token const tokenPayload = { userId: admin.id, type: 'admin', role: admin.role, }; const token = generateToken(tokenPayload); const refreshToken = generateRefreshToken(tokenPayload); // Update last login time await admin.update({ lastLoginAt: new Date(), }); // Record login history await LoginHistory.create({ userId: admin.id, userType: 'admin', ipAddress: loginInfo.ipAddress, userAgent: loginInfo.userAgent, deviceInfo: loginInfo.deviceInfo, loginAt: new Date(), }); logger.info(`Admin login successful: ${admin.username} (${admin.id})`); // Return admin data (excluding password) return { admin: { id: admin.id, username: admin.username, email: admin.email, role: admin.role, status: admin.status, lastLoginAt: admin.lastLoginAt, }, token, refreshToken, }; } catch (error) { logger.error('Admin login error:', error); throw error; } }; /** * Refresh admin access token * @param {string} refreshToken - Refresh token * @returns {Object} New tokens */ const refreshAdminToken = async (refreshToken) => { try { // Verify refresh token const decoded = verifyToken(refreshToken); // Ensure it's an admin token if (decoded.type !== 'admin') { throw new Error('Invalid refresh token'); } // Find admin to ensure they still exist and are active const admin = await Admin.findByPk(decoded.userId); if (!admin) { throw new Error('Admin not found'); } if (admin.status === 'inactive') { throw new Error('Admin account is inactive'); } // Generate new tokens const tokenPayload = { userId: admin.id, type: 'admin', role: admin.role, }; const newToken = generateToken(tokenPayload); const newRefreshToken = generateRefreshToken(tokenPayload); logger.info(`Admin token refreshed: ${admin.username} (${admin.id})`); return { token: newToken, refreshToken: newRefreshToken, }; } catch (error) { logger.error('Admin token refresh error:', error); throw error; } }; /** * Get admin profile by ID * @param {string} adminId - Admin ID * @returns {Object} Admin profile */ const getAdminProfile = async (adminId) => { try { const admin = await Admin.findByPk(adminId, { attributes: { exclude: ['password'] }, }); if (!admin) { throw new Error('Admin not found'); } return admin; } catch (error) { logger.error('Get admin profile error:', error); throw error; } }; module.exports = { loginAdmin, getAdminProfile, refreshAdminToken, };