Compare commits

...

60 Commits

Author SHA1 Message Date
copilot-swe-agent[bot] 89140c552f Restore AppLockService and wrap acquireApObjectLock
Co-authored-by: syuilo <4439005+syuilo@users.noreply.github.com>
2025-11-30 00:40:02 +00:00
copilot-swe-agent[bot] 2a23054249 Initial plan 2025-11-30 00:28:06 +00:00
kakkokari-gtyih 8429e9f9ed Update Changelog [ci skip] 2025-11-29 22:44:54 +09:00
kakkokari-gtyih 8b82f880c2 run pnpm dedupe [ci skip] 2025-11-29 22:27:41 +09:00
kakkokari-gtyih fc43017439 Merge remote-tracking branch 'msky/develop' into renovate/major-backend-update-dependencies 2025-11-29 22:16:11 +09:00
kakkokari-gtyih cf88d2357c update deps 2025-11-29 19:11:32 +09:00
kakkokari-gtyih d8dc7e265f run pnpm dedupe 2025-11-29 19:08:15 +09:00
kakkokari-gtyih 607e4ed4de Merge branch 'develop' into renovate/major-backend-update-dependencies 2025-11-29 19:07:57 +09:00
kakkokari-gtyih 707e2647c1 fix [ci skip] 2025-11-25 09:56:01 +09:00
kakkokari-gtyih 3ce3bacccc fix: rollback jsdom types 2025-11-25 09:52:03 +09:00
kakkokari-gtyih bc63e367bc fix: rollback jsdom, parse5 2025-11-25 09:50:16 +09:00
kakkokari-gtyih 7ae0aa5bd8 fix: rollback sharp-read-bmp to 1.2.0 2025-11-25 09:35:53 +09:00
kakkokari-gtyih 9aa3e5de4c Merge branch 'develop' into renovate/major-backend-update-dependencies 2025-11-25 09:34:21 +09:00
kakkokari-gtyih c1c21e2c16 update deps 2025-11-25 09:31:16 +09:00
kakkokari-gtyih a49f2943ba recreate lockfile 2025-11-01 13:50:08 +09:00
kakkokari-gtyih de55462bd8 fix: rollback nsfwjs to 4.2.0 2025-11-01 12:42:37 +09:00
kakkokari-gtyih 689b20b033 fix: rollback sharp-read-bmp to 1.2.0 2025-11-01 12:37:35 +09:00
kakkokari-gtyih f0d7a9da21 update deps 2025-11-01 12:31:40 +09:00
kakkokari-gtyih 21d04e246f Merge branch 'develop' into renovate/major-backend-update-dependencies 2025-11-01 12:30:36 +09:00
kakkokari-gtyih e2ca887d65 fix: rollback sharp to 0.33 2025-10-08 10:41:56 +09:00
kakkokari-gtyih 71550eaa3a fix: rollback nsfwjs to 4.2.0 2025-10-08 10:36:31 +09:00
kakkokari-gtyih c52425886e fix test 2025-10-08 10:33:14 +09:00
kakkokari-gtyih e6a960e060 update deps 2025-10-08 10:30:15 +09:00
kakkokari-gtyih 789bdd255f revert jest 30 related changes 2025-10-08 10:23:21 +09:00
kakkokari-gtyih 37207f6e1d Merge branch 'develop' into renovate/major-backend-update-dependencies 2025-10-08 10:16:32 +09:00
syuilo e47e6fc527 Merge branch 'develop' into renovate/major-backend-update-dependencies 2025-08-09 13:47:24 +09:00
かっこかり b2edff4888
Merge branch 'develop' into renovate/major-backend-update-dependencies 2025-07-17 11:15:06 +09:00
kakkokari-gtyih cb6d054c71 attempt to fix test 2025-06-14 11:17:58 +09:00
kakkokari-gtyih c306cdbdd9 attempt to fix test 2025-06-13 21:44:53 +09:00
kakkokari-gtyih 0770ee82eb attempt to fix test 2025-06-13 21:32:18 +09:00
kakkokari-gtyih a629985ef6 attempt to fix test 2025-06-13 21:23:49 +09:00
kakkokari-gtyih 7f425ddc77 fix 2025-06-13 16:25:42 +09:00
kakkokari-gtyih 335c77e9e5 fix: rollback nsfwjs to 4.2.0 2025-06-13 16:24:31 +09:00
kakkokari-gtyih 113728797f fix: update node.js min version to 20.18.1 2025-06-13 16:19:30 +09:00
kakkokari-gtyih 3dbeb4659b fix test 2025-06-13 16:18:09 +09:00
kakkokari-gtyih ec92919cb1 update deps / migrate jest 30 2025-06-13 15:43:26 +09:00
kakkokari-gtyih 40cfaf2b87 Merge branch 'develop' into renovate/major-backend-update-dependencies 2025-06-13 15:35:34 +09:00
kakkokari-gtyih c2b1718863 Merge branch 'develop' into renovate/major-backend-update-dependencies 2025-06-02 09:47:37 +09:00
かっこかり 66f7e5acd5
Merge branch 'develop' into renovate/major-backend-update-dependencies 2025-05-29 13:19:35 +09:00
kakkokari-gtyih deebcb29da migrate sinonjs/fake-timers 2025-05-27 17:11:36 +09:00
kakkokari-gtyih ac61d49873 Revert "temporarily roll back simonjs/fake-timers to v11.3.1"
This reverts commit 54f1fc3d79.
2025-05-27 17:03:15 +09:00
kakkokari-gtyih 104505fe5a Merge branch 'renovate/major-backend-update-dependencies' of https://github.com/misskey-dev/misskey into renovate/major-backend-update-dependencies 2025-05-27 16:44:35 +09:00
kakkokari-gtyih 54f1fc3d79 temporarily roll back simonjs/fake-timers to v11.3.1 2025-05-27 16:44:31 +09:00
kakkokari-gtyih 737019c03f Revert "attempt to fix test"
This reverts commit c508318627.
2025-05-27 16:35:34 +09:00
かっこかり 263f0a8bba
Merge branch 'develop' into renovate/major-backend-update-dependencies 2025-05-27 15:12:23 +09:00
kakkokari-gtyih c508318627 attempt to fix test 2025-05-27 15:09:16 +09:00
kakkokari-gtyih 5c78dd3e34 attempt to fix test 2025-05-27 14:24:34 +09:00
kakkokari-gtyih 50a5e7a5dc Merge remote-tracking branch 'msky/develop' into renovate/major-backend-update-dependencies 2025-05-27 13:56:35 +09:00
kakkokari-gtyih 7b53b88209 [ci skip] dedupe 2025-05-27 13:25:26 +09:00
kakkokari-gtyih d381eaa309 tweak max retries 2025-05-27 12:59:55 +09:00
kakkokari-gtyih 179b18b347 spdx 2025-05-27 12:20:16 +09:00
kakkokari-gtyih 70415bdab5 use main redis for lock 2025-05-27 12:18:50 +09:00
あわわわとーにゅ 90e2dfef4e update deps (MisskeyIO#889)
- メンテナンスされないredis-lockを自前実装に変更
- 既にロックされている場合のリトライ間隔を調整
2025-05-27 12:14:13 +09:00
kakkokari-gtyih 46edd40409 Update Changelog 2025-05-27 11:58:44 +09:00
kakkokari-gtyih 1b999c6536 migrate webauthnservice 2025-05-27 11:58:08 +09:00
kakkokari-gtyih e4f85f59ff fix(backend): remove removed type previously exported from file-type 2025-05-27 11:54:22 +09:00
kakkokari-gtyih 6c1b17510d remove types/bcryptjs 2025-05-27 11:53:51 +09:00
kakkokari-gtyih 5d043b0049 update minimum node version for testing 2025-05-27 11:52:08 +09:00
kakkokari-gtyih aa36ebb39b update approve builds 2025-05-27 11:51:56 +09:00
renovate[bot] 026e9897ca
fix(deps): update [backend] update dependencies 2025-05-26 22:50:00 +00:00
38 changed files with 886 additions and 708 deletions

View File

@ -9,6 +9,7 @@
### Server ### Server
- Enhance: メモリ使用量を削減しました - Enhance: メモリ使用量を削減しました
- Enhance: ActivityPubアクティビティを送信する際のパフォーマンス向上 - Enhance: ActivityPubアクティビティを送信する際のパフォーマンス向上
- Enhance: 依存関係の更新
## 2025.11.1 ## 2025.11.1

View File

@ -40,17 +40,17 @@
}, },
"optionalDependencies": { "optionalDependencies": {
"@swc/core-android-arm64": "1.3.11", "@swc/core-android-arm64": "1.3.11",
"@swc/core-darwin-arm64": "1.15.2", "@swc/core-darwin-arm64": "1.15.3",
"@swc/core-darwin-x64": "1.15.2", "@swc/core-darwin-x64": "1.15.3",
"@swc/core-freebsd-x64": "1.3.11", "@swc/core-freebsd-x64": "1.3.11",
"@swc/core-linux-arm-gnueabihf": "1.15.2", "@swc/core-linux-arm-gnueabihf": "1.15.3",
"@swc/core-linux-arm64-gnu": "1.15.2", "@swc/core-linux-arm64-gnu": "1.15.3",
"@swc/core-linux-arm64-musl": "1.15.2", "@swc/core-linux-arm64-musl": "1.15.3",
"@swc/core-linux-x64-gnu": "1.15.2", "@swc/core-linux-x64-gnu": "1.15.3",
"@swc/core-linux-x64-musl": "1.15.2", "@swc/core-linux-x64-musl": "1.15.3",
"@swc/core-win32-arm64-msvc": "1.15.2", "@swc/core-win32-arm64-msvc": "1.15.3",
"@swc/core-win32-ia32-msvc": "1.15.2", "@swc/core-win32-ia32-msvc": "1.15.3",
"@swc/core-win32-x64-msvc": "1.15.2", "@swc/core-win32-x64-msvc": "1.15.3",
"@tensorflow/tfjs": "4.22.0", "@tensorflow/tfjs": "4.22.0",
"@tensorflow/tfjs-node": "4.22.0", "@tensorflow/tfjs-node": "4.22.0",
"bufferutil": "4.0.9", "bufferutil": "4.0.9",
@ -70,17 +70,17 @@
"utf-8-validate": "6.0.5" "utf-8-validate": "6.0.5"
}, },
"dependencies": { "dependencies": {
"@aws-sdk/client-s3": "3.936.0", "@aws-sdk/client-s3": "3.937.0",
"@aws-sdk/lib-storage": "3.936.0", "@aws-sdk/lib-storage": "3.937.0",
"@discordapp/twemoji": "16.0.1", "@discordapp/twemoji": "16.0.1",
"@fastify/accepts": "5.0.3", "@fastify/accepts": "5.0.3",
"@fastify/cookie": "11.0.2", "@fastify/cookie": "11.0.2",
"@fastify/cors": "10.1.0", "@fastify/cors": "11.1.0",
"@fastify/express": "4.0.2", "@fastify/express": "4.0.2",
"@fastify/http-proxy": "10.0.2", "@fastify/http-proxy": "11.3.0",
"@fastify/multipart": "9.3.0", "@fastify/multipart": "9.3.0",
"@fastify/static": "8.3.0", "@fastify/static": "8.3.0",
"@fastify/view": "10.0.2", "@fastify/view": "11.1.1",
"@misskey-dev/sharp-read-bmp": "1.2.0", "@misskey-dev/sharp-read-bmp": "1.2.0",
"@misskey-dev/summaly": "5.2.5", "@misskey-dev/summaly": "5.2.5",
"@napi-rs/canvas": "0.1.82", "@napi-rs/canvas": "0.1.82",
@ -90,33 +90,33 @@
"@peertube/http-signature": "1.7.0", "@peertube/http-signature": "1.7.0",
"@sentry/node": "10.26.0", "@sentry/node": "10.26.0",
"@sentry/profiling-node": "10.26.0", "@sentry/profiling-node": "10.26.0",
"@simplewebauthn/server": "12.0.0", "@simplewebauthn/server": "13.2.2",
"@sinonjs/fake-timers": "11.3.1", "@sinonjs/fake-timers": "15.0.0",
"@smithy/node-http-handler": "2.5.0", "@smithy/node-http-handler": "4.4.5",
"@swc/cli": "0.7.9", "@swc/cli": "0.7.9",
"@swc/core": "1.15.2", "@swc/core": "1.15.3",
"@twemoji/parser": "16.0.0", "@twemoji/parser": "16.0.0",
"@types/redis-info": "3.0.3", "@types/redis-info": "3.0.3",
"accepts": "1.3.8", "accepts": "1.3.8",
"ajv": "8.17.1", "ajv": "8.17.1",
"archiver": "7.0.1", "archiver": "7.0.1",
"async-mutex": "0.5.0", "async-mutex": "0.5.0",
"bcryptjs": "2.4.3", "bcryptjs": "3.0.3",
"blurhash": "2.0.5", "blurhash": "2.0.5",
"body-parser": "1.20.3", "body-parser": "2.2.0",
"bullmq": "5.63.2", "bullmq": "5.64.1",
"cacheable-lookup": "7.0.0", "cacheable-lookup": "7.0.0",
"cbor": "9.0.2", "cbor": "10.0.11",
"chalk": "5.6.2", "chalk": "5.6.2",
"chalk-template": "1.1.2", "chalk-template": "1.1.2",
"chokidar": "4.0.3", "chokidar": "4.0.3",
"color-convert": "2.0.1", "color-convert": "3.1.3",
"content-disposition": "0.5.4", "content-disposition": "1.0.1",
"date-fns": "2.30.0", "date-fns": "4.1.0",
"deep-email-validator": "0.1.21", "deep-email-validator": "0.1.21",
"fastify": "5.6.2", "fastify": "5.6.2",
"fastify-raw-body": "5.0.0", "fastify-raw-body": "5.0.0",
"feed": "4.2.2", "feed": "5.1.0",
"file-type": "21.1.1", "file-type": "21.1.1",
"fluent-ffmpeg": "2.1.3", "fluent-ffmpeg": "2.1.3",
"form-data": "4.0.5", "form-data": "4.0.5",
@ -126,16 +126,16 @@
"ioredis": "5.8.2", "ioredis": "5.8.2",
"ip-cidr": "4.0.2", "ip-cidr": "4.0.2",
"ipaddr.js": "2.2.0", "ipaddr.js": "2.2.0",
"is-svg": "5.1.0", "is-svg": "6.1.0",
"js-yaml": "4.1.1", "js-yaml": "4.1.1",
"json5": "2.2.3", "json5": "2.2.3",
"jsonld": "8.3.3", "jsonld": "9.0.0",
"jsrsasign": "11.1.0", "jsrsasign": "11.1.0",
"juice": "11.0.3", "juice": "11.0.3",
"meilisearch": "0.54.0", "meilisearch": "0.54.0",
"mfm-js": "0.25.0", "mfm-js": "0.25.0",
"microformats-parser": "2.0.4", "microformats-parser": "2.0.4",
"mime-types": "2.1.35", "mime-types": "3.0.2",
"misskey-js": "workspace:*", "misskey-js": "workspace:*",
"misskey-reversi": "workspace:*", "misskey-reversi": "workspace:*",
"ms": "3.0.0-canary.202508261828", "ms": "3.0.0-canary.202508261828",
@ -151,7 +151,7 @@
"os-utils": "0.0.14", "os-utils": "0.0.14",
"otpauth": "9.4.1", "otpauth": "9.4.1",
"pg": "8.16.3", "pg": "8.16.3",
"pkce-challenge": "4.1.0", "pkce-challenge": "5.0.0",
"probe-image-size": "7.2.3", "probe-image-size": "7.2.3",
"promise-limit": "2.7.0", "promise-limit": "2.7.0",
"pug": "3.0.3", "pug": "3.0.3",
@ -160,13 +160,12 @@
"ratelimiter": "3.4.1", "ratelimiter": "3.4.1",
"re2": "1.22.3", "re2": "1.22.3",
"redis-info": "3.1.0", "redis-info": "3.1.0",
"redis-lock": "0.1.4",
"reflect-metadata": "0.2.2", "reflect-metadata": "0.2.2",
"rename": "1.0.4", "rename": "1.0.4",
"rss-parser": "3.13.0", "rss-parser": "3.13.0",
"rxjs": "7.8.2", "rxjs": "7.8.2",
"sanitize-html": "2.17.0", "sanitize-html": "2.17.0",
"secure-json-parse": "3.0.2", "secure-json-parse": "4.1.0",
"semver": "7.7.3", "semver": "7.7.3",
"sharp": "0.33.5", "sharp": "0.33.5",
"slacc": "0.0.10", "slacc": "0.0.10",
@ -179,7 +178,7 @@
"tsconfig-paths": "4.2.0", "tsconfig-paths": "4.2.0",
"typeorm": "0.3.27", "typeorm": "0.3.27",
"typescript": "5.9.3", "typescript": "5.9.3",
"ulid": "2.4.0", "ulid": "3.0.1",
"vary": "1.1.2", "vary": "1.1.2",
"web-push": "3.6.7", "web-push": "3.6.7",
"ws": "8.18.3", "ws": "8.18.3",
@ -187,13 +186,12 @@
}, },
"devDependencies": { "devDependencies": {
"@jest/globals": "29.7.0", "@jest/globals": "29.7.0",
"@nestjs/platform-express": "10.4.20", "@nestjs/platform-express": "11.1.9",
"@sentry/vue": "10.26.0", "@sentry/vue": "10.26.0",
"@simplewebauthn/types": "12.0.0", "@simplewebauthn/types": "12.0.0",
"@swc/jest": "0.2.39", "@swc/jest": "0.2.39",
"@types/accepts": "1.3.7", "@types/accepts": "1.3.7",
"@types/archiver": "6.0.4", "@types/archiver": "7.0.0",
"@types/bcryptjs": "2.4.6",
"@types/body-parser": "1.19.6", "@types/body-parser": "1.19.6",
"@types/color-convert": "2.0.4", "@types/color-convert": "2.0.4",
"@types/content-disposition": "0.5.9", "@types/content-disposition": "0.5.9",
@ -203,10 +201,10 @@
"@types/js-yaml": "4.0.9", "@types/js-yaml": "4.0.9",
"@types/jsonld": "1.5.15", "@types/jsonld": "1.5.15",
"@types/jsrsasign": "10.5.15", "@types/jsrsasign": "10.5.15",
"@types/mime-types": "2.1.4", "@types/mime-types": "3.0.1",
"@types/ms": "0.7.34", "@types/ms": "2.1.0",
"@types/node": "24.10.1", "@types/node": "24.10.1",
"@types/nodemailer": "6.4.21", "@types/nodemailer": "7.0.4",
"@types/oauth": "0.9.6", "@types/oauth": "0.9.6",
"@types/oauth2orize": "1.11.5", "@types/oauth2orize": "1.11.5",
"@types/oauth2orize-pkce": "0.1.2", "@types/oauth2orize-pkce": "0.1.2",
@ -219,7 +217,7 @@
"@types/sanitize-html": "2.16.0", "@types/sanitize-html": "2.16.0",
"@types/semver": "7.7.1", "@types/semver": "7.7.1",
"@types/simple-oauth2": "5.0.7", "@types/simple-oauth2": "5.0.7",
"@types/sinonjs__fake-timers": "8.1.5", "@types/sinonjs__fake-timers": "15.0.1",
"@types/supertest": "6.0.3", "@types/supertest": "6.0.3",
"@types/tinycolor2": "1.4.6", "@types/tinycolor2": "1.4.6",
"@types/tmp": "0.2.6", "@types/tmp": "0.2.6",
@ -229,14 +227,15 @@
"@typescript-eslint/eslint-plugin": "8.47.0", "@typescript-eslint/eslint-plugin": "8.47.0",
"@typescript-eslint/parser": "8.47.0", "@typescript-eslint/parser": "8.47.0",
"aws-sdk-client-mock": "4.1.0", "aws-sdk-client-mock": "4.1.0",
"cross-env": "7.0.3", "cross-env": "10.1.0",
"eslint-plugin-import": "2.32.0", "eslint-plugin-import": "2.32.0",
"execa": "8.0.1", "execa": "9.6.0",
"fkill": "9.0.0", "fkill": "10.0.1",
"jest": "29.7.0", "jest": "29.7.0",
"jest-mock": "29.7.0", "jest-mock": "29.7.0",
"jest-util": "29.7.0",
"nodemon": "3.1.11", "nodemon": "3.1.11",
"pid-port": "1.0.2", "pid-port": "2.0.0",
"simple-oauth2": "5.1.0", "simple-oauth2": "5.1.0",
"supertest": "7.1.4" "supertest": "7.1.4"
} }

View File

@ -1,13 +0,0 @@
/*
* SPDX-FileCopyrightText: syuilo and misskey-project
* SPDX-License-Identifier: AGPL-3.0-only
*/
declare module 'redis-lock' {
import type Redis from 'ioredis';
type Lock = (lockName: string, timeout?: number, taskToPerform?: () => Promise<void>) => void;
function redisLock(client: Redis.Redis, retryDelay: number): Lock;
export = redisLock;
}

View File

@ -3,42 +3,37 @@
* SPDX-License-Identifier: AGPL-3.0-only * SPDX-License-Identifier: AGPL-3.0-only
*/ */
import { promisify } from 'node:util';
import { Inject, Injectable } from '@nestjs/common'; import { Inject, Injectable } from '@nestjs/common';
import redisLock from 'redis-lock';
import * as Redis from 'ioredis'; import * as Redis from 'ioredis';
import { DI } from '@/di-symbols.js'; import { DI } from '@/di-symbols.js';
import { acquireApObjectLock, acquireChartInsertLock } from '@/misc/distributed-lock.js';
import { bindThis } from '@/decorators.js'; import { bindThis } from '@/decorators.js';
/**
* Retry delay (ms) for lock acquisition
*/
const retryDelay = 100;
@Injectable() @Injectable()
export class AppLockService { export class AppLockService {
private lock: (key: string, timeout?: number, _?: (() => Promise<void>) | undefined) => Promise<() => void>;
constructor( constructor(
@Inject(DI.redis) @Inject(DI.redis)
private redisClient: Redis.Redis, private redisClient: Redis.Redis,
) { ) {
this.lock = promisify(redisLock(this.redisClient, retryDelay));
} }
/** /**
* Get AP Object lock * Get AP Object lock
* @param uri AP object ID * @param uri AP object ID
* @param timeout Lock timeout (ms), The timeout releases previous lock.
* @returns Unlock function * @returns Unlock function
*/ */
@bindThis @bindThis
public getApLock(uri: string, timeout = 30 * 1000): Promise<() => void> { public getApLock(uri: string): Promise<() => Promise<void>> {
return this.lock(`ap-object:${uri}`, timeout); return acquireApObjectLock(this.redisClient, uri);
} }
/**
* Get chart insert lock
* @param lockKey Lock key
* @returns Unlock function
*/
@bindThis @bindThis
public getChartInsertLock(lockKey: string, timeout = 30 * 1000): Promise<() => void> { public getChartInsertLock(lockKey: string): Promise<() => Promise<void>> {
return this.lock(`chart-insert:${lockKey}`, timeout); return acquireChartInsertLock(this.redisClient, lockKey);
} }
} }

View File

@ -19,9 +19,9 @@ import { ChannelMutingService } from '@/core/ChannelMutingService.js';
import { AccountMoveService } from './AccountMoveService.js'; import { AccountMoveService } from './AccountMoveService.js';
import { AccountUpdateService } from './AccountUpdateService.js'; import { AccountUpdateService } from './AccountUpdateService.js';
import { AiService } from './AiService.js'; import { AiService } from './AiService.js';
import { AppLockService } from './AppLockService.js';
import { AnnouncementService } from './AnnouncementService.js'; import { AnnouncementService } from './AnnouncementService.js';
import { AntennaService } from './AntennaService.js'; import { AntennaService } from './AntennaService.js';
import { AppLockService } from './AppLockService.js';
import { AchievementService } from './AchievementService.js'; import { AchievementService } from './AchievementService.js';
import { AvatarDecorationService } from './AvatarDecorationService.js'; import { AvatarDecorationService } from './AvatarDecorationService.js';
import { CaptchaService } from './CaptchaService.js'; import { CaptchaService } from './CaptchaService.js';
@ -164,9 +164,9 @@ const $AbuseReportNotificationService: Provider = { provide: 'AbuseReportNotific
const $AccountMoveService: Provider = { provide: 'AccountMoveService', useExisting: AccountMoveService }; const $AccountMoveService: Provider = { provide: 'AccountMoveService', useExisting: AccountMoveService };
const $AccountUpdateService: Provider = { provide: 'AccountUpdateService', useExisting: AccountUpdateService }; const $AccountUpdateService: Provider = { provide: 'AccountUpdateService', useExisting: AccountUpdateService };
const $AiService: Provider = { provide: 'AiService', useExisting: AiService }; const $AiService: Provider = { provide: 'AiService', useExisting: AiService };
const $AppLockService: Provider = { provide: 'AppLockService', useExisting: AppLockService };
const $AnnouncementService: Provider = { provide: 'AnnouncementService', useExisting: AnnouncementService }; const $AnnouncementService: Provider = { provide: 'AnnouncementService', useExisting: AnnouncementService };
const $AntennaService: Provider = { provide: 'AntennaService', useExisting: AntennaService }; const $AntennaService: Provider = { provide: 'AntennaService', useExisting: AntennaService };
const $AppLockService: Provider = { provide: 'AppLockService', useExisting: AppLockService };
const $AchievementService: Provider = { provide: 'AchievementService', useExisting: AchievementService }; const $AchievementService: Provider = { provide: 'AchievementService', useExisting: AchievementService };
const $AvatarDecorationService: Provider = { provide: 'AvatarDecorationService', useExisting: AvatarDecorationService }; const $AvatarDecorationService: Provider = { provide: 'AvatarDecorationService', useExisting: AvatarDecorationService };
const $CaptchaService: Provider = { provide: 'CaptchaService', useExisting: CaptchaService }; const $CaptchaService: Provider = { provide: 'CaptchaService', useExisting: CaptchaService };
@ -318,9 +318,9 @@ const $ApQuestionService: Provider = { provide: 'ApQuestionService', useExisting
AccountMoveService, AccountMoveService,
AccountUpdateService, AccountUpdateService,
AiService, AiService,
AppLockService,
AnnouncementService, AnnouncementService,
AntennaService, AntennaService,
AppLockService,
AchievementService, AchievementService,
AvatarDecorationService, AvatarDecorationService,
CaptchaService, CaptchaService,
@ -468,9 +468,9 @@ const $ApQuestionService: Provider = { provide: 'ApQuestionService', useExisting
$AccountMoveService, $AccountMoveService,
$AccountUpdateService, $AccountUpdateService,
$AiService, $AiService,
$AppLockService,
$AnnouncementService, $AnnouncementService,
$AntennaService, $AntennaService,
$AppLockService,
$AchievementService, $AchievementService,
$AvatarDecorationService, $AvatarDecorationService,
$CaptchaService, $CaptchaService,
@ -619,9 +619,9 @@ const $ApQuestionService: Provider = { provide: 'ApQuestionService', useExisting
AccountMoveService, AccountMoveService,
AccountUpdateService, AccountUpdateService,
AiService, AiService,
AppLockService,
AnnouncementService, AnnouncementService,
AntennaService, AntennaService,
AppLockService,
AchievementService, AchievementService,
AvatarDecorationService, AvatarDecorationService,
CaptchaService, CaptchaService,
@ -768,9 +768,9 @@ const $ApQuestionService: Provider = { provide: 'ApQuestionService', useExisting
$AccountMoveService, $AccountMoveService,
$AccountUpdateService, $AccountUpdateService,
$AiService, $AiService,
$AppLockService,
$AnnouncementService, $AnnouncementService,
$AntennaService, $AntennaService,
$AppLockService,
$AchievementService, $AchievementService,
$AvatarDecorationService, $AvatarDecorationService,
$CaptchaService, $CaptchaService,

View File

@ -66,7 +66,6 @@ export class WebAuthnService {
userID: isoUint8Array.fromUTF8String(userId), userID: isoUint8Array.fromUTF8String(userId),
userName: userName, userName: userName,
userDisplayName: userDisplayName, userDisplayName: userDisplayName,
attestationType: 'indirect',
excludeCredentials: keys.map(key => (<{ id: string; transports?: AuthenticatorTransportFuture[]; }>{ excludeCredentials: keys.map(key => (<{ id: string; transports?: AuthenticatorTransportFuture[]; }>{
id: key.id, id: key.id,
transports: key.transports ?? undefined, transports: key.transports ?? undefined,

View File

@ -15,7 +15,6 @@ import { UserBlockingService } from '@/core/UserBlockingService.js';
import { NoteDeleteService } from '@/core/NoteDeleteService.js'; import { NoteDeleteService } from '@/core/NoteDeleteService.js';
import { NoteCreateService } from '@/core/NoteCreateService.js'; import { NoteCreateService } from '@/core/NoteCreateService.js';
import { concat, toArray, toSingle, unique } from '@/misc/prelude/array.js'; import { concat, toArray, toSingle, unique } from '@/misc/prelude/array.js';
import { AppLockService } from '@/core/AppLockService.js';
import type Logger from '@/logger.js'; import type Logger from '@/logger.js';
import { IdService } from '@/core/IdService.js'; import { IdService } from '@/core/IdService.js';
import { StatusError } from '@/misc/status-error.js'; import { StatusError } from '@/misc/status-error.js';
@ -29,6 +28,7 @@ import type { MiRemoteUser } from '@/models/User.js';
import { GlobalEventService } from '@/core/GlobalEventService.js'; import { GlobalEventService } from '@/core/GlobalEventService.js';
import { AbuseReportService } from '@/core/AbuseReportService.js'; import { AbuseReportService } from '@/core/AbuseReportService.js';
import { IdentifiableError } from '@/misc/identifiable-error.js'; import { IdentifiableError } from '@/misc/identifiable-error.js';
import { AppLockService } from '@/core/AppLockService.js';
import { getApHrefNullable, getApId, getApIds, getApType, isAccept, isActor, isAdd, isAnnounce, isBlock, isCollection, isCollectionOrOrderedCollection, isCreate, isDelete, isFlag, isFollow, isLike, isMove, isPost, isReject, isRemove, isTombstone, isUndo, isUpdate, validActor, validPost } from './type.js'; import { getApHrefNullable, getApId, getApIds, getApType, isAccept, isActor, isAdd, isAnnounce, isBlock, isCollection, isCollectionOrOrderedCollection, isCreate, isDelete, isFlag, isFollow, isLike, isMove, isPost, isReject, isRemove, isTombstone, isUndo, isUpdate, validActor, validPost } from './type.js';
import { ApNoteService } from './models/ApNoteService.js'; import { ApNoteService } from './models/ApNoteService.js';
import { ApLoggerService } from './ApLoggerService.js'; import { ApLoggerService } from './ApLoggerService.js';
@ -48,9 +48,6 @@ export class ApInboxService {
@Inject(DI.config) @Inject(DI.config)
private config: Config, private config: Config,
@Inject(DI.meta)
private meta: MiMeta,
@Inject(DI.usersRepository) @Inject(DI.usersRepository)
private usersRepository: UsersRepository, private usersRepository: UsersRepository,
@ -76,7 +73,6 @@ export class ApInboxService {
private userBlockingService: UserBlockingService, private userBlockingService: UserBlockingService,
private noteCreateService: NoteCreateService, private noteCreateService: NoteCreateService,
private noteDeleteService: NoteDeleteService, private noteDeleteService: NoteDeleteService,
private appLockService: AppLockService,
private apResolverService: ApResolverService, private apResolverService: ApResolverService,
private apDbResolverService: ApDbResolverService, private apDbResolverService: ApDbResolverService,
private apLoggerService: ApLoggerService, private apLoggerService: ApLoggerService,
@ -85,6 +81,7 @@ export class ApInboxService {
private apQuestionService: ApQuestionService, private apQuestionService: ApQuestionService,
private queueService: QueueService, private queueService: QueueService,
private globalEventService: GlobalEventService, private globalEventService: GlobalEventService,
private appLockService: AppLockService,
) { ) {
this.logger = this.apLoggerService.logger; this.logger = this.apLoggerService.logger;
} }

View File

@ -12,7 +12,6 @@ import type { MiRemoteUser } from '@/models/User.js';
import type { MiNote } from '@/models/Note.js'; import type { MiNote } from '@/models/Note.js';
import { toArray, toSingle, unique } from '@/misc/prelude/array.js'; import { toArray, toSingle, unique } from '@/misc/prelude/array.js';
import type { MiEmoji } from '@/models/Emoji.js'; import type { MiEmoji } from '@/models/Emoji.js';
import { AppLockService } from '@/core/AppLockService.js';
import type { MiDriveFile } from '@/models/DriveFile.js'; import type { MiDriveFile } from '@/models/DriveFile.js';
import { NoteCreateService } from '@/core/NoteCreateService.js'; import { NoteCreateService } from '@/core/NoteCreateService.js';
import type Logger from '@/logger.js'; import type Logger from '@/logger.js';
@ -23,6 +22,7 @@ import { UtilityService } from '@/core/UtilityService.js';
import { bindThis } from '@/decorators.js'; import { bindThis } from '@/decorators.js';
import { checkHttps } from '@/misc/check-https.js'; import { checkHttps } from '@/misc/check-https.js';
import { IdentifiableError } from '@/misc/identifiable-error.js'; import { IdentifiableError } from '@/misc/identifiable-error.js';
import { AppLockService } from '@/core/AppLockService.js';
import { getOneApId, getApId, getOneApHrefNullable, validPost, isEmoji, getApType } from '../type.js'; import { getOneApId, getApId, getOneApHrefNullable, validPost, isEmoji, getApType } from '../type.js';
import { ApLoggerService } from '../ApLoggerService.js'; import { ApLoggerService } from '../ApLoggerService.js';
import { ApMfmService } from '../ApMfmService.js'; import { ApMfmService } from '../ApMfmService.js';
@ -67,11 +67,11 @@ export class ApNoteService {
private apMentionService: ApMentionService, private apMentionService: ApMentionService,
private apImageService: ApImageService, private apImageService: ApImageService,
private apQuestionService: ApQuestionService, private apQuestionService: ApQuestionService,
private appLockService: AppLockService,
private pollService: PollService, private pollService: PollService,
private noteCreateService: NoteCreateService, private noteCreateService: NoteCreateService,
private apDbResolverService: ApDbResolverService, private apDbResolverService: ApDbResolverService,
private apLoggerService: ApLoggerService, private apLoggerService: ApLoggerService,
private appLockService: AppLockService,
) { ) {
this.logger = this.apLoggerService.logger; this.logger = this.apLoggerService.logger;
} }

View File

@ -5,11 +5,12 @@
import { Injectable, Inject } from '@nestjs/common'; import { Injectable, Inject } from '@nestjs/common';
import { DataSource } from 'typeorm'; import { DataSource } from 'typeorm';
import { AppLockService } from '@/core/AppLockService.js'; import * as Redis from 'ioredis';
import type { MiUser } from '@/models/User.js'; import type { MiUser } from '@/models/User.js';
import { DI } from '@/di-symbols.js'; import { DI } from '@/di-symbols.js';
import { bindThis } from '@/decorators.js'; import { bindThis } from '@/decorators.js';
import { IdService } from '@/core/IdService.js'; import { IdService } from '@/core/IdService.js';
import { acquireChartInsertLock } from '@/misc/distributed-lock.js';
import Chart from '../core.js'; import Chart from '../core.js';
import { ChartLoggerService } from '../ChartLoggerService.js'; import { ChartLoggerService } from '../ChartLoggerService.js';
import { name, schema } from './entities/active-users.js'; import { name, schema } from './entities/active-users.js';
@ -28,11 +29,13 @@ export default class ActiveUsersChart extends Chart<typeof schema> { // eslint-d
@Inject(DI.db) @Inject(DI.db)
private db: DataSource, private db: DataSource,
private appLockService: AppLockService, @Inject(DI.redis)
private redisClient: Redis.Redis,
private chartLoggerService: ChartLoggerService, private chartLoggerService: ChartLoggerService,
private idService: IdService, private idService: IdService,
) { ) {
super(db, (k) => appLockService.getChartInsertLock(k), chartLoggerService.logger, name, schema); super(db, (k) => acquireChartInsertLock(redisClient, k), chartLoggerService.logger, name, schema);
} }
protected async tickMajor(): Promise<Partial<KVs<typeof schema>>> { protected async tickMajor(): Promise<Partial<KVs<typeof schema>>> {

View File

@ -5,9 +5,10 @@
import { Injectable, Inject } from '@nestjs/common'; import { Injectable, Inject } from '@nestjs/common';
import { DataSource } from 'typeorm'; import { DataSource } from 'typeorm';
import { AppLockService } from '@/core/AppLockService.js'; import * as Redis from 'ioredis';
import { DI } from '@/di-symbols.js'; import { DI } from '@/di-symbols.js';
import { bindThis } from '@/decorators.js'; import { bindThis } from '@/decorators.js';
import { acquireChartInsertLock } from '@/misc/distributed-lock.js';
import Chart from '../core.js'; import Chart from '../core.js';
import { ChartLoggerService } from '../ChartLoggerService.js'; import { ChartLoggerService } from '../ChartLoggerService.js';
import { name, schema } from './entities/ap-request.js'; import { name, schema } from './entities/ap-request.js';
@ -22,10 +23,12 @@ export default class ApRequestChart extends Chart<typeof schema> { // eslint-dis
@Inject(DI.db) @Inject(DI.db)
private db: DataSource, private db: DataSource,
private appLockService: AppLockService, @Inject(DI.redis)
private redisClient: Redis.Redis,
private chartLoggerService: ChartLoggerService, private chartLoggerService: ChartLoggerService,
) { ) {
super(db, (k) => appLockService.getChartInsertLock(k), chartLoggerService.logger, name, schema); super(db, (k) => acquireChartInsertLock(redisClient, k), chartLoggerService.logger, name, schema);
} }
protected async tickMajor(): Promise<Partial<KVs<typeof schema>>> { protected async tickMajor(): Promise<Partial<KVs<typeof schema>>> {

View File

@ -5,10 +5,11 @@
import { Injectable, Inject } from '@nestjs/common'; import { Injectable, Inject } from '@nestjs/common';
import { DataSource } from 'typeorm'; import { DataSource } from 'typeorm';
import * as Redis from 'ioredis';
import type { MiDriveFile } from '@/models/DriveFile.js'; import type { MiDriveFile } from '@/models/DriveFile.js';
import { AppLockService } from '@/core/AppLockService.js';
import { DI } from '@/di-symbols.js'; import { DI } from '@/di-symbols.js';
import { bindThis } from '@/decorators.js'; import { bindThis } from '@/decorators.js';
import { acquireChartInsertLock } from '@/misc/distributed-lock.js';
import Chart from '../core.js'; import Chart from '../core.js';
import { ChartLoggerService } from '../ChartLoggerService.js'; import { ChartLoggerService } from '../ChartLoggerService.js';
import { name, schema } from './entities/drive.js'; import { name, schema } from './entities/drive.js';
@ -23,10 +24,12 @@ export default class DriveChart extends Chart<typeof schema> { // eslint-disable
@Inject(DI.db) @Inject(DI.db)
private db: DataSource, private db: DataSource,
private appLockService: AppLockService, @Inject(DI.redis)
private redisClient: Redis.Redis,
private chartLoggerService: ChartLoggerService, private chartLoggerService: ChartLoggerService,
) { ) {
super(db, (k) => appLockService.getChartInsertLock(k), chartLoggerService.logger, name, schema); super(db, (k) => acquireChartInsertLock(redisClient, k), chartLoggerService.logger, name, schema);
} }
protected async tickMajor(): Promise<Partial<KVs<typeof schema>>> { protected async tickMajor(): Promise<Partial<KVs<typeof schema>>> {

View File

@ -5,10 +5,11 @@
import { Injectable, Inject } from '@nestjs/common'; import { Injectable, Inject } from '@nestjs/common';
import { DataSource } from 'typeorm'; import { DataSource } from 'typeorm';
import * as Redis from 'ioredis';
import type { FollowingsRepository, InstancesRepository, MiMeta } from '@/models/_.js'; import type { FollowingsRepository, InstancesRepository, MiMeta } from '@/models/_.js';
import { AppLockService } from '@/core/AppLockService.js';
import { DI } from '@/di-symbols.js'; import { DI } from '@/di-symbols.js';
import { bindThis } from '@/decorators.js'; import { bindThis } from '@/decorators.js';
import { acquireChartInsertLock } from '@/misc/distributed-lock.js';
import Chart from '../core.js'; import Chart from '../core.js';
import { ChartLoggerService } from '../ChartLoggerService.js'; import { ChartLoggerService } from '../ChartLoggerService.js';
import { name, schema } from './entities/federation.js'; import { name, schema } from './entities/federation.js';
@ -26,16 +27,18 @@ export default class FederationChart extends Chart<typeof schema> { // eslint-di
@Inject(DI.meta) @Inject(DI.meta)
private meta: MiMeta, private meta: MiMeta,
@Inject(DI.redis)
private redisClient: Redis.Redis,
@Inject(DI.followingsRepository) @Inject(DI.followingsRepository)
private followingsRepository: FollowingsRepository, private followingsRepository: FollowingsRepository,
@Inject(DI.instancesRepository) @Inject(DI.instancesRepository)
private instancesRepository: InstancesRepository, private instancesRepository: InstancesRepository,
private appLockService: AppLockService,
private chartLoggerService: ChartLoggerService, private chartLoggerService: ChartLoggerService,
) { ) {
super(db, (k) => appLockService.getChartInsertLock(k), chartLoggerService.logger, name, schema); super(db, (k) => acquireChartInsertLock(redisClient, k), chartLoggerService.logger, name, schema);
} }
protected async tickMajor(): Promise<Partial<KVs<typeof schema>>> { protected async tickMajor(): Promise<Partial<KVs<typeof schema>>> {

View File

@ -5,13 +5,14 @@
import { Injectable, Inject } from '@nestjs/common'; import { Injectable, Inject } from '@nestjs/common';
import { DataSource } from 'typeorm'; import { DataSource } from 'typeorm';
import * as Redis from 'ioredis';
import type { DriveFilesRepository, FollowingsRepository, UsersRepository, NotesRepository } from '@/models/_.js'; import type { DriveFilesRepository, FollowingsRepository, UsersRepository, NotesRepository } from '@/models/_.js';
import type { MiDriveFile } from '@/models/DriveFile.js'; import type { MiDriveFile } from '@/models/DriveFile.js';
import type { MiNote } from '@/models/Note.js'; import type { MiNote } from '@/models/Note.js';
import { AppLockService } from '@/core/AppLockService.js';
import { DI } from '@/di-symbols.js'; import { DI } from '@/di-symbols.js';
import { UtilityService } from '@/core/UtilityService.js'; import { UtilityService } from '@/core/UtilityService.js';
import { bindThis } from '@/decorators.js'; import { bindThis } from '@/decorators.js';
import { acquireChartInsertLock } from '@/misc/distributed-lock.js';
import Chart from '../core.js'; import Chart from '../core.js';
import { ChartLoggerService } from '../ChartLoggerService.js'; import { ChartLoggerService } from '../ChartLoggerService.js';
import { name, schema } from './entities/instance.js'; import { name, schema } from './entities/instance.js';
@ -26,6 +27,9 @@ export default class InstanceChart extends Chart<typeof schema> { // eslint-disa
@Inject(DI.db) @Inject(DI.db)
private db: DataSource, private db: DataSource,
@Inject(DI.redis)
private redisClient: Redis.Redis,
@Inject(DI.usersRepository) @Inject(DI.usersRepository)
private usersRepository: UsersRepository, private usersRepository: UsersRepository,
@ -39,10 +43,9 @@ export default class InstanceChart extends Chart<typeof schema> { // eslint-disa
private followingsRepository: FollowingsRepository, private followingsRepository: FollowingsRepository,
private utilityService: UtilityService, private utilityService: UtilityService,
private appLockService: AppLockService,
private chartLoggerService: ChartLoggerService, private chartLoggerService: ChartLoggerService,
) { ) {
super(db, (k) => appLockService.getChartInsertLock(k), chartLoggerService.logger, name, schema, true); super(db, (k) => acquireChartInsertLock(redisClient, k), chartLoggerService.logger, name, schema, true);
} }
protected async tickMajor(group: string): Promise<Partial<KVs<typeof schema>>> { protected async tickMajor(group: string): Promise<Partial<KVs<typeof schema>>> {

View File

@ -5,11 +5,12 @@
import { Injectable, Inject } from '@nestjs/common'; import { Injectable, Inject } from '@nestjs/common';
import { Not, IsNull, DataSource } from 'typeorm'; import { Not, IsNull, DataSource } from 'typeorm';
import * as Redis from 'ioredis';
import type { NotesRepository } from '@/models/_.js'; import type { NotesRepository } from '@/models/_.js';
import type { MiNote } from '@/models/Note.js'; import type { MiNote } from '@/models/Note.js';
import { AppLockService } from '@/core/AppLockService.js';
import { DI } from '@/di-symbols.js'; import { DI } from '@/di-symbols.js';
import { bindThis } from '@/decorators.js'; import { bindThis } from '@/decorators.js';
import { acquireChartInsertLock } from '@/misc/distributed-lock.js';
import Chart from '../core.js'; import Chart from '../core.js';
import { ChartLoggerService } from '../ChartLoggerService.js'; import { ChartLoggerService } from '../ChartLoggerService.js';
import { name, schema } from './entities/notes.js'; import { name, schema } from './entities/notes.js';
@ -24,13 +25,15 @@ export default class NotesChart extends Chart<typeof schema> { // eslint-disable
@Inject(DI.db) @Inject(DI.db)
private db: DataSource, private db: DataSource,
@Inject(DI.redis)
private redisClient: Redis.Redis,
@Inject(DI.notesRepository) @Inject(DI.notesRepository)
private notesRepository: NotesRepository, private notesRepository: NotesRepository,
private appLockService: AppLockService,
private chartLoggerService: ChartLoggerService, private chartLoggerService: ChartLoggerService,
) { ) {
super(db, (k) => appLockService.getChartInsertLock(k), chartLoggerService.logger, name, schema); super(db, (k) => acquireChartInsertLock(redisClient, k), chartLoggerService.logger, name, schema);
} }
protected async tickMajor(): Promise<Partial<KVs<typeof schema>>> { protected async tickMajor(): Promise<Partial<KVs<typeof schema>>> {

View File

@ -5,12 +5,13 @@
import { Injectable, Inject } from '@nestjs/common'; import { Injectable, Inject } from '@nestjs/common';
import { DataSource } from 'typeorm'; import { DataSource } from 'typeorm';
import * as Redis from 'ioredis';
import type { DriveFilesRepository } from '@/models/_.js'; import type { DriveFilesRepository } from '@/models/_.js';
import type { MiDriveFile } from '@/models/DriveFile.js'; import type { MiDriveFile } from '@/models/DriveFile.js';
import { AppLockService } from '@/core/AppLockService.js';
import { DI } from '@/di-symbols.js'; import { DI } from '@/di-symbols.js';
import { DriveFileEntityService } from '@/core/entities/DriveFileEntityService.js'; import { DriveFileEntityService } from '@/core/entities/DriveFileEntityService.js';
import { bindThis } from '@/decorators.js'; import { bindThis } from '@/decorators.js';
import { acquireChartInsertLock } from '@/misc/distributed-lock.js';
import Chart from '../core.js'; import Chart from '../core.js';
import { ChartLoggerService } from '../ChartLoggerService.js'; import { ChartLoggerService } from '../ChartLoggerService.js';
import { name, schema } from './entities/per-user-drive.js'; import { name, schema } from './entities/per-user-drive.js';
@ -25,14 +26,16 @@ export default class PerUserDriveChart extends Chart<typeof schema> { // eslint-
@Inject(DI.db) @Inject(DI.db)
private db: DataSource, private db: DataSource,
@Inject(DI.redis)
private redisClient: Redis.Redis,
@Inject(DI.driveFilesRepository) @Inject(DI.driveFilesRepository)
private driveFilesRepository: DriveFilesRepository, private driveFilesRepository: DriveFilesRepository,
private appLockService: AppLockService,
private driveFileEntityService: DriveFileEntityService, private driveFileEntityService: DriveFileEntityService,
private chartLoggerService: ChartLoggerService, private chartLoggerService: ChartLoggerService,
) { ) {
super(db, (k) => appLockService.getChartInsertLock(k), chartLoggerService.logger, name, schema, true); super(db, (k) => acquireChartInsertLock(redisClient, k), chartLoggerService.logger, name, schema, true);
} }
protected async tickMajor(group: string): Promise<Partial<KVs<typeof schema>>> { protected async tickMajor(group: string): Promise<Partial<KVs<typeof schema>>> {

View File

@ -5,12 +5,13 @@
import { Injectable, Inject } from '@nestjs/common'; import { Injectable, Inject } from '@nestjs/common';
import { Not, IsNull, DataSource } from 'typeorm'; import { Not, IsNull, DataSource } from 'typeorm';
import * as Redis from 'ioredis';
import type { MiUser } from '@/models/User.js'; import type { MiUser } from '@/models/User.js';
import { AppLockService } from '@/core/AppLockService.js';
import { DI } from '@/di-symbols.js'; import { DI } from '@/di-symbols.js';
import { UserEntityService } from '@/core/entities/UserEntityService.js'; import { UserEntityService } from '@/core/entities/UserEntityService.js';
import type { FollowingsRepository } from '@/models/_.js'; import type { FollowingsRepository } from '@/models/_.js';
import { bindThis } from '@/decorators.js'; import { bindThis } from '@/decorators.js';
import { acquireChartInsertLock } from '@/misc/distributed-lock.js';
import Chart from '../core.js'; import Chart from '../core.js';
import { ChartLoggerService } from '../ChartLoggerService.js'; import { ChartLoggerService } from '../ChartLoggerService.js';
import { name, schema } from './entities/per-user-following.js'; import { name, schema } from './entities/per-user-following.js';
@ -25,14 +26,16 @@ export default class PerUserFollowingChart extends Chart<typeof schema> { // esl
@Inject(DI.db) @Inject(DI.db)
private db: DataSource, private db: DataSource,
@Inject(DI.redis)
private redisClient: Redis.Redis,
@Inject(DI.followingsRepository) @Inject(DI.followingsRepository)
private followingsRepository: FollowingsRepository, private followingsRepository: FollowingsRepository,
private appLockService: AppLockService,
private userEntityService: UserEntityService, private userEntityService: UserEntityService,
private chartLoggerService: ChartLoggerService, private chartLoggerService: ChartLoggerService,
) { ) {
super(db, (k) => appLockService.getChartInsertLock(k), chartLoggerService.logger, name, schema, true); super(db, (k) => acquireChartInsertLock(redisClient, k), chartLoggerService.logger, name, schema, true);
} }
protected async tickMajor(group: string): Promise<Partial<KVs<typeof schema>>> { protected async tickMajor(group: string): Promise<Partial<KVs<typeof schema>>> {

View File

@ -5,12 +5,13 @@
import { Injectable, Inject } from '@nestjs/common'; import { Injectable, Inject } from '@nestjs/common';
import { DataSource } from 'typeorm'; import { DataSource } from 'typeorm';
import * as Redis from 'ioredis';
import type { MiUser } from '@/models/User.js'; import type { MiUser } from '@/models/User.js';
import type { MiNote } from '@/models/Note.js'; import type { MiNote } from '@/models/Note.js';
import { AppLockService } from '@/core/AppLockService.js';
import { DI } from '@/di-symbols.js'; import { DI } from '@/di-symbols.js';
import type { NotesRepository } from '@/models/_.js'; import type { NotesRepository } from '@/models/_.js';
import { bindThis } from '@/decorators.js'; import { bindThis } from '@/decorators.js';
import { acquireChartInsertLock } from '@/misc/distributed-lock.js';
import Chart from '../core.js'; import Chart from '../core.js';
import { ChartLoggerService } from '../ChartLoggerService.js'; import { ChartLoggerService } from '../ChartLoggerService.js';
import { name, schema } from './entities/per-user-notes.js'; import { name, schema } from './entities/per-user-notes.js';
@ -25,13 +26,15 @@ export default class PerUserNotesChart extends Chart<typeof schema> { // eslint-
@Inject(DI.db) @Inject(DI.db)
private db: DataSource, private db: DataSource,
@Inject(DI.redis)
private redisClient: Redis.Redis,
@Inject(DI.notesRepository) @Inject(DI.notesRepository)
private notesRepository: NotesRepository, private notesRepository: NotesRepository,
private appLockService: AppLockService,
private chartLoggerService: ChartLoggerService, private chartLoggerService: ChartLoggerService,
) { ) {
super(db, (k) => appLockService.getChartInsertLock(k), chartLoggerService.logger, name, schema, true); super(db, (k) => acquireChartInsertLock(redisClient, k), chartLoggerService.logger, name, schema, true);
} }
protected async tickMajor(group: string): Promise<Partial<KVs<typeof schema>>> { protected async tickMajor(group: string): Promise<Partial<KVs<typeof schema>>> {

View File

@ -5,10 +5,11 @@
import { Injectable, Inject } from '@nestjs/common'; import { Injectable, Inject } from '@nestjs/common';
import { DataSource } from 'typeorm'; import { DataSource } from 'typeorm';
import * as Redis from 'ioredis';
import type { MiUser } from '@/models/User.js'; import type { MiUser } from '@/models/User.js';
import { AppLockService } from '@/core/AppLockService.js';
import { DI } from '@/di-symbols.js'; import { DI } from '@/di-symbols.js';
import { bindThis } from '@/decorators.js'; import { bindThis } from '@/decorators.js';
import { acquireChartInsertLock } from '@/misc/distributed-lock.js';
import Chart from '../core.js'; import Chart from '../core.js';
import { ChartLoggerService } from '../ChartLoggerService.js'; import { ChartLoggerService } from '../ChartLoggerService.js';
import { name, schema } from './entities/per-user-pv.js'; import { name, schema } from './entities/per-user-pv.js';
@ -23,10 +24,12 @@ export default class PerUserPvChart extends Chart<typeof schema> { // eslint-dis
@Inject(DI.db) @Inject(DI.db)
private db: DataSource, private db: DataSource,
private appLockService: AppLockService, @Inject(DI.redis)
private redisClient: Redis.Redis,
private chartLoggerService: ChartLoggerService, private chartLoggerService: ChartLoggerService,
) { ) {
super(db, (k) => appLockService.getChartInsertLock(k), chartLoggerService.logger, name, schema, true); super(db, (k) => acquireChartInsertLock(redisClient, k), chartLoggerService.logger, name, schema, true);
} }
protected async tickMajor(): Promise<Partial<KVs<typeof schema>>> { protected async tickMajor(): Promise<Partial<KVs<typeof schema>>> {

View File

@ -5,12 +5,13 @@
import { Injectable, Inject } from '@nestjs/common'; import { Injectable, Inject } from '@nestjs/common';
import { DataSource } from 'typeorm'; import { DataSource } from 'typeorm';
import * as Redis from 'ioredis';
import type { MiUser } from '@/models/User.js'; import type { MiUser } from '@/models/User.js';
import type { MiNote } from '@/models/Note.js'; import type { MiNote } from '@/models/Note.js';
import { AppLockService } from '@/core/AppLockService.js';
import { DI } from '@/di-symbols.js'; import { DI } from '@/di-symbols.js';
import { UserEntityService } from '@/core/entities/UserEntityService.js'; import { UserEntityService } from '@/core/entities/UserEntityService.js';
import { bindThis } from '@/decorators.js'; import { bindThis } from '@/decorators.js';
import { acquireChartInsertLock } from '@/misc/distributed-lock.js';
import Chart from '../core.js'; import Chart from '../core.js';
import { ChartLoggerService } from '../ChartLoggerService.js'; import { ChartLoggerService } from '../ChartLoggerService.js';
import { name, schema } from './entities/per-user-reactions.js'; import { name, schema } from './entities/per-user-reactions.js';
@ -25,11 +26,13 @@ export default class PerUserReactionsChart extends Chart<typeof schema> { // esl
@Inject(DI.db) @Inject(DI.db)
private db: DataSource, private db: DataSource,
private appLockService: AppLockService, @Inject(DI.redis)
private redisClient: Redis.Redis,
private userEntityService: UserEntityService, private userEntityService: UserEntityService,
private chartLoggerService: ChartLoggerService, private chartLoggerService: ChartLoggerService,
) { ) {
super(db, (k) => appLockService.getChartInsertLock(k), chartLoggerService.logger, name, schema, true); super(db, (k) => acquireChartInsertLock(redisClient, k), chartLoggerService.logger, name, schema, true);
} }
protected async tickMajor(group: string): Promise<Partial<KVs<typeof schema>>> { protected async tickMajor(group: string): Promise<Partial<KVs<typeof schema>>> {

View File

@ -5,10 +5,11 @@
import { Injectable, Inject } from '@nestjs/common'; import { Injectable, Inject } from '@nestjs/common';
import { DataSource } from 'typeorm'; import { DataSource } from 'typeorm';
import { AppLockService } from '@/core/AppLockService.js'; import * as Redis from 'ioredis';
import { DI } from '@/di-symbols.js'; import { DI } from '@/di-symbols.js';
import Logger from '@/logger.js'; import Logger from '@/logger.js';
import { bindThis } from '@/decorators.js'; import { bindThis } from '@/decorators.js';
import { acquireChartInsertLock } from '@/misc/distributed-lock.js';
import Chart from '../core.js'; import Chart from '../core.js';
import { name, schema } from './entities/test-grouped.js'; import { name, schema } from './entities/test-grouped.js';
import type { KVs } from '../core.js'; import type { KVs } from '../core.js';
@ -24,10 +25,12 @@ export default class TestGroupedChart extends Chart<typeof schema> { // eslint-d
@Inject(DI.db) @Inject(DI.db)
private db: DataSource, private db: DataSource,
private appLockService: AppLockService, @Inject(DI.redis)
private redisClient: Redis.Redis,
logger: Logger, logger: Logger,
) { ) {
super(db, (k) => appLockService.getChartInsertLock(k), logger, name, schema, true); super(db, (k) => acquireChartInsertLock(redisClient, k), logger, name, schema, true);
} }
protected async tickMajor(group: string): Promise<Partial<KVs<typeof schema>>> { protected async tickMajor(group: string): Promise<Partial<KVs<typeof schema>>> {

View File

@ -5,10 +5,11 @@
import { Injectable, Inject } from '@nestjs/common'; import { Injectable, Inject } from '@nestjs/common';
import { DataSource } from 'typeorm'; import { DataSource } from 'typeorm';
import { AppLockService } from '@/core/AppLockService.js'; import * as Redis from 'ioredis';
import { DI } from '@/di-symbols.js'; import { DI } from '@/di-symbols.js';
import Logger from '@/logger.js'; import Logger from '@/logger.js';
import { bindThis } from '@/decorators.js'; import { bindThis } from '@/decorators.js';
import { acquireChartInsertLock } from '@/misc/distributed-lock.js';
import Chart from '../core.js'; import Chart from '../core.js';
import { name, schema } from './entities/test-intersection.js'; import { name, schema } from './entities/test-intersection.js';
import type { KVs } from '../core.js'; import type { KVs } from '../core.js';
@ -22,10 +23,12 @@ export default class TestIntersectionChart extends Chart<typeof schema> { // esl
@Inject(DI.db) @Inject(DI.db)
private db: DataSource, private db: DataSource,
private appLockService: AppLockService, @Inject(DI.redis)
private redisClient: Redis.Redis,
logger: Logger, logger: Logger,
) { ) {
super(db, (k) => appLockService.getChartInsertLock(k), logger, name, schema); super(db, (k) => acquireChartInsertLock(redisClient, k), logger, name, schema);
} }
protected async tickMajor(): Promise<Partial<KVs<typeof schema>>> { protected async tickMajor(): Promise<Partial<KVs<typeof schema>>> {

View File

@ -5,10 +5,11 @@
import { Injectable, Inject } from '@nestjs/common'; import { Injectable, Inject } from '@nestjs/common';
import { DataSource } from 'typeorm'; import { DataSource } from 'typeorm';
import { AppLockService } from '@/core/AppLockService.js'; import * as Redis from 'ioredis';
import { DI } from '@/di-symbols.js'; import { DI } from '@/di-symbols.js';
import Logger from '@/logger.js'; import Logger from '@/logger.js';
import { bindThis } from '@/decorators.js'; import { bindThis } from '@/decorators.js';
import { acquireChartInsertLock } from '@/misc/distributed-lock.js';
import Chart from '../core.js'; import Chart from '../core.js';
import { name, schema } from './entities/test-unique.js'; import { name, schema } from './entities/test-unique.js';
import type { KVs } from '../core.js'; import type { KVs } from '../core.js';
@ -22,10 +23,12 @@ export default class TestUniqueChart extends Chart<typeof schema> { // eslint-di
@Inject(DI.db) @Inject(DI.db)
private db: DataSource, private db: DataSource,
private appLockService: AppLockService, @Inject(DI.redis)
private redisClient: Redis.Redis,
logger: Logger, logger: Logger,
) { ) {
super(db, (k) => appLockService.getChartInsertLock(k), logger, name, schema); super(db, (k) => acquireChartInsertLock(redisClient, k), logger, name, schema);
} }
protected async tickMajor(): Promise<Partial<KVs<typeof schema>>> { protected async tickMajor(): Promise<Partial<KVs<typeof schema>>> {

View File

@ -5,10 +5,11 @@
import { Injectable, Inject } from '@nestjs/common'; import { Injectable, Inject } from '@nestjs/common';
import { DataSource } from 'typeorm'; import { DataSource } from 'typeorm';
import { AppLockService } from '@/core/AppLockService.js'; import * as Redis from 'ioredis';
import { DI } from '@/di-symbols.js'; import { DI } from '@/di-symbols.js';
import Logger from '@/logger.js'; import Logger from '@/logger.js';
import { bindThis } from '@/decorators.js'; import { bindThis } from '@/decorators.js';
import { acquireChartInsertLock } from '@/misc/distributed-lock.js';
import Chart from '../core.js'; import Chart from '../core.js';
import { name, schema } from './entities/test.js'; import { name, schema } from './entities/test.js';
import type { KVs } from '../core.js'; import type { KVs } from '../core.js';
@ -24,10 +25,12 @@ export default class TestChart extends Chart<typeof schema> { // eslint-disable-
@Inject(DI.db) @Inject(DI.db)
private db: DataSource, private db: DataSource,
private appLockService: AppLockService, @Inject(DI.redis)
private redisClient: Redis.Redis,
logger: Logger, logger: Logger,
) { ) {
super(db, (k) => appLockService.getChartInsertLock(k), logger, name, schema); super(db, (k) => acquireChartInsertLock(redisClient, k), logger, name, schema);
} }
protected async tickMajor(): Promise<Partial<KVs<typeof schema>>> { protected async tickMajor(): Promise<Partial<KVs<typeof schema>>> {

View File

@ -5,12 +5,13 @@
import { Injectable, Inject } from '@nestjs/common'; import { Injectable, Inject } from '@nestjs/common';
import { Not, IsNull, DataSource } from 'typeorm'; import { Not, IsNull, DataSource } from 'typeorm';
import * as Redis from 'ioredis';
import type { MiUser } from '@/models/User.js'; import type { MiUser } from '@/models/User.js';
import { AppLockService } from '@/core/AppLockService.js';
import { DI } from '@/di-symbols.js'; import { DI } from '@/di-symbols.js';
import { UserEntityService } from '@/core/entities/UserEntityService.js'; import { UserEntityService } from '@/core/entities/UserEntityService.js';
import type { UsersRepository } from '@/models/_.js'; import type { UsersRepository } from '@/models/_.js';
import { bindThis } from '@/decorators.js'; import { bindThis } from '@/decorators.js';
import { acquireChartInsertLock } from '@/misc/distributed-lock.js';
import Chart from '../core.js'; import Chart from '../core.js';
import { ChartLoggerService } from '../ChartLoggerService.js'; import { ChartLoggerService } from '../ChartLoggerService.js';
import { name, schema } from './entities/users.js'; import { name, schema } from './entities/users.js';
@ -25,14 +26,16 @@ export default class UsersChart extends Chart<typeof schema> { // eslint-disable
@Inject(DI.db) @Inject(DI.db)
private db: DataSource, private db: DataSource,
@Inject(DI.redis)
private redisClient: Redis.Redis,
@Inject(DI.usersRepository) @Inject(DI.usersRepository)
private usersRepository: UsersRepository, private usersRepository: UsersRepository,
private appLockService: AppLockService,
private userEntityService: UserEntityService, private userEntityService: UserEntityService,
private chartLoggerService: ChartLoggerService, private chartLoggerService: ChartLoggerService,
) { ) {
super(db, (k) => appLockService.getChartInsertLock(k), chartLoggerService.logger, name, schema); super(db, (k) => acquireChartInsertLock(redisClient, k), chartLoggerService.logger, name, schema);
} }
protected async tickMajor(): Promise<Partial<KVs<typeof schema>>> { protected async tickMajor(): Promise<Partial<KVs<typeof schema>>> {

View File

@ -0,0 +1,49 @@
/*
* SPDX-FileCopyrightText: syuilo and misskey-project
* SPDX-License-Identifier: AGPL-3.0-only
*/
import * as Redis from 'ioredis';
export async function acquireDistributedLock(
redis: Redis.Redis,
name: string,
timeout: number,
maxRetries: number,
retryInterval: number,
): Promise<() => Promise<void>> {
const lockKey = `lock:${name}`;
const identifier = Math.random().toString(36).slice(2);
let retries = 0;
while (retries < maxRetries) {
const result = await redis.set(lockKey, identifier, 'PX', timeout, 'NX');
if (result === 'OK') {
return async () => {
const currentIdentifier = await redis.get(lockKey);
if (currentIdentifier === identifier) {
await redis.del(lockKey);
}
};
}
await new Promise(resolve => setTimeout(resolve, retryInterval));
retries++;
}
throw new Error(`Failed to acquire lock ${name}`);
}
export function acquireApObjectLock(
redis: Redis.Redis,
uri: string,
): Promise<() => Promise<void>> {
return acquireDistributedLock(redis, `ap-object:${uri}`, 30 * 1000, 50, 100);
}
export function acquireChartInsertLock(
redis: Redis.Redis,
name: string,
): Promise<() => Promise<void>> {
return acquireDistributedLock(redis, `chart-insert:${name}`, 30 * 1000, 50, 500);
}

View File

@ -9,3 +9,4 @@ beforeAll(async () => {
await initTestDb(false); await initTestDb(false);
await sendEnvResetRequest(); await sendEnvResetRequest();
}); });

View File

@ -26,7 +26,7 @@ import { GlobalEventService } from '@/core/GlobalEventService.js';
import { ModerationLogService } from '@/core/ModerationLogService.js'; import { ModerationLogService } from '@/core/ModerationLogService.js';
import { secureRndstr } from '@/misc/secure-rndstr.js'; import { secureRndstr } from '@/misc/secure-rndstr.js';
import type { TestingModule } from '@nestjs/testing'; import type { TestingModule } from '@nestjs/testing';
import type { MockFunctionMetadata } from 'jest-mock'; import type { MockMetadata } from 'jest-mock';
const moduleMocker = new ModuleMocker(global); const moduleMocker = new ModuleMocker(global);
@ -84,7 +84,7 @@ describe('AnnouncementService', () => {
log: jest.fn(), log: jest.fn(),
}; };
} else if (typeof token === 'function') { } else if (typeof token === 'function') {
const mockMetadata = moduleMocker.getMetadata(token) as MockFunctionMetadata<any, any>; const mockMetadata = moduleMocker.getMetadata(token) as MockMetadata<any, any>;
const Mock = moduleMocker.generateFromMetadata(mockMetadata); const Mock = moduleMocker.generateFromMetadata(mockMetadata);
return new Mock(); return new Mock();
} }

View File

@ -446,7 +446,7 @@ describe('CaptchaService', () => {
if (!res.success) { if (!res.success) {
expect(res.error.code).toBe(code); expect(res.error.code).toBe(code);
} }
expect(metaService.update).not.toBeCalled(); expect(metaService.update).not.toHaveBeenCalled();
} }
describe('invalidParameters', () => { describe('invalidParameters', () => {

View File

@ -53,7 +53,7 @@ describe('DriveService', () => {
s3Mock.on(DeleteObjectCommand) s3Mock.on(DeleteObjectCommand)
.rejects(new InvalidObjectState({ $metadata: {}, message: '' })); .rejects(new InvalidObjectState({ $metadata: {}, message: '' }));
await expect(driveService.deleteObjectStorageFile('unexpected')).rejects.toThrowError(Error); await expect(driveService.deleteObjectStorageFile('unexpected')).rejects.toThrow(Error);
}); });
test('delete a file with no valid key', async () => { test('delete a file with no valid key', async () => {

View File

@ -17,7 +17,7 @@ import { FileInfo, FileInfoService } from '@/core/FileInfoService.js';
import { AiService } from '@/core/AiService.js'; import { AiService } from '@/core/AiService.js';
import { LoggerService } from '@/core/LoggerService.js'; import { LoggerService } from '@/core/LoggerService.js';
import type { TestingModule } from '@nestjs/testing'; import type { TestingModule } from '@nestjs/testing';
import type { MockFunctionMetadata } from 'jest-mock'; import type { MockMetadata } from 'jest-mock';
const _filename = fileURLToPath(import.meta.url); const _filename = fileURLToPath(import.meta.url);
const _dirname = dirname(_filename); const _dirname = dirname(_filename);
@ -54,7 +54,7 @@ describe('FileInfoService', () => {
// return { }; // return { };
//} //}
if (typeof token === 'function') { if (typeof token === 'function') {
const mockMetadata = moduleMocker.getMetadata(token) as MockFunctionMetadata<any, any>; const mockMetadata = moduleMocker.getMetadata(token) as MockMetadata<any, any>;
const Mock = moduleMocker.generateFromMetadata(mockMetadata); const Mock = moduleMocker.generateFromMetadata(mockMetadata);
return new Mock(); return new Mock();
} }

View File

@ -9,7 +9,7 @@ import { jest } from '@jest/globals';
import { Test } from '@nestjs/testing'; import { Test } from '@nestjs/testing';
import { ModuleMocker } from 'jest-mock'; import { ModuleMocker } from 'jest-mock';
import type { TestingModule } from '@nestjs/testing'; import type { TestingModule } from '@nestjs/testing';
import type { MockFunctionMetadata } from 'jest-mock'; import type { MockMetadata } from 'jest-mock';
import { ApRendererService } from '@/core/activitypub/ApRendererService.js'; import { ApRendererService } from '@/core/activitypub/ApRendererService.js';
import { UserEntityService } from '@/core/entities/UserEntityService.js'; import { UserEntityService } from '@/core/entities/UserEntityService.js';
import { IdService } from '@/core/IdService.js'; import { IdService } from '@/core/IdService.js';
@ -45,7 +45,7 @@ describe('RelayService', () => {
return { deliver: jest.fn() }; return { deliver: jest.fn() };
} }
if (typeof token === 'function') { if (typeof token === 'function') {
const mockMetadata = moduleMocker.getMetadata(token) as MockFunctionMetadata<any, any>; const mockMetadata = moduleMocker.getMetadata(token) as MockMetadata<any, any>;
const Mock = moduleMocker.generateFromMetadata(mockMetadata); const Mock = moduleMocker.generateFromMetadata(mockMetadata);
return new Mock(); return new Mock();
} }

View File

@ -11,7 +11,7 @@ import { ModuleMocker } from 'jest-mock';
import { Test } from '@nestjs/testing'; import { Test } from '@nestjs/testing';
import * as lolex from '@sinonjs/fake-timers'; import * as lolex from '@sinonjs/fake-timers';
import type { TestingModule } from '@nestjs/testing'; import type { TestingModule } from '@nestjs/testing';
import type { MockFunctionMetadata } from 'jest-mock'; import type { MockMetadata } from 'jest-mock';
import { GlobalModule } from '@/GlobalModule.js'; import { GlobalModule } from '@/GlobalModule.js';
import { RoleService } from '@/core/RoleService.js'; import { RoleService } from '@/core/RoleService.js';
import { import {
@ -104,6 +104,8 @@ describe('RoleService', () => {
beforeEach(async () => { beforeEach(async () => {
clock = lolex.install({ clock = lolex.install({
// https://github.com/sinonjs/sinon/issues/2620
toFake: Object.keys(lolex.timers).filter((key) => !['nextTick', 'queueMicrotask'].includes(key)) as lolex.FakeMethod[],
now: new Date(), now: new Date(),
shouldClearNativeTimers: true, shouldClearNativeTimers: true,
}); });
@ -135,7 +137,7 @@ describe('RoleService', () => {
return { fetch: jest.fn() }; return { fetch: jest.fn() };
} }
if (typeof token === 'function') { if (typeof token === 'function') {
const mockMetadata = moduleMocker.getMetadata(token) as MockFunctionMetadata<any, any>; const mockMetadata = moduleMocker.getMetadata(token) as MockMetadata<any, any>;
const Mock = moduleMocker.generateFromMetadata(mockMetadata); const Mock = moduleMocker.generateFromMetadata(mockMetadata);
return new Mock(); return new Mock();
} }

View File

@ -72,7 +72,7 @@ describe('S3Service', () => {
Bucket: 'fake', Bucket: 'fake',
Key: 'fake', Key: 'fake',
Body: 'x', Body: 'x',
})).rejects.toThrowError(Error); })).rejects.toThrow(Error);
}); });
test('upload a large file error', async () => { test('upload a large file error', async () => {
@ -82,7 +82,7 @@ describe('S3Service', () => {
Bucket: 'fake', Bucket: 'fake',
Key: 'fake', Key: 'fake',
Body: 'x'.repeat(8 * 1024 * 1024 + 1), // デフォルトpartSizeにしている 8 * 1024 * 1024 を越えるサイズ Body: 'x'.repeat(8 * 1024 * 1024 + 1), // デフォルトpartSizeにしている 8 * 1024 * 1024 を越えるサイズ
})).rejects.toThrowError(Error); })).rejects.toThrow(Error);
}); });
}); });
}); });

View File

@ -9,7 +9,7 @@ import { Test, TestingModule } from '@nestjs/testing';
import { FastifyReply, FastifyRequest } from 'fastify'; import { FastifyReply, FastifyRequest } from 'fastify';
import { AuthenticationResponseJSON } from '@simplewebauthn/types'; import { AuthenticationResponseJSON } from '@simplewebauthn/types';
import { HttpHeader } from 'fastify/types/utils.js'; import { HttpHeader } from 'fastify/types/utils.js';
import { MockFunctionMetadata, ModuleMocker } from 'jest-mock'; import { MockMetadata, ModuleMocker } from 'jest-mock';
import { MiUser } from '@/models/User.js'; import { MiUser } from '@/models/User.js';
import { MiUserProfile, UserProfilesRepository, UsersRepository } from '@/models/_.js'; import { MiUserProfile, UserProfilesRepository, UsersRepository } from '@/models/_.js';
import { IdService } from '@/core/IdService.js'; import { IdService } from '@/core/IdService.js';
@ -95,7 +95,7 @@ describe('SigninWithPasskeyApiService', () => {
], ],
}).useMocker((token) => { }).useMocker((token) => {
if (typeof token === 'function') { if (typeof token === 'function') {
const mockMetadata = moduleMocker.getMetadata(token) as MockFunctionMetadata<any, any>; const mockMetadata = moduleMocker.getMetadata(token) as MockMetadata<any, any>;
const Mock = moduleMocker.generateFromMetadata(mockMetadata); const Mock = moduleMocker.generateFromMetadata(mockMetadata);
return new Mock(); return new Mock();
} }

View File

@ -9,6 +9,7 @@ import * as assert from 'assert';
import { jest } from '@jest/globals'; import { jest } from '@jest/globals';
import * as lolex from '@sinonjs/fake-timers'; import * as lolex from '@sinonjs/fake-timers';
import { DataSource } from 'typeorm'; import { DataSource } from 'typeorm';
import * as Redis from 'ioredis';
import TestChart from '@/core/chart/charts/test.js'; import TestChart from '@/core/chart/charts/test.js';
import TestGroupedChart from '@/core/chart/charts/test-grouped.js'; import TestGroupedChart from '@/core/chart/charts/test-grouped.js';
import TestUniqueChart from '@/core/chart/charts/test-unique.js'; import TestUniqueChart from '@/core/chart/charts/test-unique.js';
@ -18,16 +19,16 @@ import { entity as TestGroupedChartEntity } from '@/core/chart/charts/entities/t
import { entity as TestUniqueChartEntity } from '@/core/chart/charts/entities/test-unique.js'; import { entity as TestUniqueChartEntity } from '@/core/chart/charts/entities/test-unique.js';
import { entity as TestIntersectionChartEntity } from '@/core/chart/charts/entities/test-intersection.js'; import { entity as TestIntersectionChartEntity } from '@/core/chart/charts/entities/test-intersection.js';
import { loadConfig } from '@/config.js'; import { loadConfig } from '@/config.js';
import type { AppLockService } from '@/core/AppLockService.js';
import Logger from '@/logger.js'; import Logger from '@/logger.js';
describe('Chart', () => { describe('Chart', () => {
const config = loadConfig(); const config = loadConfig();
const appLockService = {
getChartInsertLock: () => () => Promise.resolve(() => {}),
} as unknown as jest.Mocked<AppLockService>;
let db: DataSource | undefined; let db: DataSource | undefined;
let redisClient = {
set: () => Promise.resolve('OK'),
get: () => Promise.resolve(null),
} as unknown as jest.Mocked<Redis.Redis>;
let testChart: TestChart; let testChart: TestChart;
let testGroupedChart: TestGroupedChart; let testGroupedChart: TestGroupedChart;
@ -64,12 +65,14 @@ describe('Chart', () => {
await db.initialize(); await db.initialize();
const logger = new Logger('chart'); // TODO: モックにする const logger = new Logger('chart'); // TODO: モックにする
testChart = new TestChart(db, appLockService, logger); testChart = new TestChart(db, redisClient, logger);
testGroupedChart = new TestGroupedChart(db, appLockService, logger); testGroupedChart = new TestGroupedChart(db, redisClient, logger);
testUniqueChart = new TestUniqueChart(db, appLockService, logger); testUniqueChart = new TestUniqueChart(db, redisClient, logger);
testIntersectionChart = new TestIntersectionChart(db, appLockService, logger); testIntersectionChart = new TestIntersectionChart(db, redisClient, logger);
clock = lolex.install({ clock = lolex.install({
// https://github.com/sinonjs/sinon/issues/2620
toFake: Object.keys(lolex.timers).filter((key) => !['nextTick', 'queueMicrotask'].includes(key)) as lolex.FakeMethod[],
now: new Date(Date.UTC(2000, 0, 1, 0, 0, 0)), now: new Date(Date.UTC(2000, 0, 1, 0, 0, 0)),
shouldClearNativeTimers: true, shouldClearNativeTimers: true,
}); });

View File

@ -141,6 +141,8 @@ describe('CheckModeratorsActivityProcessorService', () => {
beforeEach(async () => { beforeEach(async () => {
clock = lolex.install({ clock = lolex.install({
// https://github.com/sinonjs/sinon/issues/2620
toFake: Object.keys(lolex.timers).filter((key) => !['nextTick', 'queueMicrotask'].includes(key)) as lolex.FakeMethod[],
now: new Date(baseDate), now: new Date(baseDate),
shouldClearNativeTimers: true, shouldClearNativeTimers: true,
}); });

File diff suppressed because it is too large Load Diff

View File

@ -14,6 +14,7 @@ onlyBuiltDependencies:
- '@nestjs/core' - '@nestjs/core'
- '@parcel/watcher' - '@parcel/watcher'
- '@sentry/profiling-node' - '@sentry/profiling-node'
- '@sentry-internal/node-cpu-profiler'
- '@swc/core' - '@swc/core'
- '@tensorflow/tfjs-node' - '@tensorflow/tfjs-node'
- bufferutil - bufferutil