thread-master/apps/web/src/lib/passwordPolicy.ts

73 lines
2.6 KiB
TypeScript
Raw Normal View History

2026-07-10 12:54:45 +00:00
/**
* 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("");
}