/** * 密碼政策(與後端 ValidatePasswordPolicy 完全一致): * - 至少 12 字元 * - 含大寫、小寫、數字、符號 * * 使用者可見說明(語言包,勿在 UI 另寫一句): * - zh: password.policy.hint / api.err.400003 * 「密碼須至少 12 碼,且含大寫、小寫、數字與符號」 * - en: 同 key * 「Password must be at least 12 characters with upper, lower, digit, and symbol」 * 後端 API 400003 message 使用上述英文句;FE 依 code 轉語言包。 */ /** 與後端 domain.PasswordMinLen 一致 */ export const PASSWORD_MIN_LEN = 12; /** 與後端 domain.PasswordPolicyZH / FE password.policy.hint 一致 */ export const PASSWORD_POLICY_ZH = "密碼須至少 12 碼,且含大寫、小寫、數字與符號"; /** 與後端 domain.PasswordPolicyEN / API 400003 / FE en password.policy.hint 一致 */ export const PASSWORD_POLICY_EN = "Password must be at least 12 characters with upper, lower, digit, and symbol"; export type PasswordPolicyResult = { ok: boolean; /** i18n key under password.policy.* or empty if ok */ errorKey?: string; }; const HAS_UPPER = /[A-Z]/; const HAS_LOWER = /[a-z]/; const HAS_DIGIT = /[0-9]/; /** 符號:非字母數字(與後端 unicode.IsPunct/Symbol 大致對齊) */ const HAS_SYMBOL = /[^A-Za-z0-9]/; export function checkPasswordPolicy(password: string): PasswordPolicyResult { const p = password ?? ""; // 任一條件不符都回同一句(與後端 400003 / password.policy.hint 一致,不拆多種文案) const ok = [...p].length >= PASSWORD_MIN_LEN && HAS_UPPER.test(p) && HAS_LOWER.test(p) && HAS_DIGIT.test(p) && HAS_SYMBOL.test(p); if (!ok) { return { ok: false, errorKey: "password.policy.hint" }; } return { ok: true }; } /** 是否符合政策(提交前 gate) */ export function isPasswordPolicyOk(password: string): boolean { return checkPasswordPolicy(password).ok; } /** 產生符合政策的臨時密碼(mock admin 用) */ export function generateTempPassword(): string { const lower = "abcdefghijkmnopqrstuvwxyz"; const upper = "ABCDEFGHJKLMNPQRSTUVWXYZ"; const digits = "23456789"; const symbols = "!@#$%^&*+-="; const all = lower + upper + digits + symbols; const pick = (s: string) => s[Math.floor(Math.random() * s.length)]!; const chars = [pick(lower), pick(upper), pick(digits), pick(symbols)]; while (chars.length < 16) chars.push(pick(all)); for (let i = chars.length - 1; i > 0; i--) { const j = Math.floor(Math.random() * (i + 1)); [chars[i], chars[j]] = [chars[j]!, chars[i]!]; } return chars.join(""); }