From 402933004a8094038e2fc9949626a7c2aa9ff9b1 Mon Sep 17 00:00:00 2001 From: Hazelnoot Date: Sun, 2 Feb 2025 17:23:36 -0500 Subject: [PATCH 01/13] increase sign-in rate limit --- packages/backend/src/server/api/SigninApiService.ts | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/packages/backend/src/server/api/SigninApiService.ts b/packages/backend/src/server/api/SigninApiService.ts index fa9155d82d..25ea03a356 100644 --- a/packages/backend/src/server/api/SigninApiService.ts +++ b/packages/backend/src/server/api/SigninApiService.ts @@ -27,11 +27,18 @@ import { CaptchaService } from '@/core/CaptchaService.js'; import { FastifyReplyError } from '@/misc/fastify-reply-error.js'; import { isSystemAccount } from '@/misc/is-system-account.js'; import { SkRateLimiterService } from '@/server/api/SkRateLimiterService.js'; -import { sendRateLimitHeaders } from '@/misc/rate-limit-utils.js'; +import { Keyed, RateLimit, sendRateLimitHeaders } from '@/misc/rate-limit-utils.js'; import { SigninService } from './SigninService.js'; import type { AuthenticationResponseJSON } from '@simplewebauthn/types'; import type { FastifyReply, FastifyRequest } from 'fastify'; +// Up to 10 attempts, then 1 per minute +const signinRateLimit: Keyed = { + key: 'signin', + max: 10, + dripRate: 1000 * 60, +}; + @Injectable() export class SigninApiService { constructor( @@ -94,7 +101,7 @@ export class SigninApiService { } // 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)); + const rateLimit = await this.rateLimiterService.limit(signinRateLimit, getIpHash(request.ip)); sendRateLimitHeaders(reply, rateLimit); From 09669d72e7e2474141a2712a12c6dafe290ccf88 Mon Sep 17 00:00:00 2001 From: Hazelnoot Date: Sun, 2 Feb 2025 22:02:08 -0500 Subject: [PATCH 02/13] lookup and cache rate limit factors directly within SkRateLimiterService --- packages/backend/src/core/CoreModule.ts | 6 ++ .../src/server/ActivityPubServerService.ts | 4 +- .../backend/src/server/FileServerService.ts | 21 +++--- .../backend/src/server/api/ApiCallService.ts | 37 +++++------ .../src/server/api/SkRateLimiterService.ts | 45 +++++++++---- .../server/api/StreamingApiServerService.ts | 16 ++--- .../server/api/SkRateLimiterServiceTests.ts | 66 +++++++++++++++---- 7 files changed, 124 insertions(+), 71 deletions(-) diff --git a/packages/backend/src/core/CoreModule.ts b/packages/backend/src/core/CoreModule.ts index 141f905d7f..8c9f419c44 100644 --- a/packages/backend/src/core/CoreModule.ts +++ b/packages/backend/src/core/CoreModule.ts @@ -232,6 +232,8 @@ const $FanoutTimelineEndpointService: Provider = { provide: 'FanoutTimelineEndpo const $ChannelFollowingService: Provider = { provide: 'ChannelFollowingService', useExisting: ChannelFollowingService }; const $RegistryApiService: Provider = { provide: 'RegistryApiService', useExisting: RegistryApiService }; const $ReversiService: Provider = { provide: 'ReversiService', useExisting: ReversiService }; +const $TimeService: Provider = { provide: 'TimeService', useExisting: TimeService }; +const $EnvService: Provider = { provide: 'EnvService', useExisting: EnvService }; const $ChartLoggerService: Provider = { provide: 'ChartLoggerService', useExisting: ChartLoggerService }; const $FederationChart: Provider = { provide: 'FederationChart', useExisting: FederationChart }; @@ -538,6 +540,8 @@ const $SponsorsService: Provider = { provide: 'SponsorsService', useExisting: Sp $ChannelFollowingService, $RegistryApiService, $ReversiService, + $TimeService, + $EnvService, $ChartLoggerService, $FederationChart, @@ -839,6 +843,8 @@ const $SponsorsService: Provider = { provide: 'SponsorsService', useExisting: Sp $ChannelFollowingService, $RegistryApiService, $ReversiService, + $TimeService, + $EnvService, $FederationChart, $NotesChart, diff --git a/packages/backend/src/server/ActivityPubServerService.ts b/packages/backend/src/server/ActivityPubServerService.ts index 815bf278c7..19049e528c 100644 --- a/packages/backend/src/server/ActivityPubServerService.ts +++ b/packages/backend/src/server/ActivityPubServerService.ts @@ -34,10 +34,10 @@ import { bindThis } from '@/decorators.js'; import { IActivity } from '@/core/activitypub/type.js'; import { isQuote, isRenote } from '@/misc/is-renote.js'; import * as Acct from '@/misc/acct.js'; -import type { FastifyInstance, FastifyRequest, FastifyReply, FastifyPluginOptions, FastifyBodyParser } from 'fastify'; -import type { FindOptionsWhere } from 'typeorm'; import type Logger from '@/logger.js'; import { LoggerService } from '@/core/LoggerService.js'; +import type { FastifyInstance, FastifyRequest, FastifyReply, FastifyPluginOptions, FastifyBodyParser } from 'fastify'; +import type { FindOptionsWhere } from 'typeorm'; const ACTIVITY_JSON = 'application/activity+json; charset=utf-8'; const LD_JSON = 'application/ld+json; profile="https://www.w3.org/ns/activitystreams"; charset=utf-8'; diff --git a/packages/backend/src/server/FileServerService.ts b/packages/backend/src/server/FileServerService.ts index 5293d529ad..18cdda5430 100644 --- a/packages/backend/src/server/FileServerService.ts +++ b/packages/backend/src/server/FileServerService.ts @@ -11,7 +11,7 @@ import rename from 'rename'; import sharp from 'sharp'; import { sharpBmp } from '@misskey-dev/sharp-read-bmp'; import type { Config } from '@/config.js'; -import type { MiDriveFile, DriveFilesRepository } from '@/models/_.js'; +import type { MiDriveFile, DriveFilesRepository, MiUser } from '@/models/_.js'; import { DI } from '@/di-symbols.js'; import { createTemp } from '@/misc/create-temp.js'; import { FILE_TYPE_BROWSERSAFE } from '@/const.js'; @@ -30,7 +30,6 @@ import { correctFilename } from '@/misc/correct-filename.js'; import { handleRequestRedirectToOmitSearch } from '@/misc/fastify-hook-handlers.js'; import { getIpHash } from '@/misc/get-ip-hash.js'; import { AuthenticateService } from '@/server/api/AuthenticateService.js'; -import { RoleService } from '@/core/RoleService.js'; import { SkRateLimiterService } from '@/server/api/SkRateLimiterService.js'; import { Keyed, RateLimit, sendRateLimitHeaders } from '@/misc/rate-limit-utils.js'; import type { FastifyInstance, FastifyRequest, FastifyReply, FastifyPluginOptions } from 'fastify'; @@ -59,7 +58,6 @@ export class FileServerService { private loggerService: LoggerService, private authenticateService: AuthenticateService, private rateLimiterService: SkRateLimiterService, - private roleService: RoleService, ) { this.logger = this.loggerService.getLogger('server', 'gray'); @@ -625,14 +623,13 @@ export class FileServerService { // koa will automatically load the `X-Forwarded-For` header if `proxy: true` is configured in the app. const [user] = await this.authenticateService.authenticate(token); - const actor = user?.id ?? getIpHash(request.ip); - const factor = user ? (await this.roleService.getUserPolicies(user.id)).rateLimitFactor : 1; + const actor = user ?? getIpHash(request.ip); // Call both limits: the per-resource limit and the shared cross-resource limit - return await this.checkResourceLimit(reply, actor, group, resource, factor) && await this.checkSharedLimit(reply, actor, group, factor); + return await this.checkResourceLimit(reply, actor, group, resource) && await this.checkSharedLimit(reply, actor, group); } - private async checkResourceLimit(reply: FastifyReply, actor: string, group: string, resource: string, factor = 1): Promise { + private async checkResourceLimit(reply: FastifyReply, actor: string | MiUser, group: string, resource: string): Promise { const limit: Keyed = { // Group by resource key: `${group}${resource}`, @@ -643,10 +640,10 @@ export class FileServerService { dripRate: 1000 * 60, }; - return await this.checkLimit(reply, actor, limit, factor); + return await this.checkLimit(reply, actor, limit); } - private async checkSharedLimit(reply: FastifyReply, actor: string, group: string, factor = 1): Promise { + private async checkSharedLimit(reply: FastifyReply, actor: string | MiUser, group: string): Promise { const limit: Keyed = { key: group, type: 'bucket', @@ -655,11 +652,11 @@ export class FileServerService { size: 3600, }; - return await this.checkLimit(reply, actor, limit, factor); + return await this.checkLimit(reply, actor, limit); } - private async checkLimit(reply: FastifyReply, actor: string, limit: Keyed, factor = 1): Promise { - const info = await this.rateLimiterService.limit(limit, actor, factor); + private async checkLimit(reply: FastifyReply, actor: string | MiUser, limit: Keyed): Promise { + const info = await this.rateLimiterService.limit(limit, actor); sendRateLimitHeaders(reply, info); diff --git a/packages/backend/src/server/api/ApiCallService.ts b/packages/backend/src/server/api/ApiCallService.ts index 03f25a51fe..9c3952d541 100644 --- a/packages/backend/src/server/api/ApiCallService.ts +++ b/packages/backend/src/server/api/ApiCallService.ts @@ -313,35 +313,30 @@ export class ApiCallService implements OnApplicationShutdown { // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition if (endpointLimit) { // koa will automatically load the `X-Forwarded-For` header if `proxy: true` is configured in the app. - let limitActor: string; + let limitActor: string | MiLocalUser; if (user) { - limitActor = user.id; + limitActor = user; } else { limitActor = getIpHash(request.ip); } - // TODO: 毎リクエスト計算するのもあれだしキャッシュしたい - const factor = user ? (await this.roleService.getUserPolicies(user.id)).rateLimitFactor : 1; + const limit = { + key: ep.name, + ...endpointLimit, + }; - if (factor > 0) { - const limit = { - key: ep.name, - ...endpointLimit, - }; + // Rate limit + const info = await this.rateLimiterService.limit(limit, limitActor); - // Rate limit - const info = await this.rateLimiterService.limit(limit, limitActor, factor); + sendRateLimitHeaders(reply, 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); - } + 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); } } diff --git a/packages/backend/src/server/api/SkRateLimiterService.ts b/packages/backend/src/server/api/SkRateLimiterService.ts index 38c97b63df..70103222f3 100644 --- a/packages/backend/src/server/api/SkRateLimiterService.ts +++ b/packages/backend/src/server/api/SkRateLimiterService.ts @@ -5,36 +5,59 @@ import { Inject, Injectable } from '@nestjs/common'; import Redis from 'ioredis'; -import { TimeService } from '@/core/TimeService.js'; -import { EnvService } from '@/core/EnvService.js'; +import type { TimeService } from '@/core/TimeService.js'; +import type { EnvService } from '@/core/EnvService.js'; import { BucketRateLimit, LegacyRateLimit, LimitInfo, RateLimit, hasMinLimit, isLegacyRateLimit, Keyed, hasMaxLimit, disabledLimitInfo, MaxLegacyLimit, MinLegacyLimit } from '@/misc/rate-limit-utils.js'; import { DI } from '@/di-symbols.js'; +import { MemoryKVCache } from '@/misc/cache.js'; +import type { MiUser } from '@/models/_.js'; +import type { RoleService } from '@/core/RoleService.js'; + +// Sentinel value used for caching the default role template. +// Required because MemoryKVCache doesn't support null keys. +const defaultUserKey = ''; @Injectable() export class SkRateLimiterService { + // 1-minute cache interval + private readonly factorCache = new MemoryKVCache(1000 * 60); private readonly disabled: boolean; constructor( - @Inject(TimeService) + @Inject('TimeService') private readonly timeService: TimeService, @Inject(DI.redis) private readonly redisClient: Redis.Redis, - @Inject(EnvService) + @Inject('RoleService') + private readonly roleService: RoleService, + + @Inject('EnvService') envService: EnvService, ) { this.disabled = envService.env.NODE_ENV === 'test'; } /** - * Check & increment a rate limit + * Check & increment a rate limit for a client * @param limit The limit definition - * @param actor Client who is calling this limit - * @param factor Scaling factor - smaller = larger limit (less restrictive) + * @param actorOrUser authenticated client user or IP hash */ - public async limit(limit: Keyed, actor: string, factor = 1): Promise { - if (this.disabled || factor === 0) { + public async limit(limit: Keyed, actorOrUser: string | MiUser): Promise { + if (this.disabled) { + return disabledLimitInfo; + } + + const actor = typeof(actorOrUser) === 'object' ? actorOrUser.id : actorOrUser; + const userCacheKey = typeof(actorOrUser) === 'object' ? actorOrUser.id : defaultUserKey; + const userRoleKey = typeof(actorOrUser) === 'object' ? actorOrUser.id : null; + const factor = this.factorCache.get(userCacheKey) ?? await this.factorCache.fetch(userCacheKey, async () => { + const role = await this.roleService.getUserPolicies(userRoleKey); + return role.rateLimitFactor; + }); + + if (factor === 0) { return disabledLimitInfo; } @@ -42,10 +65,6 @@ export class SkRateLimiterService { throw new Error(`Rate limit factor is zero or negative: ${factor}`); } - return await this.tryLimit(limit, actor, factor); - } - - private async tryLimit(limit: Keyed, actor: string, factor: number): Promise { if (isLegacyRateLimit(limit)) { return await this.limitLegacy(limit, actor, factor); } else { diff --git a/packages/backend/src/server/api/StreamingApiServerService.ts b/packages/backend/src/server/api/StreamingApiServerService.ts index e3fd1312ae..f30bbb928b 100644 --- a/packages/backend/src/server/api/StreamingApiServerService.ts +++ b/packages/backend/src/server/api/StreamingApiServerService.ts @@ -18,7 +18,6 @@ import { CacheService } from '@/core/CacheService.js'; import { MiLocalUser } from '@/models/User.js'; import { UserService } from '@/core/UserService.js'; import { ChannelFollowingService } from '@/core/ChannelFollowingService.js'; -import { RoleService } from '@/core/RoleService.js'; import { getIpHash } from '@/misc/get-ip-hash.js'; import { LoggerService } from '@/core/LoggerService.js'; import { SkRateLimiterService } from '@/server/api/SkRateLimiterService.js'; @@ -49,7 +48,6 @@ export class StreamingApiServerService { private usersService: UserService, private channelFollowingService: ChannelFollowingService, private rateLimiterService: SkRateLimiterService, - private roleService: RoleService, private loggerService: LoggerService, ) { } @@ -57,22 +55,18 @@ export class StreamingApiServerService { @bindThis private async rateLimitThis( user: MiLocalUser | null | undefined, - requestIp: string | undefined, + requestIp: string, limit: IEndpointMeta['limit'] & { key: NonNullable }, ) : Promise { - let limitActor: string; + let limitActor: string | MiLocalUser; if (user) { - limitActor = user.id; + limitActor = user; } else { - limitActor = getIpHash(requestIp || 'wtf'); + limitActor = getIpHash(requestIp); } - const factor = user ? (await this.roleService.getUserPolicies(user.id)).rateLimitFactor : 1; - - if (factor <= 0) return false; - // Rate limit - const rateLimit = await this.rateLimiterService.limit(limit, limitActor, factor); + const rateLimit = await this.rateLimiterService.limit(limit, limitActor); return rateLimit.blocked; } diff --git a/packages/backend/test/unit/server/api/SkRateLimiterServiceTests.ts b/packages/backend/test/unit/server/api/SkRateLimiterServiceTests.ts index d13dbd2a71..6d4f117c87 100644 --- a/packages/backend/test/unit/server/api/SkRateLimiterServiceTests.ts +++ b/packages/backend/test/unit/server/api/SkRateLimiterServiceTests.ts @@ -4,6 +4,8 @@ */ import type Redis from 'ioredis'; +import type { MiUser } from '@/models/User.js'; +import type { RolePolicies, RoleService } from '@/core/RoleService.js'; import { SkRateLimiterService } from '@/server/api/SkRateLimiterService.js'; import { BucketRateLimit, Keyed, LegacyRateLimit } from '@/misc/rate-limit-utils.js'; @@ -15,6 +17,8 @@ describe(SkRateLimiterService, () => { let mockRedisExec: (batch: [string, ...unknown[]][]) => Promise<[Error | null, unknown][] | null> = null!; let mockEnvironment: Record = null!; let serviceUnderTest: () => SkRateLimiterService = null!; + let mockDefaultUserPolicies: Partial = null!; + let mockUserPolicies: Record> = null!; beforeEach(() => { mockTimeService = { @@ -69,9 +73,18 @@ describe(SkRateLimiterService, () => { env: mockEnvironment, }; + mockDefaultUserPolicies = { rateLimitFactor: 1 }; + mockUserPolicies = {}; + const mockRoleService = { + getUserPolicies(key: string | null) { + const policies = key != null ? mockUserPolicies[key] : null; + return Promise.resolve(policies ?? mockDefaultUserPolicies); + }, + } as unknown as RoleService; + let service: SkRateLimiterService | undefined = undefined; serviceUnderTest = () => { - return service ??= new SkRateLimiterService(mockTimeService, mockRedisClient, mockEnvService); + return service ??= new SkRateLimiterService(mockTimeService, mockRedisClient, mockRoleService, mockEnvService); }; }); @@ -284,11 +297,12 @@ describe(SkRateLimiterService, () => { }); it('should scale limit by factor', async () => { + mockDefaultUserPolicies.rateLimitFactor = 0.5; limitCounter = 1; limitTimestamp = 0; - const i1 = await serviceUnderTest().limit(limit, actor, 0.5); // 1 + 1 = 2 - const i2 = await serviceUnderTest().limit(limit, actor, 0.5); // 2 + 1 = 3 + const i1 = await serviceUnderTest().limit(limit, actor); // 1 + 1 = 2 + const i2 = await serviceUnderTest().limit(limit, actor); // 2 + 1 = 3 expect(i1.blocked).toBeFalsy(); expect(i2.blocked).toBeTruthy(); @@ -330,14 +344,18 @@ describe(SkRateLimiterService, () => { }); it('should skip if factor is zero', async () => { - const info = await serviceUnderTest().limit(limit, actor, 0); + mockDefaultUserPolicies.rateLimitFactor = 0; + + const info = await serviceUnderTest().limit(limit, actor); expect(info.blocked).toBeFalsy(); expect(info.remaining).toBe(Number.MAX_SAFE_INTEGER); }); it('should throw if factor is negative', async () => { - const promise = serviceUnderTest().limit(limit, actor, -1); + mockDefaultUserPolicies.rateLimitFactor = -1; + + const promise = serviceUnderTest().limit(limit, actor); await expect(promise).rejects.toThrow(/factor is zero or negative/); }); @@ -426,6 +444,19 @@ describe(SkRateLimiterService, () => { expect(info.fullResetMs).toBe(2000); expect(info.fullResetSec).toBe(2); }); + + it('should look up factor by user ID', async () => { + const userActor = { id: actor } as unknown as MiUser; + mockUserPolicies[actor] = { rateLimitFactor: 0.5 }; + limitCounter = 1; + limitTimestamp = 0; + + const i1 = await serviceUnderTest().limit(limit, userActor); // 1 + 1 = 2 + const i2 = await serviceUnderTest().limit(limit, userActor); // 2 + 1 = 3 + + expect(i1.blocked).toBeFalsy(); + expect(i2.blocked).toBeTruthy(); + }); }); describe('with min interval', () => { @@ -529,11 +560,12 @@ describe(SkRateLimiterService, () => { }); it('should scale interval by factor', async () => { + mockDefaultUserPolicies.rateLimitFactor = 0.5; limitCounter = 1; limitTimestamp = 0; mockTimeService.now += 500; - const info = await serviceUnderTest().limit(limit, actor, 0.5); + const info = await serviceUnderTest().limit(limit, actor); expect(info.blocked).toBeFalsy(); }); @@ -574,14 +606,18 @@ describe(SkRateLimiterService, () => { }); it('should skip if factor is zero', async () => { - const info = await serviceUnderTest().limit(limit, actor, 0); + mockDefaultUserPolicies.rateLimitFactor = 0; + + const info = await serviceUnderTest().limit(limit, actor); expect(info.blocked).toBeFalsy(); expect(info.remaining).toBe(Number.MAX_SAFE_INTEGER); }); it('should throw if factor is negative', async () => { - const promise = serviceUnderTest().limit(limit, actor, -1); + mockDefaultUserPolicies.rateLimitFactor = -1; + + const promise = serviceUnderTest().limit(limit, actor); await expect(promise).rejects.toThrow(/factor is zero or negative/); }); @@ -701,10 +737,11 @@ describe(SkRateLimiterService, () => { }); it('should scale limit by factor', async () => { + mockDefaultUserPolicies.rateLimitFactor = 0.5; limitCounter = 10; limitTimestamp = 0; - const info = await serviceUnderTest().limit(limit, actor, 0.5); // 10 + 1 = 11 + const info = await serviceUnderTest().limit(limit, actor); // 10 + 1 = 11 expect(info.blocked).toBeTruthy(); }); @@ -760,14 +797,18 @@ describe(SkRateLimiterService, () => { }); it('should skip if factor is zero', async () => { - const info = await serviceUnderTest().limit(limit, actor, 0); + mockDefaultUserPolicies.rateLimitFactor = 0; + + const info = await serviceUnderTest().limit(limit, actor); expect(info.blocked).toBeFalsy(); expect(info.remaining).toBe(Number.MAX_SAFE_INTEGER); }); it('should throw if factor is negative', async () => { - const promise = serviceUnderTest().limit(limit, actor, -1); + mockDefaultUserPolicies.rateLimitFactor = -1; + + const promise = serviceUnderTest().limit(limit, actor); await expect(promise).rejects.toThrow(/factor is zero or negative/); }); @@ -890,11 +931,12 @@ describe(SkRateLimiterService, () => { }); it('should scale limit and interval by factor', async () => { + mockDefaultUserPolicies.rateLimitFactor = 0.5; limitCounter = 5; limitTimestamp = 0; mockTimeService.now += 500; - const info = await serviceUnderTest().limit(limit, actor, 0.5); + const info = await serviceUnderTest().limit(limit, actor); expect(info.blocked).toBeFalsy(); }); From d4311ea041eb746f3fdc5b4d8dbba2adb2470380 Mon Sep 17 00:00:00 2001 From: Hazelnoot Date: Wed, 5 Feb 2025 10:04:42 -0500 Subject: [PATCH 03/13] document new rate limit factor calculations --- packages/backend/src/server/SkRateLimiterService.md | 5 +++++ .../backend/src/server/api/SkRateLimiterService.ts | 10 +++++++++- 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/packages/backend/src/server/SkRateLimiterService.md b/packages/backend/src/server/SkRateLimiterService.md index 762f8dfe14..fb007538fa 100644 --- a/packages/backend/src/server/SkRateLimiterService.md +++ b/packages/backend/src/server/SkRateLimiterService.md @@ -12,6 +12,11 @@ SkRateLimiterService is not quite plug-and-play compatible with existing call si Instead, the returned LimitInfo object will have `blocked` set to true. Callers are responsible for checking this property and taking any desired action, such as rejecting a request or returning limit details. +Rate limit factors are also handled differently. +Instead of providing an optional parameter for callers, SkRateLimiterServer accepts an `MiUser` parameter that is used to compute the factor directly. +If a user is not available (such as for unauthenticated callers), then the Role Template factor is used instead. +To avoid confusion, the `factor` parameter has been removed entirely and is now an implementation detail. + ## Headers LimitInfo objects (returned by `SkRateLimitService.limit()`) can be passed to `rate-limit-utils.sendRateLimitHeaders()` to send standard rate limit headers with an HTTP response. diff --git a/packages/backend/src/server/api/SkRateLimiterService.ts b/packages/backend/src/server/api/SkRateLimiterService.ts index 70103222f3..038f12cb25 100644 --- a/packages/backend/src/server/api/SkRateLimiterService.ts +++ b/packages/backend/src/server/api/SkRateLimiterService.ts @@ -40,7 +40,15 @@ export class SkRateLimiterService { } /** - * Check & increment a rate limit for a client + * Check & increment a rate limit for a client. + * + * If the client (actorOrUser) is passed as a string, then it uses the default rate limit factor from the role template. + * If the client (actorOrUser) is passed as an MiUser, then it queries the user's actual rate limit factor from their assigned roles. + * + * A factor of zero (0) will disable the limit, while any negative number will produce an error. + * A factor between zero (0) and one (1) will increase the limit from its default values (allowing more actions per time interval). + * A factor greater than one (1) will decrease the limit from its default values (allowing fewer actions per time interval). + * * @param limit The limit definition * @param actorOrUser authenticated client user or IP hash */ From f92fb3bb8c70b5ca35f4f61a437b101bd5bae7e8 Mon Sep 17 00:00:00 2001 From: Hazelnoot Date: Wed, 5 Feb 2025 11:16:39 -0500 Subject: [PATCH 04/13] move SkRateLimiterService to correct directory --- packages/backend/src/server/FileServerService.ts | 2 +- packages/backend/src/server/ServerModule.ts | 2 +- packages/backend/src/server/{api => }/SkRateLimiterService.ts | 0 packages/backend/src/server/api/ApiCallService.ts | 2 +- packages/backend/src/server/api/SigninApiService.ts | 2 +- packages/backend/src/server/api/SigninWithPasskeyApiService.ts | 2 +- packages/backend/src/server/api/StreamingApiServerService.ts | 2 +- packages/backend/test/unit/SigninWithPasskeyApiService.ts | 2 +- .../backend/test/unit/server/api/SkRateLimiterServiceTests.ts | 2 +- 9 files changed, 8 insertions(+), 8 deletions(-) rename packages/backend/src/server/{api => }/SkRateLimiterService.ts (100%) diff --git a/packages/backend/src/server/FileServerService.ts b/packages/backend/src/server/FileServerService.ts index 18cdda5430..a7e13a1b78 100644 --- a/packages/backend/src/server/FileServerService.ts +++ b/packages/backend/src/server/FileServerService.ts @@ -30,7 +30,7 @@ import { correctFilename } from '@/misc/correct-filename.js'; import { handleRequestRedirectToOmitSearch } from '@/misc/fastify-hook-handlers.js'; import { getIpHash } from '@/misc/get-ip-hash.js'; import { AuthenticateService } from '@/server/api/AuthenticateService.js'; -import { SkRateLimiterService } from '@/server/api/SkRateLimiterService.js'; +import { SkRateLimiterService } from '@/server/SkRateLimiterService.js'; import { Keyed, RateLimit, sendRateLimitHeaders } from '@/misc/rate-limit-utils.js'; import type { FastifyInstance, FastifyRequest, FastifyReply, FastifyPluginOptions } from 'fastify'; diff --git a/packages/backend/src/server/ServerModule.ts b/packages/backend/src/server/ServerModule.ts index c1d7c088f1..6b5156106a 100644 --- a/packages/backend/src/server/ServerModule.ts +++ b/packages/backend/src/server/ServerModule.ts @@ -6,7 +6,7 @@ import { Module } from '@nestjs/common'; import { EndpointsModule } from '@/server/api/EndpointsModule.js'; import { CoreModule } from '@/core/CoreModule.js'; -import { SkRateLimiterService } from '@/server/api/SkRateLimiterService.js'; +import { SkRateLimiterService } from '@/server/SkRateLimiterService.js'; import { ApiCallService } from './api/ApiCallService.js'; import { FileServerService } from './FileServerService.js'; import { HealthServerService } from './HealthServerService.js'; diff --git a/packages/backend/src/server/api/SkRateLimiterService.ts b/packages/backend/src/server/SkRateLimiterService.ts similarity index 100% rename from packages/backend/src/server/api/SkRateLimiterService.ts rename to packages/backend/src/server/SkRateLimiterService.ts diff --git a/packages/backend/src/server/api/ApiCallService.ts b/packages/backend/src/server/api/ApiCallService.ts index 9c3952d541..5ce358d68f 100644 --- a/packages/backend/src/server/api/ApiCallService.ts +++ b/packages/backend/src/server/api/ApiCallService.ts @@ -19,7 +19,7 @@ import { bindThis } from '@/decorators.js'; import { RoleService } from '@/core/RoleService.js'; import type { Config } from '@/config.js'; import { sendRateLimitHeaders } from '@/misc/rate-limit-utils.js'; -import { SkRateLimiterService } from '@/server/api/SkRateLimiterService.js'; +import { SkRateLimiterService } from '@/server/SkRateLimiterService.js'; import { ApiError } from './error.js'; import { ApiLoggerService } from './ApiLoggerService.js'; import { AuthenticateService, AuthenticationError } from './AuthenticateService.js'; diff --git a/packages/backend/src/server/api/SigninApiService.ts b/packages/backend/src/server/api/SigninApiService.ts index 25ea03a356..72712bce60 100644 --- a/packages/backend/src/server/api/SigninApiService.ts +++ b/packages/backend/src/server/api/SigninApiService.ts @@ -26,7 +26,7 @@ import { UserAuthService } from '@/core/UserAuthService.js'; import { CaptchaService } from '@/core/CaptchaService.js'; import { FastifyReplyError } from '@/misc/fastify-reply-error.js'; import { isSystemAccount } from '@/misc/is-system-account.js'; -import { SkRateLimiterService } from '@/server/api/SkRateLimiterService.js'; +import { SkRateLimiterService } from '@/server/SkRateLimiterService.js'; import { Keyed, RateLimit, sendRateLimitHeaders } from '@/misc/rate-limit-utils.js'; import { SigninService } from './SigninService.js'; import type { AuthenticationResponseJSON } from '@simplewebauthn/types'; diff --git a/packages/backend/src/server/api/SigninWithPasskeyApiService.ts b/packages/backend/src/server/api/SigninWithPasskeyApiService.ts index e94d2b6b68..f84f50523b 100644 --- a/packages/backend/src/server/api/SigninWithPasskeyApiService.ts +++ b/packages/backend/src/server/api/SigninWithPasskeyApiService.ts @@ -21,7 +21,7 @@ import { WebAuthnService } from '@/core/WebAuthnService.js'; import Logger from '@/logger.js'; import { LoggerService } from '@/core/LoggerService.js'; import type { IdentifiableError } from '@/misc/identifiable-error.js'; -import { SkRateLimiterService } from '@/server/api/SkRateLimiterService.js'; +import { SkRateLimiterService } from '@/server/SkRateLimiterService.js'; import { sendRateLimitHeaders } from '@/misc/rate-limit-utils.js'; import { SigninService } from './SigninService.js'; import type { AuthenticationResponseJSON } from '@simplewebauthn/types'; diff --git a/packages/backend/src/server/api/StreamingApiServerService.ts b/packages/backend/src/server/api/StreamingApiServerService.ts index f30bbb928b..6e7abcfae6 100644 --- a/packages/backend/src/server/api/StreamingApiServerService.ts +++ b/packages/backend/src/server/api/StreamingApiServerService.ts @@ -20,7 +20,7 @@ import { UserService } from '@/core/UserService.js'; import { ChannelFollowingService } from '@/core/ChannelFollowingService.js'; import { getIpHash } from '@/misc/get-ip-hash.js'; import { LoggerService } from '@/core/LoggerService.js'; -import { SkRateLimiterService } from '@/server/api/SkRateLimiterService.js'; +import { SkRateLimiterService } from '@/server/SkRateLimiterService.js'; import { AuthenticateService, AuthenticationError } from './AuthenticateService.js'; import MainStreamConnection from './stream/Connection.js'; import { ChannelsService } from './stream/ChannelsService.js'; diff --git a/packages/backend/test/unit/SigninWithPasskeyApiService.ts b/packages/backend/test/unit/SigninWithPasskeyApiService.ts index 7df991c15c..efed905e02 100644 --- a/packages/backend/test/unit/SigninWithPasskeyApiService.ts +++ b/packages/backend/test/unit/SigninWithPasskeyApiService.ts @@ -20,7 +20,7 @@ import { SigninWithPasskeyApiService } from '@/server/api/SigninWithPasskeyApiSe import { WebAuthnService } from '@/core/WebAuthnService.js'; import { SigninService } from '@/server/api/SigninService.js'; import { IdentifiableError } from '@/misc/identifiable-error.js'; -import { SkRateLimiterService } from '@/server/api/SkRateLimiterService.js'; +import { SkRateLimiterService } from '@/server/SkRateLimiterService.js'; import { LimitInfo } from '@/misc/rate-limit-utils.js'; const moduleMocker = new ModuleMocker(global); diff --git a/packages/backend/test/unit/server/api/SkRateLimiterServiceTests.ts b/packages/backend/test/unit/server/api/SkRateLimiterServiceTests.ts index 6d4f117c87..b1f100698b 100644 --- a/packages/backend/test/unit/server/api/SkRateLimiterServiceTests.ts +++ b/packages/backend/test/unit/server/api/SkRateLimiterServiceTests.ts @@ -6,7 +6,7 @@ import type Redis from 'ioredis'; import type { MiUser } from '@/models/User.js'; import type { RolePolicies, RoleService } from '@/core/RoleService.js'; -import { SkRateLimiterService } from '@/server/api/SkRateLimiterService.js'; +import { SkRateLimiterService } from '@/server/SkRateLimiterService.js'; import { BucketRateLimit, Keyed, LegacyRateLimit } from '@/misc/rate-limit-utils.js'; /* eslint-disable @typescript-eslint/no-non-null-assertion */ From b171c1c4408c1438976f3473c8104b68305a53f9 Mon Sep 17 00:00:00 2001 From: Hazelnoot Date: Wed, 5 Feb 2025 11:18:53 -0500 Subject: [PATCH 05/13] move imports to fix git diff in ActivityPubServerService.ts --- packages/backend/src/server/ActivityPubServerService.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/backend/src/server/ActivityPubServerService.ts b/packages/backend/src/server/ActivityPubServerService.ts index 19049e528c..6e8c249471 100644 --- a/packages/backend/src/server/ActivityPubServerService.ts +++ b/packages/backend/src/server/ActivityPubServerService.ts @@ -30,12 +30,12 @@ import type { MiNote } from '@/models/Note.js'; import { QueryService } from '@/core/QueryService.js'; import { UtilityService } from '@/core/UtilityService.js'; import { UserEntityService } from '@/core/entities/UserEntityService.js'; +import type Logger from '@/logger.js'; +import { LoggerService } from '@/core/LoggerService.js'; import { bindThis } from '@/decorators.js'; import { IActivity } from '@/core/activitypub/type.js'; import { isQuote, isRenote } from '@/misc/is-renote.js'; import * as Acct from '@/misc/acct.js'; -import type Logger from '@/logger.js'; -import { LoggerService } from '@/core/LoggerService.js'; import type { FastifyInstance, FastifyRequest, FastifyReply, FastifyPluginOptions, FastifyBodyParser } from 'fastify'; import type { FindOptionsWhere } from 'typeorm'; From 24528ed0c398077c74206abfdda56a771b09d54f Mon Sep 17 00:00:00 2001 From: Marie Date: Thu, 6 Feb 2025 22:54:47 +0100 Subject: [PATCH 06/13] fix: Allow FreeBSD to start Backend without any issues --- locales/index.d.ts | 2 +- packages/backend/package.json | 2 +- packages/backend/src/core/UserListService.ts | 2 +- packages/backend/src/misc/gen-identicon.ts | 4 +- packages/frontend-shared/package.json | 1 + pnpm-lock.yaml | 441 +++++++++++++------ 6 files changed, 305 insertions(+), 147 deletions(-) diff --git a/locales/index.d.ts b/locales/index.d.ts index 70eba52ea0..af5faefe1a 100644 --- a/locales/index.d.ts +++ b/locales/index.d.ts @@ -11631,7 +11631,7 @@ export interface Locale extends ILocale { */ "robotsTxt": string; /** - * Adding entries here will override the default robots.txt packaged with Sharkey. Maximum 2048 characters. + * Adding entries here will override the default robots.txt packaged with Sharkey. */ "robotsTxtDescription": string; } diff --git a/packages/backend/package.json b/packages/backend/package.json index bace8c1f96..f01a05076a 100644 --- a/packages/backend/package.json +++ b/packages/backend/package.json @@ -81,7 +81,6 @@ "@fastify/view": "10.0.1", "@misskey-dev/sharp-read-bmp": "1.2.0", "@misskey-dev/summaly": "5.1.0", - "@napi-rs/canvas": "0.1.56", "@nestjs/common": "10.4.7", "@nestjs/core": "10.4.7", "@nestjs/testing": "10.4.7", @@ -106,6 +105,7 @@ "body-parser": "1.20.3", "bullmq": "5.26.1", "cacheable-lookup": "7.0.0", + "canvas": "^3.1.0", "cbor": "9.0.2", "chalk": "5.3.0", "chalk-template": "1.1.0", diff --git a/packages/backend/src/core/UserListService.ts b/packages/backend/src/core/UserListService.ts index 6333356fe9..11085fff8d 100644 --- a/packages/backend/src/core/UserListService.ts +++ b/packages/backend/src/core/UserListService.ts @@ -58,7 +58,7 @@ export class UserListService implements OnApplicationShutdown, OnModuleInit { } async onModuleInit() { - this.roleService = this.moduleRef.get(RoleService.name); + this.roleService = this.moduleRef.get(RoleService); } @bindThis diff --git a/packages/backend/src/misc/gen-identicon.ts b/packages/backend/src/misc/gen-identicon.ts index 342e0f8602..f3c08cc76e 100644 --- a/packages/backend/src/misc/gen-identicon.ts +++ b/packages/backend/src/misc/gen-identicon.ts @@ -8,7 +8,7 @@ * https://en.wikipedia.org/wiki/Identicon */ -import { createCanvas } from '@napi-rs/canvas'; +import { createCanvas } from 'canvas'; import gen from 'random-seed'; const size = 128; // px @@ -100,5 +100,5 @@ export async function genIdenticon(seed: string): Promise { } } - return await canvas.encode('png'); + return await canvas.toBuffer('image/png'); } diff --git a/packages/frontend-shared/package.json b/packages/frontend-shared/package.json index 8bf25da161..8a9a6b6948 100644 --- a/packages/frontend-shared/package.json +++ b/packages/frontend-shared/package.json @@ -34,6 +34,7 @@ ], "dependencies": { "misskey-js": "workspace:*", + "nodemon": "3.1.7", "vue": "3.5.12" } } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index a5580f1a7e..1268658f16 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -128,9 +128,6 @@ importers: '@misskey-dev/summaly': specifier: 5.1.0 version: 5.1.0 - '@napi-rs/canvas': - specifier: 0.1.56 - version: 0.1.56 '@nestjs/common': specifier: 10.4.7 version: 10.4.7(reflect-metadata@0.2.2)(rxjs@7.8.1) @@ -203,6 +200,9 @@ importers: cacheable-lookup: specifier: 7.0.0 version: 7.0.0 + canvas: + specifier: ^3.1.0 + version: 3.1.0 cbor: specifier: 9.0.2 version: 9.0.2 @@ -289,7 +289,7 @@ importers: version: 4.1.0 jsdom: specifier: 24.1.1 - version: 24.1.1(bufferutil@4.0.7)(utf-8-validate@6.0.3) + version: 24.1.1(bufferutil@4.0.7)(canvas@3.1.0)(utf-8-validate@6.0.3) json5: specifier: 2.2.3 version: 2.2.3 @@ -1258,6 +1258,9 @@ importers: misskey-js: specifier: workspace:* version: link:../misskey-js + nodemon: + specifier: 3.1.7 + version: 3.1.7 vue: specifier: 3.5.12 version: 3.5.12(typescript@5.6.3) @@ -1370,7 +1373,7 @@ importers: version: 3.3.3 ts-jest: specifier: ^29.1.1 - version: 29.1.2(@babel/core@7.23.5)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.23.5))(esbuild@0.24.0)(jest@29.7.0(@types/node@22.9.0))(typescript@5.1.6) + version: 29.1.2(@babel/core@7.24.7)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.24.7))(esbuild@0.24.0)(jest@29.7.0(@types/node@22.9.0))(typescript@5.1.6) typedoc: specifier: ^0.25.3 version: 0.25.13(typescript@5.1.6) @@ -2642,6 +2645,7 @@ packages: '@humanwhocodes/config-array@0.11.14': resolution: {integrity: sha512-3T8LkOmg45BV5FICb15QQMsyUSWrQ8AygVfC7ZG32zOalnqrilm018ZVCw0eapXux8FtA33q8PSRSstjee3jSg==} engines: {node: '>=10.10.0'} + deprecated: Use @eslint/config-array instead '@humanwhocodes/module-importer@1.0.1': resolution: {integrity: sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==} @@ -2653,6 +2657,7 @@ packages: '@humanwhocodes/object-schema@2.0.3': resolution: {integrity: sha512-93zYdMES/c1D69yZiKDBj0V24vqNzB/koF26KPaagAfd3P/4gUlh3Dys5ogAK+Exi9QyzlD8x/08Zt7wIKcDcA==} + deprecated: Use @eslint/object-schema instead '@humanwhocodes/retry@0.3.0': resolution: {integrity: sha512-d2CGZR2o7fS6sWB7DG/3a95bGKQyHMACZ5aW8qGkkqQpUoZV6C0X7Pc7l4ZNMZkfNBf4VWNe9E1jRsf0G146Ew==} @@ -3010,64 +3015,6 @@ packages: resolution: {integrity: sha512-GXrJgakgJW3DWKueebkvtYgGKkxA7s0u5B0P5syJM5rvQUnrpLPigvci8Hukl7yEM+sU06l+er2Fgvx/gmiRgg==} engines: {node: '>=18'} - '@napi-rs/canvas-android-arm64@0.1.56': - resolution: {integrity: sha512-xBGqW2RZMAupkzar9t3gpbok9r524f3Wlk4PG2qnQdxbsiEND06OB8VxVtTcql6R02uJpXJGnyIhN02Te+GMVQ==} - engines: {node: '>= 10'} - cpu: [arm64] - os: [android] - - '@napi-rs/canvas-darwin-arm64@0.1.56': - resolution: {integrity: sha512-Pvuz6Ib9YZTB5MlGL9WSu9a2asUC0DZ1zBHozDiBXr/6Zurs9l/ZH5NxFYTM829BpkdkO8kuI8b8Rz7ek30zzQ==} - engines: {node: '>= 10'} - cpu: [arm64] - os: [darwin] - - '@napi-rs/canvas-darwin-x64@0.1.56': - resolution: {integrity: sha512-O393jWt7G6rg0X1ralbsbBeskSG0iwlkD7mEHhMLJxqRqe+eQn0/xnwhs9l6dUNFC+5dM8LOvfFca4o9Vs2Vww==} - engines: {node: '>= 10'} - cpu: [x64] - os: [darwin] - - '@napi-rs/canvas-linux-arm-gnueabihf@0.1.56': - resolution: {integrity: sha512-30NFb5lrF3YEwAO5XuATxpWDSXaBAgaFVswPJ+hYcAUyE3IkPPIFRY4ijQEh4frcSBvrzFGGYdNSoC18oLLWaQ==} - engines: {node: '>= 10'} - cpu: [arm] - os: [linux] - - '@napi-rs/canvas-linux-arm64-gnu@0.1.56': - resolution: {integrity: sha512-ODbWH9TLvba+39UxFwPn2Hm1ImALmWOZ0pEv5do/pz0439326Oz49hlfGot4KmkSBeKK81knWxRj9EXMSPwXPg==} - engines: {node: '>= 10'} - cpu: [arm64] - os: [linux] - - '@napi-rs/canvas-linux-arm64-musl@0.1.56': - resolution: {integrity: sha512-zqE4nz8CWiJJ0q5By7q9CDPicNkc0oyErgavK3ZV279zJL7Aapd3cIqayT6ynECArg7GgBl2WYSvr5AaRFmYgg==} - engines: {node: '>= 10'} - cpu: [arm64] - os: [linux] - - '@napi-rs/canvas-linux-x64-gnu@0.1.56': - resolution: {integrity: sha512-JTnGAtJBQMhfSpN8/rbMnf5oxuO/juUNa0n4LA0LlW0JS9UBpmsS2BwFNCakFqOeAPaqIM6sFFsK3M4hve+Esw==} - engines: {node: '>= 10'} - cpu: [x64] - os: [linux] - - '@napi-rs/canvas-linux-x64-musl@0.1.56': - resolution: {integrity: sha512-mpws7DhVDIj8ZKa/qcnUVLAm0fxD9RK5ojfNNSI9TOzn2E0f+GUXx8sGsCxDpMVMtN+mtyrMwRqH3F3rTUMWXw==} - engines: {node: '>= 10'} - cpu: [x64] - os: [linux] - - '@napi-rs/canvas-win32-x64-msvc@0.1.56': - resolution: {integrity: sha512-VKAAkgXF+lbFvRFawPOtkfV/P7ogAgWTu5FMCIiBn0Gc3vnkKFG2cLo/IHIJ7FuriToKEidkJGT88iAh7W7GDA==} - engines: {node: '>= 10'} - cpu: [x64] - os: [win32] - - '@napi-rs/canvas@0.1.56': - resolution: {integrity: sha512-SujSchzG6lLc/wT+Mwxam/w30Kk2sFTiU6bLFcidecKSmlhenAhGMQhZh2iGFfKoh2+8iit0jrt99n6TqReICQ==} - engines: {node: '>= 10'} - '@nestjs/common@10.4.7': resolution: {integrity: sha512-gIOpjD3Mx8gfYGxYm/RHPcJzqdknNNFCyY+AxzBT3gc5Xvvik1Dn5OxaMGw5EbVfhZgJKVP0n83giUOAlZQe7w==} peerDependencies: @@ -4448,6 +4395,7 @@ packages: '@types/form-data@2.5.0': resolution: {integrity: sha512-23/wYiuckYYtFpL+4RPWiWmRQH2BjFuqCUi2+N3amB1a1Drv+i/byTrGvlLwRVLFNAZbwpbQ7JvTK+VCAPMbcg==} + deprecated: This is a stub types definition. form-data provides its own type definitions, so you do not need this installed. '@types/glob@7.2.0': resolution: {integrity: sha512-ZUxbzKl0IfJILTS6t7ip5fQQM/J3TJYubDm3nMbgubNNYS62eXeUpoLUC8/7fJNiFYHTrGPQn7hspDUzIHX3UA==} @@ -5385,6 +5333,9 @@ packages: resolution: {integrity: sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==} engines: {node: '>=8'} + bl@4.1.0: + resolution: {integrity: sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==} + blob-util@2.0.2: resolution: {integrity: sha512-T7JQa+zsXXEa6/8ZhHcQEW1UFfVM49Ts65uBkFL6fz2QmrElqmbajIDJvuA0tEhRe5eIjpV9ZF+0RfZR9voJFQ==} @@ -5561,6 +5512,10 @@ packages: canvas-confetti@1.9.3: resolution: {integrity: sha512-rFfTURMvmVEX1gyXFgn5QMn81bYk70qa0HLzcIOSVEyl57n6o9ItHeBtUSWdvKAPY0xlvBHno4/v3QPrT83q9g==} + canvas@3.1.0: + resolution: {integrity: sha512-tTj3CqqukVJ9NgSahykNwtGda7V33VLObwrHfzT0vqJXu7J4d4C/7kQQW3fOEGDfZZoILPut5H00gOjyttPGyg==} + engines: {node: ^18.12.0 || >= 20.9.0} + caseless@0.12.0: resolution: {integrity: sha512-4tYFyifaFfGacoiObjJegolkwSU4xQNGbVgUiNYVUxbQ2x2lUsFvY4hVgVzGiIe6WLOPqycWXA40l+PWsxthUw==} @@ -5666,6 +5621,9 @@ packages: resolution: {integrity: sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==} engines: {node: '>= 8.10.0'} + chownr@1.1.4: + resolution: {integrity: sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==} + chownr@2.0.0: resolution: {integrity: sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==} engines: {node: '>=10'} @@ -6111,6 +6069,10 @@ packages: deep-equal@2.2.0: resolution: {integrity: sha512-RdpzE0Hv4lhowpIUKKMJfeH6C1pXdtT1/it80ubgWqwI3qpuxUBpC1S4hnHg+zjnuOoDkzUtUCEEkG+XG5l3Mw==} + deep-extend@0.6.0: + resolution: {integrity: sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==} + engines: {node: '>=4.0.0'} + deep-is@0.1.4: resolution: {integrity: sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==} @@ -6500,6 +6462,7 @@ packages: eslint@8.57.0: resolution: {integrity: sha512-dZ6+mexnaTIbSBZWgou51U6OmzIhYM2VcNdtiTtI7qPNZm35Akpr0f6vtw3w1Kmn5PYo+tZVfh13WrhpS6oLqQ==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + deprecated: This version is no longer supported. Please see https://eslint.org/version-support for other options. hasBin: true eslint@9.14.0: @@ -6603,6 +6566,10 @@ packages: resolution: {integrity: sha512-Zk/eNKV2zbjpKzrsQ+n1G6poVbErQxJ0LBOJXaKZ1EViLzH+hrLu9cdXI4zw9dBQJslwBEpbQ2P1oS7nDxs6jQ==} engines: {node: '>= 0.8.0'} + expand-template@2.0.3: + resolution: {integrity: sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg==} + engines: {node: '>=6'} + expect@29.7.0: resolution: {integrity: sha512-2Zks0hf1VLFYI1kbh0I5jP3KHHyCHpkfyHBzsSXRFgl/Bg9mWYfMW8oD+PdMPlEwy5HNsR9JutYy6pMeOh61nw==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} @@ -6864,6 +6831,9 @@ packages: from@0.1.7: resolution: {integrity: sha512-twe20eF1OxVxp/ML/kq2p1uc6KvFK/+vs8WjEbeKmV2He22MKm7YF2ANIt+EOqhJ5L3K/SuuPhk0hWQDjOM23g==} + fs-constants@1.0.0: + resolution: {integrity: sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==} + fs-extra@7.0.1: resolution: {integrity: sha512-YJDaCJZEnBmcbw13fvdAM9AwNOJwOzrE4pqMqBq5nFiEqXUqHwlK4B+3pUw6JNvfSPtX05xFHtYy/1ni01eGCw==} engines: {node: '>=6 <7 || >=8'} @@ -6965,6 +6935,9 @@ packages: getpass@0.1.7: resolution: {integrity: sha512-0fzj9JxOLfJ+XGLhR8ze3unN0KZCgZwiSSDz168VERjK8Wl8kVSdcu2kspd4s4wtAa1y/qrVRiAA0WclVsu0ng==} + github-from-package@0.0.0: + resolution: {integrity: sha512-SyHy3T1v2NUXn29OsWdxmK6RwHD+vkj3v8en8AOBZ1wBQ/hCAQ5bAQTD02kW4W9tUp/3Qh6J8r9EvntiyCmOOw==} + glob-parent@5.1.2: resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==} engines: {node: '>= 6'} @@ -6991,10 +6964,12 @@ packages: glob@7.2.3: resolution: {integrity: sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==} + deprecated: Glob versions prior to v9 are no longer supported glob@8.1.0: resolution: {integrity: sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ==} engines: {node: '>=12'} + deprecated: Glob versions prior to v9 are no longer supported global-dirs@3.0.1: resolution: {integrity: sha512-NBcGGFbBA9s1VzD41QXDG+3++t9Mn5t1FpLdhESY6oKY4gYTFpX4wO3sqGUa0Srjtbfj3szX0RnemmrVRUdULA==} @@ -7289,6 +7264,7 @@ packages: inflight@1.0.6: resolution: {integrity: sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==} + deprecated: This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful. inherits@2.0.4: resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==} @@ -7976,6 +7952,7 @@ packages: lodash.get@4.4.2: resolution: {integrity: sha512-z+Uw/vLuy6gQe8cfaFWD7p0wVv8fJl3mbzXh33RS+0oW2wvUqiRXiQ69gLWSLpgB5/6sU+r6BlQR0MBILadqTQ==} + deprecated: This package is deprecated. Use the optional chaining (?.) operator instead. lodash.isarguments@3.1.0: resolution: {integrity: sha512-chi4NHZlZqZD18a0imDHnZPrDeBbTtVN7GXMwuGdRH9qotxAjYs3aVLKc7zNOG9eddR5Ksd8rvFEBc9SsggPpg==} @@ -8403,6 +8380,9 @@ packages: resolution: {integrity: sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==} engines: {node: '>= 8'} + mkdirp-classic@0.5.3: + resolution: {integrity: sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==} + mkdirp@0.5.6: resolution: {integrity: sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==} hasBin: true @@ -8499,6 +8479,9 @@ packages: engines: {node: ^18 || >=20} hasBin: true + napi-build-utils@2.0.0: + resolution: {integrity: sha512-GEbrYkbfF7MoNaoh2iGG84Mnf/WZfB0GdGEsM8wz7Expx/LlWf5U8t9nvJKXSp3qr5IsEbK04cBGhol/KwOsWA==} + natural-compare@1.4.0: resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==} @@ -9274,6 +9257,11 @@ packages: postgres-range@1.1.3: resolution: {integrity: sha512-VdlZoocy5lCP0c/t66xAfclglEapXPCIVhqqJRncYpvbCgImF0w67aPKfbqUMr72tO2k5q0TdTZwCLjPTI6C9g==} + prebuild-install@7.1.3: + resolution: {integrity: sha512-8Mf2cbV7x1cXPUILADGI3wuhfqWvtiLA1iclTDbFRZkgRQS0NqsPZphna9V+HyTEadheuPmjaJMsbzKQFOzLug==} + engines: {node: '>=10'} + hasBin: true + prelude-ls@1.2.1: resolution: {integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==} engines: {node: '>= 0.8.0'} @@ -9480,6 +9468,10 @@ packages: resolution: {integrity: sha512-RmkhL8CAyCRPXCE28MMH0z2PNWQBNk2Q09ZdxM9IOOXwxwZbN+qbWaatPkdkWIKL2ZVDImrN/pK5HTRz2PcS4g==} engines: {node: '>= 0.8'} + rc@1.2.8: + resolution: {integrity: sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==} + hasBin: true + rdf-canonize@3.4.0: resolution: {integrity: sha512-fUeWjrkOO0t1rg7B2fdyDTvngj+9RlUyL92vOdiB7c0FPguWVsniIMjEtHH+meLBO9rzkUlUzBVXgWrjI8P9LA==} engines: {node: '>=12'} @@ -9675,6 +9667,7 @@ packages: rimraf@3.0.2: resolution: {integrity: sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==} + deprecated: Rimraf versions prior to v4 are no longer supported hasBin: true rollup@4.26.0: @@ -9869,6 +9862,12 @@ packages: resolution: {integrity: sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==} engines: {node: '>=14'} + simple-concat@1.0.1: + resolution: {integrity: sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q==} + + simple-get@4.0.1: + resolution: {integrity: sha512-brv7p5WgH0jmQJr1ZDDfKDOSeWWg+OVypG99A/5vYGPqJ6pxiaHLy8nxtFjBA7oMa01ebA9gfh1uMCFqOuXxvA==} + simple-oauth2@5.1.0: resolution: {integrity: sha512-gWDa38Ccm4MwlG5U7AlcJxPv3lvr80dU7ARJWrGdgvOKyzSj1gr3GBPN1rABTedAYvC/LsGYoFuFxwDBPtGEbw==} @@ -10232,6 +10231,10 @@ packages: resolution: {integrity: sha512-mnVSV2l+Zv6BLpSD/8V87CW/y9EmmbYzGCIavsnsI6/nwn26DwffM/yztm30Z/I2DY9wdS3vXVCMnHDgZaVNoA==} engines: {node: '>=12'} + strip-json-comments@2.0.1: + resolution: {integrity: sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==} + engines: {node: '>=0.10.0'} + strip-json-comments@3.1.1: resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==} engines: {node: '>=8'} @@ -10298,6 +10301,13 @@ packages: os: [darwin, linux, win32, freebsd, openbsd, netbsd, sunos, android] hasBin: true + tar-fs@2.1.2: + resolution: {integrity: sha512-EsaAXwxmx8UB7FRKqeozqEPop69DXcmYwTQwXvyAPF352HJsPdkVhvTaDPYqfNgruveJIJy3TA2l+2zj8LJIJA==} + + tar-stream@2.2.0: + resolution: {integrity: sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==} + engines: {node: '>=6'} + tar-stream@3.1.6: resolution: {integrity: sha512-B/UyjYwPpMBv+PaFSWAmtYjwdrlEaZQEhMIBFNC5oEG8lpiW8XjcSdmEaClj28ArfKScKHs2nshz3k2le6crsg==} @@ -11976,26 +11986,56 @@ snapshots: '@babel/core': 7.23.5 '@babel/helper-plugin-utils': 7.22.5 + '@babel/plugin-syntax-async-generators@7.8.4(@babel/core@7.24.7)': + dependencies: + '@babel/core': 7.24.7 + '@babel/helper-plugin-utils': 7.22.5 + optional: true + '@babel/plugin-syntax-bigint@7.8.3(@babel/core@7.23.5)': dependencies: '@babel/core': 7.23.5 '@babel/helper-plugin-utils': 7.22.5 + '@babel/plugin-syntax-bigint@7.8.3(@babel/core@7.24.7)': + dependencies: + '@babel/core': 7.24.7 + '@babel/helper-plugin-utils': 7.22.5 + optional: true + '@babel/plugin-syntax-class-properties@7.12.13(@babel/core@7.23.5)': dependencies: '@babel/core': 7.23.5 '@babel/helper-plugin-utils': 7.22.5 + '@babel/plugin-syntax-class-properties@7.12.13(@babel/core@7.24.7)': + dependencies: + '@babel/core': 7.24.7 + '@babel/helper-plugin-utils': 7.22.5 + optional: true + '@babel/plugin-syntax-import-meta@7.10.4(@babel/core@7.23.5)': dependencies: '@babel/core': 7.23.5 '@babel/helper-plugin-utils': 7.22.5 + '@babel/plugin-syntax-import-meta@7.10.4(@babel/core@7.24.7)': + dependencies: + '@babel/core': 7.24.7 + '@babel/helper-plugin-utils': 7.22.5 + optional: true + '@babel/plugin-syntax-json-strings@7.8.3(@babel/core@7.23.5)': dependencies: '@babel/core': 7.23.5 '@babel/helper-plugin-utils': 7.22.5 + '@babel/plugin-syntax-json-strings@7.8.3(@babel/core@7.24.7)': + dependencies: + '@babel/core': 7.24.7 + '@babel/helper-plugin-utils': 7.22.5 + optional: true + '@babel/plugin-syntax-jsx@7.23.3(@babel/core@7.23.5)': dependencies: '@babel/core': 7.23.5 @@ -12006,36 +12046,78 @@ snapshots: '@babel/core': 7.23.5 '@babel/helper-plugin-utils': 7.22.5 + '@babel/plugin-syntax-logical-assignment-operators@7.10.4(@babel/core@7.24.7)': + dependencies: + '@babel/core': 7.24.7 + '@babel/helper-plugin-utils': 7.22.5 + optional: true + '@babel/plugin-syntax-nullish-coalescing-operator@7.8.3(@babel/core@7.23.5)': dependencies: '@babel/core': 7.23.5 '@babel/helper-plugin-utils': 7.22.5 + '@babel/plugin-syntax-nullish-coalescing-operator@7.8.3(@babel/core@7.24.7)': + dependencies: + '@babel/core': 7.24.7 + '@babel/helper-plugin-utils': 7.22.5 + optional: true + '@babel/plugin-syntax-numeric-separator@7.10.4(@babel/core@7.23.5)': dependencies: '@babel/core': 7.23.5 '@babel/helper-plugin-utils': 7.22.5 + '@babel/plugin-syntax-numeric-separator@7.10.4(@babel/core@7.24.7)': + dependencies: + '@babel/core': 7.24.7 + '@babel/helper-plugin-utils': 7.22.5 + optional: true + '@babel/plugin-syntax-object-rest-spread@7.8.3(@babel/core@7.23.5)': dependencies: '@babel/core': 7.23.5 '@babel/helper-plugin-utils': 7.22.5 + '@babel/plugin-syntax-object-rest-spread@7.8.3(@babel/core@7.24.7)': + dependencies: + '@babel/core': 7.24.7 + '@babel/helper-plugin-utils': 7.22.5 + optional: true + '@babel/plugin-syntax-optional-catch-binding@7.8.3(@babel/core@7.23.5)': dependencies: '@babel/core': 7.23.5 '@babel/helper-plugin-utils': 7.22.5 + '@babel/plugin-syntax-optional-catch-binding@7.8.3(@babel/core@7.24.7)': + dependencies: + '@babel/core': 7.24.7 + '@babel/helper-plugin-utils': 7.22.5 + optional: true + '@babel/plugin-syntax-optional-chaining@7.8.3(@babel/core@7.23.5)': dependencies: '@babel/core': 7.23.5 '@babel/helper-plugin-utils': 7.22.5 + '@babel/plugin-syntax-optional-chaining@7.8.3(@babel/core@7.24.7)': + dependencies: + '@babel/core': 7.24.7 + '@babel/helper-plugin-utils': 7.22.5 + optional: true + '@babel/plugin-syntax-top-level-await@7.14.5(@babel/core@7.23.5)': dependencies: '@babel/core': 7.23.5 '@babel/helper-plugin-utils': 7.22.5 + '@babel/plugin-syntax-top-level-await@7.14.5(@babel/core@7.24.7)': + dependencies: + '@babel/core': 7.24.7 + '@babel/helper-plugin-utils': 7.22.5 + optional: true + '@babel/plugin-syntax-typescript@7.23.3(@babel/core@7.23.5)': dependencies: '@babel/core': 7.23.5 @@ -12496,7 +12578,7 @@ snapshots: '@eslint/eslintrc@2.1.4': dependencies: ajv: 6.12.6 - debug: 4.3.5(supports-color@5.5.0) + debug: 4.3.7(supports-color@8.1.1) espree: 9.6.1 globals: 13.24.0 ignore: 5.3.1 @@ -12665,7 +12747,7 @@ snapshots: '@humanwhocodes/config-array@0.11.14': dependencies: '@humanwhocodes/object-schema': 2.0.3 - debug: 4.3.5(supports-color@5.5.0) + debug: 4.3.7(supports-color@8.1.1) minimatch: 3.1.2 transitivePeerDependencies: - supports-color @@ -13132,45 +13214,6 @@ snapshots: outvariant: 1.4.3 strict-event-emitter: 0.5.1 - '@napi-rs/canvas-android-arm64@0.1.56': - optional: true - - '@napi-rs/canvas-darwin-arm64@0.1.56': - optional: true - - '@napi-rs/canvas-darwin-x64@0.1.56': - optional: true - - '@napi-rs/canvas-linux-arm-gnueabihf@0.1.56': - optional: true - - '@napi-rs/canvas-linux-arm64-gnu@0.1.56': - optional: true - - '@napi-rs/canvas-linux-arm64-musl@0.1.56': - optional: true - - '@napi-rs/canvas-linux-x64-gnu@0.1.56': - optional: true - - '@napi-rs/canvas-linux-x64-musl@0.1.56': - optional: true - - '@napi-rs/canvas-win32-x64-msvc@0.1.56': - optional: true - - '@napi-rs/canvas@0.1.56': - optionalDependencies: - '@napi-rs/canvas-android-arm64': 0.1.56 - '@napi-rs/canvas-darwin-arm64': 0.1.56 - '@napi-rs/canvas-darwin-x64': 0.1.56 - '@napi-rs/canvas-linux-arm-gnueabihf': 0.1.56 - '@napi-rs/canvas-linux-arm64-gnu': 0.1.56 - '@napi-rs/canvas-linux-arm64-musl': 0.1.56 - '@napi-rs/canvas-linux-x64-gnu': 0.1.56 - '@napi-rs/canvas-linux-x64-musl': 0.1.56 - '@napi-rs/canvas-win32-x64-msvc': 0.1.56 - '@nestjs/common@10.4.7(reflect-metadata@0.2.2)(rxjs@7.8.1)': dependencies: iterare: 1.2.1 @@ -15152,7 +15195,7 @@ snapshots: '@typescript-eslint/type-utils': 6.21.0(eslint@8.57.0)(typescript@5.1.6) '@typescript-eslint/utils': 6.21.0(eslint@8.57.0)(typescript@5.1.6) '@typescript-eslint/visitor-keys': 6.21.0 - debug: 4.3.5(supports-color@5.5.0) + debug: 4.3.5 eslint: 8.57.0 graphemer: 1.4.0 ignore: 5.3.1 @@ -15208,7 +15251,7 @@ snapshots: '@typescript-eslint/types': 6.21.0 '@typescript-eslint/typescript-estree': 6.21.0(typescript@5.1.6) '@typescript-eslint/visitor-keys': 6.21.0 - debug: 4.3.5(supports-color@5.5.0) + debug: 4.3.5 eslint: 8.57.0 optionalDependencies: typescript: 5.1.6 @@ -15234,7 +15277,7 @@ snapshots: '@typescript-eslint/types': 7.17.0 '@typescript-eslint/typescript-estree': 7.17.0(typescript@5.6.3) '@typescript-eslint/visitor-keys': 7.17.0 - debug: 4.3.5(supports-color@5.5.0) + debug: 4.3.5 eslint: 9.14.0 optionalDependencies: typescript: 5.6.3 @@ -15260,7 +15303,7 @@ snapshots: dependencies: '@typescript-eslint/typescript-estree': 6.21.0(typescript@5.1.6) '@typescript-eslint/utils': 6.21.0(eslint@8.57.0)(typescript@5.1.6) - debug: 4.3.5(supports-color@5.5.0) + debug: 4.3.7(supports-color@8.1.1) eslint: 8.57.0 ts-api-utils: 1.3.0(typescript@5.1.6) optionalDependencies: @@ -15272,7 +15315,7 @@ snapshots: dependencies: '@typescript-eslint/typescript-estree': 7.1.0(typescript@5.6.3) '@typescript-eslint/utils': 7.1.0(eslint@9.14.0)(typescript@5.6.3) - debug: 4.3.4 + debug: 4.3.7(supports-color@8.1.1) eslint: 9.14.0 ts-api-utils: 1.3.0(typescript@5.6.3) optionalDependencies: @@ -15284,7 +15327,7 @@ snapshots: dependencies: '@typescript-eslint/typescript-estree': 7.17.0(typescript@5.6.3) '@typescript-eslint/utils': 7.17.0(eslint@9.14.0)(typescript@5.6.3) - debug: 4.3.5(supports-color@5.5.0) + debug: 4.3.7(supports-color@8.1.1) eslint: 9.14.0 ts-api-utils: 1.3.0(typescript@5.6.3) optionalDependencies: @@ -15302,11 +15345,11 @@ snapshots: dependencies: '@typescript-eslint/types': 6.21.0 '@typescript-eslint/visitor-keys': 6.21.0 - debug: 4.3.5(supports-color@5.5.0) + debug: 4.3.7(supports-color@8.1.1) globby: 11.1.0 is-glob: 4.0.3 minimatch: 9.0.3 - semver: 7.6.0 + semver: 7.6.3 ts-api-utils: 1.3.0(typescript@5.1.6) optionalDependencies: typescript: 5.1.6 @@ -15317,11 +15360,11 @@ snapshots: dependencies: '@typescript-eslint/types': 7.1.0 '@typescript-eslint/visitor-keys': 7.1.0 - debug: 4.3.4 + debug: 4.3.7(supports-color@8.1.1) globby: 11.1.0 is-glob: 4.0.3 minimatch: 9.0.3 - semver: 7.6.0 + semver: 7.6.3 ts-api-utils: 1.3.0(typescript@5.6.3) optionalDependencies: typescript: 5.6.3 @@ -15332,11 +15375,11 @@ snapshots: dependencies: '@typescript-eslint/types': 7.17.0 '@typescript-eslint/visitor-keys': 7.17.0 - debug: 4.3.5(supports-color@5.5.0) + debug: 4.3.7(supports-color@8.1.1) globby: 11.1.0 is-glob: 4.0.3 minimatch: 9.0.4 - semver: 7.6.0 + semver: 7.6.3 ts-api-utils: 1.3.0(typescript@5.6.3) optionalDependencies: typescript: 5.6.3 @@ -15352,7 +15395,7 @@ snapshots: '@typescript-eslint/types': 6.21.0 '@typescript-eslint/typescript-estree': 6.21.0(typescript@5.1.6) eslint: 8.57.0 - semver: 7.6.0 + semver: 7.6.3 transitivePeerDependencies: - supports-color - typescript @@ -15366,7 +15409,7 @@ snapshots: '@typescript-eslint/types': 7.1.0 '@typescript-eslint/typescript-estree': 7.1.0(typescript@5.6.3) eslint: 9.14.0 - semver: 7.6.0 + semver: 7.6.3 transitivePeerDependencies: - supports-color - typescript @@ -15413,7 +15456,7 @@ snapshots: dependencies: '@ampproject/remapping': 2.2.1 '@bcoe/v8-coverage': 0.2.3 - debug: 4.3.5(supports-color@5.5.0) + debug: 4.3.5 istanbul-lib-coverage: 3.2.2 istanbul-lib-report: 3.0.1 istanbul-lib-source-maps: 5.0.4 @@ -15432,7 +15475,7 @@ snapshots: dependencies: '@ampproject/remapping': 2.2.1 '@bcoe/v8-coverage': 0.2.3 - debug: 4.3.5(supports-color@5.5.0) + debug: 4.3.5 istanbul-lib-coverage: 3.2.2 istanbul-lib-report: 3.0.1 istanbul-lib-source-maps: 5.0.4 @@ -15671,7 +15714,7 @@ snapshots: agent-base@7.1.0: dependencies: - debug: 4.3.5(supports-color@5.5.0) + debug: 4.3.7(supports-color@8.1.1) transitivePeerDependencies: - supports-color @@ -15994,6 +16037,20 @@ snapshots: transitivePeerDependencies: - supports-color + babel-jest@29.7.0(@babel/core@7.24.7): + dependencies: + '@babel/core': 7.24.7 + '@jest/transform': 29.7.0 + '@types/babel__core': 7.20.0 + babel-plugin-istanbul: 6.1.1 + babel-preset-jest: 29.6.3(@babel/core@7.24.7) + chalk: 4.1.2 + graceful-fs: 4.2.11 + slash: 3.0.0 + transitivePeerDependencies: + - supports-color + optional: true + babel-plugin-istanbul@6.1.1: dependencies: '@babel/helper-plugin-utils': 7.22.5 @@ -16027,12 +16084,36 @@ snapshots: '@babel/plugin-syntax-optional-chaining': 7.8.3(@babel/core@7.23.5) '@babel/plugin-syntax-top-level-await': 7.14.5(@babel/core@7.23.5) + babel-preset-current-node-syntax@1.0.1(@babel/core@7.24.7): + dependencies: + '@babel/core': 7.24.7 + '@babel/plugin-syntax-async-generators': 7.8.4(@babel/core@7.24.7) + '@babel/plugin-syntax-bigint': 7.8.3(@babel/core@7.24.7) + '@babel/plugin-syntax-class-properties': 7.12.13(@babel/core@7.24.7) + '@babel/plugin-syntax-import-meta': 7.10.4(@babel/core@7.24.7) + '@babel/plugin-syntax-json-strings': 7.8.3(@babel/core@7.24.7) + '@babel/plugin-syntax-logical-assignment-operators': 7.10.4(@babel/core@7.24.7) + '@babel/plugin-syntax-nullish-coalescing-operator': 7.8.3(@babel/core@7.24.7) + '@babel/plugin-syntax-numeric-separator': 7.10.4(@babel/core@7.24.7) + '@babel/plugin-syntax-object-rest-spread': 7.8.3(@babel/core@7.24.7) + '@babel/plugin-syntax-optional-catch-binding': 7.8.3(@babel/core@7.24.7) + '@babel/plugin-syntax-optional-chaining': 7.8.3(@babel/core@7.24.7) + '@babel/plugin-syntax-top-level-await': 7.14.5(@babel/core@7.24.7) + optional: true + babel-preset-jest@29.6.3(@babel/core@7.23.5): dependencies: '@babel/core': 7.23.5 babel-plugin-jest-hoist: 29.6.3 babel-preset-current-node-syntax: 1.0.1(@babel/core@7.23.5) + babel-preset-jest@29.6.3(@babel/core@7.24.7): + dependencies: + '@babel/core': 7.24.7 + babel-plugin-jest-hoist: 29.6.3 + babel-preset-current-node-syntax: 1.0.1(@babel/core@7.24.7) + optional: true + babel-walk@3.0.0-canary-5: dependencies: '@babel/types': 7.24.7 @@ -16071,6 +16152,12 @@ snapshots: binary-extensions@2.2.0: {} + bl@4.1.0: + dependencies: + buffer: 5.7.1 + inherits: 2.0.4 + readable-stream: 3.6.0 + blob-util@2.0.2: optional: true @@ -16167,7 +16254,6 @@ snapshots: dependencies: base64-js: 1.5.1 ieee754: 1.2.1 - optional: true buffer@6.0.3: dependencies: @@ -16300,6 +16386,11 @@ snapshots: canvas-confetti@1.9.3: {} + canvas@3.1.0: + dependencies: + node-addon-api: 7.1.0 + prebuild-install: 7.1.3 + caseless@0.12.0: optional: true @@ -16436,6 +16527,8 @@ snapshots: optionalDependencies: fsevents: 2.3.3 + chownr@1.1.4: {} + chownr@2.0.0: {} chromatic@11.18.1: {} @@ -16869,9 +16962,13 @@ snapshots: dependencies: ms: 2.1.2 - debug@4.3.5(supports-color@5.5.0): + debug@4.3.5: dependencies: ms: 2.1.2 + + debug@4.3.7(supports-color@5.5.0): + dependencies: + ms: 2.1.3 optionalDependencies: supports-color: 5.5.0 @@ -16946,6 +17043,8 @@ snapshots: which-collection: 1.0.1 which-typed-array: 1.1.11 + deep-extend@0.6.0: {} + deep-is@0.1.4: {} deepmerge@4.2.2: {} @@ -17511,7 +17610,7 @@ snapshots: ajv: 6.12.6 chalk: 4.1.2 cross-spawn: 7.0.3 - debug: 4.3.5(supports-color@5.5.0) + debug: 4.3.5 doctrine: 3.0.0 escape-string-regexp: 4.0.0 eslint-scope: 7.2.2 @@ -17705,6 +17804,8 @@ snapshots: exit@0.1.2: {} + expand-template@2.0.3: {} + expect@29.7.0: dependencies: '@jest/expect-utils': 29.7.0 @@ -17874,7 +17975,7 @@ snapshots: fastify-plugin@2.3.4: dependencies: - semver: 7.6.0 + semver: 7.6.3 fastify-plugin@4.5.1: {} @@ -18077,6 +18178,8 @@ snapshots: from@0.1.7: {} + fs-constants@1.0.0: {} + fs-extra@7.0.1: dependencies: graceful-fs: 4.2.11 @@ -18190,6 +18293,8 @@ snapshots: dependencies: assert-plus: 1.0.0 + github-from-package@0.0.0: {} + glob-parent@5.1.2: dependencies: is-glob: 4.0.3 @@ -18207,7 +18312,7 @@ snapshots: dependencies: foreground-child: 3.1.1 jackspeak: 2.3.6 - minimatch: 9.0.3 + minimatch: 9.0.4 minipass: 7.0.4 path-scurry: 1.10.1 @@ -18462,7 +18567,7 @@ snapshots: http-proxy-agent@7.0.2: dependencies: agent-base: 7.1.0 - debug: 4.3.5(supports-color@5.5.0) + debug: 4.3.7(supports-color@8.1.1) transitivePeerDependencies: - supports-color @@ -18495,7 +18600,7 @@ snapshots: https-proxy-agent@7.0.5: dependencies: agent-base: 7.1.0 - debug: 4.3.5(supports-color@5.5.0) + debug: 4.3.7(supports-color@8.1.1) transitivePeerDependencies: - supports-color @@ -18849,7 +18954,7 @@ snapshots: istanbul-lib-source-maps@5.0.4: dependencies: '@jridgewell/trace-mapping': 0.3.25 - debug: 4.3.5(supports-color@5.5.0) + debug: 4.3.7(supports-color@8.1.1) istanbul-lib-coverage: 3.2.2 transitivePeerDependencies: - supports-color @@ -19143,7 +19248,7 @@ snapshots: jest-util: 29.7.0 natural-compare: 1.4.0 pretty-format: 29.7.0 - semver: 7.5.4 + semver: 7.6.3 transitivePeerDependencies: - supports-color @@ -19277,7 +19382,7 @@ snapshots: - utf-8-validate optional: true - jsdom@24.1.1(bufferutil@4.0.7)(utf-8-validate@6.0.3): + jsdom@24.1.1(bufferutil@4.0.7)(canvas@3.1.0)(utf-8-validate@6.0.3): dependencies: cssstyle: 4.0.1 data-urls: 5.0.0 @@ -19300,6 +19405,8 @@ snapshots: whatwg-url: 14.0.0 ws: 8.18.0(bufferutil@4.0.7)(utf-8-validate@6.0.3) xml-name-validator: 5.0.0 + optionalDependencies: + canvas: 3.1.0 transitivePeerDependencies: - bufferutil - supports-color @@ -19603,7 +19710,7 @@ snapshots: make-dir@4.0.0: dependencies: - semver: 7.6.0 + semver: 7.6.3 make-error@1.3.6: {} @@ -20094,6 +20201,8 @@ snapshots: minipass: 3.3.6 yallist: 4.0.0 + mkdirp-classic@0.5.3: {} + mkdirp@0.5.6: dependencies: minimist: 1.2.8 @@ -20201,6 +20310,8 @@ snapshots: nanoid@5.0.8: {} + napi-build-utils@2.0.0: {} + natural-compare@1.4.0: {} ncp@2.0.0: {} @@ -20275,7 +20386,7 @@ snapshots: make-fetch-happen: 13.0.0 nopt: 7.2.0 proc-log: 4.2.0 - semver: 7.6.0 + semver: 7.6.3 tar: 6.2.1 which: 4.0.0 transitivePeerDependencies: @@ -20290,11 +20401,11 @@ snapshots: nodemon@3.1.7: dependencies: chokidar: 3.5.3 - debug: 4.3.5(supports-color@5.5.0) + debug: 4.3.7(supports-color@5.5.0) ignore-by-default: 1.0.1 minimatch: 3.1.2 pstree.remy: 1.1.8 - semver: 7.6.0 + semver: 7.6.3 simple-update-notifier: 2.0.0 supports-color: 5.5.0 touch: 3.1.0 @@ -20325,7 +20436,7 @@ snapshots: dependencies: hosted-git-info: 4.1.0 is-core-module: 2.13.1 - semver: 7.6.0 + semver: 7.6.3 validate-npm-package-license: 3.0.4 normalize-path@3.0.0: {} @@ -20943,6 +21054,21 @@ snapshots: postgres-range@1.1.3: {} + prebuild-install@7.1.3: + dependencies: + detect-libc: 2.0.3 + expand-template: 2.0.3 + github-from-package: 0.0.0 + minimist: 1.2.8 + mkdirp-classic: 0.5.3 + napi-build-utils: 2.0.0 + node-abi: 3.62.0 + pump: 3.0.0 + rc: 1.2.8 + simple-get: 4.0.1 + tar-fs: 2.1.2 + tunnel-agent: 0.6.0 + prelude-ls@1.2.1: {} prettier@3.3.3: {} @@ -21167,6 +21293,13 @@ snapshots: iconv-lite: 0.6.3 unpipe: 1.0.0 + rc@1.2.8: + dependencies: + deep-extend: 0.6.0 + ini: 1.3.8 + minimist: 1.2.8 + strip-json-comments: 2.0.1 + rdf-canonize@3.4.0: dependencies: setimmediate: 1.0.5 @@ -21674,11 +21807,19 @@ snapshots: signal-exit@4.1.0: {} + simple-concat@1.0.1: {} + + simple-get@4.0.1: + dependencies: + decompress-response: 6.0.0 + once: 1.4.0 + simple-concat: 1.0.1 + simple-oauth2@5.1.0: dependencies: '@hapi/hoek': 11.0.4 '@hapi/wreck': 18.0.1 - debug: 4.3.5(supports-color@5.5.0) + debug: 4.3.5 joi: 17.11.0 transitivePeerDependencies: - supports-color @@ -21689,7 +21830,7 @@ snapshots: simple-update-notifier@2.0.0: dependencies: - semver: 7.5.4 + semver: 7.6.3 sinon@16.1.3: dependencies: @@ -22054,6 +22195,8 @@ snapshots: dependencies: min-indent: 1.0.1 + strip-json-comments@2.0.1: {} + strip-json-comments@3.1.1: {} strip-literal@2.1.0: @@ -22115,6 +22258,21 @@ snapshots: systeminformation@5.23.5: {} + tar-fs@2.1.2: + dependencies: + chownr: 1.1.4 + mkdirp-classic: 0.5.3 + pump: 3.0.0 + tar-stream: 2.2.0 + + tar-stream@2.2.0: + dependencies: + bl: 4.1.0 + end-of-stream: 1.4.4 + fs-constants: 1.0.0 + inherits: 2.0.4 + readable-stream: 3.6.0 + tar-stream@3.1.6: dependencies: b4a: 1.6.4 @@ -22273,7 +22431,7 @@ snapshots: ts-dedent@2.2.0: {} - ts-jest@29.1.2(@babel/core@7.23.5)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.23.5))(esbuild@0.24.0)(jest@29.7.0(@types/node@22.9.0))(typescript@5.1.6): + ts-jest@29.1.2(@babel/core@7.24.7)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.24.7))(esbuild@0.24.0)(jest@29.7.0(@types/node@22.9.0))(typescript@5.1.6): dependencies: bs-logger: 0.2.6 fast-json-stable-stringify: 2.1.0 @@ -22286,9 +22444,9 @@ snapshots: typescript: 5.1.6 yargs-parser: 21.1.1 optionalDependencies: - '@babel/core': 7.23.5 + '@babel/core': 7.24.7 '@jest/types': 29.6.3 - babel-jest: 29.7.0(@babel/core@7.23.5) + babel-jest: 29.7.0(@babel/core@7.24.7) esbuild: 0.24.0 ts-map@1.0.3: {} @@ -22341,7 +22499,6 @@ snapshots: tunnel-agent@0.6.0: dependencies: safe-buffer: 5.2.1 - optional: true tweetnacl@0.14.5: {} From ef90b2e5a7d0912794e335c518d7ea495dd8affb Mon Sep 17 00:00:00 2001 From: Marie Date: Fri, 7 Feb 2025 15:09:53 +0100 Subject: [PATCH 07/13] chore: add quotes to RoleService --- packages/backend/src/core/UserListService.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/backend/src/core/UserListService.ts b/packages/backend/src/core/UserListService.ts index 11085fff8d..4f4d59a02c 100644 --- a/packages/backend/src/core/UserListService.ts +++ b/packages/backend/src/core/UserListService.ts @@ -58,7 +58,7 @@ export class UserListService implements OnApplicationShutdown, OnModuleInit { } async onModuleInit() { - this.roleService = this.moduleRef.get(RoleService); + this.roleService = this.moduleRef.get('RoleService'); } @bindThis From 87eac89f947c84ebff3053cca2ea08734713b21a Mon Sep 17 00:00:00 2001 From: Marie Date: Fri, 7 Feb 2025 21:33:47 +0000 Subject: [PATCH 08/13] fix(docker): Alpine build failing due to missing dev packages (pkgconf and etc) --- Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Dockerfile b/Dockerfile index aff4074079..5ab9cc343f 100644 --- a/Dockerfile +++ b/Dockerfile @@ -9,7 +9,7 @@ RUN apk add git linux-headers build-base ENV PYTHONUNBUFFERED=1 ENV COREPACK_DEFAULT_TO_LATEST=0 RUN apk add --update python3 && ln -sf python3 /usr/bin/python -RUN apk add py3-pip py3-setuptools +RUN apk add py3-pip py3-setuptools alpine-sdk RUN corepack enable From da04ff0b9acb12611fe67dbad3c4b30ecbf63828 Mon Sep 17 00:00:00 2001 From: Marie Date: Fri, 7 Feb 2025 21:38:04 +0000 Subject: [PATCH 09/13] chore: replace build-base with alpine-sdk --- Dockerfile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Dockerfile b/Dockerfile index 5ab9cc343f..766309d62b 100644 --- a/Dockerfile +++ b/Dockerfile @@ -4,12 +4,12 @@ ARG NODE_VERSION=22.11.0-alpine3.20 FROM node:${NODE_VERSION} as build -RUN apk add git linux-headers build-base +RUN apk add git linux-headers alpine-sdk ENV PYTHONUNBUFFERED=1 ENV COREPACK_DEFAULT_TO_LATEST=0 RUN apk add --update python3 && ln -sf python3 /usr/bin/python -RUN apk add py3-pip py3-setuptools alpine-sdk +RUN apk add py3-pip py3-setuptools RUN corepack enable From b60e2b363cc1e10cd6c0fde519f168eb4d461ceb Mon Sep 17 00:00:00 2001 From: Marie Date: Fri, 7 Feb 2025 22:50:19 +0000 Subject: [PATCH 10/13] docker: add more packages --- Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Dockerfile b/Dockerfile index 766309d62b..598ad1f6d4 100644 --- a/Dockerfile +++ b/Dockerfile @@ -4,7 +4,7 @@ ARG NODE_VERSION=22.11.0-alpine3.20 FROM node:${NODE_VERSION} as build -RUN apk add git linux-headers alpine-sdk +RUN apk add git linux-headers alpine-sdk pixman libpng freetype zlib ENV PYTHONUNBUFFERED=1 ENV COREPACK_DEFAULT_TO_LATEST=0 From 4ff29713afc730b7dd1a3c13224f8b07c46700d9 Mon Sep 17 00:00:00 2001 From: Marie Date: Fri, 7 Feb 2025 22:54:09 +0000 Subject: [PATCH 11/13] Update Dockerfile --- Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Dockerfile b/Dockerfile index 598ad1f6d4..c71930b278 100644 --- a/Dockerfile +++ b/Dockerfile @@ -4,7 +4,7 @@ ARG NODE_VERSION=22.11.0-alpine3.20 FROM node:${NODE_VERSION} as build -RUN apk add git linux-headers alpine-sdk pixman libpng freetype zlib +RUN apk add git linux-headers alpine-sdk pixman libpng freetype zlib pixman-dev libpng-dev freetype-dev zlib-dev ENV PYTHONUNBUFFERED=1 ENV COREPACK_DEFAULT_TO_LATEST=0 From bfe1cde5ec7309c24062ce313e4fc376aad205b9 Mon Sep 17 00:00:00 2001 From: Marie Date: Fri, 7 Feb 2025 22:59:05 +0000 Subject: [PATCH 12/13] chore: adjust add command --- Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Dockerfile b/Dockerfile index c71930b278..59f021eaf6 100644 --- a/Dockerfile +++ b/Dockerfile @@ -4,7 +4,7 @@ ARG NODE_VERSION=22.11.0-alpine3.20 FROM node:${NODE_VERSION} as build -RUN apk add git linux-headers alpine-sdk pixman libpng freetype zlib pixman-dev libpng-dev freetype-dev zlib-dev +RUN apk add git linux-headers alpine-sdk pixman pango cairo cairo-dev pango-dev pixman-dev ENV PYTHONUNBUFFERED=1 ENV COREPACK_DEFAULT_TO_LATEST=0 From 0a6ecec26cfd28c2660eb40ad27aba6b74a0f0f4 Mon Sep 17 00:00:00 2001 From: Marie Date: Fri, 7 Feb 2025 23:23:16 +0000 Subject: [PATCH 13/13] add pixman, pango, cairo and libpng to runner --- Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Dockerfile b/Dockerfile index 59f021eaf6..72f934b3ce 100644 --- a/Dockerfile +++ b/Dockerfile @@ -38,7 +38,7 @@ ARG UID="991" ARG GID="991" ENV COREPACK_DEFAULT_TO_LATEST=0 -RUN apk add ffmpeg tini jemalloc \ +RUN apk add ffmpeg tini jemalloc pixman pango cairo libpng \ && corepack enable \ && addgroup -g "${GID}" sharkey \ && adduser -D -u "${UID}" -G sharkey -h /sharkey sharkey \