diff --git a/packages/backend/src/core/CoreModule.ts b/packages/backend/src/core/CoreModule.ts index 87575ca59a..f075671d93 100644 --- a/packages/backend/src/core/CoreModule.ts +++ b/packages/backend/src/core/CoreModule.ts @@ -141,7 +141,7 @@ import { ApLoggerService } from './activitypub/ApLoggerService.js'; import { ApMfmService } from './activitypub/ApMfmService.js'; import { ApRendererService } from './activitypub/ApRendererService.js'; import { ApRequestService } from './activitypub/ApRequestService.js'; -import { ApResolverService } from './activitypub/ApResolverService.js'; +import { ApResolverService, Resolver } from './activitypub/ApResolverService.js'; import { JsonLdService } from './activitypub/JsonLdService.js'; import { RemoteLoggerService } from './RemoteLoggerService.js'; import { RemoteUserResolveService } from './RemoteUserResolveService.js'; @@ -447,6 +447,7 @@ const $ApQuestionService: Provider = { provide: 'ApQuestionService', useExisting ApRendererService, ApRequestService, ApResolverService, + Resolver, JsonLdService, RemoteLoggerService, RemoteUserResolveService, @@ -745,6 +746,7 @@ const $ApQuestionService: Provider = { provide: 'ApQuestionService', useExisting ApRendererService, ApRequestService, ApResolverService, + Resolver, JsonLdService, RemoteLoggerService, RemoteUserResolveService, diff --git a/packages/backend/src/core/activitypub/ApInboxService.ts b/packages/backend/src/core/activitypub/ApInboxService.ts index 81637580e3..ff47ca930d 100644 --- a/packages/backend/src/core/activitypub/ApInboxService.ts +++ b/packages/backend/src/core/activitypub/ApInboxService.ts @@ -95,7 +95,7 @@ export class ApInboxService { if (isCollectionOrOrderedCollection(activity)) { const results = [] as [string, string | void][]; // eslint-disable-next-line no-param-reassign - resolver ??= this.apResolverService.createResolver(); + resolver ??= await this.apResolverService.createResolver(); const items = toArray(isCollection(activity) ? activity.items : activity.orderedItems); if (items.length >= resolver.getRecursionLimit()) { @@ -221,7 +221,7 @@ export class ApInboxService { this.logger.info(`Accept: ${uri}`); // eslint-disable-next-line no-param-reassign - resolver ??= this.apResolverService.createResolver(); + resolver ??= await this.apResolverService.createResolver(); const object = await resolver.resolve(activity.object).catch(err => { this.logger.error(`Resolution failed: ${err}`); @@ -284,7 +284,7 @@ export class ApInboxService { this.logger.info(`Announce: ${uri}`); // eslint-disable-next-line no-param-reassign - resolver ??= this.apResolverService.createResolver(); + resolver ??= await this.apResolverService.createResolver(); if (!activity.object) return 'skip: activity has no object property'; const targetUri = getApId(activity.object); @@ -406,7 +406,7 @@ export class ApInboxService { } // eslint-disable-next-line no-param-reassign - resolver ??= this.apResolverService.createResolver(); + resolver ??= await this.apResolverService.createResolver(); const object = await resolver.resolve(activity.object).catch(e => { this.logger.error(`Resolution failed: ${e}`); @@ -575,7 +575,7 @@ export class ApInboxService { this.logger.info(`Reject: ${uri}`); // eslint-disable-next-line no-param-reassign - resolver ??= this.apResolverService.createResolver(); + resolver ??= await this.apResolverService.createResolver(); const object = await resolver.resolve(activity.object).catch(e => { this.logger.error(`Resolution failed: ${e}`); @@ -642,7 +642,7 @@ export class ApInboxService { this.logger.info(`Undo: ${uri}`); // eslint-disable-next-line no-param-reassign - resolver ??= this.apResolverService.createResolver(); + resolver ??= await this.apResolverService.createResolver(); const object = await resolver.resolve(activity.object).catch(e => { this.logger.error(`Resolution failed: ${e}`); @@ -774,7 +774,7 @@ export class ApInboxService { this.logger.debug('Update'); // eslint-disable-next-line no-param-reassign - resolver ??= this.apResolverService.createResolver(); + resolver ??= await this.apResolverService.createResolver(); const object = await resolver.resolve(activity.object).catch(e => { this.logger.error(`Resolution failed: ${e}`); diff --git a/packages/backend/src/core/activitypub/ApResolverService.ts b/packages/backend/src/core/activitypub/ApResolverService.ts index 646150455b..0f51b1ce8d 100644 --- a/packages/backend/src/core/activitypub/ApResolverService.ts +++ b/packages/backend/src/core/activitypub/ApResolverService.ts @@ -3,10 +3,17 @@ * SPDX-License-Identifier: AGPL-3.0-only */ -import { Inject, Injectable } from '@nestjs/common'; +import { Inject, Injectable, Scope } from '@nestjs/common'; import { IsNull, Not } from 'typeorm'; import type { MiLocalUser, MiRemoteUser } from '@/models/User.js'; -import type { NotesRepository, PollsRepository, NoteReactionsRepository, UsersRepository, FollowRequestsRepository, MiMeta } from '@/models/_.js'; +import type { + FollowRequestsRepository, + MiMeta, + NoteReactionsRepository, + NotesRepository, + PollsRepository, + UsersRepository +} from '@/models/_.js'; import type { Config } from '@/config.js'; import { HttpRequestService } from '@/core/HttpRequestService.js'; import { DI } from '@/di-symbols.js'; @@ -16,26 +23,43 @@ import { LoggerService } from '@/core/LoggerService.js'; import type Logger from '@/logger.js'; import { SystemAccountService } from '@/core/SystemAccountService.js'; import { IdentifiableError } from '@/misc/identifiable-error.js'; +import type { ICollection, IObject, IOrderedCollection } from './type.js'; import { isCollectionOrOrderedCollection } from './type.js'; import { ApDbResolverService } from './ApDbResolverService.js'; import { ApRendererService } from './ApRendererService.js'; import { ApRequestService } from './ApRequestService.js'; import { FetchAllowSoftFailMask } from './misc/check-against-url.js'; -import type { IObject, ICollection, IOrderedCollection } from './type.js'; +import { ModuleRef } from '@nestjs/core'; +@Injectable({ scope: Scope.TRANSIENT }) export class Resolver { private history: Set; private user?: MiLocalUser; private logger: Logger; + private recursionLimit = 256; constructor( + @Inject(DI.config) private config: Config, + + @Inject(DI.meta) private meta: MiMeta, + + @Inject(DI.usersRepository) private usersRepository: UsersRepository, + + @Inject(DI.notesRepository) private notesRepository: NotesRepository, + + @Inject(DI.pollsRepository) private pollsRepository: PollsRepository, + + @Inject(DI.noteReactionsRepository) private noteReactionsRepository: NoteReactionsRepository, + + @Inject(DI.followRequestsRepository) private followRequestsRepository: FollowRequestsRepository, + private utilityService: UtilityService, private systemAccountService: SystemAccountService, private apRequestService: ApRequestService, @@ -43,7 +67,6 @@ export class Resolver { private apRendererService: ApRendererService, private apDbResolverService: ApDbResolverService, private loggerService: LoggerService, - private recursionLimit = 256, ) { this.history = new Set(); this.logger = this.loggerService.getLogger('ap-resolve'); @@ -180,54 +203,12 @@ export class Resolver { @Injectable() export class ApResolverService { constructor( - @Inject(DI.config) - private config: Config, - - @Inject(DI.meta) - private meta: MiMeta, - - @Inject(DI.usersRepository) - private usersRepository: UsersRepository, - - @Inject(DI.notesRepository) - private notesRepository: NotesRepository, - - @Inject(DI.pollsRepository) - private pollsRepository: PollsRepository, - - @Inject(DI.noteReactionsRepository) - private noteReactionsRepository: NoteReactionsRepository, - - @Inject(DI.followRequestsRepository) - private followRequestsRepository: FollowRequestsRepository, - - private utilityService: UtilityService, - private systemAccountService: SystemAccountService, - private apRequestService: ApRequestService, - private httpRequestService: HttpRequestService, - private apRendererService: ApRendererService, - private apDbResolverService: ApDbResolverService, - private loggerService: LoggerService, + private moduleRef: ModuleRef, ) { } @bindThis - public createResolver(): Resolver { - return new Resolver( - this.config, - this.meta, - this.usersRepository, - this.notesRepository, - this.pollsRepository, - this.noteReactionsRepository, - this.followRequestsRepository, - this.utilityService, - this.systemAccountService, - this.apRequestService, - this.httpRequestService, - this.apRendererService, - this.apDbResolverService, - this.loggerService, - ); + public async createResolver(): Promise { + return await this.moduleRef.create(Resolver); } } diff --git a/packages/backend/src/core/activitypub/models/ApImageService.ts b/packages/backend/src/core/activitypub/models/ApImageService.ts index e7ece87b01..0496774c19 100644 --- a/packages/backend/src/core/activitypub/models/ApImageService.ts +++ b/packages/backend/src/core/activitypub/models/ApImageService.ts @@ -46,7 +46,7 @@ export class ApImageService { throw new Error('actor has been suspended'); } - const image = await this.apResolverService.createResolver().resolve(value); + const image = await (await this.apResolverService.createResolver()).resolve(value); if (!isDocument(image)) return null; diff --git a/packages/backend/src/core/activitypub/models/ApNoteService.ts b/packages/backend/src/core/activitypub/models/ApNoteService.ts index 214d32f67f..1fc5728c98 100644 --- a/packages/backend/src/core/activitypub/models/ApNoteService.ts +++ b/packages/backend/src/core/activitypub/models/ApNoteService.ts @@ -128,7 +128,7 @@ export class ApNoteService { @bindThis public async createNote(value: string | IObject, actor?: MiRemoteUser, resolver?: Resolver, silent = false): Promise { // eslint-disable-next-line no-param-reassign - if (resolver == null) resolver = this.apResolverService.createResolver(); + if (resolver == null) resolver = await this.apResolverService.createResolver(); const object = await resolver.resolve(value); diff --git a/packages/backend/src/core/activitypub/models/ApPersonService.ts b/packages/backend/src/core/activitypub/models/ApPersonService.ts index e52078ed0f..ebe8e9c964 100644 --- a/packages/backend/src/core/activitypub/models/ApPersonService.ts +++ b/packages/backend/src/core/activitypub/models/ApPersonService.ts @@ -310,7 +310,7 @@ export class ApPersonService implements OnModuleInit { } // eslint-disable-next-line no-param-reassign - if (resolver == null) resolver = this.apResolverService.createResolver(); + if (resolver == null) resolver = await this.apResolverService.createResolver(); const object = await resolver.resolve(uri); if (object.id == null) throw new Error('invalid object.id: ' + object.id); @@ -500,7 +500,7 @@ export class ApPersonService implements OnModuleInit { //#endregion // eslint-disable-next-line no-param-reassign - if (resolver == null) resolver = this.apResolverService.createResolver(); + if (resolver == null) resolver = await this.apResolverService.createResolver(); const object = hint ?? await resolver.resolve(uri); @@ -678,7 +678,7 @@ export class ApPersonService implements OnModuleInit { // リモートサーバーからフェッチしてきて登録 // eslint-disable-next-line no-param-reassign - if (resolver == null) resolver = this.apResolverService.createResolver(); + if (resolver == null) resolver = await this.apResolverService.createResolver(); return await this.createPerson(uri, resolver); } @@ -707,7 +707,7 @@ export class ApPersonService implements OnModuleInit { this.logger.info(`Updating the featured: ${user.uri}`); - const _resolver = resolver ?? this.apResolverService.createResolver(); + const _resolver = resolver ?? await this.apResolverService.createResolver(); // Resolve to (Ordered)Collection Object const collection = await _resolver.resolveCollection(user.featured); diff --git a/packages/backend/src/core/activitypub/models/ApQuestionService.ts b/packages/backend/src/core/activitypub/models/ApQuestionService.ts index a2cdaf02ca..8ac2f21e26 100644 --- a/packages/backend/src/core/activitypub/models/ApQuestionService.ts +++ b/packages/backend/src/core/activitypub/models/ApQuestionService.ts @@ -45,7 +45,7 @@ export class ApQuestionService { @bindThis public async extractPollFromQuestion(source: string | IObject, resolver?: Resolver): Promise { // eslint-disable-next-line no-param-reassign - if (resolver == null) resolver = this.apResolverService.createResolver(); + if (resolver == null) resolver = await this.apResolverService.createResolver(); const question = await resolver.resolve(source); if (!isQuestion(question)) throw new Error('invalid type'); @@ -91,7 +91,7 @@ export class ApQuestionService { // resolve new Question object // eslint-disable-next-line no-param-reassign - if (resolver == null) resolver = this.apResolverService.createResolver(); + if (resolver == null) resolver = await this.apResolverService.createResolver(); const question = await resolver.resolve(value); this.logger.debug(`fetched question: ${JSON.stringify(question, null, 2)}`); diff --git a/packages/backend/src/server/ServerModule.ts b/packages/backend/src/server/ServerModule.ts index 111421472d..8259a2a9e4 100644 --- a/packages/backend/src/server/ServerModule.ts +++ b/packages/backend/src/server/ServerModule.ts @@ -13,7 +13,6 @@ import { NodeinfoServerService } from './NodeinfoServerService.js'; import { ServerService } from './ServerService.js'; import { WellKnownServerService } from './WellKnownServerService.js'; import { GetterService } from './api/GetterService.js'; -import { ChannelsService } from './api/stream/ChannelsService.js'; import { ActivityPubServerService } from './ActivityPubServerService.js'; import { ApiLoggerService } from './api/ApiLoggerService.js'; import { ApiServerService } from './api/ApiServerService.js'; @@ -31,24 +30,25 @@ import { UrlPreviewService } from './web/UrlPreviewService.js'; import { ClientLoggerService } from './web/ClientLoggerService.js'; import { OAuth2ProviderService } from './oauth/OAuth2ProviderService.js'; -import { MainChannelService } from './api/stream/channels/main.js'; -import { AdminChannelService } from './api/stream/channels/admin.js'; -import { AntennaChannelService } from './api/stream/channels/antenna.js'; -import { ChannelChannelService } from './api/stream/channels/channel.js'; -import { DriveChannelService } from './api/stream/channels/drive.js'; -import { GlobalTimelineChannelService } from './api/stream/channels/global-timeline.js'; -import { HashtagChannelService } from './api/stream/channels/hashtag.js'; -import { HomeTimelineChannelService } from './api/stream/channels/home-timeline.js'; -import { HybridTimelineChannelService } from './api/stream/channels/hybrid-timeline.js'; -import { LocalTimelineChannelService } from './api/stream/channels/local-timeline.js'; -import { QueueStatsChannelService } from './api/stream/channels/queue-stats.js'; -import { ServerStatsChannelService } from './api/stream/channels/server-stats.js'; -import { UserListChannelService } from './api/stream/channels/user-list.js'; -import { RoleTimelineChannelService } from './api/stream/channels/role-timeline.js'; -import { ChatUserChannelService } from './api/stream/channels/chat-user.js'; -import { ChatRoomChannelService } from './api/stream/channels/chat-room.js'; -import { ReversiChannelService } from './api/stream/channels/reversi.js'; -import { ReversiGameChannelService } from './api/stream/channels/reversi-game.js'; +import MainStreamConnection from '@/server/api/stream/Connection.js'; +import { MainChannel } from './api/stream/channels/main.js'; +import { AdminChannel } from './api/stream/channels/admin.js'; +import { AntennaChannel } from './api/stream/channels/antenna.js'; +import { ChannelChannel } from './api/stream/channels/channel.js'; +import { DriveChannel } from './api/stream/channels/drive.js'; +import { GlobalTimelineChannel } from './api/stream/channels/global-timeline.js'; +import { HashtagChannel } from './api/stream/channels/hashtag.js'; +import { HomeTimelineChannel } from './api/stream/channels/home-timeline.js'; +import { HybridTimelineChannel } from './api/stream/channels/hybrid-timeline.js'; +import { LocalTimelineChannel } from './api/stream/channels/local-timeline.js'; +import { QueueStatsChannel } from './api/stream/channels/queue-stats.js'; +import { ServerStatsChannel } from './api/stream/channels/server-stats.js'; +import { UserListChannel } from './api/stream/channels/user-list.js'; +import { RoleTimelineChannel } from './api/stream/channels/role-timeline.js'; +import { ChatUserChannel } from './api/stream/channels/chat-user.js'; +import { ChatRoomChannel } from './api/stream/channels/chat-room.js'; +import { ReversiChannel } from './api/stream/channels/reversi.js'; +import { ReversiGameChannel } from './api/stream/channels/reversi-game.js'; import { SigninWithPasskeyApiService } from './api/SigninWithPasskeyApiService.js'; @Module({ @@ -69,7 +69,7 @@ import { SigninWithPasskeyApiService } from './api/SigninWithPasskeyApiService.j ServerService, WellKnownServerService, GetterService, - ChannelsService, + MainStreamConnection, ApiCallService, ApiLoggerService, ApiServerService, @@ -80,24 +80,24 @@ import { SigninWithPasskeyApiService } from './api/SigninWithPasskeyApiService.j SigninService, SignupApiService, StreamingApiServerService, - MainChannelService, - AdminChannelService, - AntennaChannelService, - ChannelChannelService, - DriveChannelService, - GlobalTimelineChannelService, - HashtagChannelService, - RoleTimelineChannelService, - ChatUserChannelService, - ChatRoomChannelService, - ReversiChannelService, - ReversiGameChannelService, - HomeTimelineChannelService, - HybridTimelineChannelService, - LocalTimelineChannelService, - QueueStatsChannelService, - ServerStatsChannelService, - UserListChannelService, + MainChannel, + AdminChannel, + AntennaChannel, + ChannelChannel, + DriveChannel, + GlobalTimelineChannel, + HashtagChannel, + RoleTimelineChannel, + ChatUserChannel, + ChatRoomChannel, + ReversiChannel, + ReversiGameChannel, + HomeTimelineChannel, + HybridTimelineChannel, + LocalTimelineChannel, + QueueStatsChannel, + ServerStatsChannel, + UserListChannel, OpenApiServerService, OAuth2ProviderService, ], diff --git a/packages/backend/src/server/api/StreamingApiServerService.ts b/packages/backend/src/server/api/StreamingApiServerService.ts index 21f2f0b7e2..359c361ed4 100644 --- a/packages/backend/src/server/api/StreamingApiServerService.ts +++ b/packages/backend/src/server/api/StreamingApiServerService.ts @@ -8,18 +8,14 @@ import { Inject, Injectable } from '@nestjs/common'; import * as Redis from 'ioredis'; import * as WebSocket from 'ws'; import { DI } from '@/di-symbols.js'; -import type { UsersRepository, MiAccessToken } from '@/models/_.js'; -import { NotificationService } from '@/core/NotificationService.js'; +import type { MiAccessToken } from '@/models/_.js'; import { bindThis } from '@/decorators.js'; -import { CacheService } from '@/core/CacheService.js'; import { MiLocalUser } from '@/models/User.js'; import { UserService } from '@/core/UserService.js'; -import { ChannelFollowingService } from '@/core/ChannelFollowingService.js'; -import { ChannelMutingService } from '@/core/ChannelMutingService.js'; import { AuthenticateService, AuthenticationError } from './AuthenticateService.js'; -import MainStreamConnection from './stream/Connection.js'; -import { ChannelsService } from './stream/ChannelsService.js'; +import MainStreamConnection, { ConnectionRequest } from './stream/Connection.js'; import type * as http from 'node:http'; +import { ContextIdFactory, ModuleRef } from '@nestjs/core'; @Injectable() export class StreamingApiServerService { @@ -31,16 +27,9 @@ export class StreamingApiServerService { @Inject(DI.redisForSub) private redisForSub: Redis.Redis, - @Inject(DI.usersRepository) - private usersRepository: UsersRepository, - - private cacheService: CacheService, + private moduleRef: ModuleRef, private authenticateService: AuthenticateService, - private channelsService: ChannelsService, - private notificationService: NotificationService, private usersService: UserService, - private channelFollowingService: ChannelFollowingService, - private channelMutingService: ChannelMutingService, ) { } @@ -94,14 +83,12 @@ export class StreamingApiServerService { return; } - const stream = new MainStreamConnection( - this.channelsService, - this.notificationService, - this.cacheService, - this.channelFollowingService, - this.channelMutingService, - user, app, - ); + const contextId = ContextIdFactory.create(); + this.moduleRef.registerRequestByContextId({ + user, + token: app, + }, contextId); + const stream = await this.moduleRef.create(MainStreamConnection, contextId); await stream.init(); diff --git a/packages/backend/src/server/api/endpoints/ap/get.ts b/packages/backend/src/server/api/endpoints/ap/get.ts index 14286bc23e..ff03fce72b 100644 --- a/packages/backend/src/server/api/endpoints/ap/get.ts +++ b/packages/backend/src/server/api/endpoints/ap/get.ts @@ -43,7 +43,7 @@ export default class extends Endpoint { // eslint- private apResolverService: ApResolverService, ) { super(meta, paramDef, async (ps, me) => { - const resolver = this.apResolverService.createResolver(); + const resolver = await this.apResolverService.createResolver(); const object = await resolver.resolve(ps.uri); return object; }); diff --git a/packages/backend/src/server/api/endpoints/ap/show.ts b/packages/backend/src/server/api/endpoints/ap/show.ts index fe48e7497a..140b054fcc 100644 --- a/packages/backend/src/server/api/endpoints/ap/show.ts +++ b/packages/backend/src/server/api/endpoints/ap/show.ts @@ -148,7 +148,7 @@ export default class extends Endpoint { // eslint- if (this.utilityService.isSelfHost(host)) return null; // リモートから一旦オブジェクトフェッチ - const resolver = this.apResolverService.createResolver(); + const resolver = await this.apResolverService.createResolver(); // allow ap/show exclusively to lookup URLs that are cross-origin or non-canonical (like https://alice.example.com/@bob@bob.example.com -> https://bob.example.com/@bob) const object = await resolver.resolve(uri, FetchAllowSoftFailMask.CrossOrigin | FetchAllowSoftFailMask.NonCanonicalId).catch((err) => { if (err instanceof IdentifiableError) { diff --git a/packages/backend/src/server/api/stream/ChannelsService.ts b/packages/backend/src/server/api/stream/ChannelsService.ts index c0ef589dea..63ad9281b2 100644 --- a/packages/backend/src/server/api/stream/ChannelsService.ts +++ b/packages/backend/src/server/api/stream/ChannelsService.ts @@ -4,72 +4,54 @@ */ import { Injectable } from '@nestjs/common'; +import { HybridTimelineChannel } from './channels/hybrid-timeline.js'; +import { LocalTimelineChannel } from './channels/local-timeline.js'; +import { HomeTimelineChannel } from './channels/home-timeline.js'; +import { GlobalTimelineChannel } from './channels/global-timeline.js'; +import { MainChannel } from './channels/main.js'; +import { ChannelChannel } from './channels/channel.js'; +import { AdminChannel } from './channels/admin.js'; +import { ServerStatsChannel } from './channels/server-stats.js'; +import { QueueStatsChannel } from './channels/queue-stats.js'; +import { UserListChannel } from './channels/user-list.js'; +import { AntennaChannel } from './channels/antenna.js'; +import { DriveChannel } from './channels/drive.js'; +import { HashtagChannel } from './channels/hashtag.js'; +import { RoleTimelineChannel } from './channels/role-timeline.js'; +import { ChatUserChannel } from './channels/chat-user.js'; +import { ChatRoomChannel } from './channels/chat-room.js'; +import { ReversiChannel } from './channels/reversi.js'; +import { ReversiGameChannel } from './channels/reversi-game.js'; +import type { ChannelConstructor } from './channel.js'; import { bindThis } from '@/decorators.js'; -import { HybridTimelineChannelService } from './channels/hybrid-timeline.js'; -import { LocalTimelineChannelService } from './channels/local-timeline.js'; -import { HomeTimelineChannelService } from './channels/home-timeline.js'; -import { GlobalTimelineChannelService } from './channels/global-timeline.js'; -import { MainChannelService } from './channels/main.js'; -import { ChannelChannelService } from './channels/channel.js'; -import { AdminChannelService } from './channels/admin.js'; -import { ServerStatsChannelService } from './channels/server-stats.js'; -import { QueueStatsChannelService } from './channels/queue-stats.js'; -import { UserListChannelService } from './channels/user-list.js'; -import { AntennaChannelService } from './channels/antenna.js'; -import { DriveChannelService } from './channels/drive.js'; -import { HashtagChannelService } from './channels/hashtag.js'; -import { RoleTimelineChannelService } from './channels/role-timeline.js'; -import { ChatUserChannelService } from './channels/chat-user.js'; -import { ChatRoomChannelService } from './channels/chat-room.js'; -import { ReversiChannelService } from './channels/reversi.js'; -import { ReversiGameChannelService } from './channels/reversi-game.js'; -import { type MiChannelService } from './channel.js'; @Injectable() export class ChannelsService { constructor( - private mainChannelService: MainChannelService, - private homeTimelineChannelService: HomeTimelineChannelService, - private localTimelineChannelService: LocalTimelineChannelService, - private hybridTimelineChannelService: HybridTimelineChannelService, - private globalTimelineChannelService: GlobalTimelineChannelService, - private userListChannelService: UserListChannelService, - private hashtagChannelService: HashtagChannelService, - private roleTimelineChannelService: RoleTimelineChannelService, - private antennaChannelService: AntennaChannelService, - private channelChannelService: ChannelChannelService, - private driveChannelService: DriveChannelService, - private serverStatsChannelService: ServerStatsChannelService, - private queueStatsChannelService: QueueStatsChannelService, - private adminChannelService: AdminChannelService, - private chatUserChannelService: ChatUserChannelService, - private chatRoomChannelService: ChatRoomChannelService, - private reversiChannelService: ReversiChannelService, - private reversiGameChannelService: ReversiGameChannelService, ) { } @bindThis - public getChannelService(name: string): MiChannelService { + public getChannelConstructor(name: string): ChannelConstructor { switch (name) { - case 'main': return this.mainChannelService; - case 'homeTimeline': return this.homeTimelineChannelService; - case 'localTimeline': return this.localTimelineChannelService; - case 'hybridTimeline': return this.hybridTimelineChannelService; - case 'globalTimeline': return this.globalTimelineChannelService; - case 'userList': return this.userListChannelService; - case 'hashtag': return this.hashtagChannelService; - case 'roleTimeline': return this.roleTimelineChannelService; - case 'antenna': return this.antennaChannelService; - case 'channel': return this.channelChannelService; - case 'drive': return this.driveChannelService; - case 'serverStats': return this.serverStatsChannelService; - case 'queueStats': return this.queueStatsChannelService; - case 'admin': return this.adminChannelService; - case 'chatUser': return this.chatUserChannelService; - case 'chatRoom': return this.chatRoomChannelService; - case 'reversi': return this.reversiChannelService; - case 'reversiGame': return this.reversiGameChannelService; + case 'main': return MainChannel; + case 'homeTimeline': return HomeTimelineChannel; + case 'localTimeline': return LocalTimelineChannel; + case 'hybridTimeline': return HybridTimelineChannel; + case 'globalTimeline': return GlobalTimelineChannel; + case 'userList': return UserListChannel; + case 'hashtag': return HashtagChannel; + case 'roleTimeline': return RoleTimelineChannel; + case 'antenna': return AntennaChannel; + case 'channel': return ChannelChannel; + case 'drive': return DriveChannel; + case 'serverStats': return ServerStatsChannel; + case 'queueStats': return QueueStatsChannel; + case 'admin': return AdminChannel; + case 'chatUser': return ChatUserChannel; + case 'chatRoom': return ChatRoomChannel; + case 'reversi': return ReversiChannel; + case 'reversiGame': return ReversiGameChannel; default: throw new Error(`no such channel: ${name}`); diff --git a/packages/backend/src/server/api/stream/Connection.ts b/packages/backend/src/server/api/stream/Connection.ts index 222086c960..d19a223a21 100644 --- a/packages/backend/src/server/api/stream/Connection.ts +++ b/packages/backend/src/server/api/stream/Connection.ts @@ -6,19 +6,39 @@ import * as WebSocket from 'ws'; import type { MiUser } from '@/models/User.js'; import type { MiAccessToken } from '@/models/AccessToken.js'; -import type { Packed } from '@/misc/json-schema.js'; -import type { NotificationService } from '@/core/NotificationService.js'; +import { NotificationService } from '@/core/NotificationService.js'; import { bindThis } from '@/decorators.js'; import { CacheService } from '@/core/CacheService.js'; import { MiFollowing, MiUserProfile } from '@/models/_.js'; import type { GlobalEvents, StreamEventEmitter } from '@/core/GlobalEventService.js'; import { ChannelFollowingService } from '@/core/ChannelFollowingService.js'; import { ChannelMutingService } from '@/core/ChannelMutingService.js'; -import { isJsonObject } from '@/misc/json-value.js'; import type { JsonObject, JsonValue } from '@/misc/json-value.js'; -import type { ChannelsService } from './ChannelsService.js'; +import { isJsonObject } from '@/misc/json-value.js'; import type { EventEmitter } from 'events'; import type Channel from './channel.js'; +import type { ChannelConstructor } from './channel.js'; +import type { ChannelRequest } from './channel.js'; +import { ContextIdFactory, ModuleRef, REQUEST } from '@nestjs/core'; +import { Inject, Injectable, Scope } from '@nestjs/common'; +import { MainChannel } from '@/server/api/stream/channels/main.js'; +import { HomeTimelineChannel } from '@/server/api/stream/channels/home-timeline.js'; +import { LocalTimelineChannel } from '@/server/api/stream/channels/local-timeline.js'; +import { HybridTimelineChannel } from '@/server/api/stream/channels/hybrid-timeline.js'; +import { GlobalTimelineChannel } from '@/server/api/stream/channels/global-timeline.js'; +import { UserListChannel } from '@/server/api/stream/channels/user-list.js'; +import { HashtagChannel } from '@/server/api/stream/channels/hashtag.js'; +import { RoleTimelineChannel } from '@/server/api/stream/channels/role-timeline.js'; +import { AntennaChannel } from '@/server/api/stream/channels/antenna.js'; +import { ChannelChannel } from '@/server/api/stream/channels/channel.js'; +import { DriveChannel } from '@/server/api/stream/channels/drive.js'; +import { ServerStatsChannel } from '@/server/api/stream/channels/server-stats.js'; +import { QueueStatsChannel } from '@/server/api/stream/channels/queue-stats.js'; +import { AdminChannel } from '@/server/api/stream/channels/admin.js'; +import { ChatUserChannel } from '@/server/api/stream/channels/chat-user.js'; +import { ChatRoomChannel } from '@/server/api/stream/channels/chat-room.js'; +import { ReversiChannel } from '@/server/api/stream/channels/reversi.js'; +import { ReversiGameChannel } from '@/server/api/stream/channels/reversi-game.js'; const MAX_CHANNELS_PER_CONNECTION = 32; @@ -26,6 +46,7 @@ const MAX_CHANNELS_PER_CONNECTION = 32; * Main stream connection */ // eslint-disable-next-line import/no-default-export +@Injectable({ scope: Scope.TRANSIENT }) export default class Connection { public user?: MiUser; public token?: MiAccessToken; @@ -44,16 +65,16 @@ export default class Connection { private fetchIntervalId: NodeJS.Timeout | null = null; constructor( - private channelsService: ChannelsService, + private moduleRef: ModuleRef, private notificationService: NotificationService, private cacheService: CacheService, private channelFollowingService: ChannelFollowingService, private channelMutingService: ChannelMutingService, - user: MiUser | null | undefined, - token: MiAccessToken | null | undefined, + @Inject(REQUEST) + request: ConnectionRequest, ) { - if (user) this.user = user; - if (token) this.token = token; + if (request.user) this.user = request.user; + if (request.token) this.token = request.token; } @bindThis @@ -232,28 +253,34 @@ export default class Connection { * チャンネルに接続 */ @bindThis - public connectChannel(id: string, params: JsonObject | undefined, channel: string, pong = false) { + public async connectChannel(id: string, params: JsonObject | undefined, channel: string, pong = false) { if (this.channels.length >= MAX_CHANNELS_PER_CONNECTION) { return; } - const channelService = this.channelsService.getChannelService(channel); + const channelConstructor = this.getChannelConstructor(channel); - if (channelService.requireCredential && this.user == null) { + if (channelConstructor.requireCredential && this.user == null) { return; } - if (this.token && ((channelService.kind && !this.token.permission.some(p => p === channelService.kind)) - || (!channelService.kind && channelService.requireCredential))) { + if (this.token && ((channelConstructor.kind && !this.token.permission.some(p => p === channelConstructor.kind)) + || (!channelConstructor.kind && channelConstructor.requireCredential))) { return; } // 共有可能チャンネルに接続しようとしていて、かつそのチャンネルに既に接続していたら無意味なので無視 - if (channelService.shouldShare && this.channels.some(c => c.chName === channel)) { + if (channelConstructor.shouldShare && this.channels.some(c => c.chName === channel)) { return; } - const ch: Channel = channelService.create(id, this); + const contextId = ContextIdFactory.create(); + this.moduleRef.registerRequestByContextId({ + id: id, + connection: this, + }, contextId); + const ch: Channel = await this.moduleRef.create(channelConstructor, contextId); + this.channels.push(ch); ch.init(params ?? {}); @@ -264,6 +291,33 @@ export default class Connection { } } + @bindThis + public getChannelConstructor(name: string): ChannelConstructor { + switch (name) { + case 'main': return MainChannel; + case 'homeTimeline': return HomeTimelineChannel; + case 'localTimeline': return LocalTimelineChannel; + case 'hybridTimeline': return HybridTimelineChannel; + case 'globalTimeline': return GlobalTimelineChannel; + case 'userList': return UserListChannel; + case 'hashtag': return HashtagChannel; + case 'roleTimeline': return RoleTimelineChannel; + case 'antenna': return AntennaChannel; + case 'channel': return ChannelChannel; + case 'drive': return DriveChannel; + case 'serverStats': return ServerStatsChannel; + case 'queueStats': return QueueStatsChannel; + case 'admin': return AdminChannel; + case 'chatUser': return ChatUserChannel; + case 'chatRoom': return ChatRoomChannel; + case 'reversi': return ReversiChannel; + case 'reversiGame': return ReversiGameChannel; + + default: + throw new Error(`no such channel: ${name}`); + } + } + /** * チャンネルから切断 * @param id チャンネルコネクションID @@ -306,3 +360,8 @@ export default class Connection { } } } + +export interface ConnectionRequest { + user: MiUser | null | undefined, + token: MiAccessToken | null | undefined, +} diff --git a/packages/backend/src/server/api/stream/channel.ts b/packages/backend/src/server/api/stream/channel.ts index 465ed4238c..86b073414d 100644 --- a/packages/backend/src/server/api/stream/channel.ts +++ b/packages/backend/src/server/api/stream/channel.ts @@ -22,7 +22,7 @@ export default abstract class Channel { public abstract readonly chName: string; public static readonly shouldShare: boolean; public static readonly requireCredential: boolean; - public static readonly kind?: string | null; + public static readonly kind: string | null; protected get user() { return this.connection.user; @@ -85,9 +85,9 @@ export default abstract class Channel { return false; } - constructor(id: string, connection: Connection) { - this.id = id; - this.connection = connection; + constructor(request: ChannelRequest) { + this.id = request.id; + this.connection = request.connection; } public send(payload: { type: string, body: JsonValue }): void; @@ -111,9 +111,14 @@ export default abstract class Channel { public onMessage?(type: string, body: JsonValue): void; } -export type MiChannelService = { +export interface ChannelRequest { + id: string, + connection: Connection, +} + +export interface ChannelConstructor { + new(...args: any[]): Channel; shouldShare: boolean; requireCredential: T; kind: T extends true ? string : string | null | undefined; - create: (id: string, connection: Connection) => Channel; -}; +} diff --git a/packages/backend/src/server/api/stream/channels/admin.ts b/packages/backend/src/server/api/stream/channels/admin.ts index 355d5dba21..821888cca0 100644 --- a/packages/backend/src/server/api/stream/channels/admin.ts +++ b/packages/backend/src/server/api/stream/channels/admin.ts @@ -3,17 +3,26 @@ * SPDX-License-Identifier: AGPL-3.0-only */ -import { Injectable } from '@nestjs/common'; +import { Inject, Injectable, Scope } from '@nestjs/common'; import { bindThis } from '@/decorators.js'; import type { JsonObject } from '@/misc/json-value.js'; -import Channel, { type MiChannelService } from '../channel.js'; +import Channel, { type ChannelRequest } from '../channel.js'; +import { REQUEST } from '@nestjs/core'; -class AdminChannel extends Channel { +@Injectable({ scope: Scope.TRANSIENT }) +export class AdminChannel extends Channel { public readonly chName = 'admin'; public static shouldShare = true; public static requireCredential = true as const; public static kind = 'read:admin:stream'; + constructor( + @Inject(REQUEST) + request: ChannelRequest, + ) { + super(request); + } + @bindThis public async init(params: JsonObject) { // Subscribe admin stream @@ -22,22 +31,3 @@ class AdminChannel extends Channel { }); } } - -@Injectable() -export class AdminChannelService implements MiChannelService { - public readonly shouldShare = AdminChannel.shouldShare; - public readonly requireCredential = AdminChannel.requireCredential; - public readonly kind = AdminChannel.kind; - - constructor( - ) { - } - - @bindThis - public create(id: string, connection: Channel['connection']): AdminChannel { - return new AdminChannel( - id, - connection, - ); - } -} diff --git a/packages/backend/src/server/api/stream/channels/antenna.ts b/packages/backend/src/server/api/stream/channels/antenna.ts index e08562fdf9..ece9d2c8b1 100644 --- a/packages/backend/src/server/api/stream/channels/antenna.ts +++ b/packages/backend/src/server/api/stream/channels/antenna.ts @@ -3,14 +3,16 @@ * SPDX-License-Identifier: AGPL-3.0-only */ -import { Injectable } from '@nestjs/common'; +import { Inject, Injectable, Scope } from '@nestjs/common'; import { NoteEntityService } from '@/core/entities/NoteEntityService.js'; import { bindThis } from '@/decorators.js'; import type { GlobalEvents } from '@/core/GlobalEventService.js'; import type { JsonObject } from '@/misc/json-value.js'; -import Channel, { type MiChannelService } from '../channel.js'; +import Channel, { type ChannelRequest } from '../channel.js'; +import { REQUEST } from '@nestjs/core'; -class AntennaChannel extends Channel { +@Injectable({ scope: Scope.TRANSIENT }) +export class AntennaChannel extends Channel { public readonly chName = 'antenna'; public static shouldShare = false; public static requireCredential = true as const; @@ -18,12 +20,12 @@ class AntennaChannel extends Channel { private antennaId: string; constructor( - private noteEntityService: NoteEntityService, + @Inject(REQUEST) + request: ChannelRequest, - id: string, - connection: Channel['connection'], + private noteEntityService: NoteEntityService, ) { - super(id, connection); + super(request); //this.onEvent = this.onEvent.bind(this); } @@ -55,24 +57,3 @@ class AntennaChannel extends Channel { this.subscriber.off(`antennaStream:${this.antennaId}`, this.onEvent); } } - -@Injectable() -export class AntennaChannelService implements MiChannelService { - public readonly shouldShare = AntennaChannel.shouldShare; - public readonly requireCredential = AntennaChannel.requireCredential; - public readonly kind = AntennaChannel.kind; - - constructor( - private noteEntityService: NoteEntityService, - ) { - } - - @bindThis - public create(id: string, connection: Channel['connection']): AntennaChannel { - return new AntennaChannel( - this.noteEntityService, - id, - connection, - ); - } -} diff --git a/packages/backend/src/server/api/stream/channels/channel.ts b/packages/backend/src/server/api/stream/channels/channel.ts index c07eaac98d..1706b17526 100644 --- a/packages/backend/src/server/api/stream/channels/channel.ts +++ b/packages/backend/src/server/api/stream/channels/channel.ts @@ -3,7 +3,7 @@ * SPDX-License-Identifier: AGPL-3.0-only */ -import { Injectable } from '@nestjs/common'; +import { Inject, Injectable, Scope } from '@nestjs/common'; import type { Packed } from '@/misc/json-schema.js'; import { NoteEntityService } from '@/core/entities/NoteEntityService.js'; import { bindThis } from '@/decorators.js'; @@ -11,20 +11,23 @@ import { isRenotePacked, isQuotePacked } from '@/misc/is-renote.js'; import { isInstanceMuted } from '@/misc/is-instance-muted.js'; import { isUserRelated } from '@/misc/is-user-related.js'; import type { JsonObject } from '@/misc/json-value.js'; -import Channel, { type MiChannelService } from '../channel.js'; +import Channel, { type ChannelRequest } from '../channel.js'; +import { REQUEST } from '@nestjs/core'; -class ChannelChannel extends Channel { +@Injectable({ scope: Scope.TRANSIENT }) +export class ChannelChannel extends Channel { public readonly chName = 'channel'; public static shouldShare = false; public static requireCredential = false as const; private channelId: string; constructor( + @Inject(REQUEST) + request: ChannelRequest, + private noteEntityService: NoteEntityService, - id: string, - connection: Channel['connection'], ) { - super(id, connection); + super(request); //this.onNote = this.onNote.bind(this); } @@ -92,24 +95,3 @@ class ChannelChannel extends Channel { this.subscriber.off('notesStream', this.onNote); } } - -@Injectable() -export class ChannelChannelService implements MiChannelService { - public readonly shouldShare = ChannelChannel.shouldShare; - public readonly requireCredential = ChannelChannel.requireCredential; - public readonly kind = ChannelChannel.kind; - - constructor( - private noteEntityService: NoteEntityService, - ) { - } - - @bindThis - public create(id: string, connection: Channel['connection']): ChannelChannel { - return new ChannelChannel( - this.noteEntityService, - id, - connection, - ); - } -} diff --git a/packages/backend/src/server/api/stream/channels/chat-room.ts b/packages/backend/src/server/api/stream/channels/chat-room.ts index eda333dd30..7f949032e2 100644 --- a/packages/backend/src/server/api/stream/channels/chat-room.ts +++ b/packages/backend/src/server/api/stream/channels/chat-room.ts @@ -3,14 +3,16 @@ * SPDX-License-Identifier: AGPL-3.0-only */ -import { Injectable } from '@nestjs/common'; +import { Inject, Injectable, Scope } from '@nestjs/common'; import { bindThis } from '@/decorators.js'; import type { GlobalEvents } from '@/core/GlobalEventService.js'; import type { JsonObject } from '@/misc/json-value.js'; import { ChatService } from '@/core/ChatService.js'; -import Channel, { type MiChannelService } from '../channel.js'; +import Channel, { type ChannelRequest } from '../channel.js'; +import { REQUEST } from '@nestjs/core'; -class ChatRoomChannel extends Channel { +@Injectable({ scope: Scope.TRANSIENT }) +export class ChatRoomChannel extends Channel { public readonly chName = 'chatRoom'; public static shouldShare = false; public static requireCredential = true as const; @@ -18,12 +20,12 @@ class ChatRoomChannel extends Channel { private roomId: string; constructor( - private chatService: ChatService, + @Inject(REQUEST) + request: ChannelRequest, - id: string, - connection: Channel['connection'], + private chatService: ChatService, ) { - super(id, connection); + super(request); } @bindThis @@ -55,24 +57,3 @@ class ChatRoomChannel extends Channel { this.subscriber.off(`chatRoomStream:${this.roomId}`, this.onEvent); } } - -@Injectable() -export class ChatRoomChannelService implements MiChannelService { - public readonly shouldShare = ChatRoomChannel.shouldShare; - public readonly requireCredential = ChatRoomChannel.requireCredential; - public readonly kind = ChatRoomChannel.kind; - - constructor( - private chatService: ChatService, - ) { - } - - @bindThis - public create(id: string, connection: Channel['connection']): ChatRoomChannel { - return new ChatRoomChannel( - this.chatService, - id, - connection, - ); - } -} diff --git a/packages/backend/src/server/api/stream/channels/chat-user.ts b/packages/backend/src/server/api/stream/channels/chat-user.ts index 5323484ed7..36f3f67b28 100644 --- a/packages/backend/src/server/api/stream/channels/chat-user.ts +++ b/packages/backend/src/server/api/stream/channels/chat-user.ts @@ -3,14 +3,16 @@ * SPDX-License-Identifier: AGPL-3.0-only */ -import { Injectable } from '@nestjs/common'; +import { Inject, Injectable, Scope } from '@nestjs/common'; import { bindThis } from '@/decorators.js'; import type { GlobalEvents } from '@/core/GlobalEventService.js'; import type { JsonObject } from '@/misc/json-value.js'; import { ChatService } from '@/core/ChatService.js'; -import Channel, { type MiChannelService } from '../channel.js'; +import Channel, { type ChannelRequest } from '../channel.js'; +import { REQUEST } from '@nestjs/core'; -class ChatUserChannel extends Channel { +@Injectable({ scope: Scope.TRANSIENT }) +export class ChatUserChannel extends Channel { public readonly chName = 'chatUser'; public static shouldShare = false; public static requireCredential = true as const; @@ -18,12 +20,12 @@ class ChatUserChannel extends Channel { private otherId: string; constructor( - private chatService: ChatService, + @Inject(REQUEST) + request: ChannelRequest, - id: string, - connection: Channel['connection'], + private chatService: ChatService, ) { - super(id, connection); + super(request); } @bindThis @@ -55,24 +57,3 @@ class ChatUserChannel extends Channel { this.subscriber.off(`chatUserStream:${this.user!.id}-${this.otherId}`, this.onEvent); } } - -@Injectable() -export class ChatUserChannelService implements MiChannelService { - public readonly shouldShare = ChatUserChannel.shouldShare; - public readonly requireCredential = ChatUserChannel.requireCredential; - public readonly kind = ChatUserChannel.kind; - - constructor( - private chatService: ChatService, - ) { - } - - @bindThis - public create(id: string, connection: Channel['connection']): ChatUserChannel { - return new ChatUserChannel( - this.chatService, - id, - connection, - ); - } -} diff --git a/packages/backend/src/server/api/stream/channels/drive.ts b/packages/backend/src/server/api/stream/channels/drive.ts index 03768f3d23..6f2eb2c8f9 100644 --- a/packages/backend/src/server/api/stream/channels/drive.ts +++ b/packages/backend/src/server/api/stream/channels/drive.ts @@ -3,17 +3,26 @@ * SPDX-License-Identifier: AGPL-3.0-only */ -import { Injectable } from '@nestjs/common'; +import { Inject, Injectable, Scope } from '@nestjs/common'; import { bindThis } from '@/decorators.js'; import type { JsonObject } from '@/misc/json-value.js'; -import Channel, { type MiChannelService } from '../channel.js'; +import Channel, { type ChannelRequest } from '../channel.js'; +import { REQUEST } from '@nestjs/core'; -class DriveChannel extends Channel { +@Injectable({ scope: Scope.TRANSIENT }) +export class DriveChannel extends Channel { public readonly chName = 'drive'; public static shouldShare = true; public static requireCredential = true as const; public static kind = 'read:account'; + constructor( + @Inject(REQUEST) + request: ChannelRequest, + ) { + super(request); + } + @bindThis public async init(params: JsonObject) { // Subscribe drive stream @@ -22,22 +31,3 @@ class DriveChannel extends Channel { }); } } - -@Injectable() -export class DriveChannelService implements MiChannelService { - public readonly shouldShare = DriveChannel.shouldShare; - public readonly requireCredential = DriveChannel.requireCredential; - public readonly kind = DriveChannel.kind; - - constructor( - ) { - } - - @bindThis - public create(id: string, connection: Channel['connection']): DriveChannel { - return new DriveChannel( - id, - connection, - ); - } -} 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 d7c781ad12..be6be1b1e7 100644 --- a/packages/backend/src/server/api/stream/channels/global-timeline.ts +++ b/packages/backend/src/server/api/stream/channels/global-timeline.ts @@ -3,7 +3,7 @@ * SPDX-License-Identifier: AGPL-3.0-only */ -import { Injectable } from '@nestjs/common'; +import { Inject, Injectable, Scope } from '@nestjs/common'; import type { Packed } from '@/misc/json-schema.js'; import { MetaService } from '@/core/MetaService.js'; import { NoteEntityService } from '@/core/entities/NoteEntityService.js'; @@ -11,9 +11,11 @@ import { bindThis } from '@/decorators.js'; import { RoleService } from '@/core/RoleService.js'; import { isRenotePacked, isQuotePacked } from '@/misc/is-renote.js'; import type { JsonObject } from '@/misc/json-value.js'; -import Channel, { type MiChannelService } from '../channel.js'; +import Channel, { type ChannelRequest } from '../channel.js'; +import { REQUEST } from '@nestjs/core'; -class GlobalTimelineChannel extends Channel { +@Injectable({ scope: Scope.TRANSIENT }) +export class GlobalTimelineChannel extends Channel { public readonly chName = 'globalTimeline'; public static shouldShare = false; public static requireCredential = false as const; @@ -21,14 +23,14 @@ class GlobalTimelineChannel extends Channel { private withFiles: boolean; constructor( + @Inject(REQUEST) + request: ChannelRequest, + private metaService: MetaService, private roleService: RoleService, private noteEntityService: NoteEntityService, - - id: string, - connection: Channel['connection'], ) { - super(id, connection); + super(request); //this.onNote = this.onNote.bind(this); } @@ -74,28 +76,3 @@ class GlobalTimelineChannel extends Channel { this.subscriber.off('notesStream', this.onNote); } } - -@Injectable() -export class GlobalTimelineChannelService implements MiChannelService { - public readonly shouldShare = GlobalTimelineChannel.shouldShare; - public readonly requireCredential = GlobalTimelineChannel.requireCredential; - public readonly kind = GlobalTimelineChannel.kind; - - constructor( - private metaService: MetaService, - private roleService: RoleService, - private noteEntityService: NoteEntityService, - ) { - } - - @bindThis - public create(id: string, connection: Channel['connection']): GlobalTimelineChannel { - return new GlobalTimelineChannel( - this.metaService, - this.roleService, - this.noteEntityService, - id, - connection, - ); - } -} diff --git a/packages/backend/src/server/api/stream/channels/hashtag.ts b/packages/backend/src/server/api/stream/channels/hashtag.ts index c911d63642..1456b4f262 100644 --- a/packages/backend/src/server/api/stream/channels/hashtag.ts +++ b/packages/backend/src/server/api/stream/channels/hashtag.ts @@ -3,28 +3,30 @@ * SPDX-License-Identifier: AGPL-3.0-only */ -import { Injectable } from '@nestjs/common'; +import { Inject, Injectable, Scope } from '@nestjs/common'; import { normalizeForSearch } from '@/misc/normalize-for-search.js'; import type { Packed } from '@/misc/json-schema.js'; import { NoteEntityService } from '@/core/entities/NoteEntityService.js'; import { bindThis } from '@/decorators.js'; import { isRenotePacked, isQuotePacked } from '@/misc/is-renote.js'; import type { JsonObject } from '@/misc/json-value.js'; -import Channel, { type MiChannelService } from '../channel.js'; +import Channel, { type ChannelRequest } from '../channel.js'; +import { REQUEST } from '@nestjs/core'; -class HashtagChannel extends Channel { +@Injectable({ scope: Scope.TRANSIENT }) +export class HashtagChannel extends Channel { public readonly chName = 'hashtag'; public static shouldShare = false; public static requireCredential = false as const; private q: string[][]; constructor( - private noteEntityService: NoteEntityService, + @Inject(REQUEST) + request: ChannelRequest, - id: string, - connection: Channel['connection'], + private noteEntityService: NoteEntityService, ) { - super(id, connection); + super(request); //this.onNote = this.onNote.bind(this); } @@ -62,24 +64,3 @@ class HashtagChannel extends Channel { this.subscriber.off('notesStream', this.onNote); } } - -@Injectable() -export class HashtagChannelService implements MiChannelService { - public readonly shouldShare = HashtagChannel.shouldShare; - public readonly requireCredential = HashtagChannel.requireCredential; - public readonly kind = HashtagChannel.kind; - - constructor( - private noteEntityService: NoteEntityService, - ) { - } - - @bindThis - public create(id: string, connection: Channel['connection']): HashtagChannel { - return new HashtagChannel( - this.noteEntityService, - id, - connection, - ); - } -} 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 eb5b4a8c6c..665c11b692 100644 --- a/packages/backend/src/server/api/stream/channels/home-timeline.ts +++ b/packages/backend/src/server/api/stream/channels/home-timeline.ts @@ -3,15 +3,17 @@ * SPDX-License-Identifier: AGPL-3.0-only */ -import { Injectable } from '@nestjs/common'; +import { Inject, Injectable, Scope } from '@nestjs/common'; import type { Packed } from '@/misc/json-schema.js'; import { NoteEntityService } from '@/core/entities/NoteEntityService.js'; import { bindThis } from '@/decorators.js'; import { isRenotePacked, isQuotePacked } from '@/misc/is-renote.js'; import type { JsonObject } from '@/misc/json-value.js'; -import Channel, { type MiChannelService } from '../channel.js'; +import Channel, { type ChannelRequest } from '../channel.js'; +import { REQUEST } from '@nestjs/core'; -class HomeTimelineChannel extends Channel { +@Injectable({ scope: Scope.TRANSIENT }) +export class HomeTimelineChannel extends Channel { public readonly chName = 'homeTimeline'; public static shouldShare = false; public static requireCredential = true as const; @@ -20,12 +22,12 @@ class HomeTimelineChannel extends Channel { private withFiles: boolean; constructor( - private noteEntityService: NoteEntityService, + @Inject(REQUEST) + request: ChannelRequest, - id: string, - connection: Channel['connection'], + private noteEntityService: NoteEntityService, ) { - super(id, connection); + super(request); //this.onNote = this.onNote.bind(this); } @@ -98,24 +100,3 @@ class HomeTimelineChannel extends Channel { this.subscriber.off('notesStream', this.onNote); } } - -@Injectable() -export class HomeTimelineChannelService implements MiChannelService { - public readonly shouldShare = HomeTimelineChannel.shouldShare; - public readonly requireCredential = HomeTimelineChannel.requireCredential; - public readonly kind = HomeTimelineChannel.kind; - - constructor( - private noteEntityService: NoteEntityService, - ) { - } - - @bindThis - public create(id: string, connection: Channel['connection']): HomeTimelineChannel { - return new HomeTimelineChannel( - this.noteEntityService, - id, - connection, - ); - } -} 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 2155e02012..54250d2a90 100644 --- a/packages/backend/src/server/api/stream/channels/hybrid-timeline.ts +++ b/packages/backend/src/server/api/stream/channels/hybrid-timeline.ts @@ -3,7 +3,7 @@ * SPDX-License-Identifier: AGPL-3.0-only */ -import { Injectable } from '@nestjs/common'; +import { Inject, Injectable, Scope } from '@nestjs/common'; import type { Packed } from '@/misc/json-schema.js'; import { MetaService } from '@/core/MetaService.js'; import { NoteEntityService } from '@/core/entities/NoteEntityService.js'; @@ -11,9 +11,11 @@ import { bindThis } from '@/decorators.js'; import { RoleService } from '@/core/RoleService.js'; import { isRenotePacked, isQuotePacked } from '@/misc/is-renote.js'; import type { JsonObject } from '@/misc/json-value.js'; -import Channel, { type MiChannelService } from '../channel.js'; +import Channel, { type ChannelRequest } from '../channel.js'; +import { REQUEST } from '@nestjs/core'; -class HybridTimelineChannel extends Channel { +@Injectable({ scope: Scope.TRANSIENT }) +export class HybridTimelineChannel extends Channel { public readonly chName = 'hybridTimeline'; public static shouldShare = false; public static requireCredential = true as const; @@ -23,14 +25,14 @@ class HybridTimelineChannel extends Channel { private withFiles: boolean; constructor( + @Inject(REQUEST) + request: ChannelRequest, + private metaService: MetaService, private roleService: RoleService, private noteEntityService: NoteEntityService, - - id: string, - connection: Channel['connection'], ) { - super(id, connection); + super(request); //this.onNote = this.onNote.bind(this); } @@ -118,28 +120,3 @@ class HybridTimelineChannel extends Channel { this.subscriber.off('notesStream', this.onNote); } } - -@Injectable() -export class HybridTimelineChannelService implements MiChannelService { - public readonly shouldShare = HybridTimelineChannel.shouldShare; - public readonly requireCredential = HybridTimelineChannel.requireCredential; - public readonly kind = HybridTimelineChannel.kind; - - constructor( - private metaService: MetaService, - private roleService: RoleService, - private noteEntityService: NoteEntityService, - ) { - } - - @bindThis - public create(id: string, connection: Channel['connection']): HybridTimelineChannel { - return new HybridTimelineChannel( - this.metaService, - this.roleService, - this.noteEntityService, - id, - connection, - ); - } -} 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 3d7ed6acdb..b394e9663f 100644 --- a/packages/backend/src/server/api/stream/channels/local-timeline.ts +++ b/packages/backend/src/server/api/stream/channels/local-timeline.ts @@ -3,7 +3,7 @@ * SPDX-License-Identifier: AGPL-3.0-only */ -import { Injectable } from '@nestjs/common'; +import { Inject, Injectable, Scope } from '@nestjs/common'; import type { Packed } from '@/misc/json-schema.js'; import { MetaService } from '@/core/MetaService.js'; import { NoteEntityService } from '@/core/entities/NoteEntityService.js'; @@ -11,25 +11,27 @@ import { bindThis } from '@/decorators.js'; import { RoleService } from '@/core/RoleService.js'; import { isQuotePacked, isRenotePacked } from '@/misc/is-renote.js'; import type { JsonObject } from '@/misc/json-value.js'; -import Channel, { type MiChannelService } from '../channel.js'; +import Channel, { type ChannelRequest } from '../channel.js'; +import { REQUEST } from '@nestjs/core'; -class LocalTimelineChannel extends Channel { +@Injectable({ scope: Scope.TRANSIENT }) +export class LocalTimelineChannel extends Channel { public readonly chName = 'localTimeline'; - public static shouldShare = false; + public static shouldShare = false as const; public static requireCredential = false as const; private withRenotes: boolean; private withReplies: boolean; private withFiles: boolean; constructor( + @Inject(REQUEST) + request: ChannelRequest, + private metaService: MetaService, private roleService: RoleService, private noteEntityService: NoteEntityService, - - id: string, - connection: Channel['connection'], ) { - super(id, connection); + super(request); //this.onNote = this.onNote.bind(this); } @@ -84,28 +86,3 @@ class LocalTimelineChannel extends Channel { this.subscriber.off('notesStream', this.onNote); } } - -@Injectable() -export class LocalTimelineChannelService implements MiChannelService { - public readonly shouldShare = LocalTimelineChannel.shouldShare; - public readonly requireCredential = LocalTimelineChannel.requireCredential; - public readonly kind = LocalTimelineChannel.kind; - - constructor( - private metaService: MetaService, - private roleService: RoleService, - private noteEntityService: NoteEntityService, - ) { - } - - @bindThis - public create(id: string, connection: Channel['connection']): LocalTimelineChannel { - return new LocalTimelineChannel( - this.metaService, - this.roleService, - this.noteEntityService, - id, - connection, - ); - } -} diff --git a/packages/backend/src/server/api/stream/channels/main.ts b/packages/backend/src/server/api/stream/channels/main.ts index 525f24c105..2ce53ac288 100644 --- a/packages/backend/src/server/api/stream/channels/main.ts +++ b/packages/backend/src/server/api/stream/channels/main.ts @@ -3,26 +3,28 @@ * SPDX-License-Identifier: AGPL-3.0-only */ -import { Injectable } from '@nestjs/common'; +import { Inject, Injectable, Scope } from '@nestjs/common'; import { isInstanceMuted, isUserFromMutedInstance } from '@/misc/is-instance-muted.js'; import { NoteEntityService } from '@/core/entities/NoteEntityService.js'; import { bindThis } from '@/decorators.js'; import type { JsonObject } from '@/misc/json-value.js'; -import Channel, { type MiChannelService } from '../channel.js'; +import Channel, { type ChannelRequest } from '../channel.js'; +import { REQUEST } from '@nestjs/core'; -class MainChannel extends Channel { +@Injectable({ scope: Scope.TRANSIENT }) +export class MainChannel extends Channel { public readonly chName = 'main'; public static shouldShare = true; public static requireCredential = true as const; public static kind = 'read:account'; constructor( - private noteEntityService: NoteEntityService, + @Inject(REQUEST) + request: ChannelRequest, - id: string, - connection: Channel['connection'], + private noteEntityService: NoteEntityService, ) { - super(id, connection); + super(request); } @bindThis @@ -61,24 +63,3 @@ class MainChannel extends Channel { }); } } - -@Injectable() -export class MainChannelService implements MiChannelService { - public readonly shouldShare = MainChannel.shouldShare; - public readonly requireCredential = MainChannel.requireCredential; - public readonly kind = MainChannel.kind; - - constructor( - private noteEntityService: NoteEntityService, - ) { - } - - @bindThis - public create(id: string, connection: Channel['connection']): MainChannel { - return new MainChannel( - this.noteEntityService, - id, - connection, - ); - } -} 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 91b62255b4..a87863f26c 100644 --- a/packages/backend/src/server/api/stream/channels/queue-stats.ts +++ b/packages/backend/src/server/api/stream/channels/queue-stats.ts @@ -4,21 +4,26 @@ */ import Xev from 'xev'; -import { Injectable } from '@nestjs/common'; +import { Inject, Injectable, Scope } from '@nestjs/common'; import { bindThis } from '@/decorators.js'; import { isJsonObject } from '@/misc/json-value.js'; import type { JsonObject, JsonValue } from '@/misc/json-value.js'; -import Channel, { type MiChannelService } from '../channel.js'; +import Channel, { type ChannelRequest } from '../channel.js'; +import { REQUEST } from '@nestjs/core'; const ev = new Xev(); -class QueueStatsChannel extends Channel { +@Injectable({ scope: Scope.TRANSIENT }) +export class QueueStatsChannel extends Channel { public readonly chName = 'queueStats'; public static shouldShare = true; public static requireCredential = false as const; - constructor(id: string, connection: Channel['connection']) { - super(id, connection); + constructor( + @Inject(REQUEST) + request: ChannelRequest, + ) { + super(request); //this.onStats = this.onStats.bind(this); //this.onMessage = this.onMessage.bind(this); } @@ -56,22 +61,3 @@ class QueueStatsChannel extends Channel { ev.removeListener('queueStats', this.onStats); } } - -@Injectable() -export class QueueStatsChannelService implements MiChannelService { - public readonly shouldShare = QueueStatsChannel.shouldShare; - public readonly requireCredential = QueueStatsChannel.requireCredential; - public readonly kind = QueueStatsChannel.kind; - - constructor( - ) { - } - - @bindThis - public create(id: string, connection: Channel['connection']): QueueStatsChannel { - return new QueueStatsChannel( - id, - connection, - ); - } -} diff --git a/packages/backend/src/server/api/stream/channels/reversi-game.ts b/packages/backend/src/server/api/stream/channels/reversi-game.ts index 7597a1cfa3..58fc16e98c 100644 --- a/packages/backend/src/server/api/stream/channels/reversi-game.ts +++ b/packages/backend/src/server/api/stream/channels/reversi-game.ts @@ -3,31 +3,32 @@ * SPDX-License-Identifier: AGPL-3.0-only */ -import { Inject, Injectable } from '@nestjs/common'; +import { Inject, Injectable, Scope } from '@nestjs/common'; import type { MiReversiGame } from '@/models/_.js'; -import { DI } from '@/di-symbols.js'; import { bindThis } from '@/decorators.js'; import { ReversiService } from '@/core/ReversiService.js'; import { ReversiGameEntityService } from '@/core/entities/ReversiGameEntityService.js'; import { isJsonObject } from '@/misc/json-value.js'; import type { JsonObject, JsonValue } from '@/misc/json-value.js'; -import Channel, { type MiChannelService } from '../channel.js'; +import Channel, { type ChannelRequest } from '../channel.js'; import { reversiUpdateKeys } from 'misskey-js'; +import { REQUEST } from '@nestjs/core'; -class ReversiGameChannel extends Channel { +@Injectable({ scope: Scope.TRANSIENT }) +export class ReversiGameChannel extends Channel { public readonly chName = 'reversiGame'; public static shouldShare = false; public static requireCredential = false as const; private gameId: MiReversiGame['id'] | null = null; constructor( + @Inject(REQUEST) + request: ChannelRequest, + private reversiService: ReversiService, private reversiGameEntityService: ReversiGameEntityService, - - id: string, - connection: Channel['connection'], ) { - super(id, connection); + super(request); } @bindThis @@ -107,25 +108,3 @@ class ReversiGameChannel extends Channel { } } -@Injectable() -export class ReversiGameChannelService implements MiChannelService { - public readonly shouldShare = ReversiGameChannel.shouldShare; - public readonly requireCredential = ReversiGameChannel.requireCredential; - public readonly kind = ReversiGameChannel.kind; - - constructor( - private reversiService: ReversiService, - private reversiGameEntityService: ReversiGameEntityService, - ) { - } - - @bindThis - public create(id: string, connection: Channel['connection']): ReversiGameChannel { - return new ReversiGameChannel( - this.reversiService, - this.reversiGameEntityService, - id, - connection, - ); - } -} diff --git a/packages/backend/src/server/api/stream/channels/reversi.ts b/packages/backend/src/server/api/stream/channels/reversi.ts index 6e88939724..5eff73eeef 100644 --- a/packages/backend/src/server/api/stream/channels/reversi.ts +++ b/packages/backend/src/server/api/stream/channels/reversi.ts @@ -3,22 +3,24 @@ * SPDX-License-Identifier: AGPL-3.0-only */ -import { Injectable } from '@nestjs/common'; +import { Inject, Injectable, Scope } from '@nestjs/common'; import { bindThis } from '@/decorators.js'; import type { JsonObject } from '@/misc/json-value.js'; -import Channel, { type MiChannelService } from '../channel.js'; +import Channel, { type ChannelRequest } from '../channel.js'; +import { REQUEST } from '@nestjs/core'; -class ReversiChannel extends Channel { +@Injectable({ scope: Scope.TRANSIENT }) +export class ReversiChannel extends Channel { public readonly chName = 'reversi'; public static shouldShare = true; public static requireCredential = true as const; public static kind = 'read:account'; constructor( - id: string, - connection: Channel['connection'], + @Inject(REQUEST) + request: ChannelRequest, ) { - super(id, connection); + super(request); } @bindThis @@ -32,22 +34,3 @@ class ReversiChannel extends Channel { this.subscriber.off(`reversiStream:${this.user!.id}`, this.send); } } - -@Injectable() -export class ReversiChannelService implements MiChannelService { - public readonly shouldShare = ReversiChannel.shouldShare; - public readonly requireCredential = ReversiChannel.requireCredential; - public readonly kind = ReversiChannel.kind; - - constructor( - ) { - } - - @bindThis - public create(id: string, connection: Channel['connection']): ReversiChannel { - return new ReversiChannel( - id, - connection, - ); - } -} diff --git a/packages/backend/src/server/api/stream/channels/role-timeline.ts b/packages/backend/src/server/api/stream/channels/role-timeline.ts index fcfa26c38b..99e0b69023 100644 --- a/packages/backend/src/server/api/stream/channels/role-timeline.ts +++ b/packages/backend/src/server/api/stream/channels/role-timeline.ts @@ -3,28 +3,30 @@ * SPDX-License-Identifier: AGPL-3.0-only */ -import { Injectable } from '@nestjs/common'; +import { Inject, Injectable, Scope } from '@nestjs/common'; import { NoteEntityService } from '@/core/entities/NoteEntityService.js'; import { bindThis } from '@/decorators.js'; import { RoleService } from '@/core/RoleService.js'; import type { GlobalEvents } from '@/core/GlobalEventService.js'; import type { JsonObject } from '@/misc/json-value.js'; -import Channel, { type MiChannelService } from '../channel.js'; +import Channel, { type ChannelRequest } from '../channel.js'; +import { REQUEST } from '@nestjs/core'; -class RoleTimelineChannel extends Channel { +@Injectable({ scope: Scope.TRANSIENT }) +export class RoleTimelineChannel extends Channel { public readonly chName = 'roleTimeline'; public static shouldShare = false; public static requireCredential = false as const; private roleId: string; constructor( + @Inject(REQUEST) + request: ChannelRequest, + private noteEntityService: NoteEntityService, private roleservice: RoleService, - - id: string, - connection: Channel['connection'], ) { - super(id, connection); + super(request); //this.onNote = this.onNote.bind(this); } @@ -60,26 +62,3 @@ class RoleTimelineChannel extends Channel { this.subscriber.off(`roleTimelineStream:${this.roleId}`, this.onEvent); } } - -@Injectable() -export class RoleTimelineChannelService implements MiChannelService { - public readonly shouldShare = RoleTimelineChannel.shouldShare; - public readonly requireCredential = RoleTimelineChannel.requireCredential; - public readonly kind = RoleTimelineChannel.kind; - - constructor( - private noteEntityService: NoteEntityService, - private roleservice: RoleService, - ) { - } - - @bindThis - public create(id: string, connection: Channel['connection']): RoleTimelineChannel { - return new RoleTimelineChannel( - this.noteEntityService, - this.roleservice, - id, - connection, - ); - } -} 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 ec5352d12d..aece5435b0 100644 --- a/packages/backend/src/server/api/stream/channels/server-stats.ts +++ b/packages/backend/src/server/api/stream/channels/server-stats.ts @@ -4,21 +4,26 @@ */ import Xev from 'xev'; -import { Injectable } from '@nestjs/common'; +import { Inject, Injectable, Scope } from '@nestjs/common'; import { bindThis } from '@/decorators.js'; import { isJsonObject } from '@/misc/json-value.js'; import type { JsonObject, JsonValue } from '@/misc/json-value.js'; -import Channel, { type MiChannelService } from '../channel.js'; +import Channel, { type ChannelRequest } from '../channel.js'; +import { REQUEST } from '@nestjs/core'; const ev = new Xev(); -class ServerStatsChannel extends Channel { +@Injectable({ scope: Scope.TRANSIENT }) +export class ServerStatsChannel extends Channel { public readonly chName = 'serverStats'; public static shouldShare = true; public static requireCredential = false as const; - constructor(id: string, connection: Channel['connection']) { - super(id, connection); + constructor( + @Inject(REQUEST) + request: ChannelRequest, + ) { + super(request); //this.onStats = this.onStats.bind(this); //this.onMessage = this.onMessage.bind(this); } @@ -54,22 +59,3 @@ class ServerStatsChannel extends Channel { ev.removeListener('serverStats', this.onStats); } } - -@Injectable() -export class ServerStatsChannelService implements MiChannelService { - public readonly shouldShare = ServerStatsChannel.shouldShare; - public readonly requireCredential = ServerStatsChannel.requireCredential; - public readonly kind = ServerStatsChannel.kind; - - constructor( - ) { - } - - @bindThis - public create(id: string, connection: Channel['connection']): ServerStatsChannel { - return new ServerStatsChannel( - id, - connection, - ); - } -} 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 5bfd8fa68c..2f7345e150 100644 --- a/packages/backend/src/server/api/stream/channels/user-list.ts +++ b/packages/backend/src/server/api/stream/channels/user-list.ts @@ -3,7 +3,7 @@ * SPDX-License-Identifier: AGPL-3.0-only */ -import { Inject, Injectable } from '@nestjs/common'; +import { Inject, Injectable, Scope } from '@nestjs/common'; import type { MiUserListMembership, UserListMembershipsRepository, UserListsRepository } from '@/models/_.js'; import type { Packed } from '@/misc/json-schema.js'; import { NoteEntityService } from '@/core/entities/NoteEntityService.js'; @@ -11,9 +11,11 @@ import { DI } from '@/di-symbols.js'; import { bindThis } from '@/decorators.js'; import { isRenotePacked, isQuotePacked } from '@/misc/is-renote.js'; import type { JsonObject } from '@/misc/json-value.js'; -import Channel, { type MiChannelService } from '../channel.js'; +import Channel, { type ChannelRequest } from '../channel.js'; +import { REQUEST } from '@nestjs/core'; -class UserListChannel extends Channel { +@Injectable({ scope: Scope.TRANSIENT }) +export class UserListChannel extends Channel { public readonly chName = 'userList'; public static shouldShare = false; public static requireCredential = false as const; @@ -24,14 +26,18 @@ class UserListChannel extends Channel { private withRenotes: boolean; constructor( + @Inject(DI.userListsRepository) private userListsRepository: UserListsRepository, - private userListMembershipsRepository: UserListMembershipsRepository, - private noteEntityService: NoteEntityService, - id: string, - connection: Channel['connection'], + @Inject(DI.userListMembershipsRepository) + private userListMembershipsRepository: UserListMembershipsRepository, + + @Inject(REQUEST) + request: ChannelRequest, + + private noteEntityService: NoteEntityService, ) { - super(id, connection); + super(request); //this.updateListUsers = this.updateListUsers.bind(this); //this.onNote = this.onNote.bind(this); } @@ -130,32 +136,3 @@ class UserListChannel extends Channel { clearInterval(this.listUsersClock); } } - -@Injectable() -export class UserListChannelService implements MiChannelService { - public readonly shouldShare = UserListChannel.shouldShare; - public readonly requireCredential = UserListChannel.requireCredential; - public readonly kind = UserListChannel.kind; - - constructor( - @Inject(DI.userListsRepository) - private userListsRepository: UserListsRepository, - - @Inject(DI.userListMembershipsRepository) - private userListMembershipsRepository: UserListMembershipsRepository, - - private noteEntityService: NoteEntityService, - ) { - } - - @bindThis - public create(id: string, connection: Channel['connection']): UserListChannel { - return new UserListChannel( - this.userListsRepository, - this.userListMembershipsRepository, - this.noteEntityService, - id, - connection, - ); - } -}