mirror of
https://github.com/brendan-ch/project-inter-server.git
synced 2026-04-17 07:50:31 +00:00
Merge pull request #68 from brendan-ch/chore/rate-limits
chore/rate-limits
This commit is contained in:
@@ -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=
|
||||
|
||||
@@ -12,6 +12,10 @@ x-common-environment: &common-server-environment
|
||||
APNS_PRIVATE_KEY: ${APNS_PRIVATE_KEY}
|
||||
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:
|
||||
@@ -33,6 +37,7 @@ services:
|
||||
- redis-no-persistence
|
||||
environment:
|
||||
REDIS_URL: redis://redis-no-persistence:6379
|
||||
RATE_LIMITS_DISABLED: 1
|
||||
|
||||
volumes:
|
||||
- .:/usr/src/app
|
||||
|
||||
800
package-lock.json
generated
800
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -16,6 +16,7 @@
|
||||
"@graphql-codegen/typescript": "4.1.2",
|
||||
"@graphql-codegen/typescript-resolvers": "4.4.1",
|
||||
"@jest/globals": "^29.7.0",
|
||||
"@types/express": "^5.0.3",
|
||||
"@types/jsonwebtoken": "^9.0.8",
|
||||
"@types/node": "^22.10.2",
|
||||
"@types/redis": "^4.0.11",
|
||||
@@ -26,6 +27,10 @@
|
||||
"private": true,
|
||||
"dependencies": {
|
||||
"@apollo/server": "^4.11.2",
|
||||
"@as-integrations/express5": "^1.1.2",
|
||||
"express": "^5.1.0",
|
||||
"express-rate-limit": "^8.0.1",
|
||||
"express-slow-down": "^3.0.0",
|
||||
"graphql": "^16.10.0",
|
||||
"jsonwebtoken": "^9.0.2",
|
||||
"redis": "^4.7.0"
|
||||
|
||||
@@ -3,3 +3,9 @@ export const PARKING_LOGGING_INTERVAL_MS = 10000;
|
||||
export const PARKING_HISTORICAL_AVERAGE_MINIMUM_INTERVAL = 1000;
|
||||
|
||||
export const PARKING_HISTORICAL_AVERAGE_MAXIMUM_TIMESPAN = 60000 * 60 * 24;
|
||||
|
||||
export const RATE_LIMITS_DISABLED = true;
|
||||
export const RATE_LIMIT_WINDOW_MS = 10000;
|
||||
export const RATE_LIMIT_DELAY_AFTER_REQUESTS = 10000;
|
||||
export const RATE_LIMIT_DELAY_MULTIPLIER_MS = 1000;
|
||||
|
||||
|
||||
@@ -9,3 +9,15 @@ export const PARKING_HISTORICAL_AVERAGE_MINIMUM_INTERVAL = process.env.PARKING_H
|
||||
export const PARKING_HISTORICAL_AVERAGE_MAXIMUM_TIMESPAN = process.env.PARKING_HISTORICAL_AVERAGE_MAXIMUM_TIMESPAN
|
||||
? parseInt(process.env.PARKING_HISTORICAL_AVERAGE_MAXIMUM_TIMESPAN)
|
||||
: 60000 * 60 * 24;
|
||||
|
||||
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;
|
||||
|
||||
|
||||
66
src/index.ts
66
src/index.ts
@@ -1,13 +1,18 @@
|
||||
import { readFileSync } from "fs";
|
||||
import { ApolloServer } from "@apollo/server";
|
||||
import { startStandaloneServer } from "@apollo/server/standalone";
|
||||
import { MergedResolvers } from "./MergedResolvers";
|
||||
import { ServerContext } from "./ServerContext";
|
||||
import { loadShuttleTestData } from "./loaders/shuttle/loadShuttleTestData";
|
||||
import { InterchangeSystem, InterchangeSystemBuilderArguments } from "./entities/InterchangeSystem";
|
||||
import { ChapmanApiBasedParkingRepositoryLoader } from "./loaders/parking/ChapmanApiBasedParkingRepositoryLoader";
|
||||
import { supportedIntegrationTestSystems } from "./loaders/supportedIntegrationTestSystems";
|
||||
import { loadParkingTestData } from "./loaders/parking/loadParkingTestData";
|
||||
import express from "express";
|
||||
import { expressMiddleware } from "@as-integrations/express5";
|
||||
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");
|
||||
|
||||
@@ -27,37 +32,18 @@ async function main() {
|
||||
resolvers: MergedResolvers,
|
||||
introspection: process.env.NODE_ENV !== "production",
|
||||
});
|
||||
await server.start();
|
||||
|
||||
let systems: InterchangeSystem[];
|
||||
|
||||
if (process.argv.length > 2 && process.argv[2] == "integration-testing") {
|
||||
console.log("Using integration testing setup")
|
||||
|
||||
systems = await Promise.all(supportedIntegrationTestSystems.map(
|
||||
async (systemArguments) => {
|
||||
const system = InterchangeSystem.buildForTesting(systemArguments);
|
||||
|
||||
// TODO: Have loading of different data for different systems in the future
|
||||
await loadShuttleTestData(system.shuttleRepository);
|
||||
if (system.parkingRepository) {
|
||||
await loadParkingTestData(system.parkingRepository);
|
||||
}
|
||||
|
||||
return system;
|
||||
}
|
||||
));
|
||||
} else {
|
||||
systems = await Promise.all(supportedSystems.map(
|
||||
async (systemArguments) => {
|
||||
return await InterchangeSystem.build(systemArguments);
|
||||
},
|
||||
));
|
||||
}
|
||||
|
||||
const { url } = await startStandaloneServer(server, {
|
||||
listen: {
|
||||
port: process.env.PORT ? parseInt(process.env.PORT) : 4000,
|
||||
},
|
||||
const app = express();
|
||||
const options = {
|
||||
context: async () => {
|
||||
return {
|
||||
systems,
|
||||
@@ -70,10 +56,34 @@ async function main() {
|
||||
},
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
if (RATE_LIMITS_DISABLED) {
|
||||
app.use(
|
||||
"/",
|
||||
express.json(),
|
||||
expressMiddleware(server, options)
|
||||
);
|
||||
} else {
|
||||
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(
|
||||
"/",
|
||||
express.json(),
|
||||
limiter,
|
||||
expressMiddleware(server, options),
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
console.log(`Server ready at: ${url}`);
|
||||
const port = process.env.PORT ? parseInt(process.env.PORT) : 4000;
|
||||
app.listen(port, () => {
|
||||
console.log(`Server ready at port ${port}`);
|
||||
});
|
||||
}
|
||||
|
||||
main();
|
||||
|
||||
Reference in New Issue
Block a user