Why: accounts are admin-issued (user decision 2026-09-30, no self registration); initial and admin-reset passwords must force a change on first login. Evidence: - LoginPage: password (primary) + API key tabs; ChangePasswordGate blocks the app until the forced change completes (mismatch/length validation inline); App auth state machine checking -> login -> mustchange -> ok, session cookie flows through fetch automatically. - Account menu gains change-password and admin-only user management; AdminUsersModal: create account (random initial password generator, admin checkbox), reset password, status table (ADMIN/USER, pending change chips). - Fixed a real bug found in the headless walk: menu toggles passed e.currentTarget into the setState updater where React nulls it by render time, so anchor was null and the menu never rendered; capture the element before the updater. - Headless journey on the real binary with --auth on: admin bootstrap via CLI -> first-login forced change -> app; admin creates bob (bob sees no user management); alice reset via CLI -> forced change on next login; API-key tab login; logout; zero console errors. npm run build + vitest green; make test full suite exit 0. Impact: replaces the old API-key-only LoginGate; embedded no-auth mode unchanged (no login page shown). Refs: proposal 2026-09-30-ui-redesign-and-auth.md section 5 (R2, admin-issued variant)
12 lines
441 B
TypeScript
12 lines
441 B
TypeScript
// 浏览器端随机初始密码(管理员未显式给定密码时):
|
|
// crypto.getRandomValues 拒绝易混淆字符(0/O/1/l/I),保证可抄写。
|
|
const ALPHABET = 'abcdefghjkmnpqrstuvwxyz23456789'
|
|
|
|
export function randomPassword(length = 12): string {
|
|
const buf = new Uint32Array(length)
|
|
crypto.getRandomValues(buf)
|
|
let out = ''
|
|
for (let i = 0; i < length; i++) out += ALPHABET[buf[i] % ALPHABET.length]
|
|
return out
|
|
}
|