server: fix rate limit error propagation
Changelog: Fixed
This commit is contained in:
parent
1171567db2
commit
48405fba3b
2 changed files with 42 additions and 42 deletions
|
@ -36,7 +36,7 @@ export class ApiError extends Error {
|
||||||
break;
|
break;
|
||||||
case 429:
|
case 429:
|
||||||
if (typeof this.info === 'object' && typeof this.info.reset === 'number') {
|
if (typeof this.info === 'object' && typeof this.info.reset === 'number') {
|
||||||
ctx.respose.set('Retry-After', Math.floor(this.info.reset - (Date.now() / 1000)));
|
ctx.response.set('Retry-After', Math.floor(this.info.reset - (Date.now() / 1000)));
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,7 +6,7 @@ import { ApiError } from './error.js';
|
||||||
|
|
||||||
const logger = new Logger('limiter');
|
const logger = new Logger('limiter');
|
||||||
|
|
||||||
export const limiter = (limitation: IEndpointMeta['limit'] & { key: NonNullable<string> }, actor: string) => new Promise<void>((resolve) => {
|
export const limiter = (limitation: IEndpointMeta['limit'] & { key: NonNullable<string> }, actor: string) => new Promise<void>((resolve, reject) => {
|
||||||
if (process.env.NODE_ENV === 'test') resolve();
|
if (process.env.NODE_ENV === 'test') resolve();
|
||||||
|
|
||||||
const hasShortTermLimit = typeof limitation.minInterval === 'number';
|
const hasShortTermLimit = typeof limitation.minInterval === 'number';
|
||||||
|
@ -15,45 +15,8 @@ export const limiter = (limitation: IEndpointMeta['limit'] & { key: NonNullable<
|
||||||
typeof limitation.duration === 'number' &&
|
typeof limitation.duration === 'number' &&
|
||||||
typeof limitation.max === 'number';
|
typeof limitation.max === 'number';
|
||||||
|
|
||||||
if (hasShortTermLimit) {
|
|
||||||
min();
|
|
||||||
} else if (hasLongTermLimit) {
|
|
||||||
max();
|
|
||||||
} else {
|
|
||||||
resolve();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Short-term limit, calls long term limit if appropriate.
|
|
||||||
function min(): void {
|
|
||||||
const minIntervalLimiter = new Limiter({
|
|
||||||
id: `${actor}:${limitation.key}:min`,
|
|
||||||
duration: limitation.minInterval,
|
|
||||||
max: 1,
|
|
||||||
db: redisClient,
|
|
||||||
});
|
|
||||||
|
|
||||||
minIntervalLimiter.get((err, info) => {
|
|
||||||
if (err) {
|
|
||||||
logger.error(err);
|
|
||||||
throw new ApiError('INTERNAL_ERROR');
|
|
||||||
}
|
|
||||||
|
|
||||||
logger.debug(`${actor} ${limitation.key} min remaining: ${info.remaining}`);
|
|
||||||
|
|
||||||
if (info.remaining === 0) {
|
|
||||||
throw new ApiError('RATE_LIMIT_EXCEEDED', info);
|
|
||||||
} else {
|
|
||||||
if (hasLongTermLimit) {
|
|
||||||
max();
|
|
||||||
} else {
|
|
||||||
resolve();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// Long term limit
|
// Long term limit
|
||||||
function max(): void {
|
const max = (): void => {
|
||||||
const limiter = new Limiter({
|
const limiter = new Limiter({
|
||||||
id: `${actor}:${limitation.key}`,
|
id: `${actor}:${limitation.key}`,
|
||||||
duration: limitation.duration,
|
duration: limitation.duration,
|
||||||
|
@ -64,16 +27,53 @@ export const limiter = (limitation: IEndpointMeta['limit'] & { key: NonNullable<
|
||||||
limiter.get((err, info) => {
|
limiter.get((err, info) => {
|
||||||
if (err) {
|
if (err) {
|
||||||
logger.error(err);
|
logger.error(err);
|
||||||
throw new ApiError('INTERNAL_ERROR');
|
reject(new ApiError('INTERNAL_ERROR'));
|
||||||
}
|
}
|
||||||
|
|
||||||
logger.debug(`${actor} ${limitation.key} max remaining: ${info.remaining}`);
|
logger.debug(`${actor} ${limitation.key} max remaining: ${info.remaining}`);
|
||||||
|
|
||||||
if (info.remaining === 0) {
|
if (info.remaining === 0) {
|
||||||
throw new ApiError('RATE_LIMIT_EXCEEDED', info);
|
reject(new ApiError('RATE_LIMIT_EXCEEDED', info));
|
||||||
} else {
|
} else {
|
||||||
resolve();
|
resolve();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Short-term limit, calls long term limit if appropriate.
|
||||||
|
const min = (): void => {
|
||||||
|
const minIntervalLimiter = new Limiter({
|
||||||
|
id: `${actor}:${limitation.key}:min`,
|
||||||
|
duration: limitation.minInterval,
|
||||||
|
max: 1,
|
||||||
|
db: redisClient,
|
||||||
|
});
|
||||||
|
|
||||||
|
minIntervalLimiter.get((err, info) => {
|
||||||
|
if (err) {
|
||||||
|
logger.error(err);
|
||||||
|
reject(new ApiError('INTERNAL_ERROR'));
|
||||||
|
}
|
||||||
|
|
||||||
|
logger.debug(`${actor} ${limitation.key} min remaining: ${info.remaining}`);
|
||||||
|
|
||||||
|
if (info.remaining === 0) {
|
||||||
|
reject(new ApiError('RATE_LIMIT_EXCEEDED', info));
|
||||||
|
} else {
|
||||||
|
if (hasLongTermLimit) {
|
||||||
|
max();
|
||||||
|
} else {
|
||||||
|
resolve();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hasShortTermLimit) {
|
||||||
|
min();
|
||||||
|
} else if (hasLongTermLimit) {
|
||||||
|
max();
|
||||||
|
} else {
|
||||||
|
resolve();
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
Loading…
Reference in a new issue