From bbb49457f9fb5d46402e913c92ebf77722cad6ff Mon Sep 17 00:00:00 2001 From: syuilo Date: Sun, 4 Dec 2022 15:03:09 +0900 Subject: [PATCH] refactor: introduce bindThis decorator to bind this automaticaly --- .../backend/src/core/AccountUpdateService.ts | 2 + packages/backend/src/core/AiService.ts | 3 + packages/backend/src/core/AntennaService.ts | 6 ++ packages/backend/src/core/AppLockService.ts | 4 + packages/backend/src/core/CaptchaService.ts | 5 + .../src/core/CreateNotificationService.ts | 4 + .../src/core/CreateSystemUserService.ts | 2 + .../backend/src/core/CustomEmojiService.ts | 8 ++ .../backend/src/core/DeleteAccountService.ts | 2 + packages/backend/src/core/DownloadService.ts | 4 + packages/backend/src/core/DriveService.ts | 11 ++ packages/backend/src/core/EmailService.ts | 3 + .../src/core/FederatedInstanceService.ts | 2 + .../src/core/FetchInstanceMetadataService.ts | 10 ++ packages/backend/src/core/FileInfoService.ts | 11 ++ .../backend/src/core/GlobalEventService.ts | 3 + packages/backend/src/core/HashtagService.ts | 4 + .../backend/src/core/HttpRequestService.ts | 5 + packages/backend/src/core/IdService.ts | 2 + .../src/core/ImageProcessingService.ts | 7 ++ .../backend/src/core/InstanceActorService.ts | 2 + .../src/core/InternalStorageService.ts | 6 ++ packages/backend/src/core/LoggerService.ts | 2 + packages/backend/src/core/MessagingService.ts | 7 ++ packages/backend/src/core/MetaService.ts | 7 +- packages/backend/src/core/MfmService.ts | 3 + .../backend/src/core/ModerationLogService.ts | 2 + .../backend/src/core/NoteCreateService.ts | 13 +++ .../backend/src/core/NoteDeleteService.ts | 4 + .../backend/src/core/NotePiningService.ts | 4 + packages/backend/src/core/NoteReadService.ts | 3 + .../backend/src/core/NotificationService.ts | 5 + packages/backend/src/core/PollService.ts | 3 + .../backend/src/core/ProxyAccountService.ts | 2 + .../src/core/PushNotificationService.ts | 1 + packages/backend/src/core/QueryService.ts | 10 ++ packages/backend/src/core/QueueService.ts | 20 ++++ packages/backend/src/core/ReactionService.ts | 8 ++ packages/backend/src/core/RelayService.ts | 8 ++ .../backend/src/core/RemoteLoggerService.ts | 1 + .../src/core/RemoteUserResolveService.ts | 3 + packages/backend/src/core/S3Service.ts | 2 + packages/backend/src/core/SignupService.ts | 2 + .../core/TwoFactorAuthenticationService.ts | 4 + .../backend/src/core/UserBlockingService.ts | 6 ++ packages/backend/src/core/UserCacheService.ts | 5 +- .../backend/src/core/UserFollowingService.ts | 16 +++ .../src/core/UserKeypairStoreService.ts | 2 + packages/backend/src/core/UserListService.ts | 2 + .../backend/src/core/UserMutingService.ts | 2 + .../backend/src/core/UserSuspendService.ts | 3 + packages/backend/src/core/UtilityService.ts | 6 ++ .../src/core/VideoProcessingService.ts | 2 + packages/backend/src/core/WebfingerService.ts | 3 + packages/backend/src/core/WebhookService.ts | 6 +- .../src/core/activitypub/ApAudienceService.ts | 5 + .../core/activitypub/ApDbResolverService.ts | 7 ++ .../activitypub/ApDeliverManagerService.ts | 8 ++ .../src/core/activitypub/ApInboxService.ts | 28 +++++ .../src/core/activitypub/ApLoggerService.ts | 1 + .../src/core/activitypub/ApMfmService.ts | 3 + .../src/core/activitypub/ApRendererService.ts | 33 ++++++ .../src/core/activitypub/ApRequestService.ts | 9 ++ .../src/core/activitypub/ApResolverService.ts | 100 ++++++++++-------- .../core/activitypub/LdSignatureService.ts | 33 +++--- .../core/activitypub/models/ApImageService.ts | 3 + .../activitypub/models/ApMentionService.ts | 3 + .../core/activitypub/models/ApNoteService.ts | 6 ++ .../activitypub/models/ApPersonService.ts | 8 ++ .../activitypub/models/ApQuestionService.ts | 3 + .../src/core/chart/ChartLoggerService.ts | 1 + .../src/core/chart/ChartManagementService.ts | 2 + .../src/core/chart/charts/active-users.ts | 3 + .../src/core/chart/charts/ap-request.ts | 4 + .../backend/src/core/chart/charts/drive.ts | 2 + .../src/core/chart/charts/federation.ts | 3 + .../backend/src/core/chart/charts/hashtag.ts | 2 + .../backend/src/core/chart/charts/instance.ts | 8 ++ .../backend/src/core/chart/charts/notes.ts | 2 + .../src/core/chart/charts/per-user-drive.ts | 2 + .../core/chart/charts/per-user-following.ts | 2 + .../src/core/chart/charts/per-user-notes.ts | 2 + .../core/chart/charts/per-user-reactions.ts | 2 + .../src/core/chart/charts/test-grouped.ts | 2 + .../core/chart/charts/test-intersection.ts | 3 + .../src/core/chart/charts/test-unique.ts | 2 + .../backend/src/core/chart/charts/test.ts | 3 + .../backend/src/core/chart/charts/users.ts | 2 + packages/backend/src/core/chart/core.ts | 11 ++ .../entities/AbuseUserReportEntityService.ts | 3 + .../src/core/entities/AntennaEntityService.ts | 2 + .../src/core/entities/AppEntityService.ts | 2 + .../core/entities/AuthSessionEntityService.ts | 2 + .../core/entities/BlockingEntityService.ts | 3 + .../src/core/entities/ChannelEntityService.ts | 2 + .../src/core/entities/ClipEntityService.ts | 3 + .../core/entities/DriveFileEntityService.ts | 11 ++ .../core/entities/DriveFolderEntityService.ts | 2 + .../src/core/entities/EmojiEntityService.ts | 3 + .../entities/FollowRequestEntityService.ts | 2 + .../core/entities/FollowingEntityService.ts | 7 ++ .../core/entities/GalleryLikeEntityService.ts | 3 + .../core/entities/GalleryPostEntityService.ts | 3 + .../src/core/entities/HashtagEntityService.ts | 3 + .../core/entities/InstanceEntityService.ts | 3 + .../entities/MessagingMessageEntityService.ts | 2 + .../entities/ModerationLogEntityService.ts | 3 + .../src/core/entities/MutingEntityService.ts | 3 + .../src/core/entities/NoteEntityService.ts | 8 ++ .../entities/NoteFavoriteEntityService.ts | 3 + .../entities/NoteReactionEntityService.ts | 2 + .../entities/NotificationEntityService.ts | 3 + .../src/core/entities/PageEntityService.ts | 3 + .../core/entities/PageLikeEntityService.ts | 3 + .../src/core/entities/SigninEntityService.ts | 2 + .../src/core/entities/UserEntityService.ts | 12 +++ .../core/entities/UserGroupEntityService.ts | 2 + .../UserGroupInvitationEntityService.ts | 3 + .../core/entities/UserListEntityService.ts | 2 + .../backend/src/daemons/JanitorService.ts | 3 + .../backend/src/daemons/QueueStatsService.ts | 3 + .../backend/src/daemons/ServerStatsService.ts | 3 + packages/backend/src/decorators.ts | 41 +++++++ packages/backend/src/logger.ts | 8 ++ packages/backend/src/misc/cache.ts | 7 ++ packages/backend/src/misc/i18n.ts | 3 +- packages/backend/src/postgre.ts | 8 ++ .../src/queue/DbQueueProcessorsService.ts | 2 + .../ObjectStorageQueueProcessorsService.ts | 2 + .../backend/src/queue/QueueLoggerService.ts | 1 + .../src/queue/QueueProcessorService.ts | 2 + .../src/queue/SystemQueueProcessorsService.ts | 2 + .../CheckExpiredMutingsProcessorService.ts | 2 + .../processors/CleanChartsProcessorService.ts | 2 + .../queue/processors/CleanProcessorService.ts | 2 + .../CleanRemoteFilesProcessorService.ts | 2 + .../DeleteAccountProcessorService.ts | 2 + .../DeleteDriveFilesProcessorService.ts | 2 + .../processors/DeleteFileProcessorService.ts | 2 + .../processors/DeliverProcessorService.ts | 2 + .../EndedPollNotificationProcessorService.ts | 2 + .../ExportBlockingProcessorService.ts | 2 + .../ExportCustomEmojisProcessorService.ts | 2 + .../ExportFollowingProcessorService.ts | 2 + .../ExportMutingProcessorService.ts | 2 + .../processors/ExportNotesProcessorService.ts | 2 + .../ExportUserListsProcessorService.ts | 2 + .../ImportBlockingProcessorService.ts | 2 + .../ImportCustomEmojisProcessorService.ts | 2 + .../ImportFollowingProcessorService.ts | 2 + .../ImportMutingProcessorService.ts | 2 + .../ImportUserListsProcessorService.ts | 2 + .../queue/processors/InboxProcessorService.ts | 2 + .../ResyncChartsProcessorService.ts | 2 + .../processors/TickChartsProcessorService.ts | 2 + .../WebhookDeliverProcessorService.ts | 2 + .../src/server/ActivityPubServerService.ts | 12 ++- .../backend/src/server/FileServerService.ts | 6 +- .../src/server/MediaProxyServerService.ts | 5 +- .../src/server/NodeinfoServerService.ts | 5 +- packages/backend/src/server/ServerService.ts | 2 + .../src/server/WellKnownServerService.ts | 4 +- .../backend/src/server/api/ApiCallService.ts | 7 ++ .../src/server/api/ApiLoggerService.ts | 1 + .../src/server/api/ApiServerService.ts | 4 +- .../src/server/api/AuthenticateService.ts | 2 + .../backend/src/server/api/GetterService.ts | 5 + .../src/server/api/RateLimiterService.ts | 2 + .../src/server/api/SigninApiService.ts | 2 + .../backend/src/server/api/SigninService.ts | 2 + .../src/server/api/SignupApiService.ts | 3 + .../server/api/StreamingApiServerService.ts | 2 + .../api/endpoints/admin/suspend-user.ts | 3 + .../src/server/api/endpoints/ap/show.ts | 3 + .../api/integration/DiscordServerService.ts | 6 +- .../api/integration/GithubServerService.ts | 6 +- .../api/integration/TwitterServerService.ts | 6 +- .../src/server/api/stream/ChannelsService.ts | 2 + .../backend/src/server/api/stream/channel.ts | 2 + .../src/server/api/stream/channels/admin.ts | 3 + .../src/server/api/stream/channels/antenna.ts | 7 +- .../src/server/api/stream/channels/channel.ts | 11 +- .../src/server/api/stream/channels/drive.ts | 3 + .../api/stream/channels/global-timeline.ts | 7 +- .../src/server/api/stream/channels/hashtag.ts | 7 +- .../api/stream/channels/home-timeline.ts | 7 +- .../api/stream/channels/hybrid-timeline.ts | 7 +- .../api/stream/channels/local-timeline.ts | 7 +- .../src/server/api/stream/channels/main.ts | 3 + .../api/stream/channels/messaging-index.ts | 3 + .../server/api/stream/channels/messaging.ts | 13 ++- .../server/api/stream/channels/queue-stats.ts | 10 +- .../api/stream/channels/server-stats.ts | 10 +- .../server/api/stream/channels/user-list.ts | 13 ++- .../backend/src/server/api/stream/index.ts | 32 +++++- .../src/server/web/ClientServerService.ts | 5 +- .../backend/src/server/web/FeedService.ts | 2 + .../src/server/web/UrlPreviewService.ts | 3 + packages/backend/test/misc/mock-resolver.ts | 1 + 199 files changed, 969 insertions(+), 96 deletions(-) create mode 100644 packages/backend/src/decorators.ts diff --git a/packages/backend/src/core/AccountUpdateService.ts b/packages/backend/src/core/AccountUpdateService.ts index a5ab4fdfce..5f6dfca0ca 100644 --- a/packages/backend/src/core/AccountUpdateService.ts +++ b/packages/backend/src/core/AccountUpdateService.ts @@ -7,6 +7,7 @@ import { ApRendererService } from '@/core/activitypub/ApRendererService.js'; import { RelayService } from '@/core/RelayService.js'; import { ApDeliverManagerService } from '@/core/activitypub/ApDeliverManagerService.js'; import { UserEntityService } from '@/core/entities/UserEntityService.js'; +import { bindThis } from '@/decorators.js'; @Injectable() export class AccountUpdateService { @@ -24,6 +25,7 @@ export class AccountUpdateService { ) { } + @bindThis public async publishToFollowers(userId: User['id']) { const user = await this.usersRepository.findOneBy({ id: userId }); if (user == null) throw new Error('user not found'); diff --git a/packages/backend/src/core/AiService.ts b/packages/backend/src/core/AiService.ts index 15084b8ff1..aee9504c85 100644 --- a/packages/backend/src/core/AiService.ts +++ b/packages/backend/src/core/AiService.ts @@ -12,6 +12,7 @@ const _dirname = dirname(_filename); const REQUIRED_CPU_FLAGS = ['avx2', 'fma']; let isSupportedCpu: undefined | boolean = undefined; +import { bindThis } from '@/decorators.js'; @Injectable() export class AiService { @@ -23,6 +24,7 @@ export class AiService { ) { } + @bindThis public async detectSensitive(path: string): Promise { try { if (isSupportedCpu === undefined) { @@ -53,6 +55,7 @@ export class AiService { } } + @bindThis private async getCpuFlags(): Promise { const str = await si.cpuFlags(); return str.split(/\s+/); diff --git a/packages/backend/src/core/AntennaService.ts b/packages/backend/src/core/AntennaService.ts index 8046ba5311..9c120c993d 100644 --- a/packages/backend/src/core/AntennaService.ts +++ b/packages/backend/src/core/AntennaService.ts @@ -13,6 +13,7 @@ import { DI } from '@/di-symbols.js'; import type { MutingsRepository, BlockingsRepository, NotesRepository, AntennaNotesRepository, AntennasRepository, UserGroupJoiningsRepository, UserListJoiningsRepository } from '@/models/index.js'; import { UtilityService } from '@/core/UtilityService.js'; import type { OnApplicationShutdown } from '@nestjs/common'; +import { bindThis } from '@/decorators.js'; @Injectable() export class AntennaService implements OnApplicationShutdown { @@ -56,10 +57,12 @@ export class AntennaService implements OnApplicationShutdown { this.redisSubscriber.on('message', this.onRedisMessage); } + @bindThis public onApplicationShutdown(signal?: string | undefined) { this.redisSubscriber.off('message', this.onRedisMessage); } + @bindThis private async onRedisMessage(_: string, data: string): Promise { const obj = JSON.parse(data); @@ -81,6 +84,7 @@ export class AntennaService implements OnApplicationShutdown { } } + @bindThis public async addNoteToAntenna(antenna: Antenna, note: Note, noteUser: { id: User['id']; }): Promise { // 通知しない設定になっているか、自分自身の投稿なら既読にする const read = !antenna.notify || (antenna.userId === noteUser.id); @@ -133,6 +137,7 @@ export class AntennaService implements OnApplicationShutdown { /** * noteUserFollowers / antennaUserFollowing はどちらか一方が指定されていればよい */ + @bindThis public async checkHitAntenna(antenna: Antenna, note: (Note | Packed<'Note'>), noteUser: { id: User['id']; username: string; host: string | null; }, noteUserFollowers?: User['id'][], antennaUserFollowing?: User['id'][]): Promise { if (note.visibility === 'specified') return false; @@ -217,6 +222,7 @@ export class AntennaService implements OnApplicationShutdown { return true; } + @bindThis public async getAntennas() { if (!this.antennasFetched) { this.antennas = await this.antennasRepository.find(); diff --git a/packages/backend/src/core/AppLockService.ts b/packages/backend/src/core/AppLockService.ts index 04b3d8b112..1f512b5790 100644 --- a/packages/backend/src/core/AppLockService.ts +++ b/packages/backend/src/core/AppLockService.ts @@ -8,6 +8,7 @@ import { DI } from '@/di-symbols.js'; * Retry delay (ms) for lock acquisition */ const retryDelay = 100; +import { bindThis } from '@/decorators.js'; @Injectable() export class AppLockService { @@ -26,14 +27,17 @@ export class AppLockService { * @param timeout Lock timeout (ms), The timeout releases previous lock. * @returns Unlock function */ + @bindThis public getApLock(uri: string, timeout = 30 * 1000): Promise<() => void> { return this.lock(`ap-object:${uri}`, timeout); } + @bindThis public getFetchInstanceMetadataLock(host: string, timeout = 30 * 1000): Promise<() => void> { return this.lock(`instance:${host}`, timeout); } + @bindThis public getChartInsertLock(lockKey: string, timeout = 30 * 1000): Promise<() => void> { return this.lock(`chart-insert:${lockKey}`, timeout); } diff --git a/packages/backend/src/core/CaptchaService.ts b/packages/backend/src/core/CaptchaService.ts index b60271812c..0207cf58a0 100644 --- a/packages/backend/src/core/CaptchaService.ts +++ b/packages/backend/src/core/CaptchaService.ts @@ -3,6 +3,7 @@ import { DI } from '@/di-symbols.js'; import type { UsersRepository } from '@/models/index.js'; import type { Config } from '@/config.js'; import { HttpRequestService } from '@/core/HttpRequestService.js'; +import { bindThis } from '@/decorators.js'; type CaptchaResponse = { success: boolean; @@ -19,6 +20,7 @@ export class CaptchaService { ) { } + @bindThis private async getCaptchaResponse(url: string, secret: string, response: string): Promise { const params = new URLSearchParams({ secret, @@ -45,6 +47,7 @@ export class CaptchaService { return await res.json() as CaptchaResponse; } + @bindThis public async verifyRecaptcha(secret: string, response: string | null | undefined): Promise { if (response == null) { throw 'recaptcha-failed: no response provided'; @@ -60,6 +63,7 @@ export class CaptchaService { } } + @bindThis public async verifyHcaptcha(secret: string, response: string | null | undefined): Promise { if (response == null) { throw 'hcaptcha-failed: no response provided'; @@ -75,6 +79,7 @@ export class CaptchaService { } } + @bindThis public async verifyTurnstile(secret: string, response: string | null | undefined): Promise { if (response == null) { throw 'turnstile-failed: no response provided'; diff --git a/packages/backend/src/core/CreateNotificationService.ts b/packages/backend/src/core/CreateNotificationService.ts index 504661c3bd..f376b7b9cf 100644 --- a/packages/backend/src/core/CreateNotificationService.ts +++ b/packages/backend/src/core/CreateNotificationService.ts @@ -7,6 +7,7 @@ import { IdService } from '@/core/IdService.js'; import { DI } from '@/di-symbols.js'; import { NotificationEntityService } from '@/core/entities/NotificationEntityService.js'; import { PushNotificationService } from '@/core/PushNotificationService.js'; +import { bindThis } from '@/decorators.js'; @Injectable() export class CreateNotificationService { @@ -30,6 +31,7 @@ export class CreateNotificationService { ) { } + @bindThis public async createNotification( notifieeId: User['id'], type: Notification['type'], @@ -90,6 +92,7 @@ export class CreateNotificationService { // TODO: locale ファイルをクライアント用とサーバー用で分けたい + @bindThis private async emailNotificationFollow(userId: User['id'], follower: User) { /* const userProfile = await UserProfiles.findOneByOrFail({ userId: userId }); @@ -101,6 +104,7 @@ export class CreateNotificationService { */ } + @bindThis private async emailNotificationReceiveFollowRequest(userId: User['id'], follower: User) { /* const userProfile = await UserProfiles.findOneByOrFail({ userId: userId }); diff --git a/packages/backend/src/core/CreateSystemUserService.ts b/packages/backend/src/core/CreateSystemUserService.ts index 71f50d7cb3..1e753f65cc 100644 --- a/packages/backend/src/core/CreateSystemUserService.ts +++ b/packages/backend/src/core/CreateSystemUserService.ts @@ -10,6 +10,7 @@ import { UserKeypair } from '@/models/entities/UserKeypair.js'; import { UsedUsername } from '@/models/entities/UsedUsername.js'; import { DI } from '@/di-symbols.js'; import generateNativeUserToken from '@/misc/generate-native-user-token.js'; +import { bindThis } from '@/decorators.js'; @Injectable() export class CreateSystemUserService { @@ -21,6 +22,7 @@ export class CreateSystemUserService { ) { } + @bindThis public async createSystemUser(username: string): Promise { const password = uuid(); diff --git a/packages/backend/src/core/CustomEmojiService.ts b/packages/backend/src/core/CustomEmojiService.ts index 3319f3efa8..36f88fd743 100644 --- a/packages/backend/src/core/CustomEmojiService.ts +++ b/packages/backend/src/core/CustomEmojiService.ts @@ -20,6 +20,7 @@ type PopulatedEmoji = { name: string; url: string; }; +import { bindThis } from '@/decorators.js'; @Injectable() export class CustomEmojiService { @@ -43,6 +44,7 @@ export class CustomEmojiService { this.cache = new Cache(1000 * 60 * 60 * 12); } + @bindThis public async add(data: { driveFile: DriveFile; name: string; @@ -67,6 +69,7 @@ export class CustomEmojiService { return emoji; } + @bindThis private normalizeHost(src: string | undefined, noteUserHost: string | null): string | null { // クエリに使うホスト let host = src === '.' ? null // .はローカルホスト (ここがマッチするのはリアクションのみ) @@ -79,6 +82,7 @@ export class CustomEmojiService { return host; } + @bindThis private parseEmojiStr(emojiName: string, noteUserHost: string | null) { const match = emojiName.match(/^(\w+)(?:@([\w.-]+))?$/); if (!match) return { name: null, host: null }; @@ -97,6 +101,7 @@ export class CustomEmojiService { * @param noteUserHost ノートやユーザープロフィールの所有者のホスト * @returns 絵文字情報, nullは未マッチを意味する */ + @bindThis public async populateEmoji(emojiName: string, noteUserHost: string | null): Promise { const { name, host } = this.parseEmojiStr(emojiName, noteUserHost); if (name == null) return null; @@ -123,11 +128,13 @@ export class CustomEmojiService { /** * 複数の添付用絵文字情報を解決する (キャシュ付き, 存在しないものは結果から除外される) */ + @bindThis public async populateEmojis(emojiNames: string[], noteUserHost: string | null): Promise { const emojis = await Promise.all(emojiNames.map(x => this.populateEmoji(x, noteUserHost))); return emojis.filter((x): x is PopulatedEmoji => x != null); } + @bindThis public aggregateNoteEmojis(notes: Note[]) { let emojis: { name: string | null; host: string | null; }[] = []; for (const note of notes) { @@ -154,6 +161,7 @@ export class CustomEmojiService { /** * 与えられた絵文字のリストをデータベースから取得し、キャッシュに追加します */ + @bindThis public async prefetchEmojis(emojis: { name: string; host: string | null; }[]): Promise { const notCachedEmojis = emojis.filter(emoji => this.cache.get(`${emoji.name} ${emoji.host}`) == null); const emojisQuery: any[] = []; diff --git a/packages/backend/src/core/DeleteAccountService.ts b/packages/backend/src/core/DeleteAccountService.ts index 53d48c450b..e42c738707 100644 --- a/packages/backend/src/core/DeleteAccountService.ts +++ b/packages/backend/src/core/DeleteAccountService.ts @@ -4,6 +4,7 @@ import { QueueService } from '@/core/QueueService.js'; import { UserSuspendService } from '@/core/UserSuspendService.js'; import { GlobalEventService } from '@/core/GlobalEventService.js'; import { DI } from '@/di-symbols.js'; +import { bindThis } from '@/decorators.js'; @Injectable() export class DeleteAccountService { @@ -17,6 +18,7 @@ export class DeleteAccountService { ) { } + @bindThis public async deleteAccount(user: { id: string; host: string | null; diff --git a/packages/backend/src/core/DownloadService.ts b/packages/backend/src/core/DownloadService.ts index 25965b7ac4..9097bb08e0 100644 --- a/packages/backend/src/core/DownloadService.ts +++ b/packages/backend/src/core/DownloadService.ts @@ -15,6 +15,7 @@ import { LoggerService } from '@/core/LoggerService.js'; import type Logger from '@/logger.js'; const pipeline = util.promisify(stream.pipeline); +import { bindThis } from '@/decorators.js'; @Injectable() export class DownloadService { @@ -30,6 +31,7 @@ export class DownloadService { this.logger = this.loggerService.getLogger('download'); } + @bindThis public async downloadUrl(url: string, path: string): Promise { this.logger.info(`Downloading ${chalk.cyan(url)} ...`); @@ -94,6 +96,7 @@ export class DownloadService { this.logger.succ(`Download finished: ${chalk.cyan(url)}`); } + @bindThis public async downloadTextFile(url: string): Promise { // Create temp file const [path, cleanup] = await createTemp(); @@ -112,6 +115,7 @@ export class DownloadService { } } + @bindThis private isPrivateIp(ip: string): boolean { for (const net of this.config.allowedPrivateNetworks ?? []) { const cidr = new IPCIDR(net); diff --git a/packages/backend/src/core/DriveService.ts b/packages/backend/src/core/DriveService.ts index 1d2ba5df8c..b83047dbc2 100644 --- a/packages/backend/src/core/DriveService.ts +++ b/packages/backend/src/core/DriveService.ts @@ -71,6 +71,7 @@ type UploadFromUrlArgs = { requestIp?: string | null; requestHeaders?: Record | null; }; +import { bindThis } from '@/decorators.js'; @Injectable() export class DriveService { @@ -122,6 +123,7 @@ export class DriveService { * @param hash Hash for original * @param size Size for original */ + @bindThis private async save(file: DriveFile, path: string, name: string, type: string, hash: string, size: number): Promise { // thunbnail, webpublic を必要なら生成 const alts = await this.generateAlts(path, type, !file.uri); @@ -242,6 +244,7 @@ export class DriveService { * @param type Content-Type for original * @param generateWeb Generate webpublic or not */ + @bindThis public async generateAlts(path: string, type: string, generateWeb: boolean) { if (type.startsWith('video/')) { try { @@ -345,6 +348,7 @@ export class DriveService { /** * Upload to ObjectStorage */ + @bindThis private async upload(key: string, stream: fs.ReadStream | Buffer, type: string, filename?: string) { if (type === 'image/apng') type = 'image/png'; if (!FILE_TYPE_BROWSERSAFE.includes(type)) type = 'application/octet-stream'; @@ -372,6 +376,7 @@ export class DriveService { if (result) this.registerLogger.debug(`Uploaded: ${result.Bucket}/${result.Key} => ${result.Location}`); } + @bindThis private async deleteOldFile(user: IRemoteUser) { const q = this.driveFilesRepository.createQueryBuilder('file') .where('file.userId = :userId', { userId: user.id }) @@ -398,6 +403,7 @@ export class DriveService { * Add file to drive * */ + @bindThis public async addFile({ user, path, @@ -601,6 +607,7 @@ export class DriveService { return file; } + @bindThis public async deleteFile(file: DriveFile, isExpired = false) { if (file.storedInternal) { this.internalStorageService.del(file.accessKey!); @@ -627,6 +634,7 @@ export class DriveService { this.deletePostProcess(file, isExpired); } + @bindThis public async deleteFileSync(file: DriveFile, isExpired = false) { if (file.storedInternal) { this.internalStorageService.del(file.accessKey!); @@ -657,6 +665,7 @@ export class DriveService { this.deletePostProcess(file, isExpired); } + @bindThis private async deletePostProcess(file: DriveFile, isExpired = false) { // リモートファイル期限切れ削除後は直リンクにする if (isExpired && file.userHost !== null && file.uri != null) { @@ -683,6 +692,7 @@ export class DriveService { } } + @bindThis public async deleteObjectStorageFile(key: string) { const meta = await this.metaService.fetch(); @@ -694,6 +704,7 @@ export class DriveService { }).promise(); } + @bindThis public async uploadFromUrl({ url, user, diff --git a/packages/backend/src/core/EmailService.ts b/packages/backend/src/core/EmailService.ts index 019b9087cd..59932a5b88 100644 --- a/packages/backend/src/core/EmailService.ts +++ b/packages/backend/src/core/EmailService.ts @@ -7,6 +7,7 @@ import type { Config } from '@/config.js'; import type Logger from '@/logger.js'; import type { UserProfilesRepository } from '@/models/index.js'; import { LoggerService } from '@/core/LoggerService.js'; +import { bindThis } from '@/decorators.js'; @Injectable() export class EmailService { @@ -25,6 +26,7 @@ export class EmailService { this.logger = this.loggerService.getLogger('email'); } + @bindThis public async sendEmail(to: string, subject: string, html: string, text: string) { const meta = await this.metaService.fetch(true); @@ -141,6 +143,7 @@ export class EmailService { } } + @bindThis public async validateEmailForAccount(emailAddress: string): Promise<{ available: boolean; reason: null | 'used' | 'format' | 'disposable' | 'mx' | 'smtp'; diff --git a/packages/backend/src/core/FederatedInstanceService.ts b/packages/backend/src/core/FederatedInstanceService.ts index a05c95a2ae..97745a1168 100644 --- a/packages/backend/src/core/FederatedInstanceService.ts +++ b/packages/backend/src/core/FederatedInstanceService.ts @@ -5,6 +5,7 @@ import { Cache } from '@/misc/cache.js'; import { IdService } from '@/core/IdService.js'; import { DI } from '@/di-symbols.js'; import { UtilityService } from '@/core/UtilityService.js'; +import { bindThis } from '@/decorators.js'; @Injectable() export class FederatedInstanceService { @@ -20,6 +21,7 @@ export class FederatedInstanceService { this.cache = new Cache(1000 * 60 * 60); } + @bindThis public async registerOrFetchInstanceDoc(host: string): Promise { host = this.utilityService.toPuny(host); diff --git a/packages/backend/src/core/FetchInstanceMetadataService.ts b/packages/backend/src/core/FetchInstanceMetadataService.ts index b92ebe6059..4d4945ca7f 100644 --- a/packages/backend/src/core/FetchInstanceMetadataService.ts +++ b/packages/backend/src/core/FetchInstanceMetadataService.ts @@ -30,6 +30,7 @@ type NodeInfo = { themeColor?: unknown; }; }; +import { bindThis } from '@/decorators.js'; @Injectable() export class FetchInstanceMetadataService { @@ -46,6 +47,7 @@ export class FetchInstanceMetadataService { this.logger = this.loggerService.getLogger('metadata', 'cyan'); } + @bindThis public async fetchInstanceMetadata(instance: Instance, force = false): Promise { const unlock = await this.appLockService.getFetchInstanceMetadataLock(instance.host); @@ -105,6 +107,7 @@ export class FetchInstanceMetadataService { } } + @bindThis private async fetchNodeinfo(instance: Instance): Promise { this.logger.info(`Fetching nodeinfo of ${instance.host} ...`); @@ -148,6 +151,7 @@ export class FetchInstanceMetadataService { } } + @bindThis private async fetchDom(instance: Instance): Promise { this.logger.info(`Fetching HTML of ${instance.host} ...`); @@ -161,6 +165,7 @@ export class FetchInstanceMetadataService { return doc; } + @bindThis private async fetchManifest(instance: Instance): Promise | null> { const url = 'https://' + instance.host; @@ -171,6 +176,7 @@ export class FetchInstanceMetadataService { return manifest; } + @bindThis private async fetchFaviconUrl(instance: Instance, doc: DOMWindow['document'] | null): Promise { const url = 'https://' + instance.host; @@ -198,6 +204,7 @@ export class FetchInstanceMetadataService { return null; } + @bindThis private async fetchIconUrl(instance: Instance, doc: DOMWindow['document'] | null, manifest: Record | null): Promise { if (manifest && manifest.icons && manifest.icons.length > 0 && manifest.icons[0].src) { const url = 'https://' + instance.host; @@ -226,6 +233,7 @@ export class FetchInstanceMetadataService { return null; } + @bindThis private async getThemeColor(info: NodeInfo | null, doc: DOMWindow['document'] | null, manifest: Record | null): Promise { const themeColor = info?.metadata?.themeColor ?? doc?.querySelector('meta[name="theme-color"]')?.getAttribute('content') ?? manifest?.theme_color; @@ -237,6 +245,7 @@ export class FetchInstanceMetadataService { return null; } + @bindThis private async getSiteName(info: NodeInfo | null, doc: DOMWindow['document'] | null, manifest: Record | null): Promise { if (info && info.metadata) { if (typeof info.metadata.nodeName === 'string') { @@ -261,6 +270,7 @@ export class FetchInstanceMetadataService { return null; } + @bindThis private async getDescription(info: NodeInfo | null, doc: DOMWindow['document'] | null, manifest: Record | null): Promise { if (info && info.metadata) { if (typeof info.metadata.nodeDescription === 'string') { diff --git a/packages/backend/src/core/FileInfoService.ts b/packages/backend/src/core/FileInfoService.ts index fd8a4fdd3a..bea1b3402e 100644 --- a/packages/backend/src/core/FileInfoService.ts +++ b/packages/backend/src/core/FileInfoService.ts @@ -14,6 +14,7 @@ import sharp from 'sharp'; import { encode } from 'blurhash'; import { createTempDir } from '@/misc/create-temp.js'; import { AiService } from '@/core/AiService.js'; +import { bindThis } from '@/decorators.js'; const pipeline = util.promisify(stream.pipeline); @@ -42,6 +43,7 @@ const TYPE_SVG = { mime: 'image/svg+xml', ext: 'svg', }; + @Injectable() export class FileInfoService { constructor( @@ -52,6 +54,7 @@ export class FileInfoService { /** * Get file information */ + @bindThis public async getFileInfo(path: string, opts: { skipSensitiveDetection: boolean; sensitiveThreshold?: number; @@ -135,6 +138,7 @@ export class FileInfoService { }; } + @bindThis private async detectSensitivity(source: string, mime: string, sensitiveThreshold: number, sensitiveThresholdForPorn: number, analyzeVideo: boolean): Promise<[sensitive: boolean, porn: boolean]> { let sensitive = false; let porn = false; @@ -269,6 +273,7 @@ export class FileInfoService { } } + @bindThis private exists(path: string): Promise { return fs.promises.access(path).then(() => true, () => false); } @@ -276,6 +281,7 @@ export class FileInfoService { /** * Detect MIME Type and extension */ + @bindThis public async detectType(path: string): Promise<{ mime: string; ext: string | null; @@ -312,6 +318,7 @@ export class FileInfoService { /** * Check the file is SVG or not */ + @bindThis public async checkSvg(path: string) { try { const size = await this.getFileSize(path); @@ -325,6 +332,7 @@ export class FileInfoService { /** * Get file size */ + @bindThis public async getFileSize(path: string): Promise { const getStat = util.promisify(fs.stat); return (await getStat(path)).size; @@ -333,6 +341,7 @@ export class FileInfoService { /** * Calculate MD5 hash */ + @bindThis private async calcHash(path: string): Promise { const hash = crypto.createHash('md5').setEncoding('hex'); await pipeline(fs.createReadStream(path), hash); @@ -342,6 +351,7 @@ export class FileInfoService { /** * Detect dimensions of image */ + @bindThis private async detectImageSize(path: string): Promise<{ width: number; height: number; @@ -358,6 +368,7 @@ export class FileInfoService { /** * Calculate average color of image */ + @bindThis private getBlurhash(path: string): Promise { return new Promise((resolve, reject) => { sharp(path) diff --git a/packages/backend/src/core/GlobalEventService.ts b/packages/backend/src/core/GlobalEventService.ts index df0c9b5ccb..90f911027e 100644 --- a/packages/backend/src/core/GlobalEventService.ts +++ b/packages/backend/src/core/GlobalEventService.ts @@ -25,6 +25,7 @@ import type { import type { Packed } from '@/misc/schema.js'; import { DI } from '@/di-symbols.js'; import type { Config } from '@/config.js'; +import { bindThis } from '@/decorators.js'; @Injectable() export class GlobalEventService { @@ -37,6 +38,7 @@ export class GlobalEventService { ) { } + @bindThis private publish(channel: StreamChannels, type: string | null, value?: any): void { const message = type == null ? value : value == null ? { type: type, body: null } : @@ -99,6 +101,7 @@ export class GlobalEventService { this.publish(`messagingIndexStream:${userId}`, type, typeof value === 'undefined' ? null : value); } + @bindThis public publishNotesStream(note: Packed<'Note'>): void { this.publish('notesStream', null, note); } diff --git a/packages/backend/src/core/HashtagService.ts b/packages/backend/src/core/HashtagService.ts index 5ca058e9a4..309cfe8c3f 100644 --- a/packages/backend/src/core/HashtagService.ts +++ b/packages/backend/src/core/HashtagService.ts @@ -7,6 +7,7 @@ import type { Hashtag } from '@/models/entities/Hashtag.js'; import HashtagChart from '@/core/chart/charts/hashtag.js'; import type { HashtagsRepository, UsersRepository } from '@/models/index.js'; import { UserEntityService } from '@/core/entities/UserEntityService.js'; +import { bindThis } from '@/decorators.js'; @Injectable() export class HashtagService { @@ -23,12 +24,14 @@ export class HashtagService { ) { } + @bindThis public async updateHashtags(user: { id: User['id']; host: User['host']; }, tags: string[]) { for (const tag of tags) { await this.updateHashtag(user, tag); } } + @bindThis public async updateUsertags(user: User, tags: string[]) { for (const tag of tags) { await this.updateHashtag(user, tag, true, true); @@ -39,6 +42,7 @@ export class HashtagService { } } + @bindThis public async updateHashtag(user: { id: User['id']; host: User['host']; }, tag: string, isUserAttached = false, inc = true) { tag = normalizeForSearch(tag); diff --git a/packages/backend/src/core/HttpRequestService.ts b/packages/backend/src/core/HttpRequestService.ts index 396fefad1c..d998307973 100644 --- a/packages/backend/src/core/HttpRequestService.ts +++ b/packages/backend/src/core/HttpRequestService.ts @@ -7,6 +7,7 @@ import { Inject, Injectable } from '@nestjs/common'; import { DI } from '@/di-symbols.js'; import type { Config } from '@/config.js'; import { StatusError } from '@/misc/status-error.js'; +import { bindThis } from '@/decorators.js'; import type { Response } from 'node-fetch'; import type { URL } from 'node:url'; @@ -84,6 +85,7 @@ export class HttpRequestService { * @param url URL * @param bypassProxy Allways bypass proxy */ + @bindThis public getAgentByUrl(url: URL, bypassProxy = false): http.Agent | https.Agent { if (bypassProxy || (this.config.proxyBypassHosts || []).includes(url.hostname)) { return url.protocol === 'http:' ? this.http : this.https; @@ -92,6 +94,7 @@ export class HttpRequestService { } } + @bindThis public async getJson(url: string, accept = 'application/json, */*', timeout = 10000, headers?: Record): Promise { const res = await this.getResponse({ url, @@ -106,6 +109,7 @@ export class HttpRequestService { return await res.json(); } + @bindThis public async getHtml(url: string, accept = 'text/html, */*', timeout = 10000, headers?: Record): Promise { const res = await this.getResponse({ url, @@ -120,6 +124,7 @@ export class HttpRequestService { return await res.text(); } + @bindThis public async getResponse(args: { url: string, method: string, diff --git a/packages/backend/src/core/IdService.ts b/packages/backend/src/core/IdService.ts index 997be17937..0e8a7b13ad 100644 --- a/packages/backend/src/core/IdService.ts +++ b/packages/backend/src/core/IdService.ts @@ -6,6 +6,7 @@ import { genAid } from '@/misc/id/aid.js'; import { genMeid } from '@/misc/id/meid.js'; import { genMeidg } from '@/misc/id/meidg.js'; import { genObjectId } from '@/misc/id/object-id.js'; +import { bindThis } from '@/decorators.js'; @Injectable() export class IdService { @@ -18,6 +19,7 @@ export class IdService { this.metohd = config.id.toLowerCase(); } + @bindThis public genId(date?: Date): string { if (!date || (date > new Date())) date = new Date(); diff --git a/packages/backend/src/core/ImageProcessingService.ts b/packages/backend/src/core/ImageProcessingService.ts index 3a50361a42..3a61873044 100644 --- a/packages/backend/src/core/ImageProcessingService.ts +++ b/packages/backend/src/core/ImageProcessingService.ts @@ -8,6 +8,7 @@ export type IImage = { ext: string | null; type: string; }; +import { bindThis } from '@/decorators.js'; @Injectable() export class ImageProcessingService { @@ -21,10 +22,12 @@ export class ImageProcessingService { * Convert to JPEG * with resize, remove metadata, resolve orientation, stop animation */ + @bindThis public async convertToJpeg(path: string, width: number, height: number): Promise { return this.convertSharpToJpeg(await sharp(path), width, height); } + @bindThis public async convertSharpToJpeg(sharp: sharp.Sharp, width: number, height: number): Promise { const data = await sharp .resize(width, height, { @@ -49,10 +52,12 @@ export class ImageProcessingService { * Convert to WebP * with resize, remove metadata, resolve orientation, stop animation */ + @bindThis public async convertToWebp(path: string, width: number, height: number, quality = 85): Promise { return this.convertSharpToWebp(await sharp(path), width, height, quality); } + @bindThis public async convertSharpToWebp(sharp: sharp.Sharp, width: number, height: number, quality = 85): Promise { const data = await sharp .resize(width, height, { @@ -76,10 +81,12 @@ export class ImageProcessingService { * Convert to PNG * with resize, remove metadata, resolve orientation, stop animation */ + @bindThis public async convertToPng(path: string, width: number, height: number): Promise { return this.convertSharpToPng(await sharp(path), width, height); } + @bindThis public async convertSharpToPng(sharp: sharp.Sharp, width: number, height: number): Promise { const data = await sharp .resize(width, height, { diff --git a/packages/backend/src/core/InstanceActorService.ts b/packages/backend/src/core/InstanceActorService.ts index f35a28147d..abd6685d61 100644 --- a/packages/backend/src/core/InstanceActorService.ts +++ b/packages/backend/src/core/InstanceActorService.ts @@ -7,6 +7,7 @@ import { DI } from '@/di-symbols.js'; import { CreateSystemUserService } from '@/core/CreateSystemUserService.js'; const ACTOR_USERNAME = 'instance.actor' as const; +import { bindThis } from '@/decorators.js'; @Injectable() export class InstanceActorService { @@ -21,6 +22,7 @@ export class InstanceActorService { this.cache = new Cache(Infinity); } + @bindThis public async getInstanceActor(): Promise { const cached = this.cache.get(null); if (cached) return cached; diff --git a/packages/backend/src/core/InternalStorageService.ts b/packages/backend/src/core/InternalStorageService.ts index 6d2a9b2db6..e32cbf645c 100644 --- a/packages/backend/src/core/InternalStorageService.ts +++ b/packages/backend/src/core/InternalStorageService.ts @@ -10,6 +10,7 @@ const _filename = fileURLToPath(import.meta.url); const _dirname = dirname(_filename); const path = Path.resolve(_dirname, '../../../../files'); +import { bindThis } from '@/decorators.js'; @Injectable() export class InternalStorageService { @@ -19,26 +20,31 @@ export class InternalStorageService { ) { } + @bindThis public resolvePath(key: string) { return Path.resolve(path, key); } + @bindThis public read(key: string) { return fs.createReadStream(this.resolvePath(key)); } + @bindThis public saveFromPath(key: string, srcPath: string) { fs.mkdirSync(path, { recursive: true }); fs.copyFileSync(srcPath, this.resolvePath(key)); return `${this.config.url}/files/${key}`; } + @bindThis public saveFromBuffer(key: string, data: Buffer) { fs.mkdirSync(path, { recursive: true }); fs.writeFileSync(this.resolvePath(key), data); return `${this.config.url}/files/${key}`; } + @bindThis public del(key: string) { fs.unlink(this.resolvePath(key), () => {}); } diff --git a/packages/backend/src/core/LoggerService.ts b/packages/backend/src/core/LoggerService.ts index a3192c0262..4303f3ae22 100644 --- a/packages/backend/src/core/LoggerService.ts +++ b/packages/backend/src/core/LoggerService.ts @@ -3,6 +3,7 @@ import * as SyslogPro from 'syslog-pro'; import { DI } from '@/di-symbols.js'; import type { Config } from '@/config.js'; import Logger from '@/logger.js'; +import { bindThis } from '@/decorators.js'; @Injectable() export class LoggerService { @@ -27,6 +28,7 @@ export class LoggerService { } } + @bindThis public getLogger(domain: string, color?: string | undefined, store?: boolean) { return new Logger(domain, color, store, this.syslogClient); } diff --git a/packages/backend/src/core/MessagingService.ts b/packages/backend/src/core/MessagingService.ts index 9de28ad8db..f4a1090658 100644 --- a/packages/backend/src/core/MessagingService.ts +++ b/packages/backend/src/core/MessagingService.ts @@ -17,6 +17,7 @@ import { UserEntityService } from '@/core/entities/UserEntityService.js'; import { ApRendererService } from '@/core/activitypub/ApRendererService.js'; import { MessagingMessageEntityService } from '@/core/entities/MessagingMessageEntityService.js'; import { PushNotificationService } from '@/core/PushNotificationService.js'; +import { bindThis } from '@/decorators.js'; @Injectable() export class MessagingService { @@ -46,6 +47,7 @@ export class MessagingService { ) { } + @bindThis public async createMessage(user: { id: User['id']; host: User['host']; }, recipientUser: CacheableUser | undefined, recipientGroup: UserGroup | undefined, text: string | null | undefined, file: DriveFile | null, uri?: string) { const message = { id: this.idService.genId(), @@ -140,11 +142,13 @@ export class MessagingService { return messageObj; } + @bindThis public async deleteMessage(message: MessagingMessage) { await this.messagingMessagesRepository.delete(message.id); this.postDeleteMessage(message); } + @bindThis private async postDeleteMessage(message: MessagingMessage) { if (message.recipientId) { const user = await this.usersRepository.findOneByOrFail({ id: message.userId }); @@ -165,6 +169,7 @@ export class MessagingService { /** * Mark messages as read */ + @bindThis public async readUserMessagingMessage( userId: User['id'], otherpartyId: User['id'], @@ -220,6 +225,7 @@ export class MessagingService { /** * Mark messages as read */ + @bindThis public async readGroupMessagingMessage( userId: User['id'], groupId: UserGroup['id'], @@ -284,6 +290,7 @@ export class MessagingService { } } + @bindThis public async deliverReadActivity(user: { id: User['id']; host: null; }, recipient: IRemoteUser, messages: MessagingMessage | MessagingMessage[]) { messages = toArray(messages).filter(x => x.uri); const contents = messages.map(x => this.apRendererService.renderRead(user, x)); diff --git a/packages/backend/src/core/MetaService.ts b/packages/backend/src/core/MetaService.ts index c3d41bfccb..ff05779aee 100644 --- a/packages/backend/src/core/MetaService.ts +++ b/packages/backend/src/core/MetaService.ts @@ -5,6 +5,7 @@ import { DI } from '@/di-symbols.js'; import { Meta } from '@/models/entities/Meta.js'; import { GlobalEventService } from '@/core/GlobalEventService.js'; import type { OnApplicationShutdown } from '@nestjs/common'; +import { bindThis } from '@/decorators.js'; @Injectable() export class MetaService implements OnApplicationShutdown { @@ -20,7 +21,7 @@ export class MetaService implements OnApplicationShutdown { private globalEventService: GlobalEventService, ) { - this.onMessage = this.onMessage.bind(this); + //this.onMessage = this.onMessage.bind(this); if (process.env.NODE_ENV !== 'test') { this.intervalId = setInterval(() => { @@ -34,6 +35,7 @@ export class MetaService implements OnApplicationShutdown { this.redisSubscriber.on('message', this.onMessage); } + @bindThis private async onMessage(_: string, data: string): Promise { const obj = JSON.parse(data); @@ -50,6 +52,7 @@ export class MetaService implements OnApplicationShutdown { } } + @bindThis public async fetch(noCache = false): Promise { if (!noCache && this.cache) return this.cache; @@ -84,6 +87,7 @@ export class MetaService implements OnApplicationShutdown { }); } + @bindThis public async update(data: Partial): Promise { const updated = await this.db.transaction(async transactionalEntityManager => { const metas = await transactionalEntityManager.find(Meta, { @@ -114,6 +118,7 @@ export class MetaService implements OnApplicationShutdown { return updated; } + @bindThis public onApplicationShutdown(signal?: string | undefined) { clearInterval(this.intervalId); this.redisSubscriber.off('message', this.onMessage); diff --git a/packages/backend/src/core/MfmService.ts b/packages/backend/src/core/MfmService.ts index 2e03bf3cc0..d53623baaa 100644 --- a/packages/backend/src/core/MfmService.ts +++ b/packages/backend/src/core/MfmService.ts @@ -14,6 +14,7 @@ const treeAdapter = TreeAdapter.defaultTreeAdapter; const urlRegex = /^https?:\/\/[\w\/:%#@$&?!()\[\]~.,=+\-]+/; const urlRegexFull = /^https?:\/\/[\w\/:%#@$&?!()\[\]~.,=+\-]+$/; +import { bindThis } from '@/decorators.js'; @Injectable() export class MfmService { @@ -23,6 +24,7 @@ export class MfmService { ) { } + @bindThis public fromHtml(html: string, hashtagNames?: string[]): string { // some AP servers like Pixelfed use br tags as well as newlines html = html.replace(/\r?\n/gi, '\n'); @@ -228,6 +230,7 @@ export class MfmService { } } + @bindThis public toHtml(nodes: mfm.MfmNode[] | null, mentionedRemoteUsers: IMentionedRemoteUsers = []) { if (nodes == null) { return null; diff --git a/packages/backend/src/core/ModerationLogService.ts b/packages/backend/src/core/ModerationLogService.ts index 81ae322b95..80e8cb9e52 100644 --- a/packages/backend/src/core/ModerationLogService.ts +++ b/packages/backend/src/core/ModerationLogService.ts @@ -3,6 +3,7 @@ import { DI } from '@/di-symbols.js'; import type { ModerationLogsRepository } from '@/models/index.js'; import type { User } from '@/models/entities/User.js'; import { IdService } from '@/core/IdService.js'; +import { bindThis } from '@/decorators.js'; @Injectable() export class ModerationLogService { @@ -14,6 +15,7 @@ export class ModerationLogService { ) { } + @bindThis public async insertModerationLog(moderator: { id: User['id'] }, type: string, info?: Record) { await this.moderationLogsRepository.insert({ id: this.idService.genId(), diff --git a/packages/backend/src/core/NoteCreateService.ts b/packages/backend/src/core/NoteCreateService.ts index cf1566a5e8..a41df28050 100644 --- a/packages/backend/src/core/NoteCreateService.ts +++ b/packages/backend/src/core/NoteCreateService.ts @@ -64,6 +64,7 @@ class NotificationManager { this.queue = []; } + @bindThis public push(notifiee: ILocalUser['id'], reason: NotificationType) { // 自分自身へは通知しない if (this.notifier.id === notifiee) return; @@ -83,6 +84,7 @@ class NotificationManager { } } + @bindThis public async deliver() { for (const x of this.queue) { // ミュート情報を取得 @@ -130,6 +132,7 @@ type Option = { url?: string | null; app?: App | null; }; +import { bindThis } from '@/decorators.js'; @Injectable() export class NoteCreateService { @@ -188,6 +191,7 @@ export class NoteCreateService { private instanceChart: InstanceChart, ) {} + @bindThis public async create(user: { id: User['id']; username: User['username']; @@ -307,6 +311,7 @@ export class NoteCreateService { return note; } + @bindThis private async insertNote(user: { id: User['id']; host: User['host']; }, data: Option, tags: string[], emojis: string[], mentionedUsers: MinimumUser[]) { const insert = new Note({ id: this.idService.genId(data.createdAt!), @@ -403,6 +408,7 @@ export class NoteCreateService { } } + @bindThis private async postNoteCreated(note: Note, user: { id: User['id']; username: User['username']; @@ -644,6 +650,7 @@ export class NoteCreateService { this.index(note); } + @bindThis private incRenoteCount(renote: Note) { this.notesRepository.createQueryBuilder().update() .set({ @@ -654,6 +661,7 @@ export class NoteCreateService { .execute(); } + @bindThis private async createMentionedEvents(mentionedUsers: MinimumUser[], note: Note, nm: NotificationManager) { for (const u of mentionedUsers.filter(u => this.userEntityService.isLocalUser(u))) { const threadMuted = await this.noteThreadMutingsRepository.findOneBy({ @@ -683,10 +691,12 @@ export class NoteCreateService { } } + @bindThis private saveReply(reply: Note, note: Note) { this.notesRepository.increment({ id: reply.id }, 'repliesCount', 1); } + @bindThis private async renderNoteOrRenoteActivity(data: Option, note: Note) { if (data.localOnly) return null; @@ -697,6 +707,7 @@ export class NoteCreateService { return this.apRendererService.renderActivity(content); } + @bindThis private index(note: Note) { if (note.text == null || this.config.elasticsearch == null) return; /* @@ -711,6 +722,7 @@ export class NoteCreateService { });*/ } + @bindThis private incNotesCountOfUser(user: { id: User['id']; }) { this.usersRepository.createQueryBuilder().update() .set({ @@ -721,6 +733,7 @@ export class NoteCreateService { .execute(); } + @bindThis private async extractMentionedUsers(user: { host: User['host']; }, tokens: mfm.MfmNode[]): Promise { if (tokens == null) return []; diff --git a/packages/backend/src/core/NoteDeleteService.ts b/packages/backend/src/core/NoteDeleteService.ts index ce6e755a7e..7331d03552 100644 --- a/packages/backend/src/core/NoteDeleteService.ts +++ b/packages/backend/src/core/NoteDeleteService.ts @@ -15,6 +15,7 @@ import { ApRendererService } from '@/core/activitypub/ApRendererService.js'; import { ApDeliverManagerService } from '@/core/activitypub/ApDeliverManagerService.js'; import { UserEntityService } from '@/core/entities/UserEntityService.js'; import { NoteEntityService } from '@/core/entities/NoteEntityService.js'; +import { bindThis } from '@/decorators.js'; @Injectable() export class NoteDeleteService { @@ -112,6 +113,7 @@ export class NoteDeleteService { }); } + @bindThis private async findCascadingNotes(note: Note) { const cascadingNotes: Note[] = []; @@ -134,6 +136,7 @@ export class NoteDeleteService { return cascadingNotes.filter(note => note.userHost === null); // filter out non-local users } + @bindThis private async getMentionedRemoteUsers(note: Note) { const where = [] as any[]; @@ -159,6 +162,7 @@ export class NoteDeleteService { }) as IRemoteUser[]; } + @bindThis private async deliverToConcerned(user: { id: ILocalUser['id']; host: null; }, note: Note, content: any) { this.apDeliverManagerService.deliverToFollowers(user, content); this.relayService.deliverToRelays(user, content); diff --git a/packages/backend/src/core/NotePiningService.ts b/packages/backend/src/core/NotePiningService.ts index a04b52fe4c..f8997574a7 100644 --- a/packages/backend/src/core/NotePiningService.ts +++ b/packages/backend/src/core/NotePiningService.ts @@ -11,6 +11,7 @@ import type { Config } from '@/config.js'; import { UserEntityService } from '@/core/entities/UserEntityService.js'; import { ApDeliverManagerService } from '@/core/activitypub/ApDeliverManagerService.js'; import { ApRendererService } from '@/core/activitypub/ApRendererService.js'; +import { bindThis } from '@/decorators.js'; @Injectable() export class NotePiningService { @@ -40,6 +41,7 @@ export class NotePiningService { * @param user * @param noteId */ + @bindThis public async addPinned(user: { id: User['id']; host: User['host']; }, noteId: Note['id']) { // Fetch pinee const note = await this.notesRepository.findOneBy({ @@ -79,6 +81,7 @@ export class NotePiningService { * @param user * @param noteId */ + @bindThis public async removePinned(user: { id: User['id']; host: User['host']; }, noteId: Note['id']) { // Fetch unpinee const note = await this.notesRepository.findOneBy({ @@ -101,6 +104,7 @@ export class NotePiningService { } } + @bindThis public async deliverPinnedChange(userId: User['id'], noteId: Note['id'], isAddition: boolean) { const user = await this.usersRepository.findOneBy({ id: userId }); if (user == null) throw new Error('user not found'); diff --git a/packages/backend/src/core/NoteReadService.ts b/packages/backend/src/core/NoteReadService.ts index e0feaa957d..f70495ff30 100644 --- a/packages/backend/src/core/NoteReadService.ts +++ b/packages/backend/src/core/NoteReadService.ts @@ -11,6 +11,7 @@ import type { UsersRepository, NoteUnreadsRepository, MutingsRepository, NoteThr import { UserEntityService } from '@/core/entities/UserEntityService.js'; import { NotificationService } from './NotificationService.js'; import { AntennaService } from './AntennaService.js'; +import { bindThis } from '@/decorators.js'; @Injectable() export class NoteReadService { @@ -44,6 +45,7 @@ export class NoteReadService { ) { } + @bindThis public async insertNoteUnread(userId: User['id'], note: Note, params: { // NOTE: isSpecifiedがtrueならisMentionedは必ずfalse isSpecified: boolean; @@ -94,6 +96,7 @@ export class NoteReadService { }, 2000); } + @bindThis public async read( userId: User['id'], notes: (Note | Packed<'Note'>)[], diff --git a/packages/backend/src/core/NotificationService.ts b/packages/backend/src/core/NotificationService.ts index 8bbc95b02d..9fef36dd2c 100644 --- a/packages/backend/src/core/NotificationService.ts +++ b/packages/backend/src/core/NotificationService.ts @@ -8,6 +8,7 @@ import type { Notification } from '@/models/entities/Notification.js'; import { UserEntityService } from '@/core/entities/UserEntityService.js'; import { GlobalEventService } from './GlobalEventService.js'; import { PushNotificationService } from './PushNotificationService.js'; +import { bindThis } from '@/decorators.js'; @Injectable() export class NotificationService { @@ -21,6 +22,7 @@ export class NotificationService { ) { } + @bindThis public async readNotification( userId: User['id'], notificationIds: Notification['id'][], @@ -42,6 +44,7 @@ export class NotificationService { else return this.postReadNotifications(userId, notificationIds); } + @bindThis public async readNotificationByQuery( userId: User['id'], query: Record, @@ -55,11 +58,13 @@ export class NotificationService { return this.readNotification(userId, notificationIds); } + @bindThis private postReadAllNotifications(userId: User['id']) { this.globalEventService.publishMainStream(userId, 'readAllNotifications'); return this.pushNotificationService.pushNotification(userId, 'readAllNotifications', undefined); } + @bindThis private postReadNotifications(userId: User['id'], notificationIds: Notification['id'][]) { this.globalEventService.publishMainStream(userId, 'readNotifications', notificationIds); return this.pushNotificationService.pushNotification(userId, 'readNotifications', { notificationIds }); diff --git a/packages/backend/src/core/PollService.ts b/packages/backend/src/core/PollService.ts index 287ce8ada4..3cc9b0cc9b 100644 --- a/packages/backend/src/core/PollService.ts +++ b/packages/backend/src/core/PollService.ts @@ -11,6 +11,7 @@ import { CreateNotificationService } from '@/core/CreateNotificationService.js'; import { ApRendererService } from '@/core/activitypub/ApRendererService.js'; import { UserEntityService } from '@/core/entities/UserEntityService.js'; import { ApDeliverManagerService } from '@/core/activitypub/ApDeliverManagerService.js'; +import { bindThis } from '@/decorators.js'; @Injectable() export class PollService { @@ -40,6 +41,7 @@ export class PollService { ) { } + @bindThis public async vote(user: CacheableUser, note: Note, choice: number) { const poll = await this.pollsRepository.findOneBy({ noteId: note.id }); @@ -99,6 +101,7 @@ export class PollService { }); } + @bindThis public async deliverQuestionUpdate(noteId: Note['id']) { const note = await this.notesRepository.findOneBy({ id: noteId }); if (note == null) throw new Error('note not found'); diff --git a/packages/backend/src/core/ProxyAccountService.ts b/packages/backend/src/core/ProxyAccountService.ts index 4cbdadd029..55b70bfc94 100644 --- a/packages/backend/src/core/ProxyAccountService.ts +++ b/packages/backend/src/core/ProxyAccountService.ts @@ -3,6 +3,7 @@ import type { UsersRepository } from '@/models/index.js'; import type { ILocalUser, User } from '@/models/entities/User.js'; import { DI } from '@/di-symbols.js'; import { MetaService } from '@/core/MetaService.js'; +import { bindThis } from '@/decorators.js'; @Injectable() export class ProxyAccountService { @@ -14,6 +15,7 @@ export class ProxyAccountService { ) { } + @bindThis public async fetch(): Promise { const meta = await this.metaService.fetch(); if (meta.proxyAccountId == null) return null; diff --git a/packages/backend/src/core/PushNotificationService.ts b/packages/backend/src/core/PushNotificationService.ts index 98e0841799..bffb24cf4c 100644 --- a/packages/backend/src/core/PushNotificationService.ts +++ b/packages/backend/src/core/PushNotificationService.ts @@ -37,6 +37,7 @@ function truncateNotification(notification: Packed<'Notification'>): any { return notification; } +import { bindThis } from '@/decorators.js'; @Injectable() export class PushNotificationService { diff --git a/packages/backend/src/core/QueryService.ts b/packages/backend/src/core/QueryService.ts index 771adeaed5..4cc844ccea 100644 --- a/packages/backend/src/core/QueryService.ts +++ b/packages/backend/src/core/QueryService.ts @@ -4,6 +4,7 @@ import { DI } from '@/di-symbols.js'; import type { User } from '@/models/entities/User.js'; import type { UserProfilesRepository, FollowingsRepository, ChannelFollowingsRepository, MutedNotesRepository, BlockingsRepository, NoteThreadMutingsRepository, MutingsRepository } from '@/models/index.js'; import type { SelectQueryBuilder } from 'typeorm'; +import { bindThis } from '@/decorators.js'; @Injectable() export class QueryService { @@ -59,6 +60,7 @@ export class QueryService { } // ここでいうBlockedは被Blockedの意 + @bindThis public generateBlockedUserQuery(q: SelectQueryBuilder, me: { id: User['id'] }): void { const blockingQuery = this.blockingsRepository.createQueryBuilder('blocking') .select('blocking.blockerId') @@ -81,6 +83,7 @@ export class QueryService { q.setParameters(blockingQuery.getParameters()); } + @bindThis public generateBlockQueryForUsers(q: SelectQueryBuilder, me: { id: User['id'] }): void { const blockingQuery = this.blockingsRepository.createQueryBuilder('blocking') .select('blocking.blockeeId') @@ -97,6 +100,7 @@ export class QueryService { q.setParameters(blockedQuery.getParameters()); } + @bindThis public generateChannelQuery(q: SelectQueryBuilder, me?: { id: User['id'] } | null): void { if (me == null) { q.andWhere('note.channelId IS NULL'); @@ -118,6 +122,7 @@ export class QueryService { } } + @bindThis public generateMutedNoteQuery(q: SelectQueryBuilder, me: { id: User['id'] }): void { const mutedQuery = this.mutedNotesRepository.createQueryBuilder('muted') .select('muted.noteId') @@ -128,6 +133,7 @@ export class QueryService { q.setParameters(mutedQuery.getParameters()); } + @bindThis public generateMutedNoteThreadQuery(q: SelectQueryBuilder, me: { id: User['id'] }): void { const mutedQuery = this.noteThreadMutingsRepository.createQueryBuilder('threadMuted') .select('threadMuted.threadId') @@ -142,6 +148,7 @@ export class QueryService { q.setParameters(mutedQuery.getParameters()); } + @bindThis public generateMutedUserQuery(q: SelectQueryBuilder, me: { id: User['id'] }, exclude?: User): void { const mutingQuery = this.mutingsRepository.createQueryBuilder('muting') .select('muting.muteeId') @@ -186,6 +193,7 @@ export class QueryService { q.setParameters(mutingInstanceQuery.getParameters()); } + @bindThis public generateMutedUserQueryForUsers(q: SelectQueryBuilder, me: { id: User['id'] }): void { const mutingQuery = this.mutingsRepository.createQueryBuilder('muting') .select('muting.muteeId') @@ -196,6 +204,7 @@ export class QueryService { q.setParameters(mutingQuery.getParameters()); } + @bindThis public generateRepliesQuery(q: SelectQueryBuilder, me?: Pick | null): void { if (me == null) { q.andWhere(new Brackets(qb => { qb @@ -221,6 +230,7 @@ export class QueryService { } } + @bindThis public generateVisibilityQuery(q: SelectQueryBuilder, me?: { id: User['id'] } | null): void { // This code must always be synchronized with the checks in Notes.isVisibleForMe. if (me == null) { diff --git a/packages/backend/src/core/QueueService.ts b/packages/backend/src/core/QueueService.ts index a27d68ee19..7956a3a8f9 100644 --- a/packages/backend/src/core/QueueService.ts +++ b/packages/backend/src/core/QueueService.ts @@ -8,6 +8,7 @@ import { DI } from '@/di-symbols.js'; import type { DbQueue, DeliverQueue, EndedPollNotificationQueue, InboxQueue, ObjectStorageQueue, SystemQueue, WebhookDeliverQueue } from './QueueModule.js'; import type { ThinUser } from '../queue/types.js'; import type httpSignature from '@peertube/http-signature'; +import { bindThis } from '@/decorators.js'; @Injectable() export class QueueService { @@ -24,6 +25,7 @@ export class QueueService { @Inject('queue:webhookDeliver') public webhookDeliverQueue: WebhookDeliverQueue, ) {} + @bindThis public deliver(user: ThinUser, content: IActivity | null, to: string | null) { if (content == null) return null; if (to == null) return null; @@ -47,6 +49,7 @@ export class QueueService { }); } + @bindThis public inbox(activity: IActivity, signature: httpSignature.IParsedSignature) { const data = { activity: activity, @@ -64,6 +67,7 @@ export class QueueService { }); } + @bindThis public createDeleteDriveFilesJob(user: ThinUser) { return this.dbQueue.add('deleteDriveFiles', { user: user, @@ -73,6 +77,7 @@ export class QueueService { }); } + @bindThis public createExportCustomEmojisJob(user: ThinUser) { return this.dbQueue.add('exportCustomEmojis', { user: user, @@ -82,6 +87,7 @@ export class QueueService { }); } + @bindThis public createExportNotesJob(user: ThinUser) { return this.dbQueue.add('exportNotes', { user: user, @@ -91,6 +97,7 @@ export class QueueService { }); } + @bindThis public createExportFollowingJob(user: ThinUser, excludeMuting = false, excludeInactive = false) { return this.dbQueue.add('exportFollowing', { user: user, @@ -102,6 +109,7 @@ export class QueueService { }); } + @bindThis public createExportMuteJob(user: ThinUser) { return this.dbQueue.add('exportMuting', { user: user, @@ -111,6 +119,7 @@ export class QueueService { }); } + @bindThis public createExportBlockingJob(user: ThinUser) { return this.dbQueue.add('exportBlocking', { user: user, @@ -120,6 +129,7 @@ export class QueueService { }); } + @bindThis public createExportUserListsJob(user: ThinUser) { return this.dbQueue.add('exportUserLists', { user: user, @@ -129,6 +139,7 @@ export class QueueService { }); } + @bindThis public createImportFollowingJob(user: ThinUser, fileId: DriveFile['id']) { return this.dbQueue.add('importFollowing', { user: user, @@ -139,6 +150,7 @@ export class QueueService { }); } + @bindThis public createImportMutingJob(user: ThinUser, fileId: DriveFile['id']) { return this.dbQueue.add('importMuting', { user: user, @@ -149,6 +161,7 @@ export class QueueService { }); } + @bindThis public createImportBlockingJob(user: ThinUser, fileId: DriveFile['id']) { return this.dbQueue.add('importBlocking', { user: user, @@ -159,6 +172,7 @@ export class QueueService { }); } + @bindThis public createImportUserListsJob(user: ThinUser, fileId: DriveFile['id']) { return this.dbQueue.add('importUserLists', { user: user, @@ -169,6 +183,7 @@ export class QueueService { }); } + @bindThis public createImportCustomEmojisJob(user: ThinUser, fileId: DriveFile['id']) { return this.dbQueue.add('importCustomEmojis', { user: user, @@ -179,6 +194,7 @@ export class QueueService { }); } + @bindThis public createDeleteAccountJob(user: ThinUser, opts: { soft?: boolean; } = {}) { return this.dbQueue.add('deleteAccount', { user: user, @@ -189,6 +205,7 @@ export class QueueService { }); } + @bindThis public createDeleteObjectStorageFileJob(key: string) { return this.objectStorageQueue.add('deleteFile', { key: key, @@ -198,6 +215,7 @@ export class QueueService { }); } + @bindThis public createCleanRemoteFilesJob() { return this.objectStorageQueue.add('cleanRemoteFiles', {}, { removeOnComplete: true, @@ -205,6 +223,7 @@ export class QueueService { }); } + @bindThis public webhookDeliver(webhook: Webhook, type: typeof webhookEventTypes[number], content: unknown) { const data = { type, @@ -228,6 +247,7 @@ export class QueueService { }); } + @bindThis public destroy() { this.deliverQueue.once('cleaned', (jobs, status) => { //deliverLogger.succ(`Cleaned ${jobs.length} ${status} jobs`); diff --git a/packages/backend/src/core/ReactionService.ts b/packages/backend/src/core/ReactionService.ts index 7a9724e7dd..b02c990566 100644 --- a/packages/backend/src/core/ReactionService.ts +++ b/packages/backend/src/core/ReactionService.ts @@ -49,6 +49,7 @@ type DecodedReaction = { */ host?: string | null; }; +import { bindThis } from '@/decorators.js'; @Injectable() export class ReactionService { @@ -81,6 +82,7 @@ export class ReactionService { ) { } + @bindThis public async create(user: { id: User['id']; host: User['host']; }, note: Note, reaction?: string) { // Check blocking if (note.userId !== user.id) { @@ -196,6 +198,7 @@ export class ReactionService { //#endregion } + @bindThis public async delete(user: { id: User['id']; host: User['host']; }, note: Note) { // if already unreacted const exist = await this.noteReactionsRepository.findOneBy({ @@ -244,11 +247,13 @@ export class ReactionService { //#endregion } + @bindThis public async getFallbackReaction(): Promise { const meta = await this.metaService.fetch(); return meta.useStarForReactionFallback ? '⭐' : '👍'; } + @bindThis public convertLegacyReactions(reactions: Record) { const _reactions = {} as Record; @@ -279,6 +284,7 @@ export class ReactionService { return _reactions2; } + @bindThis public async toDbReaction(reaction?: string | null, reacterHost?: string | null): Promise { if (reaction == null) return await this.getFallbackReaction(); @@ -311,6 +317,7 @@ export class ReactionService { return await this.getFallbackReaction(); } + @bindThis public decodeReaction(str: string): DecodedReaction { const custom = str.match(/^:([\w+-]+)(?:@([\w.-]+))?:$/); @@ -332,6 +339,7 @@ export class ReactionService { }; } + @bindThis public convertLegacyReaction(reaction: string): string { reaction = this.decodeReaction(reaction).reaction; if (Object.keys(legacies).includes(reaction)) return legacies[reaction]; diff --git a/packages/backend/src/core/RelayService.ts b/packages/backend/src/core/RelayService.ts index 7951edddcb..5fb853f25a 100644 --- a/packages/backend/src/core/RelayService.ts +++ b/packages/backend/src/core/RelayService.ts @@ -12,6 +12,7 @@ import { DI } from '@/di-symbols.js'; import { deepClone } from '@/misc/clone.js'; const ACTOR_USERNAME = 'relay.actor' as const; +import { bindThis } from '@/decorators.js'; @Injectable() export class RelayService { @@ -32,6 +33,7 @@ export class RelayService { this.relaysCache = new Cache(1000 * 60 * 10); } + @bindThis private async getRelayActor(): Promise { const user = await this.usersRepository.findOneBy({ host: IsNull(), @@ -44,6 +46,7 @@ export class RelayService { return created as ILocalUser; } + @bindThis public async addRelay(inbox: string): Promise { const relay = await this.relaysRepository.insert({ id: this.idService.genId(), @@ -59,6 +62,7 @@ export class RelayService { return relay; } + @bindThis public async removeRelay(inbox: string): Promise { const relay = await this.relaysRepository.findOneBy({ inbox, @@ -77,11 +81,13 @@ export class RelayService { await this.relaysRepository.delete(relay.id); } + @bindThis public async listRelay(): Promise { const relays = await this.relaysRepository.find(); return relays; } + @bindThis public async relayAccepted(id: string): Promise { const result = await this.relaysRepository.update(id, { status: 'accepted', @@ -90,6 +96,7 @@ export class RelayService { return JSON.stringify(result); } + @bindThis public async relayRejected(id: string): Promise { const result = await this.relaysRepository.update(id, { status: 'rejected', @@ -98,6 +105,7 @@ export class RelayService { return JSON.stringify(result); } + @bindThis public async deliverToRelays(user: { id: User['id']; host: null; }, activity: any): Promise { if (activity == null) return; diff --git a/packages/backend/src/core/RemoteLoggerService.ts b/packages/backend/src/core/RemoteLoggerService.ts index 68246466c8..0ea5d7b42f 100644 --- a/packages/backend/src/core/RemoteLoggerService.ts +++ b/packages/backend/src/core/RemoteLoggerService.ts @@ -1,6 +1,7 @@ import { Inject, Injectable } from '@nestjs/common'; import type Logger from '@/logger.js'; import { LoggerService } from '@/core/LoggerService.js'; +import { bindThis } from '@/decorators.js'; @Injectable() export class RemoteLoggerService { diff --git a/packages/backend/src/core/RemoteUserResolveService.ts b/packages/backend/src/core/RemoteUserResolveService.ts index 809b50f6e9..dde4098624 100644 --- a/packages/backend/src/core/RemoteUserResolveService.ts +++ b/packages/backend/src/core/RemoteUserResolveService.ts @@ -11,6 +11,7 @@ import { UtilityService } from '@/core/UtilityService.js'; import { WebfingerService } from '@/core/WebfingerService.js'; import { RemoteLoggerService } from '@/core/RemoteLoggerService.js'; import { ApPersonService } from '@/core/activitypub/models/ApPersonService.js'; +import { bindThis } from '@/decorators.js'; @Injectable() export class RemoteUserResolveService { @@ -31,6 +32,7 @@ export class RemoteUserResolveService { this.logger = this.remoteLoggerService.logger.createSubLogger('resolve-user'); } + @bindThis public async resolveUser(username: string, host: string | null): Promise { const usernameLower = username.toLowerCase(); @@ -116,6 +118,7 @@ export class RemoteUserResolveService { return user; } + @bindThis private async resolveSelf(acctLower: string) { this.logger.info(`WebFinger for ${chalk.yellow(acctLower)}`); const finger = await this.webfingerService.webfinger(acctLower).catch(err => { diff --git a/packages/backend/src/core/S3Service.ts b/packages/backend/src/core/S3Service.ts index 1374ee06c8..0ce69aaa74 100644 --- a/packages/backend/src/core/S3Service.ts +++ b/packages/backend/src/core/S3Service.ts @@ -5,6 +5,7 @@ import { DI } from '@/di-symbols.js'; import type { Config } from '@/config.js'; import type { Meta } from '@/models/entities/Meta.js'; import { HttpRequestService } from '@/core/HttpRequestService.js'; +import { bindThis } from '@/decorators.js'; @Injectable() export class S3Service { @@ -16,6 +17,7 @@ export class S3Service { ) { } + @bindThis public getS3(meta: Meta) { const u = meta.objectStorageEndpoint != null ? `${meta.objectStorageUseSSL ? 'https://' : 'http://'}${meta.objectStorageEndpoint}` diff --git a/packages/backend/src/core/SignupService.ts b/packages/backend/src/core/SignupService.ts index 1e34d9e4f8..9cf203566d 100644 --- a/packages/backend/src/core/SignupService.ts +++ b/packages/backend/src/core/SignupService.ts @@ -14,6 +14,7 @@ import generateUserToken from '@/misc/generate-native-user-token.js'; import UsersChart from './chart/charts/users.js'; import { UserEntityService } from '@/core/entities/UserEntityService.js'; import { UtilityService } from './UtilityService.js'; +import { bindThis } from '@/decorators.js'; @Injectable() export class SignupService { @@ -37,6 +38,7 @@ export class SignupService { ) { } + @bindThis public async signup(opts: { username: User['username']; password?: string | null; diff --git a/packages/backend/src/core/TwoFactorAuthenticationService.ts b/packages/backend/src/core/TwoFactorAuthenticationService.ts index 54e87ec36f..150047fd22 100644 --- a/packages/backend/src/core/TwoFactorAuthenticationService.ts +++ b/packages/backend/src/core/TwoFactorAuthenticationService.ts @@ -103,6 +103,7 @@ function PEMString(pemBuffer: Buffer, type = 'CERTIFICATE') { `\n-----END ${type}-----\n` ); } +import { bindThis } from '@/decorators.js'; @Injectable() export class TwoFactorAuthenticationService { @@ -115,6 +116,7 @@ export class TwoFactorAuthenticationService { ) { } + @bindThis public hash(data: Buffer) { return crypto .createHash('sha256') @@ -122,6 +124,7 @@ export class TwoFactorAuthenticationService { .digest(); } + @bindThis public verifySignin({ publicKey, authenticatorData, @@ -159,6 +162,7 @@ export class TwoFactorAuthenticationService { .verify(PEMString(publicKey), signature); } + @bindThis public getProcedures() { return { none: { diff --git a/packages/backend/src/core/UserBlockingService.ts b/packages/backend/src/core/UserBlockingService.ts index 3399bb510f..d411768dc7 100644 --- a/packages/backend/src/core/UserBlockingService.ts +++ b/packages/backend/src/core/UserBlockingService.ts @@ -14,6 +14,7 @@ import { UserEntityService } from '@/core/entities/UserEntityService.js'; import { ApRendererService } from '@/core/activitypub/ApRendererService.js'; import { LoggerService } from '@/core/LoggerService.js'; import { WebhookService } from '@/core/WebhookService.js'; +import { bindThis } from '@/decorators.js'; @Injectable() export class UserBlockingService { @@ -50,6 +51,7 @@ export class UserBlockingService { this.logger = this.loggerService.getLogger('user-block'); } + @bindThis public async block(blocker: User, blockee: User) { await Promise.all([ this.cancelRequest(blocker, blockee), @@ -76,6 +78,7 @@ export class UserBlockingService { } } + @bindThis private async cancelRequest(follower: User, followee: User) { const request = await this.followRequestsRepository.findOneBy({ followeeId: followee.id, @@ -126,6 +129,7 @@ export class UserBlockingService { } } + @bindThis private async unFollow(follower: User, followee: User) { const following = await this.followingsRepository.findOneBy({ followerId: follower.id, @@ -167,6 +171,7 @@ export class UserBlockingService { } } + @bindThis private async removeFromList(listOwner: User, user: User) { const userLists = await this.userListsRepository.findBy({ userId: listOwner.id, @@ -180,6 +185,7 @@ export class UserBlockingService { } } + @bindThis public async unblock(blocker: CacheableUser, blockee: CacheableUser) { const blocking = await this.blockingsRepository.findOneBy({ blockerId: blocker.id, diff --git a/packages/backend/src/core/UserCacheService.ts b/packages/backend/src/core/UserCacheService.ts index 25a600a8da..423c8993e3 100644 --- a/packages/backend/src/core/UserCacheService.ts +++ b/packages/backend/src/core/UserCacheService.ts @@ -6,6 +6,7 @@ import type { CacheableLocalUser, CacheableUser, ILocalUser } from '@/models/ent import { DI } from '@/di-symbols.js'; import { UserEntityService } from '@/core/entities/UserEntityService.js'; import type { OnApplicationShutdown } from '@nestjs/common'; +import { bindThis } from '@/decorators.js'; @Injectable() export class UserCacheService implements OnApplicationShutdown { @@ -23,7 +24,7 @@ export class UserCacheService implements OnApplicationShutdown { private userEntityService: UserEntityService, ) { - this.onMessage = this.onMessage.bind(this); + //this.onMessage = this.onMessage.bind(this); this.userByIdCache = new Cache(Infinity); this.localUserByNativeTokenCache = new Cache(Infinity); @@ -33,6 +34,7 @@ export class UserCacheService implements OnApplicationShutdown { this.redisSubscriber.on('message', this.onMessage); } + @bindThis private async onMessage(_: string, data: string): Promise { const obj = JSON.parse(data); @@ -68,6 +70,7 @@ export class UserCacheService implements OnApplicationShutdown { } } + @bindThis public onApplicationShutdown(signal?: string | undefined) { this.redisSubscriber.off('message', this.onMessage); } diff --git a/packages/backend/src/core/UserFollowingService.ts b/packages/backend/src/core/UserFollowingService.ts index 2f51e2a9df..a6cb042c7d 100644 --- a/packages/backend/src/core/UserFollowingService.ts +++ b/packages/backend/src/core/UserFollowingService.ts @@ -31,6 +31,7 @@ type Remote = IRemoteUser | { inbox: IRemoteUser['inbox']; }; type Both = Local | Remote; +import { bindThis } from '@/decorators.js'; @Injectable() export class UserFollowingService { @@ -66,6 +67,7 @@ export class UserFollowingService { ) { } + @bindThis public async follow(_follower: { id: User['id'] }, _followee: { id: User['id'] }, requestId?: string): Promise { const [follower, followee] = await Promise.all([ this.usersRepository.findOneByOrFail({ id: _follower.id }), @@ -140,6 +142,7 @@ export class UserFollowingService { } } + @bindThis private async insertFollowingDoc( followee: { id: User['id']; host: User['host']; uri: User['host']; inbox: User['inbox']; sharedInbox: User['sharedInbox'] @@ -253,6 +256,7 @@ export class UserFollowingService { } } + @bindThis public async unfollow( follower: { id: User['id']; host: User['host']; uri: User['host']; inbox: User['inbox']; sharedInbox: User['sharedInbox']; @@ -305,6 +309,7 @@ export class UserFollowingService { } } + @bindThis private async decrementFollowing( follower: {id: User['id']; host: User['host']; }, followee: { id: User['id']; host: User['host']; }, @@ -333,6 +338,7 @@ export class UserFollowingService { this.perUserFollowingChart.update(follower, followee, false); } + @bindThis public async createFollowRequest( follower: { id: User['id']; host: User['host']; uri: User['host']; inbox: User['inbox']; sharedInbox: User['sharedInbox']; @@ -396,6 +402,7 @@ export class UserFollowingService { } } + @bindThis public async cancelFollowRequest( followee: { id: User['id']; host: User['host']; uri: User['host']; inbox: User['inbox'] @@ -431,6 +438,7 @@ export class UserFollowingService { }).then(packed => this.globalEventServie.publishMainStream(followee.id, 'meUpdated', packed)); } + @bindThis public async acceptFollowRequest( followee: { id: User['id']; host: User['host']; uri: User['host']; inbox: User['inbox']; sharedInbox: User['sharedInbox']; @@ -458,6 +466,7 @@ export class UserFollowingService { }).then(packed => this.globalEventServie.publishMainStream(followee.id, 'meUpdated', packed)); } + @bindThis public async acceptAllFollowRequests( user: { id: User['id']; host: User['host']; uri: User['host']; inbox: User['inbox']; sharedInbox: User['sharedInbox']; @@ -476,6 +485,7 @@ export class UserFollowingService { /** * API following/request/reject */ + @bindThis public async rejectFollowRequest(user: Local, follower: Both): Promise { if (this.userEntityService.isRemoteUser(follower)) { this.deliverReject(user, follower); @@ -491,6 +501,7 @@ export class UserFollowingService { /** * API following/reject */ + @bindThis public async rejectFollow(user: Local, follower: Both): Promise { if (this.userEntityService.isRemoteUser(follower)) { this.deliverReject(user, follower); @@ -506,6 +517,7 @@ export class UserFollowingService { /** * AP Reject/Follow */ + @bindThis public async remoteReject(actor: Remote, follower: Local): Promise { await this.removeFollowRequest(actor, follower); await this.removeFollow(actor, follower); @@ -515,6 +527,7 @@ export class UserFollowingService { /** * Remove follow request record */ + @bindThis private async removeFollowRequest(followee: Both, follower: Both): Promise { const request = await this.followRequestsRepository.findOneBy({ followeeId: followee.id, @@ -529,6 +542,7 @@ export class UserFollowingService { /** * Remove follow record */ + @bindThis private async removeFollow(followee: Both, follower: Both): Promise { const following = await this.followingsRepository.findOneBy({ followeeId: followee.id, @@ -544,6 +558,7 @@ export class UserFollowingService { /** * Deliver Reject to remote */ + @bindThis private async deliverReject(followee: Local, follower: Remote): Promise { const request = await this.followRequestsRepository.findOneBy({ followeeId: followee.id, @@ -557,6 +572,7 @@ export class UserFollowingService { /** * Publish unfollow to local */ + @bindThis private async publishUnfollow(followee: Both, follower: Local): Promise { const packedFollowee = await this.userEntityService.pack(followee.id, follower, { detail: true, diff --git a/packages/backend/src/core/UserKeypairStoreService.ts b/packages/backend/src/core/UserKeypairStoreService.ts index 8eca03a10b..1d3cc87c8d 100644 --- a/packages/backend/src/core/UserKeypairStoreService.ts +++ b/packages/backend/src/core/UserKeypairStoreService.ts @@ -4,6 +4,7 @@ import type { UserKeypairsRepository } from '@/models/index.js'; import { Cache } from '@/misc/cache.js'; import type { UserKeypair } from '@/models/entities/UserKeypair.js'; import { DI } from '@/di-symbols.js'; +import { bindThis } from '@/decorators.js'; @Injectable() export class UserKeypairStoreService { @@ -16,6 +17,7 @@ export class UserKeypairStoreService { this.cache = new Cache(Infinity); } + @bindThis public async getUserKeypair(userId: User['id']): Promise { return await this.cache.fetch(userId, () => this.userKeypairsRepository.findOneByOrFail({ userId: userId })); } diff --git a/packages/backend/src/core/UserListService.ts b/packages/backend/src/core/UserListService.ts index 1d1ead5a1f..054387ff8e 100644 --- a/packages/backend/src/core/UserListService.ts +++ b/packages/backend/src/core/UserListService.ts @@ -9,6 +9,7 @@ import { GlobalEventService } from '@/core/GlobalEventService.js'; import { DI } from '@/di-symbols.js'; import { UserEntityService } from '@/core/entities/UserEntityService.js'; import { ProxyAccountService } from '@/core/ProxyAccountService.js'; +import { bindThis } from '@/decorators.js'; @Injectable() export class UserListService { @@ -27,6 +28,7 @@ export class UserListService { ) { } + @bindThis public async push(target: User, list: UserList) { await this.userListJoiningsRepository.insert({ id: this.idService.genId(), diff --git a/packages/backend/src/core/UserMutingService.ts b/packages/backend/src/core/UserMutingService.ts index 4c09e450c8..3029d02c00 100644 --- a/packages/backend/src/core/UserMutingService.ts +++ b/packages/backend/src/core/UserMutingService.ts @@ -5,6 +5,7 @@ import { QueueService } from '@/core/QueueService.js'; import { GlobalEventService } from '@/core/GlobalEventService.js'; import type { User } from '@/models/entities/User.js'; import { DI } from '@/di-symbols.js'; +import { bindThis } from '@/decorators.js'; @Injectable() export class UserMutingService { @@ -21,6 +22,7 @@ export class UserMutingService { ) { } + @bindThis public async mute(user: User, target: User): Promise { await this.mutingsRepository.insert({ id: this.idService.genId(), diff --git a/packages/backend/src/core/UserSuspendService.ts b/packages/backend/src/core/UserSuspendService.ts index 02f686bab6..df1664942f 100644 --- a/packages/backend/src/core/UserSuspendService.ts +++ b/packages/backend/src/core/UserSuspendService.ts @@ -8,6 +8,7 @@ import { DI } from '@/di-symbols.js'; import type { Config } from '@/config.js'; import { ApRendererService } from '@/core/activitypub/ApRendererService.js'; import { UserEntityService } from '@/core/entities/UserEntityService.js'; +import { bindThis } from '@/decorators.js'; @Injectable() export class UserSuspendService { @@ -28,6 +29,7 @@ export class UserSuspendService { ) { } + @bindThis public async doPostSuspend(user: { id: User['id']; host: User['host'] }): Promise { this.globalEventService.publishInternalEvent('userChangeSuspendedState', { id: user.id, isSuspended: true }); @@ -57,6 +59,7 @@ export class UserSuspendService { } } + @bindThis public async doPostUnsuspend(user: User): Promise { this.globalEventService.publishInternalEvent('userChangeSuspendedState', { id: user.id, isSuspended: false }); diff --git a/packages/backend/src/core/UtilityService.ts b/packages/backend/src/core/UtilityService.ts index 15dd684286..1412e6e9aa 100644 --- a/packages/backend/src/core/UtilityService.ts +++ b/packages/backend/src/core/UtilityService.ts @@ -3,6 +3,7 @@ import { toASCII } from 'punycode'; import { Inject, Injectable } from '@nestjs/common'; import { DI } from '@/di-symbols.js'; import type { Config } from '@/config.js'; +import { bindThis } from '@/decorators.js'; @Injectable() export class UtilityService { @@ -12,24 +13,29 @@ export class UtilityService { ) { } + @bindThis public getFullApAccount(username: string, host: string | null): string { return host ? `${username}@${this.toPuny(host)}` : `${username}@${this.toPuny(this.config.host)}`; } + @bindThis public isSelfHost(host: string | null): boolean { if (host == null) return true; return this.toPuny(this.config.host) === this.toPuny(host); } + @bindThis public extractDbHost(uri: string): string { const url = new URL(uri); return this.toPuny(url.hostname); } + @bindThis public toPuny(host: string): string { return toASCII(host.toLowerCase()); } + @bindThis public toPunyNullable(host: string | null | undefined): string | null { if (host == null) return null; return toASCII(host.toLowerCase()); diff --git a/packages/backend/src/core/VideoProcessingService.ts b/packages/backend/src/core/VideoProcessingService.ts index af4036a291..2807960cb7 100644 --- a/packages/backend/src/core/VideoProcessingService.ts +++ b/packages/backend/src/core/VideoProcessingService.ts @@ -5,6 +5,7 @@ import type { Config } from '@/config.js'; import { ImageProcessingService } from '@/core/ImageProcessingService.js'; import type { IImage } from '@/core/ImageProcessingService.js'; import { createTempDir } from '@/misc/create-temp.js'; +import { bindThis } from '@/decorators.js'; @Injectable() export class VideoProcessingService { @@ -16,6 +17,7 @@ export class VideoProcessingService { ) { } + @bindThis public async generateVideoThumbnail(source: string): Promise { const [dir, cleanup] = await createTempDir(); diff --git a/packages/backend/src/core/WebfingerService.ts b/packages/backend/src/core/WebfingerService.ts index d2a88be583..2d7afe5c88 100644 --- a/packages/backend/src/core/WebfingerService.ts +++ b/packages/backend/src/core/WebfingerService.ts @@ -14,6 +14,7 @@ type IWebFinger = { links: ILink[]; subject: string; }; +import { bindThis } from '@/decorators.js'; @Injectable() export class WebfingerService { @@ -25,12 +26,14 @@ export class WebfingerService { ) { } + @bindThis public async webfinger(query: string): Promise { const url = this.genUrl(query); return await this.httpRequestService.getJson(url, 'application/jrd+json, application/json') as IWebFinger; } + @bindThis private genUrl(query: string): string { if (query.match(/^https?:\/\//)) { const u = new URL(query); diff --git a/packages/backend/src/core/WebhookService.ts b/packages/backend/src/core/WebhookService.ts index 1a690314f8..91a39f1359 100644 --- a/packages/backend/src/core/WebhookService.ts +++ b/packages/backend/src/core/WebhookService.ts @@ -4,6 +4,7 @@ import type { WebhooksRepository } from '@/models/index.js'; import type { Webhook } from '@/models/entities/Webhook.js'; import { DI } from '@/di-symbols.js'; import type { OnApplicationShutdown } from '@nestjs/common'; +import { bindThis } from '@/decorators.js'; @Injectable() export class WebhookService implements OnApplicationShutdown { @@ -17,10 +18,11 @@ export class WebhookService implements OnApplicationShutdown { @Inject(DI.webhooksRepository) private webhooksRepository: WebhooksRepository, ) { - this.onMessage = this.onMessage.bind(this); + //this.onMessage = this.onMessage.bind(this); this.redisSubscriber.on('message', this.onMessage); } + @bindThis public async getActiveWebhooks() { if (!this.webhooksFetched) { this.webhooks = await this.webhooksRepository.findBy({ @@ -32,6 +34,7 @@ export class WebhookService implements OnApplicationShutdown { return this.webhooks; } + @bindThis private async onMessage(_: string, data: string): Promise { const obj = JSON.parse(data); @@ -64,6 +67,7 @@ export class WebhookService implements OnApplicationShutdown { } } + @bindThis public onApplicationShutdown(signal?: string | undefined) { this.redisSubscriber.off('message', this.onMessage); } diff --git a/packages/backend/src/core/activitypub/ApAudienceService.ts b/packages/backend/src/core/activitypub/ApAudienceService.ts index 744017aa3a..64f01644a7 100644 --- a/packages/backend/src/core/activitypub/ApAudienceService.ts +++ b/packages/backend/src/core/activitypub/ApAudienceService.ts @@ -4,6 +4,7 @@ import promiseLimit from 'promise-limit'; import { DI } from '@/di-symbols.js'; import type { CacheableRemoteUser, CacheableUser } from '@/models/entities/User.js'; import { concat, toArray, toSingle, unique } from '@/misc/prelude/array.js'; +import { bindThis } from '@/decorators.js'; import { getApId, getApIds, getApType, isAccept, isActor, isAdd, isAnnounce, isBlock, isCollection, isCollectionOrOrderedCollection, isCreate, isDelete, isFlag, isFollow, isLike, isPost, isRead, isReject, isRemove, isTombstone, isUndo, isUpdate, validActor, validPost } from './type.js'; import { ApPersonService } from './models/ApPersonService.js'; import type { ApObject } from './type.js'; @@ -24,6 +25,7 @@ export class ApAudienceService { ) { } + @bindThis public async parseAudience(actor: CacheableRemoteUser, to?: ApObject, cc?: ApObject, resolver?: Resolver): Promise { const toGroups = this.groupingAudience(getApIds(to), actor); const ccGroups = this.groupingAudience(getApIds(cc), actor); @@ -66,6 +68,7 @@ export class ApAudienceService { }; } + @bindThis private groupingAudience(ids: string[], actor: CacheableRemoteUser) { const groups = { public: [] as string[], @@ -88,6 +91,7 @@ export class ApAudienceService { return groups; } + @bindThis private isPublic(id: string) { return [ 'https://www.w3.org/ns/activitystreams#Public', @@ -96,6 +100,7 @@ export class ApAudienceService { ].includes(id); } + @bindThis private isFollowers(id: string, actor: CacheableRemoteUser) { return ( id === (actor.followersUri ?? `${actor.uri}/followers`) diff --git a/packages/backend/src/core/activitypub/ApDbResolverService.ts b/packages/backend/src/core/activitypub/ApDbResolverService.ts index 77d200c3c8..1f28fb3a07 100644 --- a/packages/backend/src/core/activitypub/ApDbResolverService.ts +++ b/packages/backend/src/core/activitypub/ApDbResolverService.ts @@ -9,6 +9,7 @@ import type { UserPublickey } from '@/models/entities/UserPublickey.js'; import { UserCacheService } from '@/core/UserCacheService.js'; import type { Note } from '@/models/entities/Note.js'; import type { MessagingMessage } from '@/models/entities/MessagingMessage.js'; +import { bindThis } from '@/decorators.js'; import { getApId } from './type.js'; import { ApPersonService } from './models/ApPersonService.js'; import type { IObject } from './type.js'; @@ -57,6 +58,7 @@ export class ApDbResolverService { this.publicKeyByUserIdCache = new Cache(Infinity); } + @bindThis public parseUri(value: string | IObject): UriParseResult { const uri = getApId(value); @@ -82,6 +84,7 @@ export class ApDbResolverService { /** * AP Note => Misskey Note in DB */ + @bindThis public async getNoteFromApId(value: string | IObject): Promise { const parsed = this.parseUri(value); @@ -98,6 +101,7 @@ export class ApDbResolverService { } } + @bindThis public async getMessageFromApId(value: string | IObject): Promise { const parsed = this.parseUri(value); @@ -117,6 +121,7 @@ export class ApDbResolverService { /** * AP Person => Misskey User in DB */ + @bindThis public async getUserFromApId(value: string | IObject): Promise { const parsed = this.parseUri(value); @@ -136,6 +141,7 @@ export class ApDbResolverService { /** * AP KeyId => Misskey User and Key */ + @bindThis public async getAuthUserFromKeyId(keyId: string): Promise<{ user: CacheableRemoteUser; key: UserPublickey; @@ -161,6 +167,7 @@ export class ApDbResolverService { /** * AP Actor id => Misskey User and Key */ + @bindThis public async getAuthUserFromApId(uri: string): Promise<{ user: CacheableRemoteUser; key: UserPublickey | null; diff --git a/packages/backend/src/core/activitypub/ApDeliverManagerService.ts b/packages/backend/src/core/activitypub/ApDeliverManagerService.ts index 6fc75a0397..256cf12651 100644 --- a/packages/backend/src/core/activitypub/ApDeliverManagerService.ts +++ b/packages/backend/src/core/activitypub/ApDeliverManagerService.ts @@ -6,6 +6,7 @@ import type { Config } from '@/config.js'; import type { ILocalUser, IRemoteUser, User } from '@/models/entities/User.js'; import { QueueService } from '@/core/QueueService.js'; import { UserEntityService } from '@/core/entities/UserEntityService.js'; +import { bindThis } from '@/decorators.js'; interface IRecipe { type: string; @@ -48,6 +49,7 @@ export class ApDeliverManagerService { * @param activity Activity * @param from Followee */ + @bindThis public async deliverToFollowers(actor: { id: ILocalUser['id']; host: null; }, activity: any) { const manager = new DeliverManager( this.userEntityService, @@ -65,6 +67,7 @@ export class ApDeliverManagerService { * @param activity Activity * @param to Target user */ + @bindThis public async deliverToUser(actor: { id: ILocalUser['id']; host: null; }, activity: any, to: IRemoteUser) { const manager = new DeliverManager( this.userEntityService, @@ -77,6 +80,7 @@ export class ApDeliverManagerService { await manager.execute(); } + @bindThis public createDeliverManager(actor: { id: User['id']; host: null; }, activity: any) { return new DeliverManager( this.userEntityService, @@ -114,6 +118,7 @@ class DeliverManager { /** * Add recipe for followers deliver */ + @bindThis public addFollowersRecipe() { const deliver = { type: 'Followers', @@ -126,6 +131,7 @@ class DeliverManager { * Add recipe for direct deliver * @param to To */ + @bindThis public addDirectRecipe(to: IRemoteUser) { const recipe = { type: 'Direct', @@ -139,6 +145,7 @@ class DeliverManager { * Add recipe * @param recipe Recipe */ + @bindThis public addRecipe(recipe: IRecipe) { this.recipes.push(recipe); } @@ -146,6 +153,7 @@ class DeliverManager { /** * Execute delivers */ + @bindThis public async execute() { if (!this.userEntityService.isLocalUser(this.actor)) return; diff --git a/packages/backend/src/core/activitypub/ApInboxService.ts b/packages/backend/src/core/activitypub/ApInboxService.ts index 3da384ec2d..79a917426a 100644 --- a/packages/backend/src/core/activitypub/ApInboxService.ts +++ b/packages/backend/src/core/activitypub/ApInboxService.ts @@ -32,6 +32,7 @@ import { ApPersonService } from './models/ApPersonService.js'; import { ApQuestionService } from './models/ApQuestionService.js'; import type { Resolver } from './ApResolverService.js'; import type { IAccept, IAdd, IAnnounce, IBlock, ICreate, IDelete, IFlag, IFollow, ILike, IObject, IRead, IReject, IRemove, IUndo, IUpdate } from './type.js'; +import { bindThis } from '@/decorators.js'; @Injectable() export class ApInboxService { @@ -85,6 +86,7 @@ export class ApInboxService { this.logger = this.apLoggerService.logger; } + @bindThis public async performActivity(actor: CacheableRemoteUser, activity: IObject) { if (isCollectionOrOrderedCollection(activity)) { const resolver = this.apResolverService.createResolver(); @@ -112,6 +114,7 @@ export class ApInboxService { } } + @bindThis public async performOneActivity(actor: CacheableRemoteUser, activity: IObject): Promise { if (actor.isSuspended) return; @@ -148,6 +151,7 @@ export class ApInboxService { } } + @bindThis private async follow(actor: CacheableRemoteUser, activity: IFollow): Promise { const followee = await this.apDbResolverService.getUserFromApId(activity.object); @@ -163,6 +167,7 @@ export class ApInboxService { return 'ok'; } + @bindThis private async like(actor: CacheableRemoteUser, activity: ILike): Promise { const targetUri = getApId(activity.object); @@ -180,6 +185,7 @@ export class ApInboxService { }).then(() => 'ok'); } + @bindThis private async read(actor: CacheableRemoteUser, activity: IRead): Promise { const id = await getApId(activity.object); @@ -202,6 +208,7 @@ export class ApInboxService { return `ok: mark as read (${message.userId} => ${message.recipientId} ${message.id})`; } + @bindThis private async accept(actor: CacheableRemoteUser, activity: IAccept): Promise { const uri = activity.id ?? activity; @@ -219,6 +226,7 @@ export class ApInboxService { return `skip: Unknown Accept type: ${getApType(object)}`; } + @bindThis private async acceptFollow(actor: CacheableRemoteUser, activity: IFollow): Promise { // ※ activityはこっちから投げたフォローリクエストなので、activity.actorは存在するローカルユーザーである必要がある @@ -242,6 +250,7 @@ export class ApInboxService { return 'ok'; } + @bindThis private async add(actor: CacheableRemoteUser, activity: IAdd): Promise { if ('actor' in activity && actor.uri !== activity.actor) { throw new Error('invalid actor'); @@ -261,6 +270,7 @@ export class ApInboxService { throw new Error(`unknown target: ${activity.target}`); } + @bindThis private async announce(actor: CacheableRemoteUser, activity: IAnnounce): Promise { const uri = getApId(activity); @@ -271,6 +281,7 @@ export class ApInboxService { this.announceNote(actor, activity, targetUri); } + @bindThis private async announceNote(actor: CacheableRemoteUser, activity: IAnnounce, targetUri: string): Promise { const uri = getApId(activity); @@ -330,6 +341,7 @@ export class ApInboxService { } } + @bindThis private async block(actor: CacheableRemoteUser, activity: IBlock): Promise { // ※ activity.objectにブロック対象があり、それは存在するローカルユーザーのはず @@ -347,6 +359,7 @@ export class ApInboxService { return 'ok'; } + @bindThis private async create(actor: CacheableRemoteUser, activity: ICreate): Promise { const uri = getApId(activity); @@ -382,6 +395,7 @@ export class ApInboxService { } } + @bindThis private async createNote(resolver: Resolver, actor: CacheableRemoteUser, note: IObject, silent = false, activity?: ICreate): Promise { const uri = getApId(note); @@ -416,6 +430,7 @@ export class ApInboxService { } } + @bindThis private async delete(actor: CacheableRemoteUser, activity: IDelete): Promise { if ('actor' in activity && actor.uri !== activity.actor) { throw new Error('invalid actor'); @@ -457,6 +472,7 @@ export class ApInboxService { } } + @bindThis private async deleteActor(actor: CacheableRemoteUser, uri: string): Promise { this.logger.info(`Deleting the Actor: ${uri}`); @@ -478,6 +494,7 @@ export class ApInboxService { return `ok: queued ${job.name} ${job.id}`; } + @bindThis private async deleteNote(actor: CacheableRemoteUser, uri: string): Promise { this.logger.info(`Deleting the Note: ${uri}`); @@ -510,6 +527,7 @@ export class ApInboxService { } } + @bindThis private async flag(actor: CacheableRemoteUser, activity: IFlag): Promise { // objectは `(User|Note) | (User|Note)[]` だけど、全パターンDBスキーマと対応させられないので // 対象ユーザーは一番最初のユーザー として あとはコメントとして格納する @@ -534,6 +552,7 @@ export class ApInboxService { return 'ok'; } + @bindThis private async reject(actor: CacheableRemoteUser, activity: IReject): Promise { const uri = activity.id ?? activity; @@ -551,6 +570,7 @@ export class ApInboxService { return `skip: Unknown Reject type: ${getApType(object)}`; } + @bindThis private async rejectFollow(actor: CacheableRemoteUser, activity: IFollow): Promise { // ※ activityはこっちから投げたフォローリクエストなので、activity.actorは存在するローカルユーザーである必要がある @@ -574,6 +594,7 @@ export class ApInboxService { return 'ok'; } + @bindThis private async remove(actor: CacheableRemoteUser, activity: IRemove): Promise { if ('actor' in activity && actor.uri !== activity.actor) { throw new Error('invalid actor'); @@ -593,6 +614,7 @@ export class ApInboxService { throw new Error(`unknown target: ${activity.target}`); } + @bindThis private async undo(actor: CacheableRemoteUser, activity: IUndo): Promise { if ('actor' in activity && actor.uri !== activity.actor) { throw new Error('invalid actor'); @@ -618,6 +640,7 @@ export class ApInboxService { return `skip: unknown object type ${getApType(object)}`; } + @bindThis private async undoAccept(actor: CacheableRemoteUser, activity: IAccept): Promise { const follower = await this.apDbResolverService.getUserFromApId(activity.object); if (follower == null) { @@ -637,6 +660,7 @@ export class ApInboxService { return 'skip: フォローされていない'; } + @bindThis private async undoAnnounce(actor: CacheableRemoteUser, activity: IAnnounce): Promise { const uri = getApId(activity); @@ -651,6 +675,7 @@ export class ApInboxService { return 'ok: deleted'; } + @bindThis private async undoBlock(actor: CacheableRemoteUser, activity: IBlock): Promise { const blockee = await this.apDbResolverService.getUserFromApId(activity.object); @@ -666,6 +691,7 @@ export class ApInboxService { return 'ok'; } + @bindThis private async undoFollow(actor: CacheableRemoteUser, activity: IFollow): Promise { const followee = await this.apDbResolverService.getUserFromApId(activity.object); if (followee == null) { @@ -699,6 +725,7 @@ export class ApInboxService { return 'skip: リクエストもフォローもされていない'; } + @bindThis private async undoLike(actor: CacheableRemoteUser, activity: ILike): Promise { const targetUri = getApId(activity.object); @@ -713,6 +740,7 @@ export class ApInboxService { return 'ok'; } + @bindThis private async update(actor: CacheableRemoteUser, activity: IUpdate): Promise { if ('actor' in activity && actor.uri !== activity.actor) { return 'skip: invalid actor'; diff --git a/packages/backend/src/core/activitypub/ApLoggerService.ts b/packages/backend/src/core/activitypub/ApLoggerService.ts index a742cc42da..b9bf1e4054 100644 --- a/packages/backend/src/core/activitypub/ApLoggerService.ts +++ b/packages/backend/src/core/activitypub/ApLoggerService.ts @@ -1,6 +1,7 @@ import { Inject, Injectable } from '@nestjs/common'; import type Logger from '@/logger.js'; import { RemoteLoggerService } from '@/core/RemoteLoggerService.js'; +import { bindThis } from '@/decorators.js'; @Injectable() export class ApLoggerService { diff --git a/packages/backend/src/core/activitypub/ApMfmService.ts b/packages/backend/src/core/activitypub/ApMfmService.ts index 8804fde64a..6116822f7a 100644 --- a/packages/backend/src/core/activitypub/ApMfmService.ts +++ b/packages/backend/src/core/activitypub/ApMfmService.ts @@ -6,6 +6,7 @@ import { MfmService } from '@/core/MfmService.js'; import type { Note } from '@/models/entities/Note.js'; import { extractApHashtagObjects } from './models/tag.js'; import type { IObject } from './type.js'; +import { bindThis } from '@/decorators.js'; @Injectable() export class ApMfmService { @@ -17,12 +18,14 @@ export class ApMfmService { ) { } + @bindThis public htmlToMfm(html: string, tag?: IObject | IObject[]) { const hashtagNames = extractApHashtagObjects(tag).map(x => x.name).filter((x): x is string => x != null); return this.mfmService.fromHtml(html, hashtagNames); } + @bindThis public getNoteHtml(note: Note) { if (!note.text) return ''; return this.mfmService.toHtml(mfm.parse(note.text), JSON.parse(note.mentionedRemoteUsers)); diff --git a/packages/backend/src/core/activitypub/ApRendererService.ts b/packages/backend/src/core/activitypub/ApRendererService.ts index 38a92567c3..1800840ee9 100644 --- a/packages/backend/src/core/activitypub/ApRendererService.ts +++ b/packages/backend/src/core/activitypub/ApRendererService.ts @@ -25,6 +25,7 @@ import { LdSignatureService } from './LdSignatureService.js'; import { ApMfmService } from './ApMfmService.js'; import type { IActivity, IObject } from './type.js'; import type { IIdentifier } from './models/identifier.js'; +import { bindThis } from '@/decorators.js'; @Injectable() export class ApRendererService { @@ -59,6 +60,7 @@ export class ApRendererService { ) { } + @bindThis public renderAccept(object: any, user: { id: User['id']; host: null }) { return { type: 'Accept', @@ -67,6 +69,7 @@ export class ApRendererService { }; } + @bindThis public renderAdd(user: ILocalUser, target: any, object: any) { return { type: 'Add', @@ -76,6 +79,7 @@ export class ApRendererService { }; } + @bindThis public renderAnnounce(object: any, note: Note) { const attributedTo = `${this.config.url}/users/${note.userId}`; @@ -108,6 +112,7 @@ export class ApRendererService { * * @param block The block to be rendered. The blockee relation must be loaded. */ + @bindThis public renderBlock(block: Blocking) { if (block.blockee?.uri == null) { throw new Error('renderBlock: missing blockee uri'); @@ -121,6 +126,7 @@ export class ApRendererService { }; } + @bindThis public renderCreate(object: any, note: Note) { const activity = { id: `${this.config.url}/notes/${note.id}/activity`, @@ -136,6 +142,7 @@ export class ApRendererService { return activity; } + @bindThis public renderDelete(object: any, user: { id: User['id']; host: null }) { return { type: 'Delete', @@ -145,6 +152,7 @@ export class ApRendererService { }; } + @bindThis public renderDocument(file: DriveFile) { return { type: 'Document', @@ -154,6 +162,7 @@ export class ApRendererService { }; } + @bindThis public renderEmoji(emoji: Emoji) { return { id: `${this.config.url}/emojis/${emoji.name}`, @@ -170,6 +179,7 @@ export class ApRendererService { // to anonymise reporters, the reporting actor must be a system user // object has to be a uri or array of uris + @bindThis public renderFlag(user: ILocalUser, object: [string], content: string) { return { type: 'Flag', @@ -179,6 +189,7 @@ export class ApRendererService { }; } + @bindThis public renderFollowRelay(relay: Relay, relayActor: ILocalUser) { const follow = { id: `${this.config.url}/activities/follow-relay/${relay.id}`, @@ -194,11 +205,13 @@ export class ApRendererService { * Convert (local|remote)(Follower|Followee)ID to URL * @param id Follower|Followee ID */ + @bindThis public async renderFollowUser(id: User['id']) { const user = await this.usersRepository.findOneByOrFail({ id: id }); return this.userEntityService.isLocalUser(user) ? `${this.config.url}/users/${user.id}` : user.uri; } + @bindThis public renderFollow( follower: { id: User['id']; host: User['host']; uri: User['host'] }, followee: { id: User['id']; host: User['host']; uri: User['host'] }, @@ -214,6 +227,7 @@ export class ApRendererService { return follow; } + @bindThis public renderHashtag(tag: string) { return { type: 'Hashtag', @@ -222,6 +236,7 @@ export class ApRendererService { }; } + @bindThis public renderImage(file: DriveFile) { return { type: 'Image', @@ -231,6 +246,7 @@ export class ApRendererService { }; } + @bindThis public renderKey(user: ILocalUser, key: UserKeypair, postfix?: string) { return { id: `${this.config.url}/users/${user.id}${postfix ?? '/publickey'}`, @@ -243,6 +259,7 @@ export class ApRendererService { }; } + @bindThis public async renderLike(noteReaction: NoteReaction, note: { uri: string | null }) { const reaction = noteReaction.reaction; @@ -268,6 +285,7 @@ export class ApRendererService { return object; } + @bindThis public renderMention(mention: User) { return { type: 'Mention', @@ -276,6 +294,7 @@ export class ApRendererService { }; } + @bindThis public async renderNote(note: Note, dive = true, isTalk = false): Promise { const getPromisedFiles = async (ids: string[]) => { if (!ids || ids.length === 0) return []; @@ -420,6 +439,7 @@ export class ApRendererService { }; } + @bindThis public async renderPerson(user: ILocalUser) { const id = `${this.config.url}/users/${user.id}`; const isSystem = !!user.username.match(/\./); @@ -496,6 +516,7 @@ export class ApRendererService { return person; } + @bindThis public async renderQuestion(user: { id: User['id'] }, note: Note, poll: Poll) { const question = { type: 'Question', @@ -515,6 +536,7 @@ export class ApRendererService { return question; } + @bindThis public renderRead(user: { id: User['id'] }, message: MessagingMessage) { return { type: 'Read', @@ -523,6 +545,7 @@ export class ApRendererService { }; } + @bindThis public renderReject(object: any, user: { id: User['id'] }) { return { type: 'Reject', @@ -531,6 +554,7 @@ export class ApRendererService { }; } + @bindThis public renderRemove(user: { id: User['id'] }, target: any, object: any) { return { type: 'Remove', @@ -540,6 +564,7 @@ export class ApRendererService { }; } + @bindThis public renderTombstone(id: string) { return { id, @@ -547,6 +572,7 @@ export class ApRendererService { }; } + @bindThis public renderUndo(object: any, user: { id: User['id'] }) { if (object == null) return null; const id = typeof object.id === 'string' && object.id.startsWith(this.config.url) ? `${object.id}/undo` : undefined; @@ -560,6 +586,7 @@ export class ApRendererService { }; } + @bindThis public renderUpdate(object: any, user: { id: User['id'] }) { const activity = { id: `${this.config.url}/users/${user.id}#updates/${new Date().getTime()}`, @@ -573,6 +600,7 @@ export class ApRendererService { return activity; } + @bindThis public renderVote(user: { id: User['id'] }, vote: PollVote, note: Note, poll: Poll, pollOwner: IRemoteUser) { return { id: `${this.config.url}/users/${user.id}#votes/${vote.id}/activity`, @@ -591,6 +619,7 @@ export class ApRendererService { }; } + @bindThis public renderActivity(x: any): IActivity | null { if (x == null) return null; @@ -632,6 +661,7 @@ export class ApRendererService { }, x); } + @bindThis public async attachLdSignature(activity: any, user: { id: User['id']; host: null; }): Promise { const keypair = await this.userKeypairStoreService.getUserKeypair(user.id); @@ -651,6 +681,7 @@ export class ApRendererService { * @param prev URL of prev page (optional) * @param next URL of next page (optional) */ + @bindThis public renderOrderedCollectionPage(id: string, totalItems: any, orderedItems: any, partOf: string, prev?: string, next?: string) { const page = { id, @@ -674,6 +705,7 @@ export class ApRendererService { * @param last URL of last page (optional) * @param orderedItems attached objects (optional) */ + @bindThis public renderOrderedCollection(id: string | null, totalItems: any, first?: string, last?: string, orderedItems?: IObject[]) { const page: any = { id, @@ -688,6 +720,7 @@ export class ApRendererService { return page; } + @bindThis private async getEmojis(names: string[]): Promise { if (names == null || names.length === 0) return []; diff --git a/packages/backend/src/core/activitypub/ApRequestService.ts b/packages/backend/src/core/activitypub/ApRequestService.ts index baad46d668..d1edd579fa 100644 --- a/packages/backend/src/core/activitypub/ApRequestService.ts +++ b/packages/backend/src/core/activitypub/ApRequestService.ts @@ -6,6 +6,7 @@ import type { Config } from '@/config.js'; import type { User } from '@/models/entities/User.js'; import { UserKeypairStoreService } from '@/core/UserKeypairStoreService.js'; import { HttpRequestService } from '@/core/HttpRequestService.js'; +import { bindThis } from '@/decorators.js'; type Request = { url: string; @@ -36,6 +37,7 @@ export class ApRequestService { ) { } + @bindThis private createSignedPost(args: { key: PrivateKey, url: string, body: string, additionalHeaders: Record }): Signed { const u = new URL(args.url); const digestHeader = `SHA-256=${crypto.createHash('sha256').update(args.body).digest('base64')}`; @@ -61,6 +63,7 @@ export class ApRequestService { }; } + @bindThis private createSignedGet(args: { key: PrivateKey, url: string, additionalHeaders: Record }): Signed { const u = new URL(args.url); @@ -84,6 +87,7 @@ export class ApRequestService { }; } + @bindThis private signToRequest(request: Request, key: PrivateKey, includeHeaders: string[]): Signed { const signingString = this.genSigningString(request, includeHeaders); const signature = crypto.sign('sha256', Buffer.from(signingString), key.privateKeyPem).toString('base64'); @@ -101,6 +105,7 @@ export class ApRequestService { }; } + @bindThis private genSigningString(request: Request, includeHeaders: string[]): string { request.headers = this.lcObjectKey(request.headers); @@ -117,16 +122,19 @@ export class ApRequestService { return results.join('\n'); } + @bindThis private lcObjectKey(src: Record): Record { const dst: Record = {}; for (const key of Object.keys(src).filter(x => x !== '__proto__' && typeof src[x] === 'string')) dst[key.toLowerCase()] = src[key]; return dst; } + @bindThis private objectAssignWithLcKey(a: Record, b: Record): Record { return Object.assign(this.lcObjectKey(a), this.lcObjectKey(b)); } + @bindThis public async signedPost(user: { id: User['id'] }, url: string, object: any) { const body = JSON.stringify(object); @@ -157,6 +165,7 @@ export class ApRequestService { * @param user http-signature user * @param url URL to fetch */ + @bindThis public async signedGet(url: string, user: { id: User['id'] }) { const keypair = await this.userKeypairStoreService.getUserKeypair(user.id); diff --git a/packages/backend/src/core/activitypub/ApResolverService.ts b/packages/backend/src/core/activitypub/ApResolverService.ts index bcdb9383d1..e96c84f148 100644 --- a/packages/backend/src/core/activitypub/ApResolverService.ts +++ b/packages/backend/src/core/activitypub/ApResolverService.ts @@ -7,58 +7,13 @@ import { MetaService } from '@/core/MetaService.js'; import { HttpRequestService } from '@/core/HttpRequestService.js'; import { DI } from '@/di-symbols.js'; import { UtilityService } from '@/core/UtilityService.js'; +import { bindThis } from '@/decorators.js'; import { isCollectionOrOrderedCollection } from './type.js'; import { ApDbResolverService } from './ApDbResolverService.js'; import { ApRendererService } from './ApRendererService.js'; import { ApRequestService } from './ApRequestService.js'; import type { IObject, ICollection, IOrderedCollection } from './type.js'; -@Injectable() -export class ApResolverService { - constructor( - @Inject(DI.config) - private config: Config, - - @Inject(DI.usersRepository) - private usersRepository: UsersRepository, - - @Inject(DI.notesRepository) - private notesRepository: NotesRepository, - - @Inject(DI.pollsRepository) - private pollsRepository: PollsRepository, - - @Inject(DI.noteReactionsRepository) - private noteReactionsRepository: NoteReactionsRepository, - - private utilityService: UtilityService, - private instanceActorService: InstanceActorService, - private metaService: MetaService, - private apRequestService: ApRequestService, - private httpRequestService: HttpRequestService, - private apRendererService: ApRendererService, - private apDbResolverService: ApDbResolverService, - ) { - } - - public createResolver(): Resolver { - return new Resolver( - this.config, - this.usersRepository, - this.notesRepository, - this.pollsRepository, - this.noteReactionsRepository, - this.utilityService, - this.instanceActorService, - this.metaService, - this.apRequestService, - this.httpRequestService, - this.apRendererService, - this.apDbResolverService, - ); - } -} - export class Resolver { private history: Set; private user?: ILocalUser; @@ -76,15 +31,17 @@ export class Resolver { private httpRequestService: HttpRequestService, private apRendererService: ApRendererService, private apDbResolverService: ApDbResolverService, - private recursionLimit = 100 + private recursionLimit = 100, ) { this.history = new Set(); } + @bindThis public getHistory(): string[] { return Array.from(this.history); } + @bindThis public async resolveCollection(value: string | IObject): Promise { const collection = typeof value === 'string' ? await this.resolve(value) @@ -97,6 +54,7 @@ export class Resolver { } } + @bindThis public async resolve(value: string | IObject): Promise { if (value == null) { throw new Error('resolvee is null (or undefined)'); @@ -152,6 +110,7 @@ export class Resolver { return object; } + @bindThis private resolveLocal(url: string): Promise { const parsed = this.apDbResolverService.parseUri(url); if (!parsed.local) throw new Error('resolveLocal: not local'); @@ -193,3 +152,50 @@ export class Resolver { } } } + +@Injectable() +export class ApResolverService { + constructor( + @Inject(DI.config) + private config: Config, + + @Inject(DI.usersRepository) + private usersRepository: UsersRepository, + + @Inject(DI.notesRepository) + private notesRepository: NotesRepository, + + @Inject(DI.pollsRepository) + private pollsRepository: PollsRepository, + + @Inject(DI.noteReactionsRepository) + private noteReactionsRepository: NoteReactionsRepository, + + private utilityService: UtilityService, + private instanceActorService: InstanceActorService, + private metaService: MetaService, + private apRequestService: ApRequestService, + private httpRequestService: HttpRequestService, + private apRendererService: ApRendererService, + private apDbResolverService: ApDbResolverService, + ) { + } + + @bindThis + public createResolver(): Resolver { + return new Resolver( + this.config, + this.usersRepository, + this.notesRepository, + this.pollsRepository, + this.noteReactionsRepository, + this.utilityService, + this.instanceActorService, + this.metaService, + this.apRequestService, + this.httpRequestService, + this.apRendererService, + this.apDbResolverService, + ); + } +} diff --git a/packages/backend/src/core/activitypub/LdSignatureService.ts b/packages/backend/src/core/activitypub/LdSignatureService.ts index ea39f15b2b..b71320ed0b 100644 --- a/packages/backend/src/core/activitypub/LdSignatureService.ts +++ b/packages/backend/src/core/activitypub/LdSignatureService.ts @@ -2,22 +2,11 @@ import * as crypto from 'node:crypto'; import { Inject, Injectable } from '@nestjs/common'; import fetch from 'node-fetch'; import { HttpRequestService } from '@/core/HttpRequestService.js'; +import { bindThis } from '@/decorators.js'; import { CONTEXTS } from './misc/contexts.js'; // RsaSignature2017 based from https://github.com/transmute-industries/RsaSignature2017 -@Injectable() -export class LdSignatureService { - constructor( - private httpRequestService: HttpRequestService, - ) { - } - - public use(): LdSignature { - return new LdSignature(this.httpRequestService); - } -} - class LdSignature { public debug = false; public preLoad = true; @@ -28,6 +17,7 @@ class LdSignature { ) { } + @bindThis public async signRsaSignature2017(data: any, privateKey: string, creator: string, domain?: string, created?: Date): Promise { const options = { type: 'RsaSignature2017', @@ -64,6 +54,7 @@ class LdSignature { }; } + @bindThis public async verifyRsaSignature2017(data: any, publicKey: string): Promise { const toBeSigned = await this.createVerifyData(data, data.signature); const verifier = crypto.createVerify('sha256'); @@ -71,6 +62,7 @@ class LdSignature { return verifier.verify(publicKey, data.signature.signatureValue, 'base64'); } + @bindThis public async createVerifyData(data: any, options: any) { const transformedOptions = { ...options, @@ -90,11 +82,13 @@ class LdSignature { return verifyData; } + @bindThis public async normalize(data: any) { const customLoader = this.getLoader(); return 42; } + @bindThis private getLoader() { return async (url: string): Promise => { if (!url.match('^https?\:\/\/')) throw `Invalid URL ${url}`; @@ -120,6 +114,7 @@ class LdSignature { }; } + @bindThis private async fetchDocument(url: string) { const json = await fetch(url, { headers: { @@ -139,9 +134,23 @@ class LdSignature { return json; } + @bindThis public sha256(data: string): string { const hash = crypto.createHash('sha256'); hash.update(data); return hash.digest('hex'); } } + +@Injectable() +export class LdSignatureService { + constructor( + private httpRequestService: HttpRequestService, + ) { + } + + @bindThis + public use(): LdSignature { + return new LdSignature(this.httpRequestService); + } +} diff --git a/packages/backend/src/core/activitypub/models/ApImageService.ts b/packages/backend/src/core/activitypub/models/ApImageService.ts index 9bf87f19d4..58fcc8cb53 100644 --- a/packages/backend/src/core/activitypub/models/ApImageService.ts +++ b/packages/backend/src/core/activitypub/models/ApImageService.ts @@ -11,6 +11,7 @@ import { DriveService } from '@/core/DriveService.js'; import type Logger from '@/logger.js'; import { ApResolverService } from '../ApResolverService.js'; import { ApLoggerService } from '../ApLoggerService.js'; +import { bindThis } from '@/decorators.js'; @Injectable() export class ApImageService { @@ -34,6 +35,7 @@ export class ApImageService { /** * Imageを作成します。 */ + @bindThis public async createImage(actor: CacheableRemoteUser, value: any): Promise { // 投稿者が凍結されていたらスキップ if (actor.isSuspended) { @@ -81,6 +83,7 @@ export class ApImageService { * Misskeyに対象のImageが登録されていればそれを返し、そうでなければ * リモートサーバーからフェッチしてMisskeyに登録しそれを返します。 */ + @bindThis public async resolveImage(actor: CacheableRemoteUser, value: any): Promise { // TODO diff --git a/packages/backend/src/core/activitypub/models/ApMentionService.ts b/packages/backend/src/core/activitypub/models/ApMentionService.ts index 1275e24c62..41e6c6b14f 100644 --- a/packages/backend/src/core/activitypub/models/ApMentionService.ts +++ b/packages/backend/src/core/activitypub/models/ApMentionService.ts @@ -9,6 +9,7 @@ import { isMention } from '../type.js'; import { ApResolverService, Resolver } from '../ApResolverService.js'; import { ApPersonService } from './ApPersonService.js'; import type { IObject, IApMention } from '../type.js'; +import { bindThis } from '@/decorators.js'; @Injectable() export class ApMentionService { @@ -21,6 +22,7 @@ export class ApMentionService { ) { } + @bindThis public async extractApMentions(tags: IObject | IObject[] | null | undefined, resolver: Resolver) { const hrefs = unique(this.extractApMentionObjects(tags).map(x => x.href as string)); @@ -32,6 +34,7 @@ export class ApMentionService { return mentionedUsers; } + @bindThis public extractApMentionObjects(tags: IObject | IObject[] | null | undefined): IApMention[] { if (tags == null) return []; return toArray(tags).filter(isMention); diff --git a/packages/backend/src/core/activitypub/models/ApNoteService.ts b/packages/backend/src/core/activitypub/models/ApNoteService.ts index 7cf6725a38..e1d93a08b0 100644 --- a/packages/backend/src/core/activitypub/models/ApNoteService.ts +++ b/packages/backend/src/core/activitypub/models/ApNoteService.ts @@ -32,6 +32,7 @@ import { ApQuestionService } from './ApQuestionService.js'; import { ApImageService } from './ApImageService.js'; import type { Resolver } from '../ApResolverService.js'; import type { IObject, IPost } from '../type.js'; +import { bindThis } from '@/decorators.js'; @Injectable() export class ApNoteService { @@ -74,6 +75,7 @@ export class ApNoteService { this.logger = this.apLoggerService.logger; } + @bindThis public validateNote(object: any, uri: string) { const expectHost = this.utilityService.extractDbHost(uri); @@ -101,6 +103,7 @@ export class ApNoteService { * * Misskeyに対象のNoteが登録されていればそれを返します。 */ + @bindThis public async fetchNote(object: string | IObject): Promise { return await this.apDbResolverService.getNoteFromApId(object); } @@ -108,6 +111,7 @@ export class ApNoteService { /** * Noteを作成します。 */ + @bindThis public async createNote(value: string | IObject, resolver?: Resolver, silent = false): Promise { if (resolver == null) resolver = this.apResolverService.createResolver(); @@ -313,6 +317,7 @@ export class ApNoteService { * Misskeyに対象のNoteが登録されていればそれを返し、そうでなければ * リモートサーバーからフェッチしてMisskeyに登録しそれを返します。 */ + @bindThis public async resolveNote(value: string | IObject, resolver?: Resolver): Promise { const uri = typeof value === 'string' ? value : value.id; if (uri == null) throw new Error('missing uri'); @@ -345,6 +350,7 @@ export class ApNoteService { } } + @bindThis public async extractEmojis(tags: IObject | IObject[], host: string): Promise { host = this.utilityService.toPuny(host); diff --git a/packages/backend/src/core/activitypub/models/ApPersonService.ts b/packages/backend/src/core/activitypub/models/ApPersonService.ts index f9d6f42ef6..d5faf37df2 100644 --- a/packages/backend/src/core/activitypub/models/ApPersonService.ts +++ b/packages/backend/src/core/activitypub/models/ApPersonService.ts @@ -72,6 +72,7 @@ function addService(target: { [x: string]: any }, source: IApPropertyValue) { target[source.name.split(':')[2]] = service(id, username); } } +import { bindThis } from '@/decorators.js'; @Injectable() export class ApPersonService implements OnModuleInit { @@ -161,6 +162,7 @@ export class ApPersonService implements OnModuleInit { * @param x Fetched object * @param uri Fetch target URI */ + @bindThis private validateActor(x: IObject, uri: string): IActor { const expectHost = this.utilityService.toPuny(new URL(uri).hostname); @@ -224,6 +226,7 @@ export class ApPersonService implements OnModuleInit { * * Misskeyに対象のPersonが登録されていればそれを返します。 */ + @bindThis public async fetchPerson(uri: string, resolver?: Resolver): Promise { if (typeof uri !== 'string') throw new Error('uri is not string'); @@ -253,6 +256,7 @@ export class ApPersonService implements OnModuleInit { /** * Personを作成します。 */ + @bindThis public async createPerson(uri: string, resolver?: Resolver): Promise { if (typeof uri !== 'string') throw new Error('uri is not string'); @@ -402,6 +406,7 @@ export class ApPersonService implements OnModuleInit { * @param resolver Resolver * @param hint Hint of Person object (この値が正当なPersonの場合、Remote resolveをせずに更新に利用します) */ + @bindThis public async updatePerson(uri: string, resolver?: Resolver | null, hint?: IObject): Promise { if (typeof uri !== 'string') throw new Error('uri is not string'); @@ -512,6 +517,7 @@ export class ApPersonService implements OnModuleInit { * Misskeyに対象のPersonが登録されていればそれを返し、そうでなければ * リモートサーバーからフェッチしてMisskeyに登録しそれを返します。 */ + @bindThis public async resolvePerson(uri: string, resolver?: Resolver): Promise { if (typeof uri !== 'string') throw new Error('uri is not string'); @@ -528,6 +534,7 @@ export class ApPersonService implements OnModuleInit { return await this.createPerson(uri, resolver); } + @bindThis public analyzeAttachments(attachments: IObject | IObject[] | undefined) { const fields: { name: string, @@ -551,6 +558,7 @@ export class ApPersonService implements OnModuleInit { return { fields, services }; } + @bindThis public async updateFeatured(userId: User['id'], resolver?: Resolver) { const user = await this.usersRepository.findOneByOrFail({ id: userId }); if (!this.userEntityService.isRemoteUser(user)) return; diff --git a/packages/backend/src/core/activitypub/models/ApQuestionService.ts b/packages/backend/src/core/activitypub/models/ApQuestionService.ts index 5793b98353..13a2f0fa5c 100644 --- a/packages/backend/src/core/activitypub/models/ApQuestionService.ts +++ b/packages/backend/src/core/activitypub/models/ApQuestionService.ts @@ -9,6 +9,7 @@ import { ApLoggerService } from '../ApLoggerService.js'; import { ApResolverService } from '../ApResolverService.js'; import type { Resolver } from '../ApResolverService.js'; import type { IObject, IQuestion } from '../type.js'; +import { bindThis } from '@/decorators.js'; @Injectable() export class ApQuestionService { @@ -30,6 +31,7 @@ export class ApQuestionService { this.logger = this.apLoggerService.logger; } + @bindThis public async extractPollFromQuestion(source: string | IObject, resolver?: Resolver): Promise { if (resolver == null) resolver = this.apResolverService.createResolver(); @@ -65,6 +67,7 @@ export class ApQuestionService { * @param uri URI of AP Question object * @returns true if updated */ + @bindThis public async updateQuestion(value: any, resolver?: Resolver) { const uri = typeof value === 'string' ? value : value.id; diff --git a/packages/backend/src/core/chart/ChartLoggerService.ts b/packages/backend/src/core/chart/ChartLoggerService.ts index 544a006ac9..d392c6d595 100644 --- a/packages/backend/src/core/chart/ChartLoggerService.ts +++ b/packages/backend/src/core/chart/ChartLoggerService.ts @@ -1,6 +1,7 @@ import { Inject, Injectable } from '@nestjs/common'; import type Logger from '@/logger.js'; import { LoggerService } from '@/core/LoggerService.js'; +import { bindThis } from '@/decorators.js'; @Injectable() export class ChartLoggerService { diff --git a/packages/backend/src/core/chart/ChartManagementService.ts b/packages/backend/src/core/chart/ChartManagementService.ts index 6476cd6843..13ee06c6c5 100644 --- a/packages/backend/src/core/chart/ChartManagementService.ts +++ b/packages/backend/src/core/chart/ChartManagementService.ts @@ -13,6 +13,7 @@ import PerUserFollowingChart from './charts/per-user-following.js'; import PerUserDriveChart from './charts/per-user-drive.js'; import ApRequestChart from './charts/ap-request.js'; import type { OnApplicationShutdown } from '@nestjs/common'; +import { bindThis } from '@/decorators.js'; @Injectable() export class ChartManagementService implements OnApplicationShutdown { @@ -49,6 +50,7 @@ export class ChartManagementService implements OnApplicationShutdown { ]; } + @bindThis public async run() { // 20分おきにメモリ情報をDBに書き込み this.saveIntervalId = setInterval(() => { diff --git a/packages/backend/src/core/chart/charts/active-users.ts b/packages/backend/src/core/chart/charts/active-users.ts index 40c60910ea..bc0ba25cbb 100644 --- a/packages/backend/src/core/chart/charts/active-users.ts +++ b/packages/backend/src/core/chart/charts/active-users.ts @@ -3,6 +3,7 @@ import { DataSource } from 'typeorm'; import { AppLockService } from '@/core/AppLockService.js'; import type { User } from '@/models/entities/User.js'; import { DI } from '@/di-symbols.js'; +import { bindThis } from '@/decorators.js'; import Chart from '../core.js'; import { ChartLoggerService } from '../ChartLoggerService.js'; import { name, schema } from './entities/active-users.js'; @@ -36,6 +37,7 @@ export default class ActiveUsersChart extends Chart { return {}; } + @bindThis public async read(user: { id: User['id'], host: null, createdAt: User['createdAt'] }): Promise { await this.commit({ 'read': [user.id], @@ -48,6 +50,7 @@ export default class ActiveUsersChart extends Chart { }); } + @bindThis public async write(user: { id: User['id'], host: null, createdAt: User['createdAt'] }): Promise { await this.commit({ 'write': [user.id], diff --git a/packages/backend/src/core/chart/charts/ap-request.ts b/packages/backend/src/core/chart/charts/ap-request.ts index 4b91fbbf18..ce377460c8 100644 --- a/packages/backend/src/core/chart/charts/ap-request.ts +++ b/packages/backend/src/core/chart/charts/ap-request.ts @@ -2,6 +2,7 @@ import { Injectable, Inject } from '@nestjs/common'; import { DataSource } from 'typeorm'; import { AppLockService } from '@/core/AppLockService.js'; import { DI } from '@/di-symbols.js'; +import { bindThis } from '@/decorators.js'; import Chart from '../core.js'; import { ChartLoggerService } from '../ChartLoggerService.js'; import { name, schema } from './entities/ap-request.js'; @@ -31,18 +32,21 @@ export default class ApRequestChart extends Chart { return {}; } + @bindThis public async deliverSucc(): Promise { await this.commit({ 'deliverSucceeded': 1, }); } + @bindThis public async deliverFail(): Promise { await this.commit({ 'deliverFailed': 1, }); } + @bindThis public async inbox(): Promise { await this.commit({ 'inboxReceived': 1, diff --git a/packages/backend/src/core/chart/charts/drive.ts b/packages/backend/src/core/chart/charts/drive.ts index 494dfbbe57..da36b944f5 100644 --- a/packages/backend/src/core/chart/charts/drive.ts +++ b/packages/backend/src/core/chart/charts/drive.ts @@ -3,6 +3,7 @@ import { Not, IsNull, DataSource } from 'typeorm'; import type { DriveFile } from '@/models/entities/DriveFile.js'; import { AppLockService } from '@/core/AppLockService.js'; import { DI } from '@/di-symbols.js'; +import { bindThis } from '@/decorators.js'; import Chart from '../core.js'; import { ChartLoggerService } from '../ChartLoggerService.js'; import { name, schema } from './entities/drive.js'; @@ -32,6 +33,7 @@ export default class DriveChart extends Chart { return {}; } + @bindThis public async update(file: DriveFile, isAdditional: boolean): Promise { const fileSizeKb = file.size / 1000; await this.commit(file.userHost === null ? { diff --git a/packages/backend/src/core/chart/charts/federation.ts b/packages/backend/src/core/chart/charts/federation.ts index 21e4cedea3..d9234e8028 100644 --- a/packages/backend/src/core/chart/charts/federation.ts +++ b/packages/backend/src/core/chart/charts/federation.ts @@ -4,6 +4,7 @@ import type { FollowingsRepository, InstancesRepository } from '@/models/index.j import { AppLockService } from '@/core/AppLockService.js'; import { DI } from '@/di-symbols.js'; import { MetaService } from '@/core/MetaService.js'; +import { bindThis } from '@/decorators.js'; import Chart from '../core.js'; import { ChartLoggerService } from '../ChartLoggerService.js'; import { name, schema } from './entities/federation.js'; @@ -107,6 +108,7 @@ export default class FederationChart extends Chart { }; } + @bindThis public async deliverd(host: string, succeeded: boolean): Promise { await this.commit(succeeded ? { 'deliveredInstances': [host], @@ -115,6 +117,7 @@ export default class FederationChart extends Chart { }); } + @bindThis public async inbox(host: string): Promise { await this.commit({ 'inboxInstances': [host], diff --git a/packages/backend/src/core/chart/charts/hashtag.ts b/packages/backend/src/core/chart/charts/hashtag.ts index 8b8c795cfd..3899b41363 100644 --- a/packages/backend/src/core/chart/charts/hashtag.ts +++ b/packages/backend/src/core/chart/charts/hashtag.ts @@ -4,6 +4,7 @@ import type { User } from '@/models/entities/User.js'; import { AppLockService } from '@/core/AppLockService.js'; import { DI } from '@/di-symbols.js'; import { UserEntityService } from '@/core/entities/UserEntityService.js'; +import { bindThis } from '@/decorators.js'; import Chart from '../core.js'; import { ChartLoggerService } from '../ChartLoggerService.js'; import { name, schema } from './entities/hashtag.js'; @@ -34,6 +35,7 @@ export default class HashtagChart extends Chart { return {}; } + @bindThis public async update(hashtag: string, user: { id: User['id'], host: User['host'] }): Promise { await this.commit({ 'local.users': this.userEntityService.isLocalUser(user) ? [user.id] : [], diff --git a/packages/backend/src/core/chart/charts/instance.ts b/packages/backend/src/core/chart/charts/instance.ts index 2e0f4c7126..8ca88d80e3 100644 --- a/packages/backend/src/core/chart/charts/instance.ts +++ b/packages/backend/src/core/chart/charts/instance.ts @@ -6,6 +6,7 @@ import type { Note } from '@/models/entities/Note.js'; import { AppLockService } from '@/core/AppLockService.js'; import { DI } from '@/di-symbols.js'; import { UtilityService } from '@/core/UtilityService.js'; +import { bindThis } from '@/decorators.js'; import Chart from '../core.js'; import { ChartLoggerService } from '../ChartLoggerService.js'; import { name, schema } from './entities/instance.js'; @@ -68,12 +69,14 @@ export default class InstanceChart extends Chart { return {}; } + @bindThis public async requestReceived(host: string): Promise { await this.commit({ 'requests.received': 1, }, this.utilityService.toPuny(host)); } + @bindThis public async requestSent(host: string, isSucceeded: boolean): Promise { await this.commit({ 'requests.succeeded': isSucceeded ? 1 : 0, @@ -81,6 +84,7 @@ export default class InstanceChart extends Chart { }, this.utilityService.toPuny(host)); } + @bindThis public async newUser(host: string): Promise { await this.commit({ 'users.total': 1, @@ -88,6 +92,7 @@ export default class InstanceChart extends Chart { }, this.utilityService.toPuny(host)); } + @bindThis public async updateNote(host: string, note: Note, isAdditional: boolean): Promise { await this.commit({ 'notes.total': isAdditional ? 1 : -1, @@ -100,6 +105,7 @@ export default class InstanceChart extends Chart { }, this.utilityService.toPuny(host)); } + @bindThis public async updateFollowing(host: string, isAdditional: boolean): Promise { await this.commit({ 'following.total': isAdditional ? 1 : -1, @@ -108,6 +114,7 @@ export default class InstanceChart extends Chart { }, this.utilityService.toPuny(host)); } + @bindThis public async updateFollowers(host: string, isAdditional: boolean): Promise { await this.commit({ 'followers.total': isAdditional ? 1 : -1, @@ -116,6 +123,7 @@ export default class InstanceChart extends Chart { }, this.utilityService.toPuny(host)); } + @bindThis public async updateDrive(file: DriveFile, isAdditional: boolean): Promise { const fileSizeKb = file.size / 1000; await this.commit({ diff --git a/packages/backend/src/core/chart/charts/notes.ts b/packages/backend/src/core/chart/charts/notes.ts index 2153cfe4b4..23dc248fec 100644 --- a/packages/backend/src/core/chart/charts/notes.ts +++ b/packages/backend/src/core/chart/charts/notes.ts @@ -4,6 +4,7 @@ import type { NotesRepository } from '@/models/index.js'; import type { Note } from '@/models/entities/Note.js'; import { AppLockService } from '@/core/AppLockService.js'; import { DI } from '@/di-symbols.js'; +import { bindThis } from '@/decorators.js'; import Chart from '../core.js'; import { ChartLoggerService } from '../ChartLoggerService.js'; import { name, schema } from './entities/notes.js'; @@ -44,6 +45,7 @@ export default class NotesChart extends Chart { return {}; } + @bindThis public async update(note: Note, isAdditional: boolean): Promise { const prefix = note.userHost === null ? 'local' : 'remote'; diff --git a/packages/backend/src/core/chart/charts/per-user-drive.ts b/packages/backend/src/core/chart/charts/per-user-drive.ts index a44460bb4e..ffba04b041 100644 --- a/packages/backend/src/core/chart/charts/per-user-drive.ts +++ b/packages/backend/src/core/chart/charts/per-user-drive.ts @@ -5,6 +5,7 @@ import type { DriveFile } from '@/models/entities/DriveFile.js'; import { AppLockService } from '@/core/AppLockService.js'; import { DI } from '@/di-symbols.js'; import { DriveFileEntityService } from '@/core/entities/DriveFileEntityService.js'; +import { bindThis } from '@/decorators.js'; import Chart from '../core.js'; import { ChartLoggerService } from '../ChartLoggerService.js'; import { name, schema } from './entities/per-user-drive.js'; @@ -46,6 +47,7 @@ export default class PerUserDriveChart extends Chart { return {}; } + @bindThis public async update(file: DriveFile, isAdditional: boolean): Promise { const fileSizeKb = file.size / 1000; await this.commit({ diff --git a/packages/backend/src/core/chart/charts/per-user-following.ts b/packages/backend/src/core/chart/charts/per-user-following.ts index 5ea08a0872..aea6d44a9a 100644 --- a/packages/backend/src/core/chart/charts/per-user-following.ts +++ b/packages/backend/src/core/chart/charts/per-user-following.ts @@ -5,6 +5,7 @@ import { AppLockService } from '@/core/AppLockService.js'; import { DI } from '@/di-symbols.js'; import { UserEntityService } from '@/core/entities/UserEntityService.js'; import type { FollowingsRepository } from '@/models/index.js'; +import { bindThis } from '@/decorators.js'; import Chart from '../core.js'; import { ChartLoggerService } from '../ChartLoggerService.js'; import { name, schema } from './entities/per-user-following.js'; @@ -55,6 +56,7 @@ export default class PerUserFollowingChart extends Chart { return {}; } + @bindThis public async update(follower: { id: User['id']; host: User['host']; }, followee: { id: User['id']; host: User['host']; }, isFollow: boolean): Promise { const prefixFollower = this.userEntityService.isLocalUser(follower) ? 'local' : 'remote'; const prefixFollowee = this.userEntityService.isLocalUser(followee) ? 'local' : 'remote'; diff --git a/packages/backend/src/core/chart/charts/per-user-notes.ts b/packages/backend/src/core/chart/charts/per-user-notes.ts index 5c14309d89..1e2a579dfa 100644 --- a/packages/backend/src/core/chart/charts/per-user-notes.ts +++ b/packages/backend/src/core/chart/charts/per-user-notes.ts @@ -5,6 +5,7 @@ import type { Note } from '@/models/entities/Note.js'; import { AppLockService } from '@/core/AppLockService.js'; import { DI } from '@/di-symbols.js'; import type { NotesRepository } from '@/models/index.js'; +import { bindThis } from '@/decorators.js'; import Chart from '../core.js'; import { ChartLoggerService } from '../ChartLoggerService.js'; import { name, schema } from './entities/per-user-notes.js'; @@ -43,6 +44,7 @@ export default class PerUserNotesChart extends Chart { return {}; } + @bindThis public async update(user: { id: User['id'] }, note: Note, isAdditional: boolean): Promise { await this.commit({ 'total': isAdditional ? 1 : -1, diff --git a/packages/backend/src/core/chart/charts/per-user-reactions.ts b/packages/backend/src/core/chart/charts/per-user-reactions.ts index 4160219720..7bc6d4b521 100644 --- a/packages/backend/src/core/chart/charts/per-user-reactions.ts +++ b/packages/backend/src/core/chart/charts/per-user-reactions.ts @@ -5,6 +5,7 @@ import type { Note } from '@/models/entities/Note.js'; import { AppLockService } from '@/core/AppLockService.js'; import { DI } from '@/di-symbols.js'; import { UserEntityService } from '@/core/entities/UserEntityService.js'; +import { bindThis } from '@/decorators.js'; import Chart from '../core.js'; import { ChartLoggerService } from '../ChartLoggerService.js'; import { name, schema } from './entities/per-user-reactions.js'; @@ -35,6 +36,7 @@ export default class PerUserReactionsChart extends Chart { return {}; } + @bindThis public async update(user: { id: User['id'], host: User['host'] }, note: Note): Promise { const prefix = this.userEntityService.isLocalUser(user) ? 'local' : 'remote'; this.commit({ diff --git a/packages/backend/src/core/chart/charts/test-grouped.ts b/packages/backend/src/core/chart/charts/test-grouped.ts index bc215f3942..128967bc65 100644 --- a/packages/backend/src/core/chart/charts/test-grouped.ts +++ b/packages/backend/src/core/chart/charts/test-grouped.ts @@ -3,6 +3,7 @@ import { DataSource } from 'typeorm'; import { AppLockService } from '@/core/AppLockService.js'; import { DI } from '@/di-symbols.js'; import Logger from '@/logger.js'; +import { bindThis } from '@/decorators.js'; import Chart from '../core.js'; import { name, schema } from './entities/test-grouped.js'; import type { KVs } from '../core.js'; @@ -35,6 +36,7 @@ export default class TestGroupedChart extends Chart { return {}; } + @bindThis public async increment(group: string): Promise { if (this.total[group] == null) this.total[group] = 0; diff --git a/packages/backend/src/core/chart/charts/test-intersection.ts b/packages/backend/src/core/chart/charts/test-intersection.ts index a074a7dded..6b4eed9062 100644 --- a/packages/backend/src/core/chart/charts/test-intersection.ts +++ b/packages/backend/src/core/chart/charts/test-intersection.ts @@ -3,6 +3,7 @@ import { DataSource } from 'typeorm'; import { AppLockService } from '@/core/AppLockService.js'; import { DI } from '@/di-symbols.js'; import Logger from '@/logger.js'; +import { bindThis } from '@/decorators.js'; import Chart from '../core.js'; import { name, schema } from './entities/test-intersection.js'; import type { KVs } from '../core.js'; @@ -31,12 +32,14 @@ export default class TestIntersectionChart extends Chart { return {}; } + @bindThis public async addA(key: string): Promise { await this.commit({ a: [key], }); } + @bindThis public async addB(key: string): Promise { await this.commit({ b: [key], diff --git a/packages/backend/src/core/chart/charts/test-unique.ts b/packages/backend/src/core/chart/charts/test-unique.ts index 4d3e2f2403..5d2b3f8ab1 100644 --- a/packages/backend/src/core/chart/charts/test-unique.ts +++ b/packages/backend/src/core/chart/charts/test-unique.ts @@ -3,6 +3,7 @@ import { DataSource } from 'typeorm'; import { AppLockService } from '@/core/AppLockService.js'; import { DI } from '@/di-symbols.js'; import Logger from '@/logger.js'; +import { bindThis } from '@/decorators.js'; import Chart from '../core.js'; import { name, schema } from './entities/test-unique.js'; import type { KVs } from '../core.js'; @@ -31,6 +32,7 @@ export default class TestUniqueChart extends Chart { return {}; } + @bindThis public async uniqueIncrement(key: string): Promise { await this.commit({ foo: [key], diff --git a/packages/backend/src/core/chart/charts/test.ts b/packages/backend/src/core/chart/charts/test.ts index 72caf79e0f..238351d8b3 100644 --- a/packages/backend/src/core/chart/charts/test.ts +++ b/packages/backend/src/core/chart/charts/test.ts @@ -3,6 +3,7 @@ import { DataSource } from 'typeorm'; import { AppLockService } from '@/core/AppLockService.js'; import { DI } from '@/di-symbols.js'; import Logger from '@/logger.js'; +import { bindThis } from '@/decorators.js'; import Chart from '../core.js'; import { name, schema } from './entities/test.js'; import type { KVs } from '../core.js'; @@ -35,6 +36,7 @@ export default class TestChart extends Chart { return {}; } + @bindThis public async increment(): Promise { this.total++; @@ -44,6 +46,7 @@ export default class TestChart extends Chart { }); } + @bindThis public async decrement(): Promise { this.total--; diff --git a/packages/backend/src/core/chart/charts/users.ts b/packages/backend/src/core/chart/charts/users.ts index f0359968eb..7bc3602439 100644 --- a/packages/backend/src/core/chart/charts/users.ts +++ b/packages/backend/src/core/chart/charts/users.ts @@ -5,6 +5,7 @@ import { AppLockService } from '@/core/AppLockService.js'; import { DI } from '@/di-symbols.js'; import { UserEntityService } from '@/core/entities/UserEntityService.js'; import type { UsersRepository } from '@/models/index.js'; +import { bindThis } from '@/decorators.js'; import Chart from '../core.js'; import { ChartLoggerService } from '../ChartLoggerService.js'; import { name, schema } from './entities/users.js'; @@ -46,6 +47,7 @@ export default class UsersChart extends Chart { return {}; } + @bindThis public async update(user: { id: User['id'], host: User['host'] }, isAdditional: boolean): Promise { const prefix = this.userEntityService.isLocalUser(user) ? 'local' : 'remote'; diff --git a/packages/backend/src/core/chart/core.ts b/packages/backend/src/core/chart/core.ts index cf5aa48884..2092b13b7e 100644 --- a/packages/backend/src/core/chart/core.ts +++ b/packages/backend/src/core/chart/core.ts @@ -8,6 +8,7 @@ import * as nestedProperty from 'nested-property'; import { EntitySchema, LessThan, Between } from 'typeorm'; import { dateUTC, isTimeSame, isTimeBefore, subtractTime, addTime } from '@/misc/prelude/time.js'; import type Logger from '@/logger.js'; +import { bindThis } from '@/decorators.js'; import type { Repository, DataSource } from 'typeorm'; const columnPrefix = '___' as const; @@ -249,6 +250,7 @@ export default abstract class Chart { this.repositoryForDay = db.getRepository<{ id: number; group?: string | null; date: number; }>(day); } + @bindThis private convertRawRecord(x: RawRecord): KVs { const kvs = {} as Record; for (const k of Object.keys(x).filter((k) => k.startsWith(columnPrefix)) as (keyof Columns)[]) { @@ -257,6 +259,7 @@ export default abstract class Chart { return kvs as KVs; } + @bindThis private getNewLog(latest: KVs | null): KVs { const log = {} as Record; for (const [k, v] of Object.entries(this.schema) as ([keyof typeof this['schema'], this['schema'][string]])[]) { @@ -269,6 +272,7 @@ export default abstract class Chart { return log as KVs; } + @bindThis private getLatestLog(group: string | null, span: 'hour' | 'day'): Promise | null> { const repository = span === 'hour' ? this.repositoryForHour : @@ -288,6 +292,7 @@ export default abstract class Chart { /** * 現在(=今のHour or Day)のログをデータベースから探して、あればそれを返し、なければ作成して返します。 */ + @bindThis private async claimCurrentLog(group: string | null, span: 'hour' | 'day'): Promise> { const [y, m, d, h] = Chart.getCurrentDate(); @@ -380,6 +385,7 @@ export default abstract class Chart { }); } + @bindThis public async save(): Promise { if (this.buffer.length === 0) { this.logger.info(`${this.name}: Write skipped`); @@ -498,6 +504,7 @@ export default abstract class Chart { update(logHour, logDay)))); } + @bindThis public async tick(major: boolean, group: string | null = null): Promise { const data = major ? await this.tickMajor(group) : await this.tickMinor(group); @@ -533,10 +540,12 @@ export default abstract class Chart { update(logHour, logDay)); } + @bindThis public resync(group: string | null = null): Promise { return this.tick(true, group); } + @bindThis public async clean(): Promise { const current = dateUTC(Chart.getCurrentDate()); @@ -572,6 +581,7 @@ export default abstract class Chart { ]); } + @bindThis public async getChartRaw(span: 'hour' | 'day', amount: number, cursor: Date | null, group: string | null = null): Promise> { const [y, m, d, h, _m, _s, _ms] = cursor ? Chart.parseDate(subtractTime(addTime(cursor, 1, span), 1)) : Chart.getCurrentDate(); const [y2, m2, d2, h2] = cursor ? Chart.parseDate(addTime(cursor, 1, span)) : [] as never; @@ -676,6 +686,7 @@ export default abstract class Chart { return res; } + @bindThis public async getChart(span: 'hour' | 'day', amount: number, cursor: Date | null, group: string | null = null): Promise>> { const result = await this.getChartRaw(span, amount, cursor, group); const object = {}; diff --git a/packages/backend/src/core/entities/AbuseUserReportEntityService.ts b/packages/backend/src/core/entities/AbuseUserReportEntityService.ts index 1660894571..7f8240b8b2 100644 --- a/packages/backend/src/core/entities/AbuseUserReportEntityService.ts +++ b/packages/backend/src/core/entities/AbuseUserReportEntityService.ts @@ -4,6 +4,7 @@ import type { AbuseUserReportsRepository } from '@/models/index.js'; import { awaitAll } from '@/misc/prelude/await-all.js'; import type { AbuseUserReport } from '@/models/entities/AbuseUserReport.js'; import { UserEntityService } from './UserEntityService.js'; +import { bindThis } from '@/decorators.js'; @Injectable() export class AbuseUserReportEntityService { @@ -15,6 +16,7 @@ export class AbuseUserReportEntityService { ) { } + @bindThis public async pack( src: AbuseUserReport['id'] | AbuseUserReport, ) { @@ -41,6 +43,7 @@ export class AbuseUserReportEntityService { }); } + @bindThis public packMany( reports: any[], ) { diff --git a/packages/backend/src/core/entities/AntennaEntityService.ts b/packages/backend/src/core/entities/AntennaEntityService.ts index 44110e7364..bc79ce26aa 100644 --- a/packages/backend/src/core/entities/AntennaEntityService.ts +++ b/packages/backend/src/core/entities/AntennaEntityService.ts @@ -4,6 +4,7 @@ import type { AntennaNotesRepository, AntennasRepository, UserGroupJoiningsRepos import { awaitAll } from '@/misc/prelude/await-all.js'; import type { Packed } from '@/misc/schema.js'; import type { Antenna } from '@/models/entities/Antenna.js'; +import { bindThis } from '@/decorators.js'; @Injectable() export class AntennaEntityService { @@ -19,6 +20,7 @@ export class AntennaEntityService { ) { } + @bindThis public async pack( src: Antenna['id'] | Antenna, ): Promise> { diff --git a/packages/backend/src/core/entities/AppEntityService.ts b/packages/backend/src/core/entities/AppEntityService.ts index 1cc7ca11dc..781cbdcc6b 100644 --- a/packages/backend/src/core/entities/AppEntityService.ts +++ b/packages/backend/src/core/entities/AppEntityService.ts @@ -6,6 +6,7 @@ import type { Packed } from '@/misc/schema.js'; import type { App } from '@/models/entities/App.js'; import type { User } from '@/models/entities/User.js'; import { UserEntityService } from './UserEntityService.js'; +import { bindThis } from '@/decorators.js'; @Injectable() export class AppEntityService { @@ -18,6 +19,7 @@ export class AppEntityService { ) { } + @bindThis public async pack( src: App['id'] | App, me?: { id: User['id'] } | null | undefined, diff --git a/packages/backend/src/core/entities/AuthSessionEntityService.ts b/packages/backend/src/core/entities/AuthSessionEntityService.ts index bf8efa5f78..4a74f9c2f6 100644 --- a/packages/backend/src/core/entities/AuthSessionEntityService.ts +++ b/packages/backend/src/core/entities/AuthSessionEntityService.ts @@ -7,6 +7,7 @@ import type { AuthSession } from '@/models/entities/AuthSession.js'; import type { User } from '@/models/entities/User.js'; import { UserEntityService } from './UserEntityService.js'; import { AppEntityService } from './AppEntityService.js'; +import { bindThis } from '@/decorators.js'; @Injectable() export class AuthSessionEntityService { @@ -18,6 +19,7 @@ export class AuthSessionEntityService { ) { } + @bindThis public async pack( src: AuthSession['id'] | AuthSession, me?: { id: User['id'] } | null | undefined, diff --git a/packages/backend/src/core/entities/BlockingEntityService.ts b/packages/backend/src/core/entities/BlockingEntityService.ts index 49a96037ca..c9e15207b9 100644 --- a/packages/backend/src/core/entities/BlockingEntityService.ts +++ b/packages/backend/src/core/entities/BlockingEntityService.ts @@ -6,6 +6,7 @@ import type { Packed } from '@/misc/schema.js'; import type { Blocking } from '@/models/entities/Blocking.js'; import type { User } from '@/models/entities/User.js'; import { UserEntityService } from './UserEntityService.js'; +import { bindThis } from '@/decorators.js'; @Injectable() export class BlockingEntityService { @@ -17,6 +18,7 @@ export class BlockingEntityService { ) { } + @bindThis public async pack( src: Blocking['id'] | Blocking, me?: { id: User['id'] } | null | undefined, @@ -33,6 +35,7 @@ export class BlockingEntityService { }); } + @bindThis public packMany( blockings: any[], me: { id: User['id'] }, diff --git a/packages/backend/src/core/entities/ChannelEntityService.ts b/packages/backend/src/core/entities/ChannelEntityService.ts index 860967443e..5e2f019a12 100644 --- a/packages/backend/src/core/entities/ChannelEntityService.ts +++ b/packages/backend/src/core/entities/ChannelEntityService.ts @@ -8,6 +8,7 @@ import type { User } from '@/models/entities/User.js'; import type { Channel } from '@/models/entities/Channel.js'; import { UserEntityService } from './UserEntityService.js'; import { DriveFileEntityService } from './DriveFileEntityService.js'; +import { bindThis } from '@/decorators.js'; @Injectable() export class ChannelEntityService { @@ -29,6 +30,7 @@ export class ChannelEntityService { ) { } + @bindThis public async pack( src: Channel['id'] | Channel, me?: { id: User['id'] } | null | undefined, diff --git a/packages/backend/src/core/entities/ClipEntityService.ts b/packages/backend/src/core/entities/ClipEntityService.ts index 7a5d2f7f0a..1e794391e9 100644 --- a/packages/backend/src/core/entities/ClipEntityService.ts +++ b/packages/backend/src/core/entities/ClipEntityService.ts @@ -7,6 +7,7 @@ import type { } from '@/models/entities/Blocking.js'; import type { User } from '@/models/entities/User.js'; import type { Clip } from '@/models/entities/Clip.js'; import { UserEntityService } from './UserEntityService.js'; +import { bindThis } from '@/decorators.js'; @Injectable() export class ClipEntityService { @@ -18,6 +19,7 @@ export class ClipEntityService { ) { } + @bindThis public async pack( src: Clip['id'] | Clip, ): Promise> { @@ -34,6 +36,7 @@ export class ClipEntityService { }); } + @bindThis public packMany( clips: Clip[], ) { diff --git a/packages/backend/src/core/entities/DriveFileEntityService.ts b/packages/backend/src/core/entities/DriveFileEntityService.ts index e0aeb70dfc..706c8c1186 100644 --- a/packages/backend/src/core/entities/DriveFileEntityService.ts +++ b/packages/backend/src/core/entities/DriveFileEntityService.ts @@ -19,6 +19,7 @@ type PackOptions = { self?: boolean, withUser?: boolean, }; +import { bindThis } from '@/decorators.js'; @Injectable() export class DriveFileEntityService { @@ -44,6 +45,7 @@ export class DriveFileEntityService { ) { } + @bindThis public validateFileName(name: string): boolean { return ( (name.trim().length > 0) && @@ -54,6 +56,7 @@ export class DriveFileEntityService { ); } + @bindThis public getPublicProperties(file: DriveFile): DriveFile['properties'] { if (file.properties.orientation != null) { const properties = deepClone(file.properties); @@ -67,6 +70,7 @@ export class DriveFileEntityService { return file.properties; } + @bindThis public getPublicUrl(file: DriveFile, thumbnail = false): string | null { // リモートかつメディアプロキシ if (file.uri != null && file.userHost != null && this.config.mediaProxy != null) { @@ -90,6 +94,7 @@ export class DriveFileEntityService { return thumbnail ? (file.thumbnailUrl ?? (isImage ? (file.webpublicUrl ?? file.url) : null)) : (file.webpublicUrl ?? file.url); } + @bindThis public async calcDriveUsageOf(user: User['id'] | { id: User['id'] }): Promise { const id = typeof user === 'object' ? user.id : user; @@ -103,6 +108,7 @@ export class DriveFileEntityService { return parseInt(sum, 10) ?? 0; } + @bindThis public async calcDriveUsageOfHost(host: string): Promise { const { sum } = await this.driveFilesRepository .createQueryBuilder('file') @@ -114,6 +120,7 @@ export class DriveFileEntityService { return parseInt(sum, 10) ?? 0; } + @bindThis public async calcDriveUsageOfLocal(): Promise { const { sum } = await this.driveFilesRepository .createQueryBuilder('file') @@ -125,6 +132,7 @@ export class DriveFileEntityService { return parseInt(sum, 10) ?? 0; } + @bindThis public async calcDriveUsageOfRemote(): Promise { const { sum } = await this.driveFilesRepository .createQueryBuilder('file') @@ -136,6 +144,7 @@ export class DriveFileEntityService { return parseInt(sum, 10) ?? 0; } + @bindThis public async pack( src: DriveFile['id'] | DriveFile, options?: PackOptions, @@ -169,6 +178,7 @@ export class DriveFileEntityService { }); } + @bindThis public async packNullable( src: DriveFile['id'] | DriveFile, options?: PackOptions, @@ -203,6 +213,7 @@ export class DriveFileEntityService { }); } + @bindThis public async packMany( files: (DriveFile['id'] | DriveFile)[], options?: PackOptions, diff --git a/packages/backend/src/core/entities/DriveFolderEntityService.ts b/packages/backend/src/core/entities/DriveFolderEntityService.ts index 5761fa37bc..0bb0f1754e 100644 --- a/packages/backend/src/core/entities/DriveFolderEntityService.ts +++ b/packages/backend/src/core/entities/DriveFolderEntityService.ts @@ -7,6 +7,7 @@ import type { } from '@/models/entities/Blocking.js'; import type { User } from '@/models/entities/User.js'; import type { DriveFolder } from '@/models/entities/DriveFolder.js'; import { UserEntityService } from './UserEntityService.js'; +import { bindThis } from '@/decorators.js'; @Injectable() export class DriveFolderEntityService { @@ -19,6 +20,7 @@ export class DriveFolderEntityService { ) { } + @bindThis public async pack( src: DriveFolder['id'] | DriveFolder, options?: { diff --git a/packages/backend/src/core/entities/EmojiEntityService.ts b/packages/backend/src/core/entities/EmojiEntityService.ts index fc09b5a2c7..08d83a2753 100644 --- a/packages/backend/src/core/entities/EmojiEntityService.ts +++ b/packages/backend/src/core/entities/EmojiEntityService.ts @@ -7,6 +7,7 @@ import type { } from '@/models/entities/Blocking.js'; import type { User } from '@/models/entities/User.js'; import type { Emoji } from '@/models/entities/Emoji.js'; import { UserEntityService } from './UserEntityService.js'; +import { bindThis } from '@/decorators.js'; @Injectable() export class EmojiEntityService { @@ -18,6 +19,7 @@ export class EmojiEntityService { ) { } + @bindThis public async pack( src: Emoji['id'] | Emoji, ): Promise> { @@ -34,6 +36,7 @@ export class EmojiEntityService { }; } + @bindThis public packMany( emojis: any[], ) { diff --git a/packages/backend/src/core/entities/FollowRequestEntityService.ts b/packages/backend/src/core/entities/FollowRequestEntityService.ts index 4a60c1263f..88c91d0f21 100644 --- a/packages/backend/src/core/entities/FollowRequestEntityService.ts +++ b/packages/backend/src/core/entities/FollowRequestEntityService.ts @@ -7,6 +7,7 @@ import type { } from '@/models/entities/Blocking.js'; import type { User } from '@/models/entities/User.js'; import type { FollowRequest } from '@/models/entities/FollowRequest.js'; import { UserEntityService } from './UserEntityService.js'; +import { bindThis } from '@/decorators.js'; @Injectable() export class FollowRequestEntityService { @@ -18,6 +19,7 @@ export class FollowRequestEntityService { ) { } + @bindThis public async pack( src: FollowRequest['id'] | FollowRequest, me?: { id: User['id'] } | null | undefined, diff --git a/packages/backend/src/core/entities/FollowingEntityService.ts b/packages/backend/src/core/entities/FollowingEntityService.ts index c7e040a57b..a833ae719b 100644 --- a/packages/backend/src/core/entities/FollowingEntityService.ts +++ b/packages/backend/src/core/entities/FollowingEntityService.ts @@ -31,6 +31,7 @@ type RemoteFolloweeFollowing = Following & { followeeInbox: string; followeeSharedInbox: string; }; +import { bindThis } from '@/decorators.js'; @Injectable() export class FollowingEntityService { @@ -42,22 +43,27 @@ export class FollowingEntityService { ) { } + @bindThis public isLocalFollower(following: Following): following is LocalFollowerFollowing { return following.followerHost == null; } + @bindThis public isRemoteFollower(following: Following): following is RemoteFollowerFollowing { return following.followerHost != null; } + @bindThis public isLocalFollowee(following: Following): following is LocalFolloweeFollowing { return following.followeeHost == null; } + @bindThis public isRemoteFollowee(following: Following): following is RemoteFolloweeFollowing { return following.followeeHost != null; } + @bindThis public async pack( src: Following['id'] | Following, me?: { id: User['id'] } | null | undefined, @@ -84,6 +90,7 @@ export class FollowingEntityService { }); } + @bindThis public packMany( followings: any[], me?: { id: User['id'] } | null | undefined, diff --git a/packages/backend/src/core/entities/GalleryLikeEntityService.ts b/packages/backend/src/core/entities/GalleryLikeEntityService.ts index 7e599113cc..8b15ffc2bb 100644 --- a/packages/backend/src/core/entities/GalleryLikeEntityService.ts +++ b/packages/backend/src/core/entities/GalleryLikeEntityService.ts @@ -8,6 +8,7 @@ import type { User } from '@/models/entities/User.js'; import type { GalleryLike } from '@/models/entities/GalleryLike.js'; import { UserEntityService } from './UserEntityService.js'; import { GalleryPostEntityService } from './GalleryPostEntityService.js'; +import { bindThis } from '@/decorators.js'; @Injectable() export class GalleryLikeEntityService { @@ -19,6 +20,7 @@ export class GalleryLikeEntityService { ) { } + @bindThis public async pack( src: GalleryLike['id'] | GalleryLike, me?: any, @@ -31,6 +33,7 @@ export class GalleryLikeEntityService { }; } + @bindThis public packMany( likes: any[], me: any, diff --git a/packages/backend/src/core/entities/GalleryPostEntityService.ts b/packages/backend/src/core/entities/GalleryPostEntityService.ts index ca98687d7b..ab29e7dba1 100644 --- a/packages/backend/src/core/entities/GalleryPostEntityService.ts +++ b/packages/backend/src/core/entities/GalleryPostEntityService.ts @@ -8,6 +8,7 @@ import type { User } from '@/models/entities/User.js'; import type { GalleryPost } from '@/models/entities/GalleryPost.js'; import { UserEntityService } from './UserEntityService.js'; import { DriveFileEntityService } from './DriveFileEntityService.js'; +import { bindThis } from '@/decorators.js'; @Injectable() export class GalleryPostEntityService { @@ -23,6 +24,7 @@ export class GalleryPostEntityService { ) { } + @bindThis public async pack( src: GalleryPost['id'] | GalleryPost, me?: { id: User['id'] } | null | undefined, @@ -47,6 +49,7 @@ export class GalleryPostEntityService { }); } + @bindThis public packMany( posts: GalleryPost[], me?: { id: User['id'] } | null | undefined, diff --git a/packages/backend/src/core/entities/HashtagEntityService.ts b/packages/backend/src/core/entities/HashtagEntityService.ts index 511992c44f..f79b821222 100644 --- a/packages/backend/src/core/entities/HashtagEntityService.ts +++ b/packages/backend/src/core/entities/HashtagEntityService.ts @@ -7,6 +7,7 @@ import type { } from '@/models/entities/Blocking.js'; import type { User } from '@/models/entities/User.js'; import type { Hashtag } from '@/models/entities/Hashtag.js'; import { UserEntityService } from './UserEntityService.js'; +import { bindThis } from '@/decorators.js'; @Injectable() export class HashtagEntityService { @@ -18,6 +19,7 @@ export class HashtagEntityService { ) { } + @bindThis public async pack( src: Hashtag, ): Promise> { @@ -32,6 +34,7 @@ export class HashtagEntityService { }; } + @bindThis public packMany( hashtags: Hashtag[], ) { diff --git a/packages/backend/src/core/entities/InstanceEntityService.ts b/packages/backend/src/core/entities/InstanceEntityService.ts index f4fe9a17d3..5a7ceb89a3 100644 --- a/packages/backend/src/core/entities/InstanceEntityService.ts +++ b/packages/backend/src/core/entities/InstanceEntityService.ts @@ -8,6 +8,7 @@ import type { User } from '@/models/entities/User.js'; import type { Instance } from '@/models/entities/Instance.js'; import { MetaService } from '@/core/MetaService.js'; import { UserEntityService } from './UserEntityService.js'; +import { bindThis } from '@/decorators.js'; @Injectable() export class InstanceEntityService { @@ -19,6 +20,7 @@ export class InstanceEntityService { ) { } + @bindThis public async pack( instance: Instance, ): Promise> { @@ -50,6 +52,7 @@ export class InstanceEntityService { }; } + @bindThis public packMany( instances: Instance[], ) { diff --git a/packages/backend/src/core/entities/MessagingMessageEntityService.ts b/packages/backend/src/core/entities/MessagingMessageEntityService.ts index b7c42a5760..cdb752dd81 100644 --- a/packages/backend/src/core/entities/MessagingMessageEntityService.ts +++ b/packages/backend/src/core/entities/MessagingMessageEntityService.ts @@ -9,6 +9,7 @@ import type { MessagingMessage } from '@/models/entities/MessagingMessage.js'; import { UserEntityService } from './UserEntityService.js'; import { DriveFileEntityService } from './DriveFileEntityService.js'; import { UserGroupEntityService } from './UserGroupEntityService.js'; +import { bindThis } from '@/decorators.js'; @Injectable() export class MessagingMessageEntityService { @@ -22,6 +23,7 @@ export class MessagingMessageEntityService { ) { } + @bindThis public async pack( src: MessagingMessage['id'] | MessagingMessage, me?: { id: User['id'] } | null | undefined, diff --git a/packages/backend/src/core/entities/ModerationLogEntityService.ts b/packages/backend/src/core/entities/ModerationLogEntityService.ts index 2f508710b8..ab61797910 100644 --- a/packages/backend/src/core/entities/ModerationLogEntityService.ts +++ b/packages/backend/src/core/entities/ModerationLogEntityService.ts @@ -7,6 +7,7 @@ import type { } from '@/models/entities/Blocking.js'; import type { User } from '@/models/entities/User.js'; import type { ModerationLog } from '@/models/entities/ModerationLog.js'; import { UserEntityService } from './UserEntityService.js'; +import { bindThis } from '@/decorators.js'; @Injectable() export class ModerationLogEntityService { @@ -18,6 +19,7 @@ export class ModerationLogEntityService { ) { } + @bindThis public async pack( src: ModerationLog['id'] | ModerationLog, ) { @@ -35,6 +37,7 @@ export class ModerationLogEntityService { }); } + @bindThis public packMany( reports: any[], ) { diff --git a/packages/backend/src/core/entities/MutingEntityService.ts b/packages/backend/src/core/entities/MutingEntityService.ts index 862be009da..4f02ef4087 100644 --- a/packages/backend/src/core/entities/MutingEntityService.ts +++ b/packages/backend/src/core/entities/MutingEntityService.ts @@ -7,6 +7,7 @@ import type { } from '@/models/entities/Blocking.js'; import type { User } from '@/models/entities/User.js'; import type { Muting } from '@/models/entities/Muting.js'; import { UserEntityService } from './UserEntityService.js'; +import { bindThis } from '@/decorators.js'; @Injectable() export class MutingEntityService { @@ -18,6 +19,7 @@ export class MutingEntityService { ) { } + @bindThis public async pack( src: Muting['id'] | Muting, me?: { id: User['id'] } | null | undefined, @@ -35,6 +37,7 @@ export class MutingEntityService { }); } + @bindThis public packMany( mutings: any[], me: { id: User['id'] }, diff --git a/packages/backend/src/core/entities/NoteEntityService.ts b/packages/backend/src/core/entities/NoteEntityService.ts index 5605cf8ce6..73d3184957 100644 --- a/packages/backend/src/core/entities/NoteEntityService.ts +++ b/packages/backend/src/core/entities/NoteEntityService.ts @@ -16,6 +16,7 @@ import type { CustomEmojiService } from '../CustomEmojiService.js'; import type { ReactionService } from '../ReactionService.js'; import type { UserEntityService } from './UserEntityService.js'; import type { DriveFileEntityService } from './DriveFileEntityService.js'; +import { bindThis } from '@/decorators.js'; @Injectable() export class NoteEntityService implements OnModuleInit { @@ -68,6 +69,7 @@ export class NoteEntityService implements OnModuleInit { this.reactionService = this.moduleRef.get('ReactionService'); } + @bindThis private async hideNote(packedNote: Packed<'Note'>, meId: User['id'] | null) { // TODO: isVisibleForMe を使うようにしても良さそう(型違うけど) let hide = false; @@ -128,6 +130,7 @@ export class NoteEntityService implements OnModuleInit { } } + @bindThis private async populatePoll(note: Note, meId: User['id'] | null) { const poll = await this.pollsRepository.findOneByOrFail({ noteId: note.id }); const choices = poll.choices.map(c => ({ @@ -166,6 +169,7 @@ export class NoteEntityService implements OnModuleInit { }; } + @bindThis private async populateMyReaction(note: Note, meId: User['id'], _hint_?: { myReactions: Map; }) { @@ -191,6 +195,7 @@ export class NoteEntityService implements OnModuleInit { return undefined; } + @bindThis public async isVisibleForMe(note: Note, meId: User['id'] | null): Promise { // This code must always be synchronized with the checks in generateVisibilityQuery. // visibility が specified かつ自分が指定されていなかったら非表示 @@ -244,6 +249,7 @@ export class NoteEntityService implements OnModuleInit { return true; } + @bindThis public async pack( src: Note['id'] | Note, me?: { id: User['id'] } | null | undefined, @@ -353,6 +359,7 @@ export class NoteEntityService implements OnModuleInit { return packed; } + @bindThis public async packMany( notes: Note[], me?: { id: User['id'] } | null | undefined, @@ -388,6 +395,7 @@ export class NoteEntityService implements OnModuleInit { }))); } + @bindThis public async countSameRenotes(userId: string, renoteId: string, excludeNoteId: string | undefined): Promise { // 指定したユーザーの指定したノートのリノートがいくつあるか数える const query = this.notesRepository.createQueryBuilder('note') diff --git a/packages/backend/src/core/entities/NoteFavoriteEntityService.ts b/packages/backend/src/core/entities/NoteFavoriteEntityService.ts index 1a68a5c628..aa5c354b6d 100644 --- a/packages/backend/src/core/entities/NoteFavoriteEntityService.ts +++ b/packages/backend/src/core/entities/NoteFavoriteEntityService.ts @@ -8,6 +8,7 @@ import type { User } from '@/models/entities/User.js'; import type { NoteFavorite } from '@/models/entities/NoteFavorite.js'; import { UserEntityService } from './UserEntityService.js'; import { NoteEntityService } from './NoteEntityService.js'; +import { bindThis } from '@/decorators.js'; @Injectable() export class NoteFavoriteEntityService { @@ -19,6 +20,7 @@ export class NoteFavoriteEntityService { ) { } + @bindThis public async pack( src: NoteFavorite['id'] | NoteFavorite, me?: { id: User['id'] } | null | undefined, @@ -33,6 +35,7 @@ export class NoteFavoriteEntityService { }; } + @bindThis public packMany( favorites: any[], me: { id: User['id'] }, diff --git a/packages/backend/src/core/entities/NoteReactionEntityService.ts b/packages/backend/src/core/entities/NoteReactionEntityService.ts index 47008ee08e..eba6f9d908 100644 --- a/packages/backend/src/core/entities/NoteReactionEntityService.ts +++ b/packages/backend/src/core/entities/NoteReactionEntityService.ts @@ -11,6 +11,7 @@ import type { ReactionService } from '../ReactionService.js'; import type { UserEntityService } from './UserEntityService.js'; import type { NoteEntityService } from './NoteEntityService.js'; import { ModuleRef } from '@nestjs/core'; +import { bindThis } from '@/decorators.js'; @Injectable() export class NoteReactionEntityService implements OnModuleInit { @@ -36,6 +37,7 @@ export class NoteReactionEntityService implements OnModuleInit { this.reactionService = this.moduleRef.get('ReactionService'); } + @bindThis public async pack( src: NoteReaction['id'] | NoteReaction, me?: { id: User['id'] } | null | undefined, diff --git a/packages/backend/src/core/entities/NotificationEntityService.ts b/packages/backend/src/core/entities/NotificationEntityService.ts index c415599fea..346faae6b0 100644 --- a/packages/backend/src/core/entities/NotificationEntityService.ts +++ b/packages/backend/src/core/entities/NotificationEntityService.ts @@ -13,6 +13,7 @@ import type { CustomEmojiService } from '../CustomEmojiService.js'; import type { UserEntityService } from './UserEntityService.js'; import type { NoteEntityService } from './NoteEntityService.js'; import type { UserGroupInvitationEntityService } from './UserGroupInvitationEntityService.js'; +import { bindThis } from '@/decorators.js'; @Injectable() export class NotificationEntityService implements OnModuleInit { @@ -47,6 +48,7 @@ export class NotificationEntityService implements OnModuleInit { this.customEmojiService = this.moduleRef.get('CustomEmojiService'); } + @bindThis public async pack( src: Notification['id'] | Notification, options: { @@ -120,6 +122,7 @@ export class NotificationEntityService implements OnModuleInit { }); } + @bindThis public async packMany( notifications: Notification[], meId: User['id'], diff --git a/packages/backend/src/core/entities/PageEntityService.ts b/packages/backend/src/core/entities/PageEntityService.ts index 004443759b..48e45dd019 100644 --- a/packages/backend/src/core/entities/PageEntityService.ts +++ b/packages/backend/src/core/entities/PageEntityService.ts @@ -9,6 +9,7 @@ import type { Page } from '@/models/entities/Page.js'; import type { DriveFile } from '@/models/entities/DriveFile.js'; import { UserEntityService } from './UserEntityService.js'; import { DriveFileEntityService } from './DriveFileEntityService.js'; +import { bindThis } from '@/decorators.js'; @Injectable() export class PageEntityService { @@ -27,6 +28,7 @@ export class PageEntityService { ) { } + @bindThis public async pack( src: Page['id'] | Page, me?: { id: User['id'] } | null | undefined, @@ -99,6 +101,7 @@ export class PageEntityService { }); } + @bindThis public packMany( pages: Page[], me?: { id: User['id'] } | null | undefined, diff --git a/packages/backend/src/core/entities/PageLikeEntityService.ts b/packages/backend/src/core/entities/PageLikeEntityService.ts index 62d9c82ca6..d3e45783dd 100644 --- a/packages/backend/src/core/entities/PageLikeEntityService.ts +++ b/packages/backend/src/core/entities/PageLikeEntityService.ts @@ -8,6 +8,7 @@ import type { User } from '@/models/entities/User.js'; import type { PageLike } from '@/models/entities/PageLike.js'; import { UserEntityService } from './UserEntityService.js'; import { PageEntityService } from './PageEntityService.js'; +import { bindThis } from '@/decorators.js'; @Injectable() export class PageLikeEntityService { @@ -19,6 +20,7 @@ export class PageLikeEntityService { ) { } + @bindThis public async pack( src: PageLike['id'] | PageLike, me?: { id: User['id'] } | null | undefined, @@ -31,6 +33,7 @@ export class PageLikeEntityService { }; } + @bindThis public packMany( likes: any[], me: { id: User['id'] }, diff --git a/packages/backend/src/core/entities/SigninEntityService.ts b/packages/backend/src/core/entities/SigninEntityService.ts index fd89662f7d..c402644742 100644 --- a/packages/backend/src/core/entities/SigninEntityService.ts +++ b/packages/backend/src/core/entities/SigninEntityService.ts @@ -7,6 +7,7 @@ import type { } from '@/models/entities/Blocking.js'; import type { User } from '@/models/entities/User.js'; import type { Signin } from '@/models/entities/Signin.js'; import { UserEntityService } from './UserEntityService.js'; +import { bindThis } from '@/decorators.js'; @Injectable() export class SigninEntityService { @@ -18,6 +19,7 @@ export class SigninEntityService { ) { } + @bindThis public async pack( src: Signin, ) { diff --git a/packages/backend/src/core/entities/UserEntityService.ts b/packages/backend/src/core/entities/UserEntityService.ts index c691eaebdf..4a027d1de6 100644 --- a/packages/backend/src/core/entities/UserEntityService.ts +++ b/packages/backend/src/core/entities/UserEntityService.ts @@ -41,6 +41,7 @@ function isRemoteUser(user: T): user is T & { function isRemoteUser(user: User | { host: User['host'] }): boolean { return !isLocalUser(user); } +import { bindThis } from '@/decorators.js'; @Injectable() export class UserEntityService implements OnModuleInit { @@ -143,6 +144,7 @@ export class UserEntityService implements OnModuleInit { public isLocalUser = isLocalUser; public isRemoteUser = isRemoteUser; + @bindThis public async getRelation(me: User['id'], target: User['id']) { return awaitAll({ id: target, @@ -198,6 +200,7 @@ export class UserEntityService implements OnModuleInit { }); } + @bindThis public async getHasUnreadMessagingMessage(userId: User['id']): Promise { const mute = await this.mutingsRepository.findBy({ muterId: userId, @@ -227,6 +230,7 @@ export class UserEntityService implements OnModuleInit { return withUser || withGroups.some(x => x); } + @bindThis public async getHasUnreadAnnouncement(userId: User['id']): Promise { const reads = await this.announcementReadsRepository.findBy({ userId: userId, @@ -239,6 +243,7 @@ export class UserEntityService implements OnModuleInit { return count > 0; } + @bindThis public async getHasUnreadAntenna(userId: User['id']): Promise { const myAntennas = (await this.antennaService.getAntennas()).filter(a => a.userId === userId); @@ -250,6 +255,7 @@ export class UserEntityService implements OnModuleInit { return unread != null; } + @bindThis public async getHasUnreadChannel(userId: User['id']): Promise { const channels = await this.channelFollowingsRepository.findBy({ followerId: userId }); @@ -261,6 +267,7 @@ export class UserEntityService implements OnModuleInit { return unread != null; } + @bindThis public async getHasUnreadNotification(userId: User['id']): Promise { const mute = await this.mutingsRepository.findBy({ muterId: userId, @@ -279,6 +286,7 @@ export class UserEntityService implements OnModuleInit { return count > 0; } + @bindThis public async getHasPendingReceivedFollowRequest(userId: User['id']): Promise { const count = await this.followRequestsRepository.countBy({ followeeId: userId, @@ -287,6 +295,7 @@ export class UserEntityService implements OnModuleInit { return count > 0; } + @bindThis public getOnlineStatus(user: User): 'unknown' | 'online' | 'active' | 'offline' { if (user.hideOnlineStatus) return 'unknown'; if (user.lastActiveDate == null) return 'unknown'; @@ -298,6 +307,7 @@ export class UserEntityService implements OnModuleInit { ); } + @bindThis public async getAvatarUrl(user: User): Promise { if (user.avatar) { return this.driveFileEntityService.getPublicUrl(user.avatar, true) ?? this.getIdenticonUrl(user.id); @@ -309,6 +319,7 @@ export class UserEntityService implements OnModuleInit { } } + @bindThis public getAvatarUrlSync(user: User): string { if (user.avatar) { return this.driveFileEntityService.getPublicUrl(user.avatar, true) ?? this.getIdenticonUrl(user.id); @@ -317,6 +328,7 @@ export class UserEntityService implements OnModuleInit { } } + @bindThis public getIdenticonUrl(userId: User['id']): string { return `${this.config.url}/identicon/${userId}`; } diff --git a/packages/backend/src/core/entities/UserGroupEntityService.ts b/packages/backend/src/core/entities/UserGroupEntityService.ts index e399197618..0674a76723 100644 --- a/packages/backend/src/core/entities/UserGroupEntityService.ts +++ b/packages/backend/src/core/entities/UserGroupEntityService.ts @@ -7,6 +7,7 @@ import type { } from '@/models/entities/Blocking.js'; import type { User } from '@/models/entities/User.js'; import type { UserGroup } from '@/models/entities/UserGroup.js'; import { UserEntityService } from './UserEntityService.js'; +import { bindThis } from '@/decorators.js'; @Injectable() export class UserGroupEntityService { @@ -21,6 +22,7 @@ export class UserGroupEntityService { ) { } + @bindThis public async pack( src: UserGroup['id'] | UserGroup, ): Promise> { diff --git a/packages/backend/src/core/entities/UserGroupInvitationEntityService.ts b/packages/backend/src/core/entities/UserGroupInvitationEntityService.ts index f5c9be3475..0fba1426f4 100644 --- a/packages/backend/src/core/entities/UserGroupInvitationEntityService.ts +++ b/packages/backend/src/core/entities/UserGroupInvitationEntityService.ts @@ -8,6 +8,7 @@ import type { User } from '@/models/entities/User.js'; import type { UserGroupInvitation } from '@/models/entities/UserGroupInvitation.js'; import { UserEntityService } from './UserEntityService.js'; import { UserGroupEntityService } from './UserGroupEntityService.js'; +import { bindThis } from '@/decorators.js'; @Injectable() export class UserGroupInvitationEntityService { @@ -19,6 +20,7 @@ export class UserGroupInvitationEntityService { ) { } + @bindThis public async pack( src: UserGroupInvitation['id'] | UserGroupInvitation, ) { @@ -30,6 +32,7 @@ export class UserGroupInvitationEntityService { }; } + @bindThis public packMany( invitations: any[], ) { diff --git a/packages/backend/src/core/entities/UserListEntityService.ts b/packages/backend/src/core/entities/UserListEntityService.ts index e2b0814914..f2e0426928 100644 --- a/packages/backend/src/core/entities/UserListEntityService.ts +++ b/packages/backend/src/core/entities/UserListEntityService.ts @@ -7,6 +7,7 @@ import type { } from '@/models/entities/Blocking.js'; import type { User } from '@/models/entities/User.js'; import type { UserList } from '@/models/entities/UserList.js'; import { UserEntityService } from './UserEntityService.js'; +import { bindThis } from '@/decorators.js'; @Injectable() export class UserListEntityService { @@ -21,6 +22,7 @@ export class UserListEntityService { ) { } + @bindThis public async pack( src: UserList['id'] | UserList, ): Promise> { diff --git a/packages/backend/src/daemons/JanitorService.ts b/packages/backend/src/daemons/JanitorService.ts index dbad576abe..8cdfb703f1 100644 --- a/packages/backend/src/daemons/JanitorService.ts +++ b/packages/backend/src/daemons/JanitorService.ts @@ -2,6 +2,7 @@ import { Inject, Injectable } from '@nestjs/common'; import { LessThan } from 'typeorm'; import { DI } from '@/di-symbols.js'; import type { AttestationChallengesRepository } from '@/models/index.js'; +import { bindThis } from '@/decorators.js'; import type { OnApplicationShutdown } from '@nestjs/common'; const interval = 30 * 60 * 1000; @@ -19,6 +20,7 @@ export class JanitorService implements OnApplicationShutdown { /** * Clean up database occasionally */ + @bindThis public start(): void { const tick = async () => { await this.attestationChallengesRepository.delete({ @@ -31,6 +33,7 @@ export class JanitorService implements OnApplicationShutdown { this.intervalId = setInterval(tick, interval); } + @bindThis public onApplicationShutdown(signal?: string | undefined) { clearInterval(this.intervalId); } diff --git a/packages/backend/src/daemons/QueueStatsService.ts b/packages/backend/src/daemons/QueueStatsService.ts index 931de19067..7b47d78a17 100644 --- a/packages/backend/src/daemons/QueueStatsService.ts +++ b/packages/backend/src/daemons/QueueStatsService.ts @@ -2,6 +2,7 @@ import { Inject, Injectable } from '@nestjs/common'; import Xev from 'xev'; import { DI } from '@/di-symbols.js'; import { QueueService } from '@/core/QueueService.js'; +import { bindThis } from '@/decorators.js'; import type { OnApplicationShutdown } from '@nestjs/common'; const ev = new Xev(); @@ -20,6 +21,7 @@ export class QueueStatsService implements OnApplicationShutdown { /** * Report queue stats regularly */ + @bindThis public start(): void { const log = [] as any[]; @@ -71,6 +73,7 @@ export class QueueStatsService implements OnApplicationShutdown { this.intervalId = setInterval(tick, interval); } + @bindThis public onApplicationShutdown(signal?: string | undefined) { clearInterval(this.intervalId); } diff --git a/packages/backend/src/daemons/ServerStatsService.ts b/packages/backend/src/daemons/ServerStatsService.ts index e40912442d..7971f9e810 100644 --- a/packages/backend/src/daemons/ServerStatsService.ts +++ b/packages/backend/src/daemons/ServerStatsService.ts @@ -3,6 +3,7 @@ import si from 'systeminformation'; import Xev from 'xev'; import * as osUtils from 'os-utils'; import { DI } from '@/di-symbols.js'; +import { bindThis } from '@/decorators.js'; import type { OnApplicationShutdown } from '@nestjs/common'; const ev = new Xev(); @@ -23,6 +24,7 @@ export class ServerStatsService implements OnApplicationShutdown { /** * Report server stats regularly */ + @bindThis public start(): void { const log = [] as any[]; @@ -61,6 +63,7 @@ export class ServerStatsService implements OnApplicationShutdown { this.intervalId = setInterval(tick, interval); } + @bindThis public onApplicationShutdown(signal?: string | undefined) { clearInterval(this.intervalId); } diff --git a/packages/backend/src/decorators.ts b/packages/backend/src/decorators.ts new file mode 100644 index 0000000000..94b1c4be8c --- /dev/null +++ b/packages/backend/src/decorators.ts @@ -0,0 +1,41 @@ +// https://github.com/andreypopp/autobind-decorator + +/** + * Return a descriptor removing the value and returning a getter + * The getter will return a .bind version of the function + * and memoize the result against a symbol on the instance + */ +export function bindThis(target, key, descriptor) { + let fn = descriptor.value; + + if (typeof fn !== 'function') { + throw new TypeError(`@bindThis decorator can only be applied to methods not: ${typeof fn}`); + } + + return { + configurable: true, + get() { + // eslint-disable-next-line no-prototype-builtins + if (this === target.prototype || this.hasOwnProperty(key) || + typeof fn !== 'function') { + return fn; + } + + const boundFn = fn.bind(this); + Object.defineProperty(this, key, { + configurable: true, + get() { + return boundFn; + }, + set(value) { + fn = value; + delete this[key]; + }, + }); + return boundFn; + }, + set(value) { + fn = value; + }, + }; +} diff --git a/packages/backend/src/logger.ts b/packages/backend/src/logger.ts index 6722220681..d09b479c42 100644 --- a/packages/backend/src/logger.ts +++ b/packages/backend/src/logger.ts @@ -2,6 +2,7 @@ import cluster from 'node:cluster'; import chalk from 'chalk'; import { default as convertColor } from 'color-convert'; import { format as dateFormat } from 'date-fns'; +import { bindThis } from '@/decorators.js'; import { envOption } from './env.js'; type Domain = { @@ -26,12 +27,14 @@ export default class Logger { this.syslogClient = syslogClient; } + @bindThis public createSubLogger(domain: string, color?: string, store = true): Logger { const logger = new Logger(domain, color, store); logger.parentLogger = this; return logger; } + @bindThis private log(level: Level, message: string, data?: Record | null, important = false, subDomains: Domain[] = [], store = true): void { if (envOption.quiet) return; if (!this.store) store = false; @@ -80,6 +83,7 @@ export default class Logger { } } + @bindThis public error(x: string | Error, data?: Record | null, important = false): void { // 実行を継続できない状況で使う if (x instanceof Error) { data = data ?? {}; @@ -92,20 +96,24 @@ export default class Logger { } } + @bindThis public warn(message: string, data?: Record | null, important = false): void { // 実行を継続できるが改善すべき状況で使う this.log('warning', message, data, important); } + @bindThis public succ(message: string, data?: Record | null, important = false): void { // 何かに成功した状況で使う this.log('success', message, data, important); } + @bindThis public debug(message: string, data?: Record | null, important = false): void { // デバッグ用に使う(開発者に必要だが利用者に不要な情報) if (process.env.NODE_ENV !== 'production' || envOption.verbose) { this.log('debug', message, data, important); } } + @bindThis public info(message: string, data?: Record | null, important = false): void { // それ以外 this.log('info', message, data, important); } diff --git a/packages/backend/src/misc/cache.ts b/packages/backend/src/misc/cache.ts index e5b911ed32..69512498f8 100644 --- a/packages/backend/src/misc/cache.ts +++ b/packages/backend/src/misc/cache.ts @@ -1,3 +1,5 @@ +import { bindThis } from '@/decorators.js'; + export class Cache { public cache: Map; private lifetime: number; @@ -7,6 +9,7 @@ export class Cache { this.lifetime = lifetime; } + @bindThis public set(key: string | null, value: T): void { this.cache.set(key, { date: Date.now(), @@ -14,6 +17,7 @@ export class Cache { }); } + @bindThis public get(key: string | null): T | undefined { const cached = this.cache.get(key); if (cached == null) return undefined; @@ -24,6 +28,7 @@ export class Cache { return cached.value; } + @bindThis public delete(key: string | null) { this.cache.delete(key); } @@ -32,6 +37,7 @@ export class Cache { * キャッシュがあればそれを返し、無ければfetcherを呼び出して結果をキャッシュ&返します * optional: キャッシュが存在してもvalidatorでfalseを返すとキャッシュ無効扱いにします */ + @bindThis public async fetch(key: string | null, fetcher: () => Promise, validator?: (cachedValue: T) => boolean): Promise { const cachedValue = this.get(key); if (cachedValue !== undefined) { @@ -56,6 +62,7 @@ export class Cache { * キャッシュがあればそれを返し、無ければfetcherを呼び出して結果をキャッシュ&返します * optional: キャッシュが存在してもvalidatorでfalseを返すとキャッシュ無効扱いにします */ + @bindThis public async fetchMaybe(key: string | null, fetcher: () => Promise, validator?: (cachedValue: T) => boolean): Promise { const cachedValue = this.get(key); if (cachedValue !== undefined) { diff --git a/packages/backend/src/misc/i18n.ts b/packages/backend/src/misc/i18n.ts index 4fa398763a..e304a8adac 100644 --- a/packages/backend/src/misc/i18n.ts +++ b/packages/backend/src/misc/i18n.ts @@ -5,12 +5,13 @@ export class I18n> { this.locale = locale; //#region BIND - this.t = this.t.bind(this); + //this.t = this.t.bind(this); //#endregion } // string にしているのは、ドット区切りでのパス指定を許可するため // なるべくこのメソッド使うよりもlocale直接参照の方がvueのキャッシュ効いてパフォーマンスが良いかも + @bindThis public t(key: string, args?: Record): string { try { let str = key.split('.').reduce((o, i) => o[i], this.locale) as string; diff --git a/packages/backend/src/postgre.ts b/packages/backend/src/postgre.ts index 2beb31e24e..0f9f7a6a96 100644 --- a/packages/backend/src/postgre.ts +++ b/packages/backend/src/postgre.ts @@ -72,6 +72,7 @@ import { Channel } from '@/models/entities/Channel.js'; import { Config } from '@/config.js'; import MisskeyLogger from '@/logger.js'; +import { bindThis } from '@/decorators.js'; import { envOption } from './env.js'; export const dbLogger = new MisskeyLogger('db'); @@ -79,32 +80,39 @@ export const dbLogger = new MisskeyLogger('db'); const sqlLogger = dbLogger.createSubLogger('sql', 'gray', false); class MyCustomLogger implements Logger { + @bindThis private highlight(sql: string) { return highlight.highlight(sql, { language: 'sql', ignoreIllegals: true, }); } + @bindThis public logQuery(query: string, parameters?: any[]) { sqlLogger.info(this.highlight(query).substring(0, 100)); } + @bindThis public logQueryError(error: string, query: string, parameters?: any[]) { sqlLogger.error(this.highlight(query)); } + @bindThis public logQuerySlow(time: number, query: string, parameters?: any[]) { sqlLogger.warn(this.highlight(query)); } + @bindThis public logSchemaBuild(message: string) { sqlLogger.info(message); } + @bindThis public log(message: string) { sqlLogger.info(message); } + @bindThis public logMigration(message: string) { sqlLogger.info(message); } diff --git a/packages/backend/src/queue/DbQueueProcessorsService.ts b/packages/backend/src/queue/DbQueueProcessorsService.ts index 58384c4d1b..e5568ab9bf 100644 --- a/packages/backend/src/queue/DbQueueProcessorsService.ts +++ b/packages/backend/src/queue/DbQueueProcessorsService.ts @@ -16,6 +16,7 @@ import { ImportUserListsProcessorService } from './processors/ImportUserListsPro import { ImportCustomEmojisProcessorService } from './processors/ImportCustomEmojisProcessorService.js'; import { DeleteAccountProcessorService } from './processors/DeleteAccountProcessorService.js'; import type Bull from 'bull'; +import { bindThis } from '@/decorators.js'; @Injectable() export class DbQueueProcessorsService { @@ -39,6 +40,7 @@ export class DbQueueProcessorsService { ) { } + @bindThis public start(q: Bull.Queue): void { q.process('deleteDriveFiles', (job, done) => this.deleteDriveFilesProcessorService.process(job, done)); q.process('exportCustomEmojis', (job, done) => this.exportCustomEmojisProcessorService.process(job, done)); diff --git a/packages/backend/src/queue/ObjectStorageQueueProcessorsService.ts b/packages/backend/src/queue/ObjectStorageQueueProcessorsService.ts index 3ff3dd090c..c95e1c1ba6 100644 --- a/packages/backend/src/queue/ObjectStorageQueueProcessorsService.ts +++ b/packages/backend/src/queue/ObjectStorageQueueProcessorsService.ts @@ -5,6 +5,7 @@ import type { Config } from '@/config.js'; import { CleanRemoteFilesProcessorService } from './processors/CleanRemoteFilesProcessorService.js'; import { DeleteFileProcessorService } from './processors/DeleteFileProcessorService.js'; import type Bull from 'bull'; +import { bindThis } from '@/decorators.js'; @Injectable() export class ObjectStorageQueueProcessorsService { @@ -17,6 +18,7 @@ export class ObjectStorageQueueProcessorsService { ) { } + @bindThis public start(q: Bull.Queue): void { q.process('deleteFile', 16, (job) => this.deleteFileProcessorService.process(job)); q.process('cleanRemoteFiles', 16, (job, done) => this.cleanRemoteFilesProcessorService.process(job, done)); diff --git a/packages/backend/src/queue/QueueLoggerService.ts b/packages/backend/src/queue/QueueLoggerService.ts index a311470cc9..3a8a734f10 100644 --- a/packages/backend/src/queue/QueueLoggerService.ts +++ b/packages/backend/src/queue/QueueLoggerService.ts @@ -1,6 +1,7 @@ import { Inject, Injectable } from '@nestjs/common'; import type Logger from '@/logger.js'; import { LoggerService } from '@/core/LoggerService.js'; +import { bindThis } from '@/decorators.js'; @Injectable() export class QueueLoggerService { diff --git a/packages/backend/src/queue/QueueProcessorService.ts b/packages/backend/src/queue/QueueProcessorService.ts index 8c300d479c..1d2feb5ef8 100644 --- a/packages/backend/src/queue/QueueProcessorService.ts +++ b/packages/backend/src/queue/QueueProcessorService.ts @@ -13,6 +13,7 @@ import { EndedPollNotificationProcessorService } from './processors/EndedPollNot import { DeliverProcessorService } from './processors/DeliverProcessorService.js'; import { InboxProcessorService } from './processors/InboxProcessorService.js'; import { QueueLoggerService } from './QueueLoggerService.js'; +import { bindThis } from '@/decorators.js'; @Injectable() export class QueueProcessorService { @@ -35,6 +36,7 @@ export class QueueProcessorService { this.logger = this.queueLoggerService.logger; } + @bindThis public start() { function renderError(e: Error): any { if (e) { // 何故かeがundefinedで来ることがある diff --git a/packages/backend/src/queue/SystemQueueProcessorsService.ts b/packages/backend/src/queue/SystemQueueProcessorsService.ts index a8af92b9ba..1ce4152b2c 100644 --- a/packages/backend/src/queue/SystemQueueProcessorsService.ts +++ b/packages/backend/src/queue/SystemQueueProcessorsService.ts @@ -7,6 +7,7 @@ import { CleanChartsProcessorService } from './processors/CleanChartsProcessorSe import { CheckExpiredMutingsProcessorService } from './processors/CheckExpiredMutingsProcessorService.js'; import { CleanProcessorService } from './processors/CleanProcessorService.js'; import type Bull from 'bull'; +import { bindThis } from '@/decorators.js'; @Injectable() export class SystemQueueProcessorsService { @@ -22,6 +23,7 @@ export class SystemQueueProcessorsService { ) { } + @bindThis public start(q: Bull.Queue): void { q.process('tickCharts', (job, done) => this.tickChartsProcessorService.process(job, done)); q.process('resyncCharts', (job, done) => this.resyncChartsProcessorService.process(job, done)); diff --git a/packages/backend/src/queue/processors/CheckExpiredMutingsProcessorService.ts b/packages/backend/src/queue/processors/CheckExpiredMutingsProcessorService.ts index e91cba9d10..7a1e3e71be 100644 --- a/packages/backend/src/queue/processors/CheckExpiredMutingsProcessorService.ts +++ b/packages/backend/src/queue/processors/CheckExpiredMutingsProcessorService.ts @@ -7,6 +7,7 @@ import type Logger from '@/logger.js'; import { GlobalEventService } from '@/core/GlobalEventService.js'; import { QueueLoggerService } from '../QueueLoggerService.js'; import type Bull from 'bull'; +import { bindThis } from '@/decorators.js'; @Injectable() export class CheckExpiredMutingsProcessorService { @@ -25,6 +26,7 @@ export class CheckExpiredMutingsProcessorService { this.logger = this.queueLoggerService.logger.createSubLogger('check-expired-mutings'); } + @bindThis public async process(job: Bull.Job>, done: () => void): Promise { this.logger.info('Checking expired mutings...'); diff --git a/packages/backend/src/queue/processors/CleanChartsProcessorService.ts b/packages/backend/src/queue/processors/CleanChartsProcessorService.ts index e8e90f1422..c57086240a 100644 --- a/packages/backend/src/queue/processors/CleanChartsProcessorService.ts +++ b/packages/backend/src/queue/processors/CleanChartsProcessorService.ts @@ -17,6 +17,7 @@ import PerUserDriveChart from '@/core/chart/charts/per-user-drive.js'; import ApRequestChart from '@/core/chart/charts/ap-request.js'; import { QueueLoggerService } from '../QueueLoggerService.js'; import type Bull from 'bull'; +import { bindThis } from '@/decorators.js'; @Injectable() export class CleanChartsProcessorService { @@ -44,6 +45,7 @@ export class CleanChartsProcessorService { this.logger = this.queueLoggerService.logger.createSubLogger('clean-charts'); } + @bindThis public async process(job: Bull.Job>, done: () => void): Promise { this.logger.info('Clean charts...'); diff --git a/packages/backend/src/queue/processors/CleanProcessorService.ts b/packages/backend/src/queue/processors/CleanProcessorService.ts index 6eb457ce9f..8ca39a9677 100644 --- a/packages/backend/src/queue/processors/CleanProcessorService.ts +++ b/packages/backend/src/queue/processors/CleanProcessorService.ts @@ -6,6 +6,7 @@ import type { Config } from '@/config.js'; import type Logger from '@/logger.js'; import { QueueLoggerService } from '../QueueLoggerService.js'; import type Bull from 'bull'; +import { bindThis } from '@/decorators.js'; @Injectable() export class CleanProcessorService { @@ -23,6 +24,7 @@ export class CleanProcessorService { this.logger = this.queueLoggerService.logger.createSubLogger('clean'); } + @bindThis public async process(job: Bull.Job>, done: () => void): Promise { this.logger.info('Cleaning...'); diff --git a/packages/backend/src/queue/processors/CleanRemoteFilesProcessorService.ts b/packages/backend/src/queue/processors/CleanRemoteFilesProcessorService.ts index a4fd8c502d..5a33c27188 100644 --- a/packages/backend/src/queue/processors/CleanRemoteFilesProcessorService.ts +++ b/packages/backend/src/queue/processors/CleanRemoteFilesProcessorService.ts @@ -7,6 +7,7 @@ import type Logger from '@/logger.js'; import { DriveService } from '@/core/DriveService.js'; import { QueueLoggerService } from '../QueueLoggerService.js'; import type Bull from 'bull'; +import { bindThis } from '@/decorators.js'; @Injectable() export class CleanRemoteFilesProcessorService { @@ -25,6 +26,7 @@ export class CleanRemoteFilesProcessorService { this.logger = this.queueLoggerService.logger.createSubLogger('clean-remote-files'); } + @bindThis public async process(job: Bull.Job>, done: () => void): Promise { this.logger.info('Deleting cached remote files...'); diff --git a/packages/backend/src/queue/processors/DeleteAccountProcessorService.ts b/packages/backend/src/queue/processors/DeleteAccountProcessorService.ts index 5e4c8bdd69..e36a78de6a 100644 --- a/packages/backend/src/queue/processors/DeleteAccountProcessorService.ts +++ b/packages/backend/src/queue/processors/DeleteAccountProcessorService.ts @@ -11,6 +11,7 @@ import { EmailService } from '@/core/EmailService.js'; import { QueueLoggerService } from '../QueueLoggerService.js'; import type Bull from 'bull'; import type { DbUserDeleteJobData } from '../types.js'; +import { bindThis } from '@/decorators.js'; @Injectable() export class DeleteAccountProcessorService { @@ -39,6 +40,7 @@ export class DeleteAccountProcessorService { this.logger = this.queueLoggerService.logger.createSubLogger('delete-account'); } + @bindThis public async process(job: Bull.Job): Promise { this.logger.info(`Deleting account of ${job.data.user.id} ...`); diff --git a/packages/backend/src/queue/processors/DeleteDriveFilesProcessorService.ts b/packages/backend/src/queue/processors/DeleteDriveFilesProcessorService.ts index 682382b2db..fa0c1733f6 100644 --- a/packages/backend/src/queue/processors/DeleteDriveFilesProcessorService.ts +++ b/packages/backend/src/queue/processors/DeleteDriveFilesProcessorService.ts @@ -8,6 +8,7 @@ import { DriveService } from '@/core/DriveService.js'; import { QueueLoggerService } from '../QueueLoggerService.js'; import type Bull from 'bull'; import type { DbUserJobData } from '../types.js'; +import { bindThis } from '@/decorators.js'; @Injectable() export class DeleteDriveFilesProcessorService { @@ -29,6 +30,7 @@ export class DeleteDriveFilesProcessorService { this.logger = this.queueLoggerService.logger.createSubLogger('delete-drive-files'); } + @bindThis public async process(job: Bull.Job, done: () => void): Promise { this.logger.info(`Deleting drive files of ${job.data.user.id} ...`); diff --git a/packages/backend/src/queue/processors/DeleteFileProcessorService.ts b/packages/backend/src/queue/processors/DeleteFileProcessorService.ts index 6740643fe2..2fb2f56f8d 100644 --- a/packages/backend/src/queue/processors/DeleteFileProcessorService.ts +++ b/packages/backend/src/queue/processors/DeleteFileProcessorService.ts @@ -6,6 +6,7 @@ import { DriveService } from '@/core/DriveService.js'; import { QueueLoggerService } from '../QueueLoggerService.js'; import type Bull from 'bull'; import type { ObjectStorageFileJobData } from '../types.js'; +import { bindThis } from '@/decorators.js'; @Injectable() export class DeleteFileProcessorService { @@ -21,6 +22,7 @@ export class DeleteFileProcessorService { this.logger = this.queueLoggerService.logger.createSubLogger('delete-file'); } + @bindThis public async process(job: Bull.Job): Promise { const key: string = job.data.key; diff --git a/packages/backend/src/queue/processors/DeliverProcessorService.ts b/packages/backend/src/queue/processors/DeliverProcessorService.ts index 2a4b201a7d..58969d550e 100644 --- a/packages/backend/src/queue/processors/DeliverProcessorService.ts +++ b/packages/backend/src/queue/processors/DeliverProcessorService.ts @@ -18,6 +18,7 @@ import { UtilityService } from '@/core/UtilityService.js'; import { QueueLoggerService } from '../QueueLoggerService.js'; import type Bull from 'bull'; import type { DeliverJobData } from '../types.js'; +import { bindThis } from '@/decorators.js'; @Injectable() export class DeliverProcessorService { @@ -50,6 +51,7 @@ export class DeliverProcessorService { this.latest = null; } + @bindThis public async process(job: Bull.Job): Promise { const { host } = new URL(job.data.to); diff --git a/packages/backend/src/queue/processors/EndedPollNotificationProcessorService.ts b/packages/backend/src/queue/processors/EndedPollNotificationProcessorService.ts index 2fc7fe219e..21d2dc9efc 100644 --- a/packages/backend/src/queue/processors/EndedPollNotificationProcessorService.ts +++ b/packages/backend/src/queue/processors/EndedPollNotificationProcessorService.ts @@ -8,6 +8,7 @@ import { CreateNotificationService } from '@/core/CreateNotificationService.js'; import { QueueLoggerService } from '../QueueLoggerService.js'; import type Bull from 'bull'; import type { EndedPollNotificationJobData } from '../types.js'; +import { bindThis } from '@/decorators.js'; @Injectable() export class EndedPollNotificationProcessorService { @@ -29,6 +30,7 @@ export class EndedPollNotificationProcessorService { this.logger = this.queueLoggerService.logger.createSubLogger('ended-poll-notification'); } + @bindThis public async process(job: Bull.Job, done: () => void): Promise { const note = await this.notesRepository.findOneBy({ id: job.data.noteId }); if (note == null || !note.hasPoll) { diff --git a/packages/backend/src/queue/processors/ExportBlockingProcessorService.ts b/packages/backend/src/queue/processors/ExportBlockingProcessorService.ts index db149b68cf..5b3c1a415b 100644 --- a/packages/backend/src/queue/processors/ExportBlockingProcessorService.ts +++ b/packages/backend/src/queue/processors/ExportBlockingProcessorService.ts @@ -12,6 +12,7 @@ import { UtilityService } from '@/core/UtilityService.js'; import { QueueLoggerService } from '../QueueLoggerService.js'; import type Bull from 'bull'; import type { DbUserJobData } from '../types.js'; +import { bindThis } from '@/decorators.js'; @Injectable() export class ExportBlockingProcessorService { @@ -34,6 +35,7 @@ export class ExportBlockingProcessorService { this.logger = this.queueLoggerService.logger.createSubLogger('export-blocking'); } + @bindThis public async process(job: Bull.Job, done: () => void): Promise { this.logger.info(`Exporting blocking of ${job.data.user.id} ...`); diff --git a/packages/backend/src/queue/processors/ExportCustomEmojisProcessorService.ts b/packages/backend/src/queue/processors/ExportCustomEmojisProcessorService.ts index f8f261b478..87b23f1891 100644 --- a/packages/backend/src/queue/processors/ExportCustomEmojisProcessorService.ts +++ b/packages/backend/src/queue/processors/ExportCustomEmojisProcessorService.ts @@ -14,6 +14,7 @@ import { createTemp, createTempDir } from '@/misc/create-temp.js'; import { DownloadService } from '@/core/DownloadService.js'; import { QueueLoggerService } from '../QueueLoggerService.js'; import type Bull from 'bull'; +import { bindThis } from '@/decorators.js'; @Injectable() export class ExportCustomEmojisProcessorService { @@ -36,6 +37,7 @@ export class ExportCustomEmojisProcessorService { this.logger = this.queueLoggerService.logger.createSubLogger('export-custom-emojis'); } + @bindThis public async process(job: Bull.Job, done: () => void): Promise { this.logger.info('Exporting custom emojis ...'); diff --git a/packages/backend/src/queue/processors/ExportFollowingProcessorService.ts b/packages/backend/src/queue/processors/ExportFollowingProcessorService.ts index 1e3fba06b5..064b126e44 100644 --- a/packages/backend/src/queue/processors/ExportFollowingProcessorService.ts +++ b/packages/backend/src/queue/processors/ExportFollowingProcessorService.ts @@ -13,6 +13,7 @@ import { UtilityService } from '@/core/UtilityService.js'; import { QueueLoggerService } from '../QueueLoggerService.js'; import type Bull from 'bull'; import type { DbUserJobData } from '../types.js'; +import { bindThis } from '@/decorators.js'; @Injectable() export class ExportFollowingProcessorService { @@ -38,6 +39,7 @@ export class ExportFollowingProcessorService { this.logger = this.queueLoggerService.logger.createSubLogger('export-following'); } + @bindThis public async process(job: Bull.Job, done: () => void): Promise { this.logger.info(`Exporting following of ${job.data.user.id} ...`); diff --git a/packages/backend/src/queue/processors/ExportMutingProcessorService.ts b/packages/backend/src/queue/processors/ExportMutingProcessorService.ts index e263c245fd..94c7ea8a46 100644 --- a/packages/backend/src/queue/processors/ExportMutingProcessorService.ts +++ b/packages/backend/src/queue/processors/ExportMutingProcessorService.ts @@ -12,6 +12,7 @@ import { UtilityService } from '@/core/UtilityService.js'; import { QueueLoggerService } from '../QueueLoggerService.js'; import type Bull from 'bull'; import type { DbUserJobData } from '../types.js'; +import { bindThis } from '@/decorators.js'; @Injectable() export class ExportMutingProcessorService { @@ -37,6 +38,7 @@ export class ExportMutingProcessorService { this.logger = this.queueLoggerService.logger.createSubLogger('export-muting'); } + @bindThis public async process(job: Bull.Job, done: () => void): Promise { this.logger.info(`Exporting muting of ${job.data.user.id} ...`); diff --git a/packages/backend/src/queue/processors/ExportNotesProcessorService.ts b/packages/backend/src/queue/processors/ExportNotesProcessorService.ts index 533d4bd7c6..8431829e91 100644 --- a/packages/backend/src/queue/processors/ExportNotesProcessorService.ts +++ b/packages/backend/src/queue/processors/ExportNotesProcessorService.ts @@ -13,6 +13,7 @@ import type { Note } from '@/models/entities/Note.js'; import { QueueLoggerService } from '../QueueLoggerService.js'; import type Bull from 'bull'; import type { DbUserJobData } from '../types.js'; +import { bindThis } from '@/decorators.js'; @Injectable() export class ExportNotesProcessorService { @@ -37,6 +38,7 @@ export class ExportNotesProcessorService { this.logger = this.queueLoggerService.logger.createSubLogger('export-notes'); } + @bindThis public async process(job: Bull.Job, done: () => void): Promise { this.logger.info(`Exporting notes of ${job.data.user.id} ...`); diff --git a/packages/backend/src/queue/processors/ExportUserListsProcessorService.ts b/packages/backend/src/queue/processors/ExportUserListsProcessorService.ts index 8c3e3dbe17..a8daa5e5ee 100644 --- a/packages/backend/src/queue/processors/ExportUserListsProcessorService.ts +++ b/packages/backend/src/queue/processors/ExportUserListsProcessorService.ts @@ -12,6 +12,7 @@ import { UtilityService } from '@/core/UtilityService.js'; import { QueueLoggerService } from '../QueueLoggerService.js'; import type Bull from 'bull'; import type { DbUserJobData } from '../types.js'; +import { bindThis } from '@/decorators.js'; @Injectable() export class ExportUserListsProcessorService { @@ -37,6 +38,7 @@ export class ExportUserListsProcessorService { this.logger = this.queueLoggerService.logger.createSubLogger('export-user-lists'); } + @bindThis public async process(job: Bull.Job, done: () => void): Promise { this.logger.info(`Exporting user lists of ${job.data.user.id} ...`); diff --git a/packages/backend/src/queue/processors/ImportBlockingProcessorService.ts b/packages/backend/src/queue/processors/ImportBlockingProcessorService.ts index 9442a60d8d..2eed420e96 100644 --- a/packages/backend/src/queue/processors/ImportBlockingProcessorService.ts +++ b/packages/backend/src/queue/processors/ImportBlockingProcessorService.ts @@ -12,6 +12,7 @@ import { UtilityService } from '@/core/UtilityService.js'; import { QueueLoggerService } from '../QueueLoggerService.js'; import type Bull from 'bull'; import type { DbUserImportJobData } from '../types.js'; +import { bindThis } from '@/decorators.js'; @Injectable() export class ImportBlockingProcessorService { @@ -39,6 +40,7 @@ export class ImportBlockingProcessorService { this.logger = this.queueLoggerService.logger.createSubLogger('import-blocking'); } + @bindThis public async process(job: Bull.Job, done: () => void): Promise { this.logger.info(`Importing blocking of ${job.data.user.id} ...`); diff --git a/packages/backend/src/queue/processors/ImportCustomEmojisProcessorService.ts b/packages/backend/src/queue/processors/ImportCustomEmojisProcessorService.ts index 492f17f9ff..0061c2a8f7 100644 --- a/packages/backend/src/queue/processors/ImportCustomEmojisProcessorService.ts +++ b/packages/backend/src/queue/processors/ImportCustomEmojisProcessorService.ts @@ -10,6 +10,7 @@ import { CustomEmojiService } from '@/core/CustomEmojiService.js'; import { createTempDir } from '@/misc/create-temp.js'; import { DriveService } from '@/core/DriveService.js'; import { DownloadService } from '@/core/DownloadService.js'; +import { bindThis } from '@/decorators.js'; import { QueueLoggerService } from '../QueueLoggerService.js'; import type Bull from 'bull'; import type { DbUserImportJobData } from '../types.js'; @@ -43,6 +44,7 @@ export class ImportCustomEmojisProcessorService { this.logger = this.queueLoggerService.logger.createSubLogger('import-custom-emojis'); } + @bindThis public async process(job: Bull.Job, done: () => void): Promise { this.logger.info('Importing custom emojis ...'); diff --git a/packages/backend/src/queue/processors/ImportFollowingProcessorService.ts b/packages/backend/src/queue/processors/ImportFollowingProcessorService.ts index 667f7279fb..b61846d747 100644 --- a/packages/backend/src/queue/processors/ImportFollowingProcessorService.ts +++ b/packages/backend/src/queue/processors/ImportFollowingProcessorService.ts @@ -12,6 +12,7 @@ import { UtilityService } from '@/core/UtilityService.js'; import { QueueLoggerService } from '../QueueLoggerService.js'; import type Bull from 'bull'; import type { DbUserImportJobData } from '../types.js'; +import { bindThis } from '@/decorators.js'; @Injectable() export class ImportFollowingProcessorService { @@ -36,6 +37,7 @@ export class ImportFollowingProcessorService { this.logger = this.queueLoggerService.logger.createSubLogger('import-following'); } + @bindThis public async process(job: Bull.Job, done: () => void): Promise { this.logger.info(`Importing following of ${job.data.user.id} ...`); diff --git a/packages/backend/src/queue/processors/ImportMutingProcessorService.ts b/packages/backend/src/queue/processors/ImportMutingProcessorService.ts index f3c16e73d5..21236da2ef 100644 --- a/packages/backend/src/queue/processors/ImportMutingProcessorService.ts +++ b/packages/backend/src/queue/processors/ImportMutingProcessorService.ts @@ -12,6 +12,7 @@ import { UtilityService } from '@/core/UtilityService.js'; import { QueueLoggerService } from '../QueueLoggerService.js'; import type Bull from 'bull'; import type { DbUserImportJobData } from '../types.js'; +import { bindThis } from '@/decorators.js'; @Injectable() export class ImportMutingProcessorService { @@ -36,6 +37,7 @@ export class ImportMutingProcessorService { this.logger = this.queueLoggerService.logger.createSubLogger('import-muting'); } + @bindThis public async process(job: Bull.Job, done: () => void): Promise { this.logger.info(`Importing muting of ${job.data.user.id} ...`); diff --git a/packages/backend/src/queue/processors/ImportUserListsProcessorService.ts b/packages/backend/src/queue/processors/ImportUserListsProcessorService.ts index 1519877c5f..1bec77b837 100644 --- a/packages/backend/src/queue/processors/ImportUserListsProcessorService.ts +++ b/packages/backend/src/queue/processors/ImportUserListsProcessorService.ts @@ -13,6 +13,7 @@ import { UtilityService } from '@/core/UtilityService.js'; import { QueueLoggerService } from '../QueueLoggerService.js'; import type Bull from 'bull'; import type { DbUserImportJobData } from '../types.js'; +import { bindThis } from '@/decorators.js'; @Injectable() export class ImportUserListsProcessorService { @@ -44,6 +45,7 @@ export class ImportUserListsProcessorService { this.logger = this.queueLoggerService.logger.createSubLogger('import-user-lists'); } + @bindThis public async process(job: Bull.Job, done: () => void): Promise { this.logger.info(`Importing user lists of ${job.data.user.id} ...`); diff --git a/packages/backend/src/queue/processors/InboxProcessorService.ts b/packages/backend/src/queue/processors/InboxProcessorService.ts index 8f1c474020..c032122caf 100644 --- a/packages/backend/src/queue/processors/InboxProcessorService.ts +++ b/packages/backend/src/queue/processors/InboxProcessorService.ts @@ -24,6 +24,7 @@ import { UtilityService } from '@/core/UtilityService.js'; import { ApPersonService } from '@/core/activitypub/models/ApPersonService.js'; import { LdSignatureService } from '@/core/activitypub/LdSignatureService.js'; import { ApInboxService } from '@/core/activitypub/ApInboxService.js'; +import { bindThis } from '@/decorators.js'; import { QueueLoggerService } from '../QueueLoggerService.js'; import type Bull from 'bull'; import type { DeliverJobData, InboxJobData } from '../types.js'; @@ -60,6 +61,7 @@ export class InboxProcessorService { this.logger = this.queueLoggerService.logger.createSubLogger('inbox'); } + @bindThis public async process(job: Bull.Job): Promise { const signature = job.data.signature; // HTTP-signature const activity = job.data.activity; diff --git a/packages/backend/src/queue/processors/ResyncChartsProcessorService.ts b/packages/backend/src/queue/processors/ResyncChartsProcessorService.ts index bf2fdeb7a0..1a8fe65a4f 100644 --- a/packages/backend/src/queue/processors/ResyncChartsProcessorService.ts +++ b/packages/backend/src/queue/processors/ResyncChartsProcessorService.ts @@ -17,6 +17,7 @@ import PerUserDriveChart from '@/core/chart/charts/per-user-drive.js'; import ApRequestChart from '@/core/chart/charts/ap-request.js'; import { QueueLoggerService } from '../QueueLoggerService.js'; import type Bull from 'bull'; +import { bindThis } from '@/decorators.js'; @Injectable() export class ResyncChartsProcessorService { @@ -44,6 +45,7 @@ export class ResyncChartsProcessorService { this.logger = this.queueLoggerService.logger.createSubLogger('resync-charts'); } + @bindThis public async process(job: Bull.Job>, done: () => void): Promise { this.logger.info('Resync charts...'); diff --git a/packages/backend/src/queue/processors/TickChartsProcessorService.ts b/packages/backend/src/queue/processors/TickChartsProcessorService.ts index 96607e1d68..323e5227cd 100644 --- a/packages/backend/src/queue/processors/TickChartsProcessorService.ts +++ b/packages/backend/src/queue/processors/TickChartsProcessorService.ts @@ -17,6 +17,7 @@ import PerUserDriveChart from '@/core/chart/charts/per-user-drive.js'; import ApRequestChart from '@/core/chart/charts/ap-request.js'; import { QueueLoggerService } from '../QueueLoggerService.js'; import type Bull from 'bull'; +import { bindThis } from '@/decorators.js'; @Injectable() export class TickChartsProcessorService { @@ -44,6 +45,7 @@ export class TickChartsProcessorService { this.logger = this.queueLoggerService.logger.createSubLogger('tick-charts'); } + @bindThis public async process(job: Bull.Job>, done: () => void): Promise { this.logger.info('Tick charts...'); diff --git a/packages/backend/src/queue/processors/WebhookDeliverProcessorService.ts b/packages/backend/src/queue/processors/WebhookDeliverProcessorService.ts index 43e3f37201..183ef07477 100644 --- a/packages/backend/src/queue/processors/WebhookDeliverProcessorService.ts +++ b/packages/backend/src/queue/processors/WebhookDeliverProcessorService.ts @@ -9,6 +9,7 @@ import { StatusError } from '@/misc/status-error.js'; import { QueueLoggerService } from '../QueueLoggerService.js'; import type Bull from 'bull'; import type { WebhookDeliverJobData } from '../types.js'; +import { bindThis } from '@/decorators.js'; @Injectable() export class WebhookDeliverProcessorService { @@ -27,6 +28,7 @@ export class WebhookDeliverProcessorService { this.logger = this.queueLoggerService.logger.createSubLogger('webhook'); } + @bindThis public async process(job: Bull.Job): Promise { try { this.logger.debug(`delivering ${job.data.webhookId}`); diff --git a/packages/backend/src/server/ActivityPubServerService.ts b/packages/backend/src/server/ActivityPubServerService.ts index 015c8f2b4c..94a277f4a4 100644 --- a/packages/backend/src/server/ActivityPubServerService.ts +++ b/packages/backend/src/server/ActivityPubServerService.ts @@ -18,6 +18,7 @@ import type { Note } from '@/models/entities/Note.js'; import { QueryService } from '@/core/QueryService.js'; import { UtilityService } from '@/core/UtilityService.js'; import { UserEntityService } from '@/core/entities/UserEntityService.js'; +import { bindThis } from '@/decorators.js'; import type { FindOptionsWhere } from 'typeorm'; const ACTIVITY_JSON = 'application/activity+json; charset=utf-8'; @@ -57,9 +58,10 @@ export class ActivityPubServerService { private userKeypairStoreService: UserKeypairStoreService, private queryService: QueryService, ) { - this.createServer = this.createServer.bind(this); + //this.createServer = this.createServer.bind(this); } + @bindThis private setResponseType(request: FastifyRequest, reply: FastifyReply): void { const accept = request.accepts().type([ACTIVITY_JSON, LD_JSON]); if (accept === LD_JSON) { @@ -73,6 +75,7 @@ export class ActivityPubServerService { * Pack Create or Announce Activity * @param note Note */ + @bindThis private async packActivity(note: Note): Promise { if (note.renoteId && note.text == null && !note.hasPoll && (note.fileIds == null || note.fileIds.length === 0)) { const renote = await this.notesRepository.findOneByOrFail({ id: note.renoteId }); @@ -82,6 +85,7 @@ export class ActivityPubServerService { return this.apRendererService.renderCreate(await this.apRendererService.renderNote(note, false), note); } + @bindThis private inbox(request: FastifyRequest, reply: FastifyReply) { let signature; @@ -97,6 +101,7 @@ export class ActivityPubServerService { reply.code(202); } + @bindThis private async followers( request: FastifyRequest<{ Params: { user: string; }; Querystring: { cursor?: string; page?: string; }; }>, reply: FastifyReply, @@ -184,6 +189,7 @@ export class ActivityPubServerService { } } + @bindThis private async following( request: FastifyRequest<{ Params: { user: string; }; Querystring: { cursor?: string; page?: string; }; }>, reply: FastifyReply, @@ -271,6 +277,7 @@ export class ActivityPubServerService { } } + @bindThis private async featured(request: FastifyRequest<{ Params: { user: string; }; }>, reply: FastifyReply) { const userId = request.params.user; @@ -304,6 +311,7 @@ export class ActivityPubServerService { return (this.apRendererService.renderActivity(rendered)); } + @bindThis private async outbox( request: FastifyRequest<{ Params: { user: string; }; @@ -390,6 +398,7 @@ export class ActivityPubServerService { } } + @bindThis private async userInfo(request: FastifyRequest, reply: FastifyReply, user: User | null) { if (user == null) { reply.code(404); @@ -401,6 +410,7 @@ export class ActivityPubServerService { return (this.apRendererService.renderActivity(await this.apRendererService.renderPerson(user as ILocalUser))); } + @bindThis public createServer(fastify: FastifyInstance, options: FastifyPluginOptions, done: (err?: Error) => void) { fastify.addConstraintStrategy({ name: 'apOrHtml', diff --git a/packages/backend/src/server/FileServerService.ts b/packages/backend/src/server/FileServerService.ts index 088e780d69..b7ab549611 100644 --- a/packages/backend/src/server/FileServerService.ts +++ b/packages/backend/src/server/FileServerService.ts @@ -19,6 +19,7 @@ import { InternalStorageService } from '@/core/InternalStorageService.js'; import { contentDisposition } from '@/misc/content-disposition.js'; import { FileInfoService } from '@/core/FileInfoService.js'; import { LoggerService } from '@/core/LoggerService.js'; +import { bindThis } from '@/decorators.js'; const _filename = fileURLToPath(import.meta.url); const _dirname = dirname(_filename); @@ -45,9 +46,10 @@ export class FileServerService { ) { this.logger = this.loggerService.getLogger('server', 'gray', false); - this.createServer = this.createServer.bind(this); + //this.createServer = this.createServer.bind(this); } + @bindThis public commonReadableHandlerGenerator(reply: FastifyReply) { return (err: Error): void => { this.logger.error(err); @@ -56,6 +58,7 @@ export class FileServerService { }; } + @bindThis public createServer(fastify: FastifyInstance, options: FastifyPluginOptions, done: (err?: Error) => void) { fastify.addHook('onRequest', (request, reply, done) => { reply.header('Content-Security-Policy', 'default-src \'none\'; img-src \'self\'; media-src \'self\'; style-src \'unsafe-inline\''); @@ -80,6 +83,7 @@ export class FileServerService { done(); } + @bindThis private async sendDriveFile(request: FastifyRequest<{ Params: { key: string; } }>, reply: FastifyReply) { const key = request.params.key; diff --git a/packages/backend/src/server/MediaProxyServerService.ts b/packages/backend/src/server/MediaProxyServerService.ts index 4d7bbdf599..733a7feeb5 100644 --- a/packages/backend/src/server/MediaProxyServerService.ts +++ b/packages/backend/src/server/MediaProxyServerService.ts @@ -14,6 +14,7 @@ import { StatusError } from '@/misc/status-error.js'; import type Logger from '@/logger.js'; import { FileInfoService } from '@/core/FileInfoService.js'; import { LoggerService } from '@/core/LoggerService.js'; +import { bindThis } from '@/decorators.js'; @Injectable() export class MediaProxyServerService { @@ -30,9 +31,10 @@ export class MediaProxyServerService { ) { this.logger = this.loggerService.getLogger('server', 'gray', false); - this.createServer = this.createServer.bind(this); + //this.createServer = this.createServer.bind(this); } + @bindThis public createServer(fastify: FastifyInstance, options: FastifyPluginOptions, done: (err?: Error) => void) { fastify.addHook('onRequest', (request, reply, done) => { reply.header('Content-Security-Policy', 'default-src \'none\'; img-src \'self\'; media-src \'self\'; style-src \'unsafe-inline\''); @@ -47,6 +49,7 @@ export class MediaProxyServerService { done(); } + @bindThis private async handler(request: FastifyRequest<{ Params: { url: string; }; Querystring: { url?: string; }; }>, reply: FastifyReply) { const url = 'url' in request.query ? request.query.url : 'https://' + request.params.url; diff --git a/packages/backend/src/server/NodeinfoServerService.ts b/packages/backend/src/server/NodeinfoServerService.ts index b85925f53e..0f3cc36dae 100644 --- a/packages/backend/src/server/NodeinfoServerService.ts +++ b/packages/backend/src/server/NodeinfoServerService.ts @@ -8,6 +8,7 @@ import { MetaService } from '@/core/MetaService.js'; import { MAX_NOTE_TEXT_LENGTH } from '@/const.js'; import { Cache } from '@/misc/cache.js'; import { UserEntityService } from '@/core/entities/UserEntityService.js'; +import { bindThis } from '@/decorators.js'; const nodeinfo2_1path = '/nodeinfo/2.1'; const nodeinfo2_0path = '/nodeinfo/2.0'; @@ -27,9 +28,10 @@ export class NodeinfoServerService { private userEntityService: UserEntityService, private metaService: MetaService, ) { - this.createServer = this.createServer.bind(this); + //this.createServer = this.createServer.bind(this); } + @bindThis public getLinks() { return [/* (awaiting release) { rel: 'http://nodeinfo.diaspora.software/ns/schema/2.1', @@ -40,6 +42,7 @@ export class NodeinfoServerService { }]; } + @bindThis public createServer(fastify: FastifyInstance, options: FastifyPluginOptions, done: (err?: Error) => void) { const nodeinfo2 = async () => { const now = Date.now(); diff --git a/packages/backend/src/server/ServerService.ts b/packages/backend/src/server/ServerService.ts index 96159cfc53..075b9cdff7 100644 --- a/packages/backend/src/server/ServerService.ts +++ b/packages/backend/src/server/ServerService.ts @@ -23,6 +23,7 @@ import { WellKnownServerService } from './WellKnownServerService.js'; import { MediaProxyServerService } from './MediaProxyServerService.js'; import { FileServerService } from './FileServerService.js'; import { ClientServerService } from './web/ClientServerService.js'; +import { bindThis } from '@/decorators.js'; @Injectable() export class ServerService { @@ -53,6 +54,7 @@ export class ServerService { this.logger = this.loggerService.getLogger('server', 'gray', false); } + @bindThis public launch() { const fastify = Fastify({ trustProxy: true, diff --git a/packages/backend/src/server/WellKnownServerService.ts b/packages/backend/src/server/WellKnownServerService.ts index 412c608313..ea34ad5b0f 100644 --- a/packages/backend/src/server/WellKnownServerService.ts +++ b/packages/backend/src/server/WellKnownServerService.ts @@ -10,6 +10,7 @@ import type { User } from '@/models/entities/User.js'; import * as Acct from '@/misc/acct.js'; import { NodeinfoServerService } from './NodeinfoServerService.js'; import type { FindOptionsWhere } from 'typeorm'; +import { bindThis } from '@/decorators.js'; @Injectable() export class WellKnownServerService { @@ -22,9 +23,10 @@ export class WellKnownServerService { private nodeinfoServerService: NodeinfoServerService, ) { - this.createServer = this.createServer.bind(this); + //this.createServer = this.createServer.bind(this); } + @bindThis public createServer(fastify: FastifyInstance, options: FastifyPluginOptions, done: (err?: Error) => void) { const XRD = (...x: { element: string, value?: string, attributes?: Record }[]) => `${x.map(({ element, value, attributes }) => diff --git a/packages/backend/src/server/api/ApiCallService.ts b/packages/backend/src/server/api/ApiCallService.ts index 2e72cdf9f8..fb1e17790c 100644 --- a/packages/backend/src/server/api/ApiCallService.ts +++ b/packages/backend/src/server/api/ApiCallService.ts @@ -12,6 +12,7 @@ import type Logger from '@/logger.js'; import type { UserIpsRepository } from '@/models/index.js'; import { MetaService } from '@/core/MetaService.js'; import { createTemp } from '@/misc/create-temp.js'; +import { bindThis } from '@/decorators.js'; import { ApiError } from './error.js'; import { RateLimiterService } from './RateLimiterService.js'; import { ApiLoggerService } from './ApiLoggerService.js'; @@ -50,6 +51,7 @@ export class ApiCallService implements OnApplicationShutdown { }, 1000 * 60 * 60); } + @bindThis public handleRequest( endpoint: IEndpoint & { exec: any }, request: FastifyRequest<{ Body: Record, Querystring: Record }>, @@ -90,6 +92,7 @@ export class ApiCallService implements OnApplicationShutdown { }); } + @bindThis public async handleMultipartRequest( endpoint: IEndpoint & { exec: any }, request: FastifyRequest<{ Body: Record, Querystring: Record }>, @@ -140,6 +143,7 @@ export class ApiCallService implements OnApplicationShutdown { }); } + @bindThis private send(reply: FastifyReply, x?: any, y?: ApiError) { if (x == null) { reply.code(204); @@ -160,6 +164,7 @@ export class ApiCallService implements OnApplicationShutdown { } } + @bindThis private async logIp(request: FastifyRequest, user: ILocalUser) { const meta = await this.metaService.fetch(); if (!meta.enableIpLogging) return; @@ -183,6 +188,7 @@ export class ApiCallService implements OnApplicationShutdown { } } + @bindThis private async call( ep: IEndpoint & { exec: any }, user: CacheableLocalUser | null | undefined, @@ -315,6 +321,7 @@ export class ApiCallService implements OnApplicationShutdown { }); } + @bindThis public onApplicationShutdown(signal?: string | undefined) { clearInterval(this.userIpHistoriesClearIntervalId); } diff --git a/packages/backend/src/server/api/ApiLoggerService.ts b/packages/backend/src/server/api/ApiLoggerService.ts index c4fb25036e..cabd65fd3e 100644 --- a/packages/backend/src/server/api/ApiLoggerService.ts +++ b/packages/backend/src/server/api/ApiLoggerService.ts @@ -1,6 +1,7 @@ import { Inject, Injectable } from '@nestjs/common'; import type Logger from '@/logger.js'; import { LoggerService } from '@/core/LoggerService.js'; +import { bindThis } from '@/decorators.js'; @Injectable() export class ApiLoggerService { diff --git a/packages/backend/src/server/api/ApiServerService.ts b/packages/backend/src/server/api/ApiServerService.ts index cf3f2deebf..b17456d0e2 100644 --- a/packages/backend/src/server/api/ApiServerService.ts +++ b/packages/backend/src/server/api/ApiServerService.ts @@ -14,6 +14,7 @@ import { SigninApiService } from './SigninApiService.js'; import { GithubServerService } from './integration/GithubServerService.js'; import { DiscordServerService } from './integration/DiscordServerService.js'; import { TwitterServerService } from './integration/TwitterServerService.js'; +import { bindThis } from '@/decorators.js'; @Injectable() export class ApiServerService { @@ -40,9 +41,10 @@ export class ApiServerService { private discordServerService: DiscordServerService, private twitterServerService: TwitterServerService, ) { - this.createServer = this.createServer.bind(this); + //this.createServer = this.createServer.bind(this); } + @bindThis public createServer(fastify: FastifyInstance, options: FastifyPluginOptions, done: (err?: Error) => void) { fastify.register(cors, { origin: '*', diff --git a/packages/backend/src/server/api/AuthenticateService.ts b/packages/backend/src/server/api/AuthenticateService.ts index ad387c4732..8b39f6c924 100644 --- a/packages/backend/src/server/api/AuthenticateService.ts +++ b/packages/backend/src/server/api/AuthenticateService.ts @@ -7,6 +7,7 @@ import { Cache } from '@/misc/cache.js'; import type { App } from '@/models/entities/App.js'; import { UserCacheService } from '@/core/UserCacheService.js'; import isNativeToken from '@/misc/is-native-token.js'; +import { bindThis } from '@/decorators.js'; export class AuthenticationError extends Error { constructor(message: string) { @@ -34,6 +35,7 @@ export class AuthenticateService { this.appCache = new Cache(Infinity); } + @bindThis public async authenticate(token: string | null | undefined): Promise<[CacheableLocalUser | null | undefined, AccessToken | null | undefined]> { if (token == null) { return [null, null]; diff --git a/packages/backend/src/server/api/GetterService.ts b/packages/backend/src/server/api/GetterService.ts index 70ab46ec35..c7f9916f97 100644 --- a/packages/backend/src/server/api/GetterService.ts +++ b/packages/backend/src/server/api/GetterService.ts @@ -5,6 +5,7 @@ import { IdentifiableError } from '@/misc/identifiable-error.js'; import type { User } from '@/models/entities/User.js'; import type { Note } from '@/models/entities/Note.js'; import { UserEntityService } from '@/core/entities/UserEntityService.js'; +import { bindThis } from '@/decorators.js'; @Injectable() export class GetterService { @@ -22,6 +23,7 @@ export class GetterService { /** * Get note for API processing */ + @bindThis public async getNote(noteId: Note['id']) { const note = await this.notesRepository.findOneBy({ id: noteId }); @@ -35,6 +37,7 @@ export class GetterService { /** * Get user for API processing */ + @bindThis public async getUser(userId: User['id']) { const user = await this.usersRepository.findOneBy({ id: userId }); @@ -48,6 +51,7 @@ export class GetterService { /** * Get remote user for API processing */ + @bindThis public async getRemoteUser(userId: User['id']) { const user = await this.getUser(userId); @@ -61,6 +65,7 @@ export class GetterService { /** * Get local user for API processing */ + @bindThis public async getLocalUser(userId: User['id']) { const user = await this.getUser(userId); diff --git a/packages/backend/src/server/api/RateLimiterService.ts b/packages/backend/src/server/api/RateLimiterService.ts index 35f28bfd63..94a15f94bb 100644 --- a/packages/backend/src/server/api/RateLimiterService.ts +++ b/packages/backend/src/server/api/RateLimiterService.ts @@ -5,6 +5,7 @@ import { DI } from '@/di-symbols.js'; import type Logger from '@/logger.js'; import { LoggerService } from '@/core/LoggerService.js'; import type { IEndpointMeta } from './endpoints.js'; +import { bindThis } from '@/decorators.js'; @Injectable() export class RateLimiterService { @@ -19,6 +20,7 @@ export class RateLimiterService { this.logger = this.loggerService.getLogger('limiter'); } + @bindThis public limit(limitation: IEndpointMeta['limit'] & { key: NonNullable }, actor: string) { return new Promise((ok, reject) => { if (process.env.NODE_ENV === 'test') ok(); diff --git a/packages/backend/src/server/api/SigninApiService.ts b/packages/backend/src/server/api/SigninApiService.ts index 8b3d86e5a6..b633c2888f 100644 --- a/packages/backend/src/server/api/SigninApiService.ts +++ b/packages/backend/src/server/api/SigninApiService.ts @@ -13,6 +13,7 @@ import { IdService } from '@/core/IdService.js'; import { TwoFactorAuthenticationService } from '@/core/TwoFactorAuthenticationService.js'; import { RateLimiterService } from './RateLimiterService.js'; import { SigninService } from './SigninService.js'; +import { bindThis } from '@/decorators.js'; @Injectable() export class SigninApiService { @@ -42,6 +43,7 @@ export class SigninApiService { ) { } + @bindThis public async signin( request: FastifyRequest<{ Body: { diff --git a/packages/backend/src/server/api/SigninService.ts b/packages/backend/src/server/api/SigninService.ts index 18a1d6c088..96a89956f9 100644 --- a/packages/backend/src/server/api/SigninService.ts +++ b/packages/backend/src/server/api/SigninService.ts @@ -7,6 +7,7 @@ import { IdService } from '@/core/IdService.js'; import type { ILocalUser } from '@/models/entities/User.js'; import { GlobalEventService } from '@/core/GlobalEventService.js'; import { SigninEntityService } from '@/core/entities/SigninEntityService.js'; +import { bindThis } from '@/decorators.js'; @Injectable() export class SigninService { @@ -23,6 +24,7 @@ export class SigninService { ) { } + @bindThis public signin(request: FastifyRequest, reply: FastifyReply, user: ILocalUser, redirect = false) { setImmediate(async () => { // Append signin history diff --git a/packages/backend/src/server/api/SignupApiService.ts b/packages/backend/src/server/api/SignupApiService.ts index 771858d091..59676426af 100644 --- a/packages/backend/src/server/api/SignupApiService.ts +++ b/packages/backend/src/server/api/SignupApiService.ts @@ -14,6 +14,7 @@ import { EmailService } from '@/core/EmailService.js'; import { ILocalUser } from '@/models/entities/User.js'; import { FastifyReplyError } from '@/misc/fastify-reply-error.js'; import { SigninService } from './SigninService.js'; +import { bindThis } from '@/decorators.js'; @Injectable() export class SignupApiService { @@ -43,6 +44,7 @@ export class SignupApiService { ) { } + @bindThis public async signup( request: FastifyRequest<{ Body: { @@ -165,6 +167,7 @@ export class SignupApiService { } } + @bindThis public async signupPending(request: FastifyRequest<{ Body: { code: string; } }>, reply: FastifyReply) { const body = request.body; diff --git a/packages/backend/src/server/api/StreamingApiServerService.ts b/packages/backend/src/server/api/StreamingApiServerService.ts index 46eaf8566e..487eef2d50 100644 --- a/packages/backend/src/server/api/StreamingApiServerService.ts +++ b/packages/backend/src/server/api/StreamingApiServerService.ts @@ -13,6 +13,7 @@ import MainStreamConnection from './stream/index.js'; import { ChannelsService } from './stream/ChannelsService.js'; import type { ParsedUrlQuery } from 'querystring'; import type * as http from 'node:http'; +import { bindThis } from '@/decorators.js'; @Injectable() export class StreamingApiServerService { @@ -49,6 +50,7 @@ export class StreamingApiServerService { ) { } + @bindThis public attachStreamingApi(server: http.Server) { // Init websocket server const ws = new websocket.server({ diff --git a/packages/backend/src/server/api/endpoints/admin/suspend-user.ts b/packages/backend/src/server/api/endpoints/admin/suspend-user.ts index 53de8d9495..9fc1391570 100644 --- a/packages/backend/src/server/api/endpoints/admin/suspend-user.ts +++ b/packages/backend/src/server/api/endpoints/admin/suspend-user.ts @@ -8,6 +8,7 @@ import { UserSuspendService } from '@/core/UserSuspendService.js'; import { UserFollowingService } from '@/core/UserFollowingService.js'; import { DI } from '@/di-symbols.js'; import { UserEntityService } from '@/core/entities/UserEntityService.js'; +import { bindThis } from '@/decorators.js'; export const meta = { tags: ['admin'], @@ -79,6 +80,7 @@ export default class extends Endpoint { }); } + @bindThis private async unFollowAll(follower: User) { const followings = await this.followingsRepository.findBy({ followerId: follower.id, @@ -97,6 +99,7 @@ export default class extends Endpoint { } } + @bindThis private async readAllNotify(notifier: User) { await this.notificationsRepository.update({ notifierId: notifier.id, diff --git a/packages/backend/src/server/api/endpoints/ap/show.ts b/packages/backend/src/server/api/endpoints/ap/show.ts index c218ec4642..1068a2eec7 100644 --- a/packages/backend/src/server/api/endpoints/ap/show.ts +++ b/packages/backend/src/server/api/endpoints/ap/show.ts @@ -15,6 +15,7 @@ import { UserEntityService } from '@/core/entities/UserEntityService.js'; import { NoteEntityService } from '@/core/entities/NoteEntityService.js'; import { UtilityService } from '@/core/UtilityService.js'; import { DI } from '@/di-symbols.js'; +import { bindThis } from '@/decorators.js'; import { ApiError } from '../../error.js'; export const meta = { @@ -112,6 +113,7 @@ export default class extends Endpoint { /*** * URIからUserかNoteを解決する */ + @bindThis private async fetchAny(uri: string, me: CacheableLocalUser | null | undefined): Promise | null> { // ブロックしてたら中断 const fetchedMeta = await this.metaService.fetch(); @@ -144,6 +146,7 @@ export default class extends Endpoint { ); } + @bindThis private async mergePack(me: CacheableLocalUser | null | undefined, user: User | null | undefined, note: Note | null | undefined): Promise | null> { if (user != null) { return { diff --git a/packages/backend/src/server/api/integration/DiscordServerService.ts b/packages/backend/src/server/api/integration/DiscordServerService.ts index 93c22a6c0b..a7f39a78d0 100644 --- a/packages/backend/src/server/api/integration/DiscordServerService.ts +++ b/packages/backend/src/server/api/integration/DiscordServerService.ts @@ -14,6 +14,7 @@ import { MetaService } from '@/core/MetaService.js'; import { UserEntityService } from '@/core/entities/UserEntityService.js'; import { FastifyReplyError } from '@/misc/fastify-reply-error.js'; import { SigninService } from '../SigninService.js'; +import { bindThis } from '@/decorators.js'; @Injectable() export class DiscordServerService { @@ -36,9 +37,10 @@ export class DiscordServerService { private metaService: MetaService, private signinService: SigninService, ) { - this.create = this.create.bind(this); + //this.create = this.create.bind(this); } + @bindThis public create(fastify: FastifyInstance, options: FastifyPluginOptions, done: (err?: Error) => void) { fastify.get('/disconnect/discord', async (request, reply) => { if (!this.compareOrigin(request)) { @@ -288,10 +290,12 @@ export class DiscordServerService { done(); } + @bindThis private getUserToken(request: FastifyRequest): string | null { return ((request.headers['cookie'] ?? '').match(/igi=(\w+)/) ?? [null, null])[1]; } + @bindThis private compareOrigin(request: FastifyRequest): boolean { function normalizeUrl(url?: string): string { return url ? url.endsWith('/') ? url.substr(0, url.length - 1) : url : ''; diff --git a/packages/backend/src/server/api/integration/GithubServerService.ts b/packages/backend/src/server/api/integration/GithubServerService.ts index 2fd20bf831..3aa04f72ee 100644 --- a/packages/backend/src/server/api/integration/GithubServerService.ts +++ b/packages/backend/src/server/api/integration/GithubServerService.ts @@ -14,6 +14,7 @@ import { MetaService } from '@/core/MetaService.js'; import { UserEntityService } from '@/core/entities/UserEntityService.js'; import { FastifyReplyError } from '@/misc/fastify-reply-error.js'; import { SigninService } from '../SigninService.js'; +import { bindThis } from '@/decorators.js'; @Injectable() export class GithubServerService { @@ -36,9 +37,10 @@ export class GithubServerService { private metaService: MetaService, private signinService: SigninService, ) { - this.create = this.create.bind(this); + //this.create = this.create.bind(this); } + @bindThis public create(fastify: FastifyInstance, options: FastifyPluginOptions, done: (err?: Error) => void) { fastify.get('/disconnect/github', async (request, reply) => { if (!this.compareOrigin(request)) { @@ -260,10 +262,12 @@ export class GithubServerService { done(); } + @bindThis private getUserToken(request: FastifyRequest): string | null { return ((request.headers['cookie'] ?? '').match(/igi=(\w+)/) ?? [null, null])[1]; } + @bindThis private compareOrigin(request: FastifyRequest): boolean { function normalizeUrl(url?: string): string { return url ? url.endsWith('/') ? url.substr(0, url.length - 1) : url : ''; diff --git a/packages/backend/src/server/api/integration/TwitterServerService.ts b/packages/backend/src/server/api/integration/TwitterServerService.ts index a8447f9d49..7a127fa29a 100644 --- a/packages/backend/src/server/api/integration/TwitterServerService.ts +++ b/packages/backend/src/server/api/integration/TwitterServerService.ts @@ -14,6 +14,7 @@ import { MetaService } from '@/core/MetaService.js'; import { UserEntityService } from '@/core/entities/UserEntityService.js'; import { FastifyReplyError } from '@/misc/fastify-reply-error.js'; import { SigninService } from '../SigninService.js'; +import { bindThis } from '@/decorators.js'; @Injectable() export class TwitterServerService { @@ -36,9 +37,10 @@ export class TwitterServerService { private metaService: MetaService, private signinService: SigninService, ) { - this.create = this.create.bind(this); + //this.create = this.create.bind(this); } + @bindThis public create(fastify: FastifyInstance, options: FastifyPluginOptions, done: (err?: Error) => void) { fastify.get('/disconnect/twitter', async (request, reply) => { if (!this.compareOrigin(request)) { @@ -205,10 +207,12 @@ export class TwitterServerService { done(); } + @bindThis private getUserToken(request: FastifyRequest): string | null { return ((request.headers['cookie'] ?? '').match(/igi=(\w+)/) ?? [null, null])[1]; } + @bindThis private compareOrigin(request: FastifyRequest): boolean { function normalizeUrl(url?: string): string { return url ? url.endsWith('/') ? url.substr(0, url.length - 1) : url : ''; diff --git a/packages/backend/src/server/api/stream/ChannelsService.ts b/packages/backend/src/server/api/stream/ChannelsService.ts index d6005b1ee8..198fc190d4 100644 --- a/packages/backend/src/server/api/stream/ChannelsService.ts +++ b/packages/backend/src/server/api/stream/ChannelsService.ts @@ -15,6 +15,7 @@ import { MessagingChannelService } from './channels/messaging.js'; import { MessagingIndexChannelService } from './channels/messaging-index.js'; import { DriveChannelService } from './channels/drive.js'; import { HashtagChannelService } from './channels/hashtag.js'; +import { bindThis } from '@/decorators.js'; @Injectable() export class ChannelsService { @@ -37,6 +38,7 @@ export class ChannelsService { ) { } + @bindThis public getChannelService(name: string) { switch (name) { case 'main': return this.mainChannelService; diff --git a/packages/backend/src/server/api/stream/channel.ts b/packages/backend/src/server/api/stream/channel.ts index 5480c12c09..3e67880b45 100644 --- a/packages/backend/src/server/api/stream/channel.ts +++ b/packages/backend/src/server/api/stream/channel.ts @@ -1,3 +1,4 @@ +import { bindThis } from '@/decorators.js'; import type Connection from '.'; /** @@ -43,6 +44,7 @@ export default abstract class Channel { this.connection = connection; } + @bindThis public send(typeOrPayload: any, payload?: any) { const type = payload === undefined ? typeOrPayload.type : typeOrPayload; const body = payload === undefined ? typeOrPayload.body : payload; diff --git a/packages/backend/src/server/api/stream/channels/admin.ts b/packages/backend/src/server/api/stream/channels/admin.ts index 8c3c0d2adf..210e016a7e 100644 --- a/packages/backend/src/server/api/stream/channels/admin.ts +++ b/packages/backend/src/server/api/stream/channels/admin.ts @@ -1,4 +1,5 @@ import { Inject, Injectable } from '@nestjs/common'; +import { bindThis } from '@/decorators.js'; import Channel from '../channel.js'; class AdminChannel extends Channel { @@ -6,6 +7,7 @@ class AdminChannel extends Channel { public static shouldShare = true; public static requireCredential = true; + @bindThis public async init(params: any) { // Subscribe admin stream this.subscriber.on(`adminStream:${this.user!.id}`, data => { @@ -23,6 +25,7 @@ export class AdminChannelService { ) { } + @bindThis public create(id: string, connection: Channel['connection']): AdminChannel { return new AdminChannel( id, diff --git a/packages/backend/src/server/api/stream/channels/antenna.ts b/packages/backend/src/server/api/stream/channels/antenna.ts index 7c34aef495..44beef2da2 100644 --- a/packages/backend/src/server/api/stream/channels/antenna.ts +++ b/packages/backend/src/server/api/stream/channels/antenna.ts @@ -2,6 +2,7 @@ import { Inject, Injectable } from '@nestjs/common'; import type { NotesRepository } from '@/models/index.js'; import { isUserRelated } from '@/misc/is-user-related.js'; import { NoteEntityService } from '@/core/entities/NoteEntityService.js'; +import { bindThis } from '@/decorators.js'; import Channel from '../channel.js'; import type { StreamMessages } from '../types.js'; @@ -18,9 +19,10 @@ class AntennaChannel extends Channel { connection: Channel['connection'], ) { super(id, connection); - this.onEvent = this.onEvent.bind(this); + //this.onEvent = this.onEvent.bind(this); } + @bindThis public async init(params: any) { this.antennaId = params.antennaId as string; @@ -28,6 +30,7 @@ class AntennaChannel extends Channel { this.subscriber.on(`antennaStream:${this.antennaId}`, this.onEvent); } + @bindThis private async onEvent(data: StreamMessages['antenna']['payload']) { if (data.type === 'note') { const note = await this.noteEntityService.pack(data.body.id, this.user, { detail: true }); @@ -45,6 +48,7 @@ class AntennaChannel extends Channel { } } + @bindThis public dispose() { // Unsubscribe events this.subscriber.off(`antennaStream:${this.antennaId}`, this.onEvent); @@ -61,6 +65,7 @@ export class AntennaChannelService { ) { } + @bindThis public create(id: string, connection: Channel['connection']): AntennaChannel { return new AntennaChannel( this.noteEntityService, diff --git a/packages/backend/src/server/api/stream/channels/channel.ts b/packages/backend/src/server/api/stream/channels/channel.ts index 2ef70e62e9..5ba84e43c4 100644 --- a/packages/backend/src/server/api/stream/channels/channel.ts +++ b/packages/backend/src/server/api/stream/channels/channel.ts @@ -5,6 +5,7 @@ import type { User } from '@/models/entities/User.js'; import type { Packed } from '@/misc/schema.js'; import { NoteEntityService } from '@/core/entities/NoteEntityService.js'; import { UserEntityService } from '@/core/entities/UserEntityService.js'; +import { bindThis } from '@/decorators.js'; import Channel from '../channel.js'; import type { StreamMessages } from '../types.js'; @@ -24,10 +25,11 @@ class ChannelChannel extends Channel { connection: Channel['connection'], ) { super(id, connection); - this.onNote = this.onNote.bind(this); - this.emitTypers = this.emitTypers.bind(this); + //this.onNote = this.onNote.bind(this); + //this.emitTypers = this.emitTypers.bind(this); } + @bindThis public async init(params: any) { this.channelId = params.channelId as string; @@ -37,6 +39,7 @@ class ChannelChannel extends Channel { this.emitTypersIntervalId = setInterval(this.emitTypers, 5000); } + @bindThis private async onNote(note: Packed<'Note'>) { if (note.channelId !== this.channelId) return; @@ -63,6 +66,7 @@ class ChannelChannel extends Channel { this.send('note', note); } + @bindThis private onEvent(data: StreamMessages['channel']['payload']) { if (data.type === 'typing') { const id = data.body; @@ -74,6 +78,7 @@ class ChannelChannel extends Channel { } } + @bindThis private async emitTypers() { const now = new Date(); @@ -90,6 +95,7 @@ class ChannelChannel extends Channel { }); } + @bindThis public dispose() { // Unsubscribe events this.subscriber.off('notesStream', this.onNote); @@ -110,6 +116,7 @@ export class ChannelChannelService { ) { } + @bindThis public create(id: string, connection: Channel['connection']): ChannelChannel { return new ChannelChannel( this.noteEntityService, diff --git a/packages/backend/src/server/api/stream/channels/drive.ts b/packages/backend/src/server/api/stream/channels/drive.ts index 80d83cd690..cfcb125b6b 100644 --- a/packages/backend/src/server/api/stream/channels/drive.ts +++ b/packages/backend/src/server/api/stream/channels/drive.ts @@ -1,4 +1,5 @@ import { Inject, Injectable } from '@nestjs/common'; +import { bindThis } from '@/decorators.js'; import Channel from '../channel.js'; class DriveChannel extends Channel { @@ -6,6 +7,7 @@ class DriveChannel extends Channel { public static shouldShare = true; public static requireCredential = true; + @bindThis public async init(params: any) { // Subscribe drive stream this.subscriber.on(`driveStream:${this.user!.id}`, data => { @@ -23,6 +25,7 @@ export class DriveChannelService { ) { } + @bindThis public create(id: string, connection: Channel['connection']): DriveChannel { return new DriveChannel( id, diff --git a/packages/backend/src/server/api/stream/channels/global-timeline.ts b/packages/backend/src/server/api/stream/channels/global-timeline.ts index a8617582dc..34f782e580 100644 --- a/packages/backend/src/server/api/stream/channels/global-timeline.ts +++ b/packages/backend/src/server/api/stream/channels/global-timeline.ts @@ -6,6 +6,7 @@ import { isUserRelated } from '@/misc/is-user-related.js'; import type { Packed } from '@/misc/schema.js'; import { MetaService } from '@/core/MetaService.js'; import { NoteEntityService } from '@/core/entities/NoteEntityService.js'; +import { bindThis } from '@/decorators.js'; import Channel from '../channel.js'; class GlobalTimelineChannel extends Channel { @@ -21,9 +22,10 @@ class GlobalTimelineChannel extends Channel { connection: Channel['connection'], ) { super(id, connection); - this.onNote = this.onNote.bind(this); + //this.onNote = this.onNote.bind(this); } + @bindThis public async init(params: any) { const meta = await this.metaService.fetch(); if (meta.disableGlobalTimeline) { @@ -34,6 +36,7 @@ class GlobalTimelineChannel extends Channel { this.subscriber.on('notesStream', this.onNote); } + @bindThis private async onNote(note: Packed<'Note'>) { if (note.visibility !== 'public') return; if (note.channelId != null) return; @@ -78,6 +81,7 @@ class GlobalTimelineChannel extends Channel { this.send('note', note); } + @bindThis public dispose() { // Unsubscribe events this.subscriber.off('notesStream', this.onNote); @@ -95,6 +99,7 @@ export class GlobalTimelineChannelService { ) { } + @bindThis public create(id: string, connection: Channel['connection']): GlobalTimelineChannel { return new GlobalTimelineChannel( this.metaService, diff --git a/packages/backend/src/server/api/stream/channels/hashtag.ts b/packages/backend/src/server/api/stream/channels/hashtag.ts index 0f6c081c12..073b737079 100644 --- a/packages/backend/src/server/api/stream/channels/hashtag.ts +++ b/packages/backend/src/server/api/stream/channels/hashtag.ts @@ -4,6 +4,7 @@ import { normalizeForSearch } from '@/misc/normalize-for-search.js'; import { isUserRelated } from '@/misc/is-user-related.js'; import type { Packed } from '@/misc/schema.js'; import { NoteEntityService } from '@/core/entities/NoteEntityService.js'; +import { bindThis } from '@/decorators.js'; import Channel from '../channel.js'; class HashtagChannel extends Channel { @@ -19,9 +20,10 @@ class HashtagChannel extends Channel { connection: Channel['connection'], ) { super(id, connection); - this.onNote = this.onNote.bind(this); + //this.onNote = this.onNote.bind(this); } + @bindThis public async init(params: any) { this.q = params.q; @@ -31,6 +33,7 @@ class HashtagChannel extends Channel { this.subscriber.on('notesStream', this.onNote); } + @bindThis private async onNote(note: Packed<'Note'>) { const noteTags = note.tags ? note.tags.map((t: string) => t.toLowerCase()) : []; const matched = this.q.some(tags => tags.every(tag => noteTags.includes(normalizeForSearch(tag)))); @@ -53,6 +56,7 @@ class HashtagChannel extends Channel { this.send('note', note); } + @bindThis public dispose() { // Unsubscribe events this.subscriber.off('notesStream', this.onNote); @@ -69,6 +73,7 @@ export class HashtagChannelService { ) { } + @bindThis public create(id: string, connection: Channel['connection']): HashtagChannel { return new HashtagChannel( this.noteEntityService, diff --git a/packages/backend/src/server/api/stream/channels/home-timeline.ts b/packages/backend/src/server/api/stream/channels/home-timeline.ts index 16e0cebc72..5707ddd821 100644 --- a/packages/backend/src/server/api/stream/channels/home-timeline.ts +++ b/packages/backend/src/server/api/stream/channels/home-timeline.ts @@ -5,6 +5,7 @@ import { isUserRelated } from '@/misc/is-user-related.js'; import { isInstanceMuted } from '@/misc/is-instance-muted.js'; import type { Packed } from '@/misc/schema.js'; import { NoteEntityService } from '@/core/entities/NoteEntityService.js'; +import { bindThis } from '@/decorators.js'; import Channel from '../channel.js'; class HomeTimelineChannel extends Channel { @@ -19,14 +20,16 @@ class HomeTimelineChannel extends Channel { connection: Channel['connection'], ) { super(id, connection); - this.onNote = this.onNote.bind(this); + //this.onNote = this.onNote.bind(this); } + @bindThis public async init(params: any) { // Subscribe events this.subscriber.on('notesStream', this.onNote); } + @bindThis private async onNote(note: Packed<'Note'>) { if (note.channelId) { if (!this.followingChannels.has(note.channelId)) return; @@ -85,6 +88,7 @@ class HomeTimelineChannel extends Channel { this.send('note', note); } + @bindThis public dispose() { // Unsubscribe events this.subscriber.off('notesStream', this.onNote); @@ -101,6 +105,7 @@ export class HomeTimelineChannelService { ) { } + @bindThis public create(id: string, connection: Channel['connection']): HomeTimelineChannel { return new HomeTimelineChannel( this.noteEntityService, diff --git a/packages/backend/src/server/api/stream/channels/hybrid-timeline.ts b/packages/backend/src/server/api/stream/channels/hybrid-timeline.ts index f1ce822583..6c6afb12bf 100644 --- a/packages/backend/src/server/api/stream/channels/hybrid-timeline.ts +++ b/packages/backend/src/server/api/stream/channels/hybrid-timeline.ts @@ -7,6 +7,7 @@ import type { Packed } from '@/misc/schema.js'; import { DI } from '@/di-symbols.js'; import { MetaService } from '@/core/MetaService.js'; import { NoteEntityService } from '@/core/entities/NoteEntityService.js'; +import { bindThis } from '@/decorators.js'; import Channel from '../channel.js'; class HybridTimelineChannel extends Channel { @@ -22,9 +23,10 @@ class HybridTimelineChannel extends Channel { connection: Channel['connection'], ) { super(id, connection); - this.onNote = this.onNote.bind(this); + //this.onNote = this.onNote.bind(this); } + @bindThis public async init(params: any): Promise { const meta = await this.metaService.fetch(); if (meta.disableLocalTimeline && !this.user!.isAdmin && !this.user!.isModerator) return; @@ -33,6 +35,7 @@ class HybridTimelineChannel extends Channel { this.subscriber.on('notesStream', this.onNote); } + @bindThis private async onNote(note: Packed<'Note'>) { // チャンネルの投稿ではなく、自分自身の投稿 または // チャンネルの投稿ではなく、その投稿のユーザーをフォローしている または @@ -95,6 +98,7 @@ class HybridTimelineChannel extends Channel { this.send('note', note); } + @bindThis public dispose(): void { // Unsubscribe events this.subscriber.off('notesStream', this.onNote); @@ -112,6 +116,7 @@ export class HybridTimelineChannelService { ) { } + @bindThis public create(id: string, connection: Channel['connection']): HybridTimelineChannel { return new HybridTimelineChannel( this.metaService, diff --git a/packages/backend/src/server/api/stream/channels/local-timeline.ts b/packages/backend/src/server/api/stream/channels/local-timeline.ts index 5a5a43f845..54388787ef 100644 --- a/packages/backend/src/server/api/stream/channels/local-timeline.ts +++ b/packages/backend/src/server/api/stream/channels/local-timeline.ts @@ -5,6 +5,7 @@ import { isUserRelated } from '@/misc/is-user-related.js'; import type { Packed } from '@/misc/schema.js'; import { MetaService } from '@/core/MetaService.js'; import { NoteEntityService } from '@/core/entities/NoteEntityService.js'; +import { bindThis } from '@/decorators.js'; import Channel from '../channel.js'; class LocalTimelineChannel extends Channel { @@ -20,9 +21,10 @@ class LocalTimelineChannel extends Channel { connection: Channel['connection'], ) { super(id, connection); - this.onNote = this.onNote.bind(this); + //this.onNote = this.onNote.bind(this); } + @bindThis public async init(params: any) { const meta = await this.metaService.fetch(); if (meta.disableLocalTimeline) { @@ -33,6 +35,7 @@ class LocalTimelineChannel extends Channel { this.subscriber.on('notesStream', this.onNote); } + @bindThis private async onNote(note: Packed<'Note'>) { if (note.user.host !== null) return; if (note.visibility !== 'public') return; @@ -75,6 +78,7 @@ class LocalTimelineChannel extends Channel { this.send('note', note); } + @bindThis public dispose() { // Unsubscribe events this.subscriber.off('notesStream', this.onNote); @@ -92,6 +96,7 @@ export class LocalTimelineChannelService { ) { } + @bindThis public create(id: string, connection: Channel['connection']): LocalTimelineChannel { return new LocalTimelineChannel( this.metaService, diff --git a/packages/backend/src/server/api/stream/channels/main.ts b/packages/backend/src/server/api/stream/channels/main.ts index 12908e07b4..42f255b8fe 100644 --- a/packages/backend/src/server/api/stream/channels/main.ts +++ b/packages/backend/src/server/api/stream/channels/main.ts @@ -2,6 +2,7 @@ import { Inject, Injectable } from '@nestjs/common'; import type { NotesRepository } from '@/models/index.js'; import { isInstanceMuted, isUserFromMutedInstance } from '@/misc/is-instance-muted.js'; import { NoteEntityService } from '@/core/entities/NoteEntityService.js'; +import { bindThis } from '@/decorators.js'; import Channel from '../channel.js'; class MainChannel extends Channel { @@ -18,6 +19,7 @@ class MainChannel extends Channel { super(id, connection); } + @bindThis public async init(params: any) { // Subscribe main stream channel this.subscriber.on(`mainStream:${this.user!.id}`, async data => { @@ -66,6 +68,7 @@ export class MainChannelService { ) { } + @bindThis public create(id: string, connection: Channel['connection']): MainChannel { return new MainChannel( this.noteEntityService, diff --git a/packages/backend/src/server/api/stream/channels/messaging-index.ts b/packages/backend/src/server/api/stream/channels/messaging-index.ts index bebc07f4ad..66cb79f7a7 100644 --- a/packages/backend/src/server/api/stream/channels/messaging-index.ts +++ b/packages/backend/src/server/api/stream/channels/messaging-index.ts @@ -1,4 +1,5 @@ import { Inject, Injectable } from '@nestjs/common'; +import { bindThis } from '@/decorators.js'; import Channel from '../channel.js'; class MessagingIndexChannel extends Channel { @@ -6,6 +7,7 @@ class MessagingIndexChannel extends Channel { public static shouldShare = true; public static requireCredential = true; + @bindThis public async init(params: any) { // Subscribe messaging index stream this.subscriber.on(`messagingIndexStream:${this.user!.id}`, data => { @@ -23,6 +25,7 @@ export class MessagingIndexChannelService { ) { } + @bindThis public create(id: string, connection: Channel['connection']): MessagingIndexChannel { return new MessagingIndexChannel( id, diff --git a/packages/backend/src/server/api/stream/channels/messaging.ts b/packages/backend/src/server/api/stream/channels/messaging.ts index b6ce6c217e..92af6b591c 100644 --- a/packages/backend/src/server/api/stream/channels/messaging.ts +++ b/packages/backend/src/server/api/stream/channels/messaging.ts @@ -5,6 +5,7 @@ import type { UserGroup } from '@/models/entities/UserGroup.js'; import { MessagingService } from '@/core/MessagingService.js'; import { UserEntityService } from '@/core/entities/UserEntityService.js'; import { DI } from '@/di-symbols.js'; +import { bindThis } from '@/decorators.js'; import Channel from '../channel.js'; import type { StreamMessages } from '../types.js'; @@ -31,11 +32,12 @@ class MessagingChannel extends Channel { connection: Channel['connection'], ) { super(id, connection); - this.onEvent = this.onEvent.bind(this); - this.onMessage = this.onMessage.bind(this); - this.emitTypers = this.emitTypers.bind(this); + //this.onEvent = this.onEvent.bind(this); + //this.onMessage = this.onMessage.bind(this); + //this.emitTypers = this.emitTypers.bind(this); } + @bindThis public async init(params: any) { this.otherpartyId = params.otherparty; this.otherparty = this.otherpartyId ? await this.usersRepository.findOneByOrFail({ id: this.otherpartyId }) : null; @@ -63,6 +65,7 @@ class MessagingChannel extends Channel { this.subscriber.on(this.subCh, this.onEvent); } + @bindThis private onEvent(data: StreamMessages['messaging']['payload'] | StreamMessages['groupMessaging']['payload']) { if (data.type === 'typing') { const id = data.body; @@ -76,6 +79,7 @@ class MessagingChannel extends Channel { } } + @bindThis public onMessage(type: string, body: any) { switch (type) { case 'read': @@ -95,6 +99,7 @@ class MessagingChannel extends Channel { } } + @bindThis private async emitTypers() { const now = new Date(); @@ -111,6 +116,7 @@ class MessagingChannel extends Channel { }); } + @bindThis public dispose() { this.subscriber.off(this.subCh, this.onEvent); @@ -138,6 +144,7 @@ export class MessagingChannelService { ) { } + @bindThis public create(id: string, connection: Channel['connection']): MessagingChannel { return new MessagingChannel( this.usersRepository, diff --git a/packages/backend/src/server/api/stream/channels/queue-stats.ts b/packages/backend/src/server/api/stream/channels/queue-stats.ts index 1802c6723b..c773916103 100644 --- a/packages/backend/src/server/api/stream/channels/queue-stats.ts +++ b/packages/backend/src/server/api/stream/channels/queue-stats.ts @@ -1,5 +1,6 @@ import Xev from 'xev'; import { Inject, Injectable } from '@nestjs/common'; +import { bindThis } from '@/decorators.js'; import Channel from '../channel.js'; const ev = new Xev(); @@ -11,18 +12,21 @@ class QueueStatsChannel extends Channel { constructor(id: string, connection: Channel['connection']) { super(id, connection); - this.onStats = this.onStats.bind(this); - this.onMessage = this.onMessage.bind(this); + //this.onStats = this.onStats.bind(this); + //this.onMessage = this.onMessage.bind(this); } + @bindThis public async init(params: any) { ev.addListener('queueStats', this.onStats); } + @bindThis private onStats(stats: any) { this.send('stats', stats); } + @bindThis public onMessage(type: string, body: any) { switch (type) { case 'requestLog': @@ -37,6 +41,7 @@ class QueueStatsChannel extends Channel { } } + @bindThis public dispose() { ev.removeListener('queueStats', this.onStats); } @@ -51,6 +56,7 @@ export class QueueStatsChannelService { ) { } + @bindThis public create(id: string, connection: Channel['connection']): QueueStatsChannel { return new QueueStatsChannel( id, diff --git a/packages/backend/src/server/api/stream/channels/server-stats.ts b/packages/backend/src/server/api/stream/channels/server-stats.ts index e2b00de25f..492912dbe6 100644 --- a/packages/backend/src/server/api/stream/channels/server-stats.ts +++ b/packages/backend/src/server/api/stream/channels/server-stats.ts @@ -1,5 +1,6 @@ import Xev from 'xev'; import { Inject, Injectable } from '@nestjs/common'; +import { bindThis } from '@/decorators.js'; import Channel from '../channel.js'; const ev = new Xev(); @@ -11,18 +12,21 @@ class ServerStatsChannel extends Channel { constructor(id: string, connection: Channel['connection']) { super(id, connection); - this.onStats = this.onStats.bind(this); - this.onMessage = this.onMessage.bind(this); + //this.onStats = this.onStats.bind(this); + //this.onMessage = this.onMessage.bind(this); } + @bindThis public async init(params: any) { ev.addListener('serverStats', this.onStats); } + @bindThis private onStats(stats: any) { this.send('stats', stats); } + @bindThis public onMessage(type: string, body: any) { switch (type) { case 'requestLog': @@ -37,6 +41,7 @@ class ServerStatsChannel extends Channel { } } + @bindThis public dispose() { ev.removeListener('serverStats', this.onStats); } @@ -51,6 +56,7 @@ export class ServerStatsChannelService { ) { } + @bindThis public create(id: string, connection: Channel['connection']): ServerStatsChannel { return new ServerStatsChannel( id, diff --git a/packages/backend/src/server/api/stream/channels/user-list.ts b/packages/backend/src/server/api/stream/channels/user-list.ts index f9f0d02558..16af32868c 100644 --- a/packages/backend/src/server/api/stream/channels/user-list.ts +++ b/packages/backend/src/server/api/stream/channels/user-list.ts @@ -1,11 +1,11 @@ import { Inject, Injectable } from '@nestjs/common'; -import type { UserListJoiningsRepository, UserListsRepository } from '@/models/index.js'; -import type { NotesRepository } from '@/models/index.js'; +import type { UserListJoiningsRepository, UserListsRepository, NotesRepository } from '@/models/index.js'; import type { User } from '@/models/entities/User.js'; import { isUserRelated } from '@/misc/is-user-related.js'; import type { Packed } from '@/misc/schema.js'; import { NoteEntityService } from '@/core/entities/NoteEntityService.js'; import { DI } from '@/di-symbols.js'; +import { bindThis } from '@/decorators.js'; import Channel from '../channel.js'; class UserListChannel extends Channel { @@ -25,10 +25,11 @@ class UserListChannel extends Channel { connection: Channel['connection'], ) { super(id, connection); - this.updateListUsers = this.updateListUsers.bind(this); - this.onNote = this.onNote.bind(this); + //this.updateListUsers = this.updateListUsers.bind(this); + //this.onNote = this.onNote.bind(this); } + @bindThis public async init(params: any) { this.listId = params.listId as string; @@ -48,6 +49,7 @@ class UserListChannel extends Channel { this.listUsersClock = setInterval(this.updateListUsers, 5000); } + @bindThis private async updateListUsers() { const users = await this.userListJoiningsRepository.find({ where: { @@ -59,6 +61,7 @@ class UserListChannel extends Channel { this.listUsers = users.map(x => x.userId); } + @bindThis private async onNote(note: Packed<'Note'>) { if (!this.listUsers.includes(note.userId)) return; @@ -93,6 +96,7 @@ class UserListChannel extends Channel { this.send('note', note); } + @bindThis public dispose() { // Unsubscribe events this.subscriber.off(`userListStream:${this.listId}`, this.send); @@ -118,6 +122,7 @@ export class UserListChannelService { ) { } + @bindThis public create(id: string, connection: Channel['connection']): UserListChannel { return new UserListChannel( this.userListsRepository, diff --git a/packages/backend/src/server/api/stream/index.ts b/packages/backend/src/server/api/stream/index.ts index 0c5066b736..6763953f9d 100644 --- a/packages/backend/src/server/api/stream/index.ts +++ b/packages/backend/src/server/api/stream/index.ts @@ -8,6 +8,7 @@ import type { Packed } from '@/misc/schema.js'; import type { GlobalEventService } from '@/core/GlobalEventService.js'; import type { NoteReadService } from '@/core/NoteReadService.js'; import type { NotificationService } from '@/core/NotificationService.js'; +import { bindThis } from '@/decorators.js'; import type { ChannelsService } from './ChannelsService.js'; import type * as websocket from 'websocket'; import type { EventEmitter } from 'events'; @@ -52,10 +53,10 @@ export default class Connection { if (user) this.user = user; if (token) this.token = token; - this.onWsConnectionMessage = this.onWsConnectionMessage.bind(this); - this.onUserEvent = this.onUserEvent.bind(this); - this.onNoteStreamMessage = this.onNoteStreamMessage.bind(this); - this.onBroadcastMessage = this.onBroadcastMessage.bind(this); + //this.onWsConnectionMessage = this.onWsConnectionMessage.bind(this); + //this.onUserEvent = this.onUserEvent.bind(this); + //this.onNoteStreamMessage = this.onNoteStreamMessage.bind(this); + //this.onBroadcastMessage = this.onBroadcastMessage.bind(this); this.wsConnection.on('message', this.onWsConnectionMessage); @@ -74,6 +75,7 @@ export default class Connection { } } + @bindThis private onUserEvent(data: StreamMessages['user']['payload']) { // { type, body }と展開するとそれぞれ型が分離してしまう switch (data.type) { case 'follow': @@ -119,6 +121,7 @@ export default class Connection { /** * クライアントからメッセージ受信時 */ + @bindThis private async onWsConnectionMessage(data: websocket.Message) { if (data.type !== 'utf8') return; if (data.utf8Data == null) return; @@ -153,10 +156,12 @@ export default class Connection { } } + @bindThis private onBroadcastMessage(data: StreamMessages['broadcast']['payload']) { this.sendMessageToWs(data.type, data.body); } + @bindThis public cacheNote(note: Packed<'Note'>) { const add = (note: Packed<'Note'>) => { const existIndex = this.cachedNotes.findIndex(n => n.id === note.id); @@ -176,6 +181,7 @@ export default class Connection { if (note.renote) add(note.renote); } + @bindThis private readNote(body: any) { const id = body.id; @@ -190,6 +196,7 @@ export default class Connection { } } + @bindThis private onReadNotification(payload: any) { if (!payload.id) return; this.notificationService.readNotification(this.user!.id, [payload.id]); @@ -198,6 +205,7 @@ export default class Connection { /** * 投稿購読要求時 */ + @bindThis private onSubscribeNote(payload: any) { if (!payload.id) return; @@ -215,6 +223,7 @@ export default class Connection { /** * 投稿購読解除要求時 */ + @bindThis private onUnsubscribeNote(payload: any) { if (!payload.id) return; @@ -225,6 +234,7 @@ export default class Connection { } } + @bindThis private async onNoteStreamMessage(data: StreamMessages['note']['payload']) { this.sendMessageToWs('noteUpdated', { id: data.body.id, @@ -236,6 +246,7 @@ export default class Connection { /** * チャンネル接続要求時 */ + @bindThis private onChannelConnectRequested(payload: any) { const { channel, id, params, pong } = payload; this.connectChannel(id, params, channel, pong); @@ -244,6 +255,7 @@ export default class Connection { /** * チャンネル切断要求時 */ + @bindThis private onChannelDisconnectRequested(payload: any) { const { id } = payload; this.disconnectChannel(id); @@ -252,6 +264,7 @@ export default class Connection { /** * クライアントにメッセージ送信 */ + @bindThis public sendMessageToWs(type: string, payload: any) { this.wsConnection.send(JSON.stringify({ type: type, @@ -262,6 +275,7 @@ export default class Connection { /** * チャンネルに接続 */ + @bindThis public connectChannel(id: string, params: any, channel: string, pong = false) { const channelService = this.channelsService.getChannelService(channel); @@ -289,6 +303,7 @@ export default class Connection { * チャンネルから切断 * @param id チャンネルコネクションID */ + @bindThis public disconnectChannel(id: string) { const channel = this.channels.find(c => c.id === id); @@ -302,6 +317,7 @@ export default class Connection { * チャンネルへメッセージ送信要求時 * @param data メッセージ */ + @bindThis private onChannelMessageRequested(data: any) { const channel = this.channels.find(c => c.id === data.id); if (channel != null && channel.onMessage != null) { @@ -309,12 +325,14 @@ export default class Connection { } } + @bindThis private typingOnChannel(channel: ChannelModel['id']) { if (this.user) { this.globalEventService.publishChannelStream(channel, 'typing', this.user.id); } } + @bindThis private typingOnMessaging(param: { partner?: User['id']; group?: UserGroup['id']; }) { if (this.user) { if (param.partner) { @@ -325,6 +343,7 @@ export default class Connection { } } + @bindThis private async updateFollowing() { const followings = await this.followingsRepository.find({ where: { @@ -336,6 +355,7 @@ export default class Connection { this.following = new Set(followings.map(x => x.followeeId)); } + @bindThis private async updateMuting() { const mutings = await this.mutingsRepository.find({ where: { @@ -347,6 +367,7 @@ export default class Connection { this.muting = new Set(mutings.map(x => x.muteeId)); } + @bindThis private async updateBlocking() { // ここでいうBlockingは被Blockingの意 const blockings = await this.blockingsRepository.find({ where: { @@ -358,6 +379,7 @@ export default class Connection { this.blocking = new Set(blockings.map(x => x.blockerId)); } + @bindThis private async updateFollowingChannels() { const followings = await this.channelFollowingsRepository.find({ where: { @@ -369,6 +391,7 @@ export default class Connection { this.followingChannels = new Set(followings.map(x => x.followeeId)); } + @bindThis private async updateUserProfile() { this.userProfile = await this.userProfilesRepository.findOneBy({ userId: this.user!.id, @@ -378,6 +401,7 @@ export default class Connection { /** * ストリームが切れたとき */ + @bindThis public dispose() { for (const c of this.channels.filter(c => c.dispose)) { if (c.dispose) c.dispose(); diff --git a/packages/backend/src/server/web/ClientServerService.ts b/packages/backend/src/server/web/ClientServerService.ts index 3fcf8b7c0d..727cf92831 100644 --- a/packages/backend/src/server/web/ClientServerService.ts +++ b/packages/backend/src/server/web/ClientServerService.ts @@ -23,6 +23,7 @@ import { ClipEntityService } from '@/core/entities/ClipEntityService.js'; import { ChannelEntityService } from '@/core/entities/ChannelEntityService.js'; import type { ChannelsRepository, ClipsRepository, GalleryPostsRepository, NotesRepository, PagesRepository, UserProfilesRepository, UsersRepository } from '@/models/index.js'; import { deepClone } from '@/misc/clone.js'; +import { bindThis } from '@/decorators.js'; import manifest from './manifest.json' assert { type: 'json' }; import { FeedService } from './FeedService.js'; import { UrlPreviewService } from './UrlPreviewService.js'; @@ -80,9 +81,10 @@ export class ClientServerService { @Inject('queue:objectStorage') public objectStorageQueue: ObjectStorageQueue, @Inject('queue:webhookDeliver') public webhookDeliverQueue: WebhookDeliverQueue, ) { - this.createServer = this.createServer.bind(this); + //this.createServer = this.createServer.bind(this); } + @bindThis private async manifestHandler(reply: FastifyReply) { const res = deepClone(manifest); @@ -96,6 +98,7 @@ export class ClientServerService { return (res); } + @bindThis public createServer(fastify: FastifyInstance, options: FastifyPluginOptions, done: (err?: Error) => void) { /* TODO //#region Bull Dashboard diff --git a/packages/backend/src/server/web/FeedService.ts b/packages/backend/src/server/web/FeedService.ts index 1d7d49961d..a14609adf9 100644 --- a/packages/backend/src/server/web/FeedService.ts +++ b/packages/backend/src/server/web/FeedService.ts @@ -7,6 +7,7 @@ import type { Config } from '@/config.js'; import type { User } from '@/models/entities/User.js'; import { UserEntityService } from '@/core/entities/UserEntityService.js'; import { DriveFileEntityService } from '@/core/entities/DriveFileEntityService.js'; +import { bindThis } from '@/decorators.js'; @Injectable() export class FeedService { @@ -31,6 +32,7 @@ export class FeedService { ) { } + @bindThis public async packFeed(user: User) { const author = { link: `${this.config.url}/@${user.username}`, diff --git a/packages/backend/src/server/web/UrlPreviewService.ts b/packages/backend/src/server/web/UrlPreviewService.ts index 69f52cc2f2..69bb232d4a 100644 --- a/packages/backend/src/server/web/UrlPreviewService.ts +++ b/packages/backend/src/server/web/UrlPreviewService.ts @@ -9,6 +9,7 @@ import { HttpRequestService } from '@/core/HttpRequestService.js'; import type Logger from '@/logger.js'; import { query } from '@/misc/prelude/url.js'; import { LoggerService } from '@/core/LoggerService.js'; +import { bindThis } from '@/decorators.js'; @Injectable() export class UrlPreviewService { @@ -28,6 +29,7 @@ export class UrlPreviewService { this.logger = this.loggerService.getLogger('url-preview'); } + @bindThis private wrap(url?: string): string | null { return url != null ? url.match(/^https?:\/\//) @@ -39,6 +41,7 @@ export class UrlPreviewService { : null; } + @bindThis public async handle( request: FastifyRequest<{ Querystring: { url: string; lang: string; } }>, reply: FastifyReply, diff --git a/packages/backend/test/misc/mock-resolver.ts b/packages/backend/test/misc/mock-resolver.ts index cad4d8af6c..9efed267ea 100644 --- a/packages/backend/test/misc/mock-resolver.ts +++ b/packages/backend/test/misc/mock-resolver.ts @@ -15,6 +15,7 @@ export class MockResolver extends Resolver { }); } + @bindThis public async resolve(value: string | IObject): Promise { if (typeof value !== 'string') return value;