mirror of
https://github.com/directus/directus.git
synced 2026-04-25 03:00:53 -04:00
Add Pressure-based rate limiter (#17873)
* Start setting up @directus/pressure * Build pressure middleware * Add basic readme * Install @directus/pressure * Fix this binding * Experiment * Add defaults util * Cleanup * Fix export * Use directus defaults * Start tests * Add random-utils package * Finish testing for monitor * Add prod deployment * Stop building test files in prod * My favorite * Integrate pressure handler * Add decent defaults * Add retry header + custom error support * Clean-up merge conflict & sort imports * Fix build * Remove default value for retry after * Verify sampleInterval value * ran eslint * updated package lock * updated vitest * Create slimy-zebras-jam.md * Added basic docs for config options * updated pnpm lock and changeset * Update & align new packages * Update .changeset/slimy-zebras-jam.md --------- Co-authored-by: Pascal Jufer <pascal-jufer@bluewin.ch> Co-authored-by: Brainslug <br41nslug@users.noreply.github.com> Co-authored-by: Brainslug <tim@brainslug.nl>
This commit is contained in:
@@ -1,3 +1,4 @@
|
||||
import { handlePressure } from '@directus/pressure';
|
||||
import cookieParser from 'cookie-parser';
|
||||
import type { Request, RequestHandler, Response } from 'express';
|
||||
import express from 'express';
|
||||
@@ -45,6 +46,7 @@ import {
|
||||
import emitter from './emitter.js';
|
||||
import env from './env.js';
|
||||
import { InvalidPayloadException } from './exceptions/invalid-payload.js';
|
||||
import { ServiceUnavailableException } from './exceptions/service-unavailable.js';
|
||||
import { getExtensionManager } from './extensions.js';
|
||||
import { getFlowManager } from './flows.js';
|
||||
import logger, { expressLogger } from './logger.js';
|
||||
@@ -105,6 +107,26 @@ export default async function createApp(): Promise<express.Application> {
|
||||
app.set('trust proxy', env['IP_TRUST_PROXY']);
|
||||
app.set('query parser', (str: string) => qs.parse(str, { depth: 10 }));
|
||||
|
||||
if (env['PRESSURE_LIMITER_ENABLED']) {
|
||||
const sampleInterval = Number(env['PRESSURE_LIMITER_SAMPLE_INTERVAL']);
|
||||
|
||||
if (Number.isNaN(sampleInterval) === true || Number.isFinite(sampleInterval) === false) {
|
||||
throw new Error(`Invalid value for PRESSURE_LIMITER_SAMPLE_INTERVAL environment variable`);
|
||||
}
|
||||
|
||||
app.use(
|
||||
handlePressure({
|
||||
sampleInterval,
|
||||
maxEventLoopUtilization: env['PRESSURE_LIMITER_MAX_EVENT_LOOP_UTILIZATION'],
|
||||
maxEventLoopDelay: env['PRESSURE_LIMITER_MAX_EVENT_LOOP_DELAY'],
|
||||
maxMemoryRss: env['PRESSURE_LIMITER_MAX_MEMORY_RSS'],
|
||||
maxMemoryHeapUsed: env['PRESSURE_LIMITER_MAX_MEMORY_HEAP_USED'],
|
||||
error: new ServiceUnavailableException('Under pressure', { service: 'api' }),
|
||||
retryAfter: env['PRESSURE_LIMITER_RETRY_AFTER'],
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
app.use(
|
||||
helmet.contentSecurityPolicy(
|
||||
merge(
|
||||
|
||||
@@ -291,6 +291,14 @@ const defaults: Record<string, any> = {
|
||||
|
||||
FLOWS_EXEC_ALLOWED_MODULES: false,
|
||||
FLOWS_ENV_ALLOW_LIST: false,
|
||||
|
||||
PRESSURE_LIMITER_ENABLED: true,
|
||||
PRESSURE_LIMITER_SAMPLE_INTERVAL: 250,
|
||||
PRESSURE_LIMITER_MAX_EVENT_LOOP_UTILIZATION: 0.99,
|
||||
PRESSURE_LIMITER_MAX_EVENT_LOOP_DELAY: 500,
|
||||
PRESSURE_LIMITER_MAX_MEMORY_RSS: false,
|
||||
PRESSURE_LIMITER_MAX_MEMORY_HEAP_USED: false,
|
||||
PRESSURE_LIMITER_RETRY_AFTER: false,
|
||||
};
|
||||
|
||||
// Allows us to force certain environment variable into a type, instead of relying
|
||||
|
||||
Reference in New Issue
Block a user