diff --git a/package-lock.json b/package-lock.json index cd36d85..103b382 100644 --- a/package-lock.json +++ b/package-lock.json @@ -19,6 +19,7 @@ "cacheable": "^2.0.0", "cheerio": "^1.0.0", "express": "^5.1.0", + "express-rate-limit": "^8.2.1", "fast-levenshtein": "^3.0.0", "http-proxy-agent": "^7.0.2", "https-proxy-agent": "^7.0.6", @@ -5071,6 +5072,33 @@ "url": "https://opencollective.com/express" } }, + "node_modules/express-rate-limit": { + "version": "8.2.1", + "resolved": "https://registry.npmjs.org/express-rate-limit/-/express-rate-limit-8.2.1.tgz", + "integrity": "sha512-PCZEIEIxqwhzw4KF0n7QF4QqruVTcF73O5kFKUnGOyjbCCgizBBiFaYpd/fnBLUMPw/BWw9OsiN7GgrNYr7j6g==", + "license": "MIT", + "dependencies": { + "ip-address": "10.0.1" + }, + "engines": { + "node": ">= 16" + }, + "funding": { + "url": "https://github.com/sponsors/express-rate-limit" + }, + "peerDependencies": { + "express": ">= 4.11" + } + }, + "node_modules/express-rate-limit/node_modules/ip-address": { + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/ip-address/-/ip-address-10.0.1.tgz", + "integrity": "sha512-NWv9YLW4PoW2B7xtzaS3NCot75m6nK7Icdv0o3lfMceJVRfSoQwqD4wEH5rLwoKJwUiZ/rfpiVBhnaF0FK4HoA==", + "license": "MIT", + "engines": { + "node": ">= 12" + } + }, "node_modules/ext": { "version": "1.7.0", "resolved": "https://registry.npmjs.org/ext/-/ext-1.7.0.tgz", diff --git a/package.json b/package.json index a24b274..d004933 100644 --- a/package.json +++ b/package.json @@ -51,6 +51,7 @@ "cacheable": "^2.0.0", "cheerio": "^1.0.0", "express": "^5.1.0", + "express-rate-limit": "^8.2.1", "fast-levenshtein": "^3.0.0", "http-proxy-agent": "^7.0.2", "https-proxy-agent": "^7.0.6", diff --git a/src/controller/StreamController.ts b/src/controller/StreamController.ts index d81ca71..11b59b1 100644 --- a/src/controller/StreamController.ts +++ b/src/controller/StreamController.ts @@ -56,7 +56,7 @@ export class StreamController { const { streams, ttl } = await this.streamResolver.resolve(ctx, sources, type, id); if (ttl && envIsProd()) { - res.setHeader('Cache-Control', `max-age=${Math.floor(ttl / 1000)}, public`); + res.setHeader('Cache-Control', `public, max-age=${Math.floor(ttl / 1000)}, immutable`); } res.setHeader('Content-Type', 'application/json'); diff --git a/src/index.ts b/src/index.ts index 290eae3..52319bd 100644 --- a/src/index.ts +++ b/src/index.ts @@ -3,6 +3,8 @@ import axios from 'axios'; import { setupCache } from 'axios-cache-interceptor'; import axiosRetry from 'axios-retry'; import express, { NextFunction, Request, Response } from 'express'; +// eslint-disable-next-line import/no-named-as-default +import rateLimit from 'express-rate-limit'; import winston from 'winston'; import { ConfigureController, ExtractController, ManifestController, StreamController } from './controller'; import { BlockedError, logErrorAndReturnNiceString } from './error'; @@ -47,6 +49,10 @@ const extractors = createExtractors(fetcher); const addon = express(); addon.set('trust proxy', true); +if (envIsProd()) { + addon.use(rateLimit({ windowMs: 60 * 1000, limit: 10 })); +} + addon.use((req: Request, res: Response, next: NextFunction) => { process.env['HOST'] = req.host; process.env['PROTOCOL'] = req.protocol; @@ -57,7 +63,7 @@ addon.use((req: Request, res: Response, next: NextFunction) => { res.setHeader('Access-Control-Allow-Headers', '*'); if (envIsProd()) { - res.setHeader('Cache-Control', 'max-age=10, public'); + res.setHeader('Cache-Control', 'public, max-age=10, immutable'); } next();