From f6b256620b9637ffe4bd29a07cfba1a7880c9bb1 Mon Sep 17 00:00:00 2001 From: Hazelnoot Date: Sat, 7 Dec 2024 13:13:19 -0500 Subject: separate SkRateLimiterService from RateLimiterService and update all usages --- packages/backend/src/server/api/ApiCallService.ts | 55 ++++------------------- 1 file changed, 8 insertions(+), 47 deletions(-) (limited to 'packages/backend/src/server/api/ApiCallService.ts') diff --git a/packages/backend/src/server/api/ApiCallService.ts b/packages/backend/src/server/api/ApiCallService.ts index 14367e02bb..38d33c761d 100644 --- a/packages/backend/src/server/api/ApiCallService.ts +++ b/packages/backend/src/server/api/ApiCallService.ts @@ -8,7 +8,6 @@ import * as fs from 'node:fs'; import * as stream from 'node:stream/promises'; import { Inject, Injectable } from '@nestjs/common'; import * as Sentry from '@sentry/node'; -import { LimiterInfo } from 'ratelimiter'; import { DI } from '@/di-symbols.js'; import { getIpHash } from '@/misc/get-ip-hash.js'; import type { MiLocalUser, MiUser } from '@/models/User.js'; @@ -19,9 +18,9 @@ import { createTemp } from '@/misc/create-temp.js'; import { bindThis } from '@/decorators.js'; import { RoleService } from '@/core/RoleService.js'; import type { Config } from '@/config.js'; -import { isLimitInfo } from '@/server/api/SkRateLimiterService.js'; +import { sendRateLimitHeaders } from '@/misc/rate-limit-utils.js'; +import { LegacyRateLimit, SkRateLimiterService } from '@/server/api/SkRateLimiterService.js'; import { ApiError } from './error.js'; -import { RateLimiterService } from './RateLimiterService.js'; import { ApiLoggerService } from './ApiLoggerService.js'; import { AuthenticateService, AuthenticationError } from './AuthenticateService.js'; import type { FastifyRequest, FastifyReply } from 'fastify'; @@ -51,7 +50,7 @@ export class ApiCallService implements OnApplicationShutdown { private userIpsRepository: UserIpsRepository, private authenticateService: AuthenticateService, - private rateLimiterService: RateLimiterService, + private rateLimiterService: SkRateLimiterService, private roleService: RoleService, private apiLoggerService: ApiLoggerService, ) { @@ -67,21 +66,6 @@ export class ApiCallService implements OnApplicationShutdown { let statusCode = err.httpStatusCode; if (err.httpStatusCode === 401) { reply.header('WWW-Authenticate', 'Bearer realm="Misskey"'); - } else if (err.code === 'RATE_LIMIT_EXCEEDED') { - const info: unknown = err.info; - const unixEpochInSeconds = Date.now(); - if (isLimitInfo(info)) { - // Number of seconds to wait before trying again. Left for backwards compatibility. - reply.header('Retry-After', info.resetSec.toString()); - // Number of milliseconds to wait before trying again. - reply.header('X-RateLimit-Reset', info.resetMs.toString()); - } else if (typeof(info) === 'object' && info && 'resetMs' in info && typeof(info.resetMs) === 'number') { - const cooldownInSeconds = Math.ceil((info.resetMs - unixEpochInSeconds) / 1000); - // もしかするとマイナスになる可能性がなくはないのでマイナスだったら0にしておく - reply.header('Retry-After', Math.max(cooldownInSeconds, 0).toString(10)); - } else { - this.logger.warn(`rate limit information has unexpected type: ${JSON.stringify(info)}`); - } } else if (err.kind === 'client') { reply.header('WWW-Authenticate', `Bearer realm="Misskey", error="invalid_request", error_description="${err.message}"`); statusCode = statusCode ?? 400; @@ -347,40 +331,17 @@ export class ApiCallService implements OnApplicationShutdown { if (factor > 0) { // Rate limit - const info = await this.rateLimiterService.limit(limit as IEndpointMeta['limit'] & { key: NonNullable }, limitActor, factor) - .then(info => { - // We always want these headers, because clients need them for pacing. - // Conditional check in case we somehow revert to the old limiter, which does not return info. - if (info) { - // Number of seconds until the limit has fully reset. - reply.header('X-RateLimit-Clear', info.fullResetSec.toString()); - // Number of calls that can be made before being limited. - reply.header('X-RateLimit-Remaining', info.remaining.toString()); - - // Only forward the info object if it's blocked, otherwise we'll reject *all* requests - if (info.blocked) { - return info; - } - } - - return undefined; - }) - .catch(err => { - // The old limiter throws info instead of returning it. - if ('info' in err) { - return err.info as LimiterInfo; - } else { - throw err; - } - }); + const info = await this.rateLimiterService.limit(limit as LegacyRateLimit, limitActor, factor); - if (info) { + sendRateLimitHeaders(reply, info); + + if (info.blocked) { throw new ApiError({ message: 'Rate limit exceeded. Please try again later.', code: 'RATE_LIMIT_EXCEEDED', id: 'd5826d14-3982-4d2e-8011-b9e9f02499ef', httpStatusCode: 429, - }, info); + }); } } } -- cgit v1.2.3-freya