import bcrypt from "bcryptjs"; const SALT_ROUNDS = 12; const PASSWORD_HISTORY_SIZE = 5; export function hashPassword(password: string): Promise { return bcrypt.hash(password, SALT_ROUNDS); } export async function verifyPassword( password: string, hashedPassword: string ): Promise { return bcrypt.compare(password, hashedPassword); } export async function isPasswordInHistory( password: string, history: string[] ): Promise { if (history.length === 0) return false; for (const h of history) { if (await bcrypt.compare(password, h)) return true; } return false; } export function addPasswordToHistory( newHash: string, history: string[] ): string[] { const updated = [newHash, ...history]; return updated.slice(0, PASSWORD_HISTORY_SIZE); } export interface PasswordStrengthResult { valid: boolean; errors: string[]; } export function validatePasswordStrength(password: string): PasswordStrengthResult { const errors: string[] = []; if (password.length < 8) errors.push("至少8个字符"); if (!/[A-Z]/.test(password)) errors.push("需包含大写字母"); if (!/[a-z]/.test(password)) errors.push("需包含小写字母"); if (!/[0-9]/.test(password)) errors.push("需包含数字"); if (!/[!@#$%^&*()_+\-=\[\]{};':"\\|,.<>\/?]/.test(password)) errors.push("需包含特殊字符"); return { valid: errors.length === 0, errors }; } export function isLocked(lockoutUntil: Date | null): boolean { if (!lockoutUntil) return false; return Date.now() < lockoutUntil.getTime(); }