mirror of
https://github.com/bilawalsidhu/gods-eye-view.git
synced 2026-11-05 02:44:02 +00:00
37 lines
1.7 KiB
JavaScript
37 lines
1.7 KiB
JavaScript
import { makeOptInRateLimiter, clientKey } from '../common/rate-limit.js';
|
|
|
|
// Built LAZILY on first request, NOT at module load: `.env` values are applied to process.env later
|
|
// (the plugin config hook calls loadEnv → process.env, AFTER this module is imported), so reading
|
|
// process.env here at import time would always see them unset and silently stay unlimited even when
|
|
// configured via .env. Building on first request (like the OPENAI_API_KEY reads) sees the loaded env;
|
|
// the result is cached so the limiter's per-IP window state persists. `null` = unlimited (default).
|
|
let _openAiRateLimiter;
|
|
|
|
/** OpenAI cost endpoints (realtime/token + hud-summary). Null = unlimited (default). */
|
|
function openAiRateLimiter() {
|
|
if (_openAiRateLimiter === undefined) _openAiRateLimiter = makeOptInRateLimiter(process.env.GEV_RATELIMIT_OPENAI_PER_MIN);
|
|
return _openAiRateLimiter;
|
|
}
|
|
|
|
/**
|
|
* Apply an opt-in limiter to a request, writing a 429 when over the cap.
|
|
* When `limiter` is null (unlimited, the default) this is a no-op returning
|
|
* `true`, so the handler proceeds exactly as before.
|
|
*
|
|
* @param {((key:string)=>boolean)|null} limiter
|
|
* @param {import('http').IncomingMessage} req
|
|
* @param {import('http').ServerResponse} res
|
|
* @returns {boolean} True if the request may proceed; false if a 429 was sent.
|
|
*/
|
|
function enforceOptInRateLimit(limiter, req, res) {
|
|
if (!limiter) return true; // unlimited (default) — no behavior change
|
|
if (limiter(clientKey(req))) return true;
|
|
res.statusCode = 429;
|
|
res.setHeader('Content-Type', 'application/json');
|
|
res.setHeader('Retry-After', '5');
|
|
res.end(JSON.stringify({ error: 'Rate limit exceeded' }));
|
|
return false;
|
|
}
|
|
|
|
export { enforceOptInRateLimit, openAiRateLimiter };
|