aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAdam Soltys <[email protected]>2026-07-10 22:56:12 -0700
committerAdam Soltys <[email protected]>2026-07-10 22:56:12 -0700
commitfa029437a162088ff79f36872110fa4087011530 (patch)
tree2d7f8a0e7e19962511bfea2644ef0415d854807d
parent4d97101726276543384c10f11a72a70f99336afb (diff)
downloadcoinos-server-fa029437a162088ff79f36872110fa4087011530.tar.gz
coinos-server-fa029437a162088ff79f36872110fa4087011530.zip
invoices: rate-limit invoice create and update per IP
Add a per-IP rate limit (30/10s) to POST /invoice and POST /invoice/:id, matching the existing GET /invoice/:id limit, to throttle automated invoice-creation floods. Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
-rw-r--r--index.ts4
1 files changed, 2 insertions, 2 deletions
diff --git a/index.ts b/index.ts
index 0a3bfd89..808252de 100644
--- a/index.ts
+++ b/index.ts
@@ -48,8 +48,8 @@ app.get("/locations", locations.list);
app.get("/invoice/:id", { config: { rateLimit: { max: 30, timeWindow: 10000, keyGenerator: (req) => { const ip = (req.headers["cf-connecting-ip"] as string) || req.ip; const token = (req.headers.authorization || "").slice(0, 50); return token ? `invoice:${token}` : `invoice:${ip}`; } } } }, invoices.get);
app.get("/invoices", auth, invoices.list);
-app.post("/invoice", optional, invoices.create);
-app.post("/invoice/:id", optional, invoices.update);
+app.post("/invoice", { ...optional, config: { rateLimit: { max: 30, timeWindow: 10000, keyGenerator: (req) => `invoicecreate:${(req.headers["cf-connecting-ip"] as string) || req.ip}` } } }, invoices.create);
+app.post("/invoice/:id", { ...optional, config: { rateLimit: { max: 30, timeWindow: 10000, keyGenerator: (req) => `invoiceupdate:${(req.headers["cf-connecting-ip"] as string) || req.ip}` } } }, invoices.update);
app.post("/sign", auth, invoices.sign);
app.get("/assetlinks.json", (_, res) => res.sendFile("assetlinks.json"));