const crypto = require('crypto'); const { v4: uuidv4 } = require('uuid'); const { getDb } = require('../config/database'); const SESSION_MS = 8 * 60 * 60 * 1000; // Only a hash of the token is stored, so the table never holds a usable login. const tokenHash = (token) => crypto.createHash('sha256').update(String(token)).digest('hex'); const hashPassword = (password, salt = crypto.randomBytes(16).toString('hex')) => { const hash = crypto.scryptSync(password, salt, 64).toString('hex'); return `${salt}:${hash}`; }; const verifyPassword = (password, stored) => { const [salt, expected] = String(stored || '').split(':'); if (!salt || !expected) return false; const actual = crypto.scryptSync(password, salt, 64).toString('hex'); return crypto.timingSafeEqual(Buffer.from(actual, 'hex'), Buffer.from(expected, 'hex')); }; class AdminAuthService { static login(username, password) { const user = getDb().prepare('SELECT * FROM admin_users WHERE username = ?').get(username); if (!user || !verifyPassword(password, user.password_hash)) { throw new Error('用户名或密码错误'); } const token = crypto.randomBytes(32).toString('hex'); const db = getDb(); db.prepare('DELETE FROM admin_sessions WHERE expires_at < ?').run(Date.now()); db.prepare('INSERT INTO admin_sessions (token_hash, user_id, username, expires_at) VALUES (?, ?, ?, ?)') .run(tokenHash(token), user.user_id, user.username, Date.now() + SESSION_MS); return { token, username: user.username, expiresIn: SESSION_MS / 1000 }; } static verify(token) { if (!token) return null; const row = getDb().prepare('SELECT user_id, username, expires_at FROM admin_sessions WHERE token_hash = ?').get(tokenHash(token)); if (!row || Number(row.expires_at) < Date.now()) return null; return { userId: row.user_id, username: row.username, expiresAt: Number(row.expires_at) }; } static changePassword(username, currentPassword, newPassword) { if (!newPassword || newPassword.length < 8) throw new Error('新密码至少需要8位'); const db = getDb(); const user = db.prepare('SELECT * FROM admin_users WHERE username = ?').get(username); if (!user || !verifyPassword(currentPassword, user.password_hash)) throw new Error('当前密码错误'); db.prepare('UPDATE admin_users SET password_hash = ?, updated_at = CURRENT_TIMESTAMP WHERE username = ?') .run(hashPassword(newPassword), username); db.prepare('DELETE FROM admin_sessions WHERE username = ?').run(username); return { success: true }; } static logout(token) { if (token) getDb().prepare('DELETE FROM admin_sessions WHERE token_hash = ?').run(tokenHash(token)); } } module.exports = AdminAuthService;