From 1fc2ae025c559f9531b22c470be69c657457ed1f Mon Sep 17 00:00:00 2001 From: tamaina Date: Thu, 17 Jul 2025 11:00:58 +0900 Subject: [PATCH] wip: addAllKnowingSharedInbox --- .../activitypub/ApDeliverManagerService.ts | 60 ++++++++++++++++--- 1 file changed, 51 insertions(+), 9 deletions(-) diff --git a/packages/backend/src/core/activitypub/ApDeliverManagerService.ts b/packages/backend/src/core/activitypub/ApDeliverManagerService.ts index c0d667253f..025c939831 100644 --- a/packages/backend/src/core/activitypub/ApDeliverManagerService.ts +++ b/packages/backend/src/core/activitypub/ApDeliverManagerService.ts @@ -9,10 +9,12 @@ import { DI } from '@/di-symbols.js'; import type { FollowingsRepository } from '@/models/_.js'; import type { MiLocalUser, MiRemoteUser, MiUser } from '@/models/User.js'; import { QueueService } from '@/core/QueueService.js'; -import { UserEntityService } from '@/core/entities/UserEntityService.js'; import { bindThis } from '@/decorators.js'; import type { IActivity } from '@/core/activitypub/type.js'; import { ThinUser } from '@/queue/types.js'; +import { AccountUpdateService } from '@/core/AccountUpdateService.js'; +import type Logger from '@/logger.js'; +import { ApLoggerService } from './ApLoggerService.js'; interface IRecipe { type: string; @@ -27,12 +29,19 @@ interface IDirectRecipe extends IRecipe { to: MiRemoteUser; } +interface IAllKnowingSharedInboxRecipe extends IRecipe { + type: 'AllKnowingSharedInbox'; +} + const isFollowers = (recipe: IRecipe): recipe is IFollowersRecipe => recipe.type === 'Followers'; const isDirect = (recipe: IRecipe): recipe is IDirectRecipe => recipe.type === 'Direct'; +const isAllKnowingSharedInbox = (recipe: IRecipe): recipe is IAllKnowingSharedInboxRecipe => + recipe.type === 'AllKnowingSharedInbox'; + class DeliverManager { private actor: ThinUser; private activity: IActivity | null; @@ -40,16 +49,15 @@ class DeliverManager { /** * Constructor - * @param userEntityService * @param followingsRepository * @param queueService * @param actor Actor * @param activity Activity to deliver */ constructor( - private userEntityService: UserEntityService, private followingsRepository: FollowingsRepository, private queueService: QueueService, + private logger: Logger, actor: { id: MiUser['id']; host: null; }, activity: IActivity | null, @@ -91,6 +99,18 @@ class DeliverManager { this.addRecipe(recipe); } + /** + * Add recipe for all-knowing shared inbox deliver + */ + @bindThis + public addAllKnowingSharedInboxRecipe(): void { + const deliver: IAllKnowingSharedInboxRecipe = { + type: 'AllKnowingSharedInbox', + }; + + this.addRecipe(deliver); + } + /** * Add recipe * @param recipe Recipe @@ -105,10 +125,27 @@ class DeliverManager { */ @bindThis public async execute(): Promise { + //#region collect inboxes by recipes // The value flags whether it is shared or not. // key: inbox URL, value: whether it is sharedInbox const inboxes = new Map(); + if (this.recipes.some(r => isAllKnowingSharedInbox(r))) { + // all-knowing shared inbox + const followings = await this.followingsRepository.find({ + where: [ + { followerSharedInbox: Not(IsNull()) }, + { followeeSharedInbox: Not(IsNull()) }, + ], + select: ['followerSharedInbox', 'followeeSharedInbox'], + }); + + for (const following of followings) { + if (following.followeeSharedInbox) inboxes.set(following.followeeSharedInbox, true); + if (following.followerSharedInbox) inboxes.set(following.followerSharedInbox, true); + } + } + // build inbox list // Process follower recipes first to avoid duplication when processing direct recipes later. if (this.recipes.some(r => isFollowers(r))) { @@ -143,34 +180,40 @@ class DeliverManager { inboxes.set(recipe.to.inbox, false); } + //#endregion // deliver await this.queueService.deliverMany(this.actor, this.activity, inboxes); + this.logger.info(`Deliver queues dispatched: inboxes=${inboxes.size} actorId=${this.actor.id} activityId=${this.activity?.id}`); } } @Injectable() export class ApDeliverManagerService { + private logger: Logger; + constructor( @Inject(DI.followingsRepository) private followingsRepository: FollowingsRepository, - private userEntityService: UserEntityService, private queueService: QueueService, + private apLoggerService: ApLoggerService, ) { + this.logger = this.apLoggerService.logger.createSubLogger('deliver-manager'); } /** * Deliver activity to followers * @param actor * @param activity Activity + * @param forceMainKey Force to use main (rsa) key */ @bindThis public async deliverToFollowers(actor: { id: MiLocalUser['id']; host: null; }, activity: IActivity): Promise { const manager = new DeliverManager( - this.userEntityService, this.followingsRepository, this.queueService, + this.logger, actor, activity, ); @@ -187,9 +230,9 @@ export class ApDeliverManagerService { @bindThis public async deliverToUser(actor: { id: MiLocalUser['id']; host: null; }, activity: IActivity, to: MiRemoteUser): Promise { const manager = new DeliverManager( - this.userEntityService, this.followingsRepository, this.queueService, + this.logger, actor, activity, ); @@ -206,9 +249,9 @@ export class ApDeliverManagerService { @bindThis public async deliverToUsers(actor: { id: MiLocalUser['id']; host: null; }, activity: IActivity, targets: MiRemoteUser[]): Promise { const manager = new DeliverManager( - this.userEntityService, this.followingsRepository, this.queueService, + this.logger, actor, activity, ); @@ -219,10 +262,9 @@ export class ApDeliverManagerService { @bindThis public createDeliverManager(actor: { id: MiUser['id']; host: null; }, activity: IActivity | null): DeliverManager { return new DeliverManager( - this.userEntityService, this.followingsRepository, this.queueService, - + this.logger, actor, activity, );