mirror of
https://github.com/bilawalsidhu/gods-eye-view.git
synced 2026-11-05 19:04:02 +00:00
fix(server): throttle the cost-bearing proxies by default
The OpenAI endpoints (/api/realtime/token, /api/openai/hud-summary) now allow 30 requests per minute per client IP and the Google Places endpoints 120, the caps the Pinokio build already ships. GEV_RATELIMIT_OPENAI_PER_MIN and GEV_RATELIMIT_GOOGLE_PER_MIN still override them and exactly 0 disables them; an unreadable value falls back to the default rather than to unlimited. Salvaged from #683 onto current main. Co-authored-by: Sameh Khamis <[email protected]> Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
This commit is contained in:
1 parent
7aa9bbd3be
commit
bb74071dc2
13 files changed
+409
-61
No files matched your search
@@ -2,24 +2,49 @@ import { makeRateLimiter } from '../../../src/sources/rateLimit.js';
|
||||
export { makeRateLimiter } from '../../../src/sources/rateLimit.js';
|
||||
|
||||
/**
|
||||
* Opt-in per-IP rate limiter for the cost-bearing API proxies (OpenAI / Google).
|
||||
* DEFAULT IS UNLIMITED: when the env var is unset, `0`, or non-numeric, this
|
||||
* returns `null` and the caller skips the check entirely — a runtime no-op that
|
||||
* preserves the original behavior. Only a positive integer N enables a fixed
|
||||
* 60s window of N requests/IP (built lazily once, then reused so its per-IP
|
||||
* window state persists across requests). The global backstop is set to a
|
||||
* generous multiple of the per-IP cap so a single host can't starve the rest.
|
||||
* Resolve the per-IP, per-minute cap for a cost-bearing proxy from its env var.
|
||||
*
|
||||
* @param {string|undefined} envValue - Raw env value (requests/min/IP).
|
||||
* The default is ON. An unset or blank value takes `defaultPerMin`; a positive
|
||||
* integer overrides it; an explicit `0` disables the limiter for deployments
|
||||
* that genuinely want no throttle.
|
||||
*
|
||||
* A value that cannot be read as a number — a typo, a stray unit, a negative —
|
||||
* also takes the default rather than disabling the guard. That asymmetry is the
|
||||
* point: `GEV_RATELIMIT_OPENAI_PER_MIN=3O` should not quietly hand an exposed
|
||||
* server's API key to whoever asks. Removing the throttle stays possible, but
|
||||
* only by saying `0`, which cannot be typed by accident.
|
||||
*
|
||||
* @param {string|number|undefined|null} envValue - Raw env value (requests/min/IP).
|
||||
* @param {number} defaultPerMin - Cap to apply when the value is absent or unreadable.
|
||||
* @returns {number} Requests/min/IP; `0` means unlimited.
|
||||
*/
|
||||
export function resolvePerMinuteCap(envValue, defaultPerMin) {
|
||||
const raw = typeof envValue === 'string' ? envValue.trim() : envValue;
|
||||
if (raw === undefined || raw === null || raw === '') return defaultPerMin;
|
||||
const parsed = Number(raw);
|
||||
if (!Number.isFinite(parsed) || parsed < 0) return defaultPerMin;
|
||||
return Math.floor(parsed); // includes the explicit 0 = unlimited opt-out
|
||||
}
|
||||
|
||||
/**
|
||||
* Per-IP rate limiter for the cost-bearing API proxies (OpenAI / Google).
|
||||
* Returns `null` only when the cap resolves to `0` (the explicit opt-out), in
|
||||
* which case the caller skips the check entirely. Otherwise a fixed 60s window
|
||||
* of N requests/IP, built lazily once and reused so its per-IP window state
|
||||
* persists across requests. The global backstop is a generous multiple of the
|
||||
* per-IP cap so a single host can't starve the rest.
|
||||
*
|
||||
* @param {string|number|undefined|null} envValue - Raw env value (requests/min/IP).
|
||||
* @param {number} defaultPerMin - Cap to apply when the value is absent or unreadable.
|
||||
* @returns {((key:string)=>boolean)|null} An `allow(key)` fn, or null when unlimited.
|
||||
*/
|
||||
export function makeOptInRateLimiter(envValue) {
|
||||
const max = Number(envValue);
|
||||
if (!Number.isFinite(max) || max <= 0) return null; // unset/0/garbage -> unlimited
|
||||
export function makeCostRateLimiter(envValue, defaultPerMin) {
|
||||
const max = resolvePerMinuteCap(envValue, defaultPerMin);
|
||||
if (max <= 0) return null; // explicit opt-out
|
||||
return makeRateLimiter({
|
||||
windowMs: 60_000,
|
||||
max: Math.floor(max),
|
||||
globalMax: Math.floor(max) * 20,
|
||||
max,
|
||||
globalMax: max * 20,
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
Reference in new issue
Block a user