appointment_system/backend/src/services/adminAuthService.js
2025-12-11 22:50:18 +08:00

162 lines
3.8 KiB
JavaScript

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,
};