From d35ddc77d285879a4f5dd8a40497bf58930cb30e Mon Sep 17 00:00:00 2001 From: syuilo <4439005+syuilo@users.noreply.github.com> Date: Tue, 16 Dec 2025 19:56:44 +0900 Subject: enhance(backend): request ip が localhost だった場合、レートリミットをスキップ & 警告を出すように MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/backend/src/server/api/ApiCallService.ts | 12 ++++++---- .../backend/src/server/api/SigninApiService.ts | 24 +++++++++++--------- .../src/server/api/SigninWithPasskeyApiService.ts | 26 +++++++++++++--------- 3 files changed, 37 insertions(+), 25 deletions(-) (limited to 'packages/backend/src/server/api') diff --git a/packages/backend/src/server/api/ApiCallService.ts b/packages/backend/src/server/api/ApiCallService.ts index 27c79ab438..261e147040 100644 --- a/packages/backend/src/server/api/ApiCallService.ts +++ b/packages/backend/src/server/api/ApiCallService.ts @@ -313,12 +313,16 @@ export class ApiCallService implements OnApplicationShutdown { } if (ep.meta.limit) { - // koa will automatically load the `X-Forwarded-For` header if `proxy: true` is configured in the app. - let limitActor: string; + let limitActor: string | null; if (user) { limitActor = user.id; } else { - limitActor = getIpHash(request.ip); + if (request.ip === '::1' || request.ip === '127.0.0.1') { + console.warn('request ip is localhost, maybe caused by misconfiguration of trustProxy or reverse proxy'); + limitActor = null; + } else { + limitActor = getIpHash(request.ip); + } } const limit = Object.assign({}, ep.meta.limit); @@ -330,7 +334,7 @@ export class ApiCallService implements OnApplicationShutdown { // TODO: 毎リクエスト計算するのもあれだしキャッシュしたい const factor = user ? (await this.roleService.getUserPolicies(user.id)).rateLimitFactor : 1; - if (factor > 0) { + if (limitActor != null && factor > 0) { // Rate limit const rateLimit = await this.rateLimiterService.limit(limit as IEndpointMeta['limit'] & { key: NonNullable }, limitActor, factor); if (rateLimit != null) { diff --git a/packages/backend/src/server/api/SigninApiService.ts b/packages/backend/src/server/api/SigninApiService.ts index 3e889372d8..14726f8411 100644 --- a/packages/backend/src/server/api/SigninApiService.ts +++ b/packages/backend/src/server/api/SigninApiService.ts @@ -89,17 +89,21 @@ export class SigninApiService { return { error }; } + if (request.ip === '::1' || request.ip === '127.0.0.1') { + console.warn('request ip is localhost, maybe caused by misconfiguration of trustProxy or reverse proxy'); + } else { // not more than 1 attempt per second and not more than 10 attempts per hour - const rateLimit = await this.rateLimiterService.limit({ key: 'signin', duration: 60 * 60 * 1000, max: 10, minInterval: 1000 }, getIpHash(request.ip)); - if (rateLimit != null) { - reply.code(429); - return { - error: { - message: 'Too many failed attempts to sign in. Try again later.', - code: 'TOO_MANY_AUTHENTICATION_FAILURES', - id: '22d05606-fbcf-421a-a2db-b32610dcfd1b', - }, - }; + const rateLimit = await this.rateLimiterService.limit({ key: 'signin', duration: 60 * 60 * 1000, max: 10, minInterval: 1000 }, getIpHash(request.ip)); + if (rateLimit != null) { + reply.code(429); + return { + error: { + message: 'Too many failed attempts to sign in. Try again later.', + code: 'TOO_MANY_AUTHENTICATION_FAILURES', + id: '22d05606-fbcf-421a-a2db-b32610dcfd1b', + }, + }; + } } if (typeof username !== 'string') { diff --git a/packages/backend/src/server/api/SigninWithPasskeyApiService.ts b/packages/backend/src/server/api/SigninWithPasskeyApiService.ts index 9ba23c54e2..1b89752340 100644 --- a/packages/backend/src/server/api/SigninWithPasskeyApiService.ts +++ b/packages/backend/src/server/api/SigninWithPasskeyApiService.ts @@ -84,19 +84,23 @@ export class SigninWithPasskeyApiService { return error(status ?? 500, failure ?? { id: '4e30e80c-e338-45a0-8c8f-44455efa3b76' }); }; - try { + if (request.ip === '::1' || request.ip === '127.0.0.1') { + console.warn('request ip is localhost, maybe caused by misconfiguration of trustProxy or reverse proxy'); + } else { + try { // Not more than 1 API call per 250ms and not more than 100 attempts per 30min // NOTE: 1 Sign-in require 2 API calls - await this.rateLimiterService.limit({ key: 'signin-with-passkey', duration: 60 * 30 * 1000, max: 200, minInterval: 250 }, getIpHash(request.ip)); - } catch (err) { - reply.code(429); - return { - error: { - message: 'Too many failed attempts to sign in. Try again later.', - code: 'TOO_MANY_AUTHENTICATION_FAILURES', - id: '22d05606-fbcf-421a-a2db-b32610dcfd1b', - }, - }; + await this.rateLimiterService.limit({ key: 'signin-with-passkey', duration: 60 * 30 * 1000, max: 200, minInterval: 250 }, getIpHash(request.ip)); + } catch (err) { + reply.code(429); + return { + error: { + message: 'Too many failed attempts to sign in. Try again later.', + code: 'TOO_MANY_AUTHENTICATION_FAILURES', + id: '22d05606-fbcf-421a-a2db-b32610dcfd1b', + }, + }; + } } // Initiate Passkey Auth challenge with context -- cgit v1.2.3-freya From baeed4bc8057ec2623f983550cd63dff09c42c28 Mon Sep 17 00:00:00 2001 From: syuilo <4439005+syuilo@users.noreply.github.com> Date: Thu, 18 Dec 2025 20:05:20 +0900 Subject: perf(backend): lazy load systeminformation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit systeminformationを必要とする機能を有効にしていないサーバーで無駄に読み込まれることが無いように --- packages/backend/src/core/AiService.ts | 2 +- packages/backend/src/daemons/ServerStatsService.ts | 6 ++++-- packages/backend/src/misc/show-machine-info.ts | 6 +----- packages/backend/src/server/api/endpoints/admin/server-info.ts | 3 ++- packages/backend/src/server/api/endpoints/server-info.ts | 3 ++- 5 files changed, 10 insertions(+), 10 deletions(-) (limited to 'packages/backend/src/server/api') diff --git a/packages/backend/src/core/AiService.ts b/packages/backend/src/core/AiService.ts index 7a005400bb..cbae280030 100644 --- a/packages/backend/src/core/AiService.ts +++ b/packages/backend/src/core/AiService.ts @@ -7,7 +7,6 @@ import * as fs from 'node:fs'; import { fileURLToPath } from 'node:url'; import { dirname } from 'node:path'; import { Injectable } from '@nestjs/common'; -import si from 'systeminformation'; import { Mutex } from 'async-mutex'; import fetch from 'node-fetch'; import { bindThis } from '@/decorators.js'; @@ -84,6 +83,7 @@ export class AiService { @bindThis private async getCpuFlags(): Promise { + const si = await import('systeminformation'); const str = await si.cpuFlags(); return str.split(/\s+/); } diff --git a/packages/backend/src/daemons/ServerStatsService.ts b/packages/backend/src/daemons/ServerStatsService.ts index d229efb123..a972e5861c 100644 --- a/packages/backend/src/daemons/ServerStatsService.ts +++ b/packages/backend/src/daemons/ServerStatsService.ts @@ -4,13 +4,12 @@ */ import { Inject, Injectable } from '@nestjs/common'; -import si from 'systeminformation'; import Xev from 'xev'; import * as osUtils from 'os-utils'; import { bindThis } from '@/decorators.js'; -import type { OnApplicationShutdown } from '@nestjs/common'; import { MiMeta } from '@/models/_.js'; import { DI } from '@/di-symbols.js'; +import type { OnApplicationShutdown } from '@nestjs/common'; const ev = new Xev(); @@ -97,12 +96,14 @@ function cpuUsage(): Promise { // MEMORY STAT async function mem() { + const si = await import('systeminformation'); const data = await si.mem(); return data; } // NETWORK STAT async function net() { + const si = await import('systeminformation'); const iface = await si.networkInterfaceDefault(); const data = await si.networkStats(iface); return data[0]; @@ -110,5 +111,6 @@ async function net() { // FS STAT async function fs() { + const si = await import('systeminformation'); return await si.disksIO().catch(() => ({ rIO_sec: 0, wIO_sec: 0 })); } diff --git a/packages/backend/src/misc/show-machine-info.ts b/packages/backend/src/misc/show-machine-info.ts index 8ddec35f23..b279eb9546 100644 --- a/packages/backend/src/misc/show-machine-info.ts +++ b/packages/backend/src/misc/show-machine-info.ts @@ -4,15 +4,11 @@ */ import * as os from 'node:os'; -import sysUtils from 'systeminformation'; import type Logger from '@/logger.js'; export async function showMachineInfo(parentLogger: Logger) { const logger = parentLogger.createSubLogger('machine'); logger.debug(`Hostname: ${os.hostname()}`); logger.debug(`Platform: ${process.platform} Arch: ${process.arch}`); - const mem = await sysUtils.mem(); - const totalmem = (mem.total / 1024 / 1024 / 1024).toFixed(1); - const availmem = (mem.available / 1024 / 1024 / 1024).toFixed(1); - logger.debug(`CPU: ${os.cpus().length} core MEM: ${totalmem}GB (available: ${availmem}GB)`); + logger.debug(`CPU: ${os.cpus().length} core MEM: ${(os.totalmem() / 1024 / 1024 / 1024).toFixed(1)}GB (available: ${(os.freemem() / 1024 / 1024 / 1024).toFixed(1)}GB)`); } diff --git a/packages/backend/src/server/api/endpoints/admin/server-info.ts b/packages/backend/src/server/api/endpoints/admin/server-info.ts index 80b6a4d32e..603be514c8 100644 --- a/packages/backend/src/server/api/endpoints/admin/server-info.ts +++ b/packages/backend/src/server/api/endpoints/admin/server-info.ts @@ -4,7 +4,6 @@ */ import * as os from 'node:os'; -import si from 'systeminformation'; import { Inject, Injectable } from '@nestjs/common'; import { DataSource } from 'typeorm'; import * as Redis from 'ioredis'; @@ -112,6 +111,8 @@ export default class extends Endpoint { // eslint- ) { super(meta, paramDef, async () => { + const si = await import('systeminformation'); + const memStats = await si.mem(); const fsStats = await si.fsSize(); const netInterface = await si.networkInterfaceDefault(); diff --git a/packages/backend/src/server/api/endpoints/server-info.ts b/packages/backend/src/server/api/endpoints/server-info.ts index 8301c85f2e..0e8dc73ad9 100644 --- a/packages/backend/src/server/api/endpoints/server-info.ts +++ b/packages/backend/src/server/api/endpoints/server-info.ts @@ -4,7 +4,6 @@ */ import * as os from 'node:os'; -import si from 'systeminformation'; import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { MiMeta } from '@/models/_.js'; @@ -93,6 +92,8 @@ export default class extends Endpoint { // eslint- }, }; + const si = await import('systeminformation'); + const memStats = await si.mem(); const fsStats = await si.fsSize(); -- cgit v1.2.3-freya From ee8dccea2ffb151636e520f71b7dfe2b91e06c71 Mon Sep 17 00:00:00 2001 From: かっこかり <67428053+kakkokari-gtyih@users.noreply.github.com> Date: Sat, 20 Dec 2025 19:07:05 +0900 Subject: fix(backend): fix #16994 by approach 6 (#17005) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * fix(backend): narrow down trustproxy default value and enhance documentation on how to configure it * Update Changelog * indent [ci skip] * Update CHANGELOG.md [ci skip] * add cloudflare specific example * Update .config/example.yml Co-authored-by: anatawa12 * fix: productionでIPレートリミットされる際にlocalhostからリクエストが来たらログを残すように * fix: wrong condition * fix: use own logger for signin api * flip configuration * fix * fix [ci skip] * fix: wrong message [ci skip] * fix: どこがおかしいか明記 [ci skip] --------- Co-authored-by: anatawa12 --- .config/example.yml | 47 ++++++++++++++++++---- CHANGELOG.md | 12 +++++- packages/backend/src/config.ts | 14 ++++++- packages/backend/src/server/ServerService.ts | 2 +- packages/backend/src/server/api/ApiCallService.ts | 13 +++--- .../backend/src/server/api/SigninApiService.ts | 13 ++++-- .../src/server/api/SigninWithPasskeyApiService.ts | 8 ++-- 7 files changed, 84 insertions(+), 25 deletions(-) (limited to 'packages/backend/src/server/api') diff --git a/.config/example.yml b/.config/example.yml index 1c07c4bc16..c7884a3687 100644 --- a/.config/example.yml +++ b/.config/example.yml @@ -107,14 +107,39 @@ port: 3000 # Proxy trust settings # -# Changes how the server interpret the origin IP of the request. +# Specifies the IP addresses that Misskey will use as trusted +# reverse proxies (e.g., nginx, Cloudflare). This affects how +# Misskey determines the source IP for each request and is used +# for important rate limiting and security features. If the value +# is not set correctly, Misskey may use the IP address of the +# reverse proxy instead of the actual source IP, which may lead to +# unintended rate limiting or security vulnerabilities. +# By default, the loopback network and private network address +# ranges shown below are trusted. +# If you are using a single reverse proxy and it is on the same +# machine or the same private network as Misskey, it is unlikely you +# need to change this setting, and the default setting is fine. +# Also, if you are using multiple reverse proxy servers and they are +# all on the same private network as Misskey, the default setting +# is fine. +# However, if you are using a reverse proxy server that accesses +# Misskey web servers and streaming servers via public IP addresses +# (for example, Cloudflare), you must set this variable. +# When changing this setting, you can use one of the following values: # -# Any format supported by Fastify is accepted. -# Default: trust all proxies (i.e. trustProxy: true) -# See: https://fastify.dev/docs/latest/reference/server/#trustproxy -# To improve security, we recommend that you configure your settings appropriately. -# Incorrect configuration can cause issues such as difficulty signing in, -# so please configure your settings carefully. +# - true: Trust all proxies +# - false: Do not trust any proxies +# - IP address, IP address range, or array of them: Trust hops that +# match the specified criteria. +# - Integer: Trust the nth hop from the front-facing proxy server as +# the client. +# For more information on how to configure this setting, please refer +# to the Fastify documentation: +# https://fastify.dev/docs/latest/Reference/Server/#trustproxy +# +# Note that if this variable is set, it overrides the default range, +# so if you have both an external reverse proxy and a proxy on the +# local host, you must include both IPs (or IP ranges). # #trustProxy: # - '10.0.0.0/8' @@ -123,6 +148,10 @@ port: 3000 # - '127.0.0.1/32' # - '::1/128' # - 'fc00::/7' +# # Example: If you are using some external reverse proxies like CDNs, +# # you may need to add the CDN IP ranges here. +# # If you're using Cloudflare, you can find IP Ranges at: +# # https://www.cloudflare.com/ips/ # ┌──────────────────────────┐ #───┘ PostgreSQL configuration └──────────────────────────────── @@ -292,6 +321,10 @@ id: 'aidx' # Whether disable HSTS #disableHsts: true +# Enable internal IP-based rate limiting (default: true) +# To configure them in reverse proxy instead, set this to false. +#enableIpRateLimit: true + # Number of worker processes #clusterLimit: 1 diff --git a/CHANGELOG.md b/CHANGELOG.md index 3c2a0bb8a2..e25096addc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,9 +1,13 @@ ## 2025.12.2 ### Note -v2025.12.0で行われた「configの`trustProxy`のデフォルト値を`false`に変更」について、正しく環境に応じた設定を行わないとサインインが困難になるといった状態を緩和するために、以前のデフォルト値に戻す暫定対応を行いました。 +v2025.12.0で行われた「configの`trustProxy`のデフォルト値を`false`に変更」について、正しく環境に応じた設定を行わないとサインインが困難になるといった状態を緩和するために、以下の対応を行いました。 -**セキュリティを向上させるためには適切な設定を行うことを推奨しますが、間違った設定値を入れると上述のような不具合の原因となりますので、慎重に行ってください。** +**正しく設定しないと、上記のような不具合の原因となったり、セキュリティリスクが高まったりする可能性があります。必ず現在のconfigをご確認の上、必要に応じて値を変更してください。** + +- `trustProxy`について、デフォルト(configに値が設定されていない状態)ではループバックアドレスとローカルIPアドレス空間を信頼するようにしました。 +- `trustProxy`の設定方法について、より詳細に記述しました。 +- リバースプロキシやCDNなどのより上流のレイヤでレートリミットを設定したい場合や、緊急時の一時的な緩和策として、Misskey内部でのIPアドレスペースでのレートリミットを無効化できるようにしました。 ### General - 依存関係の更新 @@ -14,6 +18,10 @@ v2025.12.0で行われた「configの`trustProxy`のデフォルト値を`false` バージョン表記のないものは v0.x 系として実行されます。v1.x 系で動作させたい場合は必ずバージョン表記を含めてください。 - Fix: デッキUIでメニュー位置を下にしているとプロファイル削除ボタンが表示されないのを修正 +### Server +- Enhance: Misskey内部でのIPアドレスペースでのレートリミットを無効化できるように + - リバースプロキシやCDNなど別のレイヤで別途レートリミットを設定する場合や、ローカルでのテスト用途等として利用することを想定しています。 + - デフォルトは `enableIpRateLimit: true`(Misskey内部でのIPアドレスペースでのレートリミットは有効)です。 ## 2025.12.1 diff --git a/packages/backend/src/config.ts b/packages/backend/src/config.ts index f9852d3578..657d7869fa 100644 --- a/packages/backend/src/config.ts +++ b/packages/backend/src/config.ts @@ -30,6 +30,7 @@ type Source = { socket?: string; trustProxy?: FastifyServerOptions['trustProxy']; chmodSocket?: string; + enableIpRateLimit?: boolean; disableHsts?: boolean; db: { host: string; @@ -120,8 +121,9 @@ export type Config = { url: string; port: number; socket: string | undefined; - trustProxy: FastifyServerOptions['trustProxy']; + trustProxy: NonNullable; chmodSocket: string | undefined; + enableIpRateLimit: boolean; disableHsts: boolean | undefined; db: { host: string; @@ -263,9 +265,17 @@ export function loadConfig(): Config { url: url.origin, port: config.port ?? parseInt(process.env.PORT ?? '', 10), socket: config.socket, - trustProxy: config.trustProxy, + trustProxy: config.trustProxy ?? [ + '10.0.0.0/8', + '172.16.0.0/12', + '192.168.0.0/16', + '127.0.0.1/32', + '::1/128', + 'fc00::/7', + ], chmodSocket: config.chmodSocket, disableHsts: config.disableHsts, + enableIpRateLimit: config.enableIpRateLimit ?? true, host, hostname, scheme, diff --git a/packages/backend/src/server/ServerService.ts b/packages/backend/src/server/ServerService.ts index 1286b4dad6..ef9ac81f95 100644 --- a/packages/backend/src/server/ServerService.ts +++ b/packages/backend/src/server/ServerService.ts @@ -75,7 +75,7 @@ export class ServerService implements OnApplicationShutdown { @bindThis public async launch(): Promise { const fastify = Fastify({ - trustProxy: this.config.trustProxy ?? true, + trustProxy: this.config.trustProxy, logger: false, }); this.#fastify = fastify; diff --git a/packages/backend/src/server/api/ApiCallService.ts b/packages/backend/src/server/api/ApiCallService.ts index 261e147040..8bae46d9fb 100644 --- a/packages/backend/src/server/api/ApiCallService.ts +++ b/packages/backend/src/server/api/ApiCallService.ts @@ -313,16 +313,15 @@ export class ApiCallService implements OnApplicationShutdown { } if (ep.meta.limit) { - let limitActor: string | null; + let limitActor: string | null = null; if (user) { limitActor = user.id; - } else { - if (request.ip === '::1' || request.ip === '127.0.0.1') { - console.warn('request ip is localhost, maybe caused by misconfiguration of trustProxy or reverse proxy'); - limitActor = null; - } else { - limitActor = getIpHash(request.ip); + } else if (this.config.enableIpRateLimit) { + if (process.env.NODE_ENV === 'production' && (request.ip === '::1' || request.ip === '127.0.0.1')) { + this.logger.warn('Recieved API request from localhost IP address for rate limiting in production environment. This is likely due to an improper trustProxy setting in the config file.'); } + + limitActor = getIpHash(request.ip); } const limit = Object.assign({}, ep.meta.limit); diff --git a/packages/backend/src/server/api/SigninApiService.ts b/packages/backend/src/server/api/SigninApiService.ts index 14726f8411..00e8828242 100644 --- a/packages/backend/src/server/api/SigninApiService.ts +++ b/packages/backend/src/server/api/SigninApiService.ts @@ -15,6 +15,7 @@ import type { UserSecurityKeysRepository, UsersRepository, } from '@/models/_.js'; +import type Logger from '@/logger.js'; import type { Config } from '@/config.js'; import { getIpHash } from '@/misc/get-ip-hash.js'; import type { MiLocalUser } from '@/models/User.js'; @@ -23,6 +24,7 @@ import { bindThis } from '@/decorators.js'; import { WebAuthnService } from '@/core/WebAuthnService.js'; import { UserAuthService } from '@/core/UserAuthService.js'; import { CaptchaService } from '@/core/CaptchaService.js'; +import { LoggerService } from '@/core/LoggerService.js'; import { FastifyReplyError } from '@/misc/fastify-reply-error.js'; import { RateLimiterService } from './RateLimiterService.js'; import { SigninService } from './SigninService.js'; @@ -31,6 +33,8 @@ import type { FastifyReply, FastifyRequest } from 'fastify'; @Injectable() export class SigninApiService { + private logger: Logger; + constructor( @Inject(DI.config) private config: Config, @@ -50,6 +54,7 @@ export class SigninApiService { @Inject(DI.signinsRepository) private signinsRepository: SigninsRepository, + private loggerService: LoggerService, private idService: IdService, private rateLimiterService: RateLimiterService, private signinService: SigninService, @@ -57,6 +62,7 @@ export class SigninApiService { private webAuthnService: WebAuthnService, private captchaService: CaptchaService, ) { + this.logger = this.loggerService.getLogger('Signin'); } @bindThis @@ -89,10 +95,11 @@ export class SigninApiService { return { error }; } - if (request.ip === '::1' || request.ip === '127.0.0.1') { - console.warn('request ip is localhost, maybe caused by misconfiguration of trustProxy or reverse proxy'); - } else { // not more than 1 attempt per second and not more than 10 attempts per hour + if (this.config.enableIpRateLimit) { + if (process.env.NODE_ENV === 'production' && (request.ip === '::1' || request.ip === '127.0.0.1')) { + this.logger.warn('Recieved signin request from localhost IP address for rate limiting in production environment. This is likely due to an improper trustProxy setting in the config file.'); + } const rateLimit = await this.rateLimiterService.limit({ key: 'signin', duration: 60 * 60 * 1000, max: 10, minInterval: 1000 }, getIpHash(request.ip)); if (rateLimit != null) { reply.code(429); diff --git a/packages/backend/src/server/api/SigninWithPasskeyApiService.ts b/packages/backend/src/server/api/SigninWithPasskeyApiService.ts index 1b89752340..920f9d0b3a 100644 --- a/packages/backend/src/server/api/SigninWithPasskeyApiService.ts +++ b/packages/backend/src/server/api/SigninWithPasskeyApiService.ts @@ -84,9 +84,11 @@ export class SigninWithPasskeyApiService { return error(status ?? 500, failure ?? { id: '4e30e80c-e338-45a0-8c8f-44455efa3b76' }); }; - if (request.ip === '::1' || request.ip === '127.0.0.1') { - console.warn('request ip is localhost, maybe caused by misconfiguration of trustProxy or reverse proxy'); - } else { + if (this.config.enableIpRateLimit) { + if (process.env.NODE_ENV === 'production' && (request.ip === '::1' || request.ip === '127.0.0.1')) { + this.logger.warn('Recieved signin with passkey request from localhost IP address for rate limiting in production environment. This is likely due to an improper trustProxy setting in the config file.'); + } + try { // Not more than 1 API call per 250ms and not more than 100 attempts per 30min // NOTE: 1 Sign-in require 2 API calls -- cgit v1.2.3-freya From f739cb6270e430a0693cc84c62c337eb77979c5b Mon Sep 17 00:00:00 2001 From: おさむのひと <46447427+samunohito@users.noreply.github.com> Date: Sat, 20 Dec 2025 19:15:05 +0900 Subject: fix: admin/queue/deliver-delayedとadmin/queue/inbox-delayedの応答速度を改善 (#17009) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CHANGELOG.md | 3 +++ .../src/server/api/endpoints/admin/queue/deliver-delayed.ts | 10 +++------- .../src/server/api/endpoints/admin/queue/inbox-delayed.ts | 10 +++------- 3 files changed, 9 insertions(+), 14 deletions(-) (limited to 'packages/backend/src/server/api') diff --git a/CHANGELOG.md b/CHANGELOG.md index e25096addc..b3a60736ab 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,9 @@ v2025.12.0で行われた「configの`trustProxy`のデフォルト値を`false` ### General - 依存関係の更新 +### Server +- Fix: コントロールパネルのジョブキューページで使用される一部APIの応答速度を改善 + ### Client - Enhance: デッキのUI説明を追加 - Fix: バージョン表記のないPlayが正しく動作しない問題を修正 diff --git a/packages/backend/src/server/api/endpoints/admin/queue/deliver-delayed.ts b/packages/backend/src/server/api/endpoints/admin/queue/deliver-delayed.ts index f3e440b4cb..86158d7e22 100644 --- a/packages/backend/src/server/api/endpoints/admin/queue/deliver-delayed.ts +++ b/packages/backend/src/server/api/endpoints/admin/queue/deliver-delayed.ts @@ -52,18 +52,14 @@ export default class extends Endpoint { // eslint- super(meta, paramDef, async (ps, me) => { const jobs = await this.deliverQueue.getJobs(['delayed']); - const res = [] as [string, number][]; + const counts = new Map(); for (const job of jobs) { const host = new URL(job.data.to).host; - if (res.find(x => x[0] === host)) { - res.find(x => x[0] === host)![1]++; - } else { - res.push([host, 1]); - } + counts.set(host, (counts.get(host) ?? 0) + 1); } - res.sort((a, b) => b[1] - a[1]); + const res = [...counts.entries()].sort((a, b) => b[1] - a[1]); return res; }); diff --git a/packages/backend/src/server/api/endpoints/admin/queue/inbox-delayed.ts b/packages/backend/src/server/api/endpoints/admin/queue/inbox-delayed.ts index e7589cba81..ad6a823b8f 100644 --- a/packages/backend/src/server/api/endpoints/admin/queue/inbox-delayed.ts +++ b/packages/backend/src/server/api/endpoints/admin/queue/inbox-delayed.ts @@ -52,18 +52,14 @@ export default class extends Endpoint { // eslint- super(meta, paramDef, async (ps, me) => { const jobs = await this.inboxQueue.getJobs(['delayed']); - const res = [] as [string, number][]; + const counts = new Map(); for (const job of jobs) { const host = new URL(job.data.signature.keyId).host; - if (res.find(x => x[0] === host)) { - res.find(x => x[0] === host)![1]++; - } else { - res.push([host, 1]); - } + counts.set(host, (counts.get(host) ?? 0) + 1); } - res.sort((a, b) => b[1] - a[1]); + const res = [...counts.entries()].sort((a, b) => b[1] - a[1]); return res; }); -- cgit v1.2.3-freya