Files
openclaw/src/gateway/rate-limit-attempt-serialization.ts
2026-04-04 21:55:09 +09:00

37 lines
1.2 KiB
TypeScript

import { AUTH_RATE_LIMIT_SCOPE_DEFAULT, normalizeRateLimitClientIp } from "./auth-rate-limit.js";
const pendingAttempts = new Map<string, Promise<void>>();
function normalizeScope(scope: string | undefined): string {
return (scope ?? AUTH_RATE_LIMIT_SCOPE_DEFAULT).trim() || AUTH_RATE_LIMIT_SCOPE_DEFAULT;
}
function buildSerializationKey(ip: string | undefined, scope: string | undefined): string {
return `${normalizeScope(scope)}:${normalizeRateLimitClientIp(ip)}`;
}
export async function withSerializedRateLimitAttempt<T>(params: {
ip: string | undefined;
scope: string | undefined;
run: () => Promise<T>;
}): Promise<T> {
const key = buildSerializationKey(params.ip, params.scope);
const previous = pendingAttempts.get(key) ?? Promise.resolve();
let releaseCurrent!: () => void;
const current = new Promise<void>((resolve) => {
releaseCurrent = resolve;
});
const tail = previous.catch(() => {}).then(() => current);
pendingAttempts.set(key, tail);
await previous.catch(() => {});
try {
return await params.run();
} finally {
releaseCurrent();
if (pendingAttempts.get(key) === tail) {
pendingAttempts.delete(key);
}
}
}