Files
new-api-key-usage/apps/server/src/login-throttle.ts
T
skycurtain 6454f4acfa feat(server): 管理面与成员视图 API + Scalar 在线文档
契约 19 路径全量:引导/会话/改密、engine 桩、keys、overview/logs、payments、
qpu、members、audit、成员视图。鉴权优先、422 信封、登录节流、上游验证接线。
2026-10-06 01:30:52 +08:00

45 lines
1.1 KiB
TypeScript

/**
* 登录节流(契约:429,最小实现)
* 同一用户名失败计数 + 固定退避;进程内存即可(单机母体)。
*/
const WINDOW_MS = 5 * 60_000;
const MAX_FAILURES = 5;
const LOCK_MS = 60_000;
type Entry = { count: number; firstAt: number; lockedUntil: number };
const buckets = new Map<string, Entry>();
export function isLoginThrottled(username: string): boolean {
const e = buckets.get(username);
if (!e) return false;
if (e.lockedUntil > Date.now()) return true;
if (Date.now() - e.firstAt > WINDOW_MS) buckets.delete(username);
return false;
}
export function recordLoginFailure(username: string): void {
const now = Date.now();
const e = buckets.get(username);
if (!e || now - e.firstAt > WINDOW_MS) {
buckets.set(username, { count: 1, firstAt: now, lockedUntil: 0 });
return;
}
e.count += 1;
if (e.count >= MAX_FAILURES) {
e.lockedUntil = now + LOCK_MS;
e.count = 0;
e.firstAt = now;
}
}
export function recordLoginSuccess(username: string): void {
buckets.delete(username);
}
/** 测试用 */
export function resetLoginThrottle(): void {
buckets.clear();
}