73 lines
2.6 KiB
TypeScript
73 lines
2.6 KiB
TypeScript
/**
|
||
* 密碼政策(與後端 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("");
|
||
}
|