api: add support for redis to ratelimiter cache

This commit is contained in:
jj 2024-11-01 13:26:18 +00:00
parent d466f8a4af
commit 2317da5ba5
No known key found for this signature in database
4 changed files with 39 additions and 1 deletions

View file

@ -46,6 +46,7 @@
}, },
"optionalDependencies": { "optionalDependencies": {
"freebind": "^0.2.2", "freebind": "^0.2.2",
"rate-limit-redis": "^4.2.0",
"redis": "^4.7.0" "redis": "^4.7.0"
} }
} }

View file

@ -12,6 +12,7 @@ import { env, setTunnelPort } from "../config.js";
import { extract } from "../processing/url.js"; import { extract } from "../processing/url.js";
import { Green, Bright, Cyan } from "../misc/console-text.js"; import { Green, Bright, Cyan } from "../misc/console-text.js";
import { hashHmac } from "../security/secrets.js"; import { hashHmac } from "../security/secrets.js";
import { createStore } from "../store/redis-ratelimit.js";
import { randomizeCiphers } from "../misc/randomize-ciphers.js"; import { randomizeCiphers } from "../misc/randomize-ciphers.js";
import { verifyTurnstileToken } from "../security/turnstile.js"; import { verifyTurnstileToken } from "../security/turnstile.js";
import { friendlyServiceName } from "../processing/service-alias.js"; import { friendlyServiceName } from "../processing/service-alias.js";
@ -40,7 +41,7 @@ const fail = (res, code, context) => {
res.status(status).json(body); res.status(status).json(body);
} }
export const runAPI = (express, app, __dirname, isPrimary = true) => { export const runAPI = async (express, app, __dirname, isPrimary = true) => {
const startTime = new Date(); const startTime = new Date();
const startTimestamp = startTime.getTime(); const startTimestamp = startTime.getTime();
@ -76,6 +77,7 @@ export const runAPI = (express, app, __dirname, isPrimary = true) => {
standardHeaders: 'draft-6', standardHeaders: 'draft-6',
legacyHeaders: false, legacyHeaders: false,
keyGenerator, keyGenerator,
store: await createStore('session'),
handler: handleRateExceeded handler: handleRateExceeded
}); });
@ -85,6 +87,7 @@ export const runAPI = (express, app, __dirname, isPrimary = true) => {
standardHeaders: 'draft-6', standardHeaders: 'draft-6',
legacyHeaders: false, legacyHeaders: false,
keyGenerator: req => req.rateLimitKey || keyGenerator(req), keyGenerator: req => req.rateLimitKey || keyGenerator(req),
store: await createStore('api'),
handler: handleRateExceeded handler: handleRateExceeded
}) })
@ -94,6 +97,7 @@ export const runAPI = (express, app, __dirname, isPrimary = true) => {
standardHeaders: 'draft-6', standardHeaders: 'draft-6',
legacyHeaders: false, legacyHeaders: false,
keyGenerator: req => req.rateLimitKey || keyGenerator(req), keyGenerator: req => req.rateLimitKey || keyGenerator(req),
store: await createStore('tunnel'),
handler: (_, res) => { handler: (_, res) => {
return res.sendStatus(429) return res.sendStatus(429)
} }

View file

@ -0,0 +1,19 @@
import { env } from "../config.js";
let client, redis, redisLimiter;
export const createStore = async (name) => {
if (!env.redisURL) return;
if (!client) {
redis = await import('redis');
redisLimiter = await import('rate-limit-redis');
client = redis.createClient({ url: env.redisURL });
await client.connect();
}
return new redisLimiter.default({
prefix: `RL${name}_`,
sendCommand: (...args) => client.sendCommand(args),
});
}

View file

@ -71,6 +71,9 @@ importers:
freebind: freebind:
specifier: ^0.2.2 specifier: ^0.2.2
version: 0.2.2 version: 0.2.2
rate-limit-redis:
specifier: ^4.2.0
version: 4.2.0(express-rate-limit@7.4.1(express@4.21.0))
redis: redis:
specifier: ^4.7.0 specifier: ^4.7.0
version: 4.7.0 version: 4.7.0
@ -1877,6 +1880,12 @@ packages:
resolution: {integrity: sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==} resolution: {integrity: sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==}
engines: {node: '>= 0.6'} engines: {node: '>= 0.6'}
rate-limit-redis@4.2.0:
resolution: {integrity: sha512-wV450NQyKC24NmPosJb2131RoczLdfIJdKCReNwtVpm5998U8SgKrAZrIHaN/NfQgqOHaan8Uq++B4sa5REwjA==}
engines: {node: '>= 16'}
peerDependencies:
express-rate-limit: '>= 6'
raw-body@2.5.2: raw-body@2.5.2:
resolution: {integrity: sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA==} resolution: {integrity: sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA==}
engines: {node: '>= 0.8'} engines: {node: '>= 0.8'}
@ -3868,6 +3877,11 @@ snapshots:
range-parser@1.2.1: {} range-parser@1.2.1: {}
rate-limit-redis@4.2.0(express-rate-limit@7.4.1(express@4.21.0)):
dependencies:
express-rate-limit: 7.4.1(express@4.21.0)
optional: true
raw-body@2.5.2: raw-body@2.5.2:
dependencies: dependencies:
bytes: 3.1.2 bytes: 3.1.2