1
0
mirror of https://github.com/Unleash/unleash.git synced 2025-01-06 00:07:44 +01:00
unleash.unleash/src/lib/routes/util.ts
Christopher Kolstad b06613d1b0
feat: Adds rate limiting to metric POST endpoints (#5075)
### What
The heaviest requests we serve are the register and metrics POSTs from
our SDKs/clients.
This PR adds ratelimiting to /api/client/register, /api/client/metrics,
/api/frontend/register and /api/frontend/metrics with a default set to
6000 requests per minute (or 100 rps) for each of the endpoints.

It will be overrideable by the environment variables documented.

### Points of discussion
@kwasniew already suggested using featuretoggles with variants to
control the rate per clientId. I struggled to see if we could
dynamically update the middleware after initialisation, so this attempt
will need a restart of the pod to update the request limit.
2023-10-18 13:00:44 +02:00

62 lines
1.9 KiB
TypeScript

import joi from 'joi';
import { Response } from 'express';
import { Logger } from '../logger';
import { UnleashError } from '../error/unleash-error';
import { fromLegacyError } from '../error/from-legacy-error';
import createError from 'http-errors';
export const customJoi = joi.extend((j) => ({
type: 'isUrlFriendly',
base: j.string(),
messages: {
'isUrlFriendly.base': '{{#label}} must be URL friendly',
},
validate(value, helpers) {
// Base validation regardless of the rules applied
if (encodeURIComponent(value) !== value) {
// Generate an error, state and options need to be passed
return { value, errors: helpers.error('isUrlFriendly.base') };
}
return undefined;
},
}));
export const nameType = customJoi.isUrlFriendly().min(1).max(100).required();
export const handleErrors: (
res: Response,
logger: Logger,
error: Error,
) => void = (res, logger, error) => {
if (createError.isHttpError(error)) {
return res
.status(
// @ts-expect-error - The error object here is not guaranteed to contain status
error.status ?? 400,
)
.json({ message: error.message });
}
const finalError =
error instanceof UnleashError ? error : fromLegacyError(error);
const format = (thing: object) => JSON.stringify(thing, null, 2);
if (!(error instanceof UnleashError)) {
logger.debug(
`I encountered an error that wasn't an instance of the \`UnleashError\` type. The original error was: ${format(
error,
)}. It was mapped to ${format(finalError.toJSON())}`,
);
}
if (finalError.statusCode === 500) {
logger.error(
`Server failed executing request: ${format(error)}`,
error,
);
}
return res.status(finalError.statusCode).json(finalError).end();
};