diff --git a/.env.example b/.env.example index 6f1a51e..1ce47a7 100644 --- a/.env.example +++ b/.env.example @@ -8,6 +8,13 @@ APNS_BUNDLE_ID= # base64-encoded APNs private key APNS_PRIVATE_KEY= +# control parking data logging PARKING_LOGGING_INTERVAL_MS= PARKING_HISTORICAL_AVERAGE_MINIMUM_INTERVAL= PARKING_HISTORICAL_AVERAGE_MAXIMUM_TIMESPAN= + +# control rate limiting features +RATE_LIMITS_DISABLED= +RATE_LIMIT_WINDOW_MS= +RATE_LIMIT_DELAY_AFTER_REQUESTS= +RATE_LIMIT_DELAY_MULTIPLIER_MS= diff --git a/docker-compose.yml b/docker-compose.yml index 5350f66..4748e8b 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -13,6 +13,9 @@ x-common-environment: &common-server-environment PARKING_LOGGING_INTERVAL_MS: ${PARKING_LOGGING_INTERVAL_MS} REDIS_URL: redis://redis:6379 RATE_LIMITS_DISABLED: ${RATE_LIMITS_DISABLED} + RATE_LIMIT_WINDOW_MS: ${RATE_LIMIT_WINDOW_MS} + RATE_LIMIT_DELAY_AFTER_REQUESTS: ${RATE_LIMIT_DELAY_AFTER_REQUESTS} + RATE_LIMIT_DELAY_MULTIPLIER_MS: ${RATE_LIMIT_DELAY_MULTIPLIER_MS} services: dev: diff --git a/src/environment.ts b/src/environment.ts index ffc9a02..bc193ff 100644 --- a/src/environment.ts +++ b/src/environment.ts @@ -10,4 +10,14 @@ export const PARKING_HISTORICAL_AVERAGE_MAXIMUM_TIMESPAN = process.env.PARKING_H ? parseInt(process.env.PARKING_HISTORICAL_AVERAGE_MAXIMUM_TIMESPAN) : 60000 * 60 * 24; -export const RATE_LIMITS_DISABLED = process.env.RATE_LIMITS_DISABLED !== undefined; +export const RATE_LIMITS_DISABLED = process.env.RATE_LIMITS_DISABLED === "1"; +export const RATE_LIMIT_WINDOW_MS = process.env.RATE_LIMIT_WINDOW_MS + ? parseInt(process.env.RATE_LIMIT_WINDOW_MS) + : 10000; +export const RATE_LIMIT_DELAY_AFTER_REQUESTS = process.env.RATE_LIMIT_DELAY_AFTER_REQUESTS + ? parseInt(process.env.RATE_LIMIT_DELAY_AFTER_REQUESTS) + : 10000; +export const RATE_LIMIT_DELAY_MULTIPLIER_MS = process.env.RATE_LIMIT_DELAY_MULTIPLIER_MS + ? parseInt(process.env.RATE_LIMIT_DELAY_MULTIPLIER_MS) + : 1000; + diff --git a/src/index.ts b/src/index.ts index 9a53aaa..bcc32a9 100644 --- a/src/index.ts +++ b/src/index.ts @@ -6,8 +6,13 @@ import { InterchangeSystem, InterchangeSystemBuilderArguments } from "./entities import { ChapmanApiBasedParkingRepositoryLoader } from "./loaders/parking/ChapmanApiBasedParkingRepositoryLoader"; import express from "express"; import { expressMiddleware } from "@as-integrations/express5"; -import { RATE_LIMITS_DISABLED } from "./environment"; -import rateLimit from "express-rate-limit"; +import { + RATE_LIMIT_DELAY_AFTER_REQUESTS, + RATE_LIMIT_DELAY_MULTIPLIER_MS, + RATE_LIMIT_WINDOW_MS, + RATE_LIMITS_DISABLED +} from "./environment"; +import slowDown from "express-slow-down"; const typeDefs = readFileSync("./schema.graphqls", "utf8"); @@ -60,12 +65,12 @@ async function main() { expressMiddleware(server, options) ); } else { - const limiter = rateLimit({ - windowMs: 60 * 1000, // Every minute - limit: 20000, - standardHeaders: 'draft-8', // draft-6: `RateLimit-*` headers; draft-7 & draft-8: combined `RateLimit` header - legacyHeaders: false, // Disable the `X-RateLimit-*` headers. - ipv6Subnet: 60, // Set to 60 or 64 to be less aggressive, or 52 or 48 to be more aggressive + const limiter = slowDown({ + windowMs: RATE_LIMIT_WINDOW_MS, + delayAfter: RATE_LIMIT_DELAY_AFTER_REQUESTS, + delayMs: (hits) => { + return hits * RATE_LIMIT_DELAY_MULTIPLIER_MS; + } }); app.use( "/", @@ -75,7 +80,6 @@ async function main() { ); } - const port = process.env.PORT ? parseInt(process.env.PORT) : 4000; app.listen(port, () => { console.log(`Server ready at port ${port}`);