From c05c504c8632591ef18170c2881b0b44b0027ed9 Mon Sep 17 00:00:00 2001
From: CyberRex <hspwinx86@gmail.com>
Date: Tue, 14 Mar 2023 19:11:31 +0900
Subject: [PATCH] =?UTF-8?q?Deliver=E3=82=AD=E3=83=A5=E3=83=BC=E3=81=AB?=
 =?UTF-8?q?=E5=AE=9B=E5=85=88=E3=81=8CSharedInbox=E3=81=8B=E3=81=A9?=
 =?UTF-8?q?=E3=81=86=E3=81=8B=E3=81=AE=E3=83=95=E3=83=A9=E3=82=B0=E3=82=92?=
 =?UTF-8?q?=E8=BF=BD=E5=8A=A0=20(=20#10298=20=E9=96=A2=E4=BF=82=20)=20(#10?=
 =?UTF-8?q?317)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

* enhance(backend): 配送先が410 Goneで応答してきた場合配送停止するように

* Update CHANGELOG.md

* Deliverキューのデータに宛先がSharedInboxかどうかのフラグを追加

* Fix lint

* Mapを使用するように

* Fix typo
---
 packages/backend/src/core/QueueService.ts        |  3 ++-
 packages/backend/src/core/RelayService.ts        |  6 +++---
 packages/backend/src/core/UserBlockingService.ts | 12 ++++++------
 .../backend/src/core/UserFollowingService.ts     | 16 ++++++++--------
 packages/backend/src/core/UserSuspendService.ts  |  4 ++--
 .../core/activitypub/ApDeliverManagerService.ts  | 10 ++++++----
 .../queue/processors/DeliverProcessorService.ts  |  2 +-
 packages/backend/src/queue/types.ts              |  2 ++
 .../endpoints/admin/resolve-abuse-user-report.ts |  2 +-
 .../src/server/api/endpoints/notes/polls/vote.ts |  2 +-
 10 files changed, 32 insertions(+), 27 deletions(-)

diff --git a/packages/backend/src/core/QueueService.ts b/packages/backend/src/core/QueueService.ts
index 4bf41e0ac1..498ceced7a 100644
--- a/packages/backend/src/core/QueueService.ts
+++ b/packages/backend/src/core/QueueService.ts
@@ -26,7 +26,7 @@ export class QueueService {
 	) {}
 
 	@bindThis
-	public deliver(user: ThinUser, content: IActivity | null, to: string | null) {
+	public deliver(user: ThinUser, content: IActivity | null, to: string | null, isSharedInbox: boolean) {
 		if (content == null) return null;
 		if (to == null) return null;
 
@@ -36,6 +36,7 @@ export class QueueService {
 			},
 			content,
 			to,
+			isSharedInbox,
 		};
 
 		return this.deliverQueue.add(data, {
diff --git a/packages/backend/src/core/RelayService.ts b/packages/backend/src/core/RelayService.ts
index 2e07825e9b..86f983cc78 100644
--- a/packages/backend/src/core/RelayService.ts
+++ b/packages/backend/src/core/RelayService.ts
@@ -57,7 +57,7 @@ export class RelayService {
 		const relayActor = await this.getRelayActor();
 		const follow = await this.apRendererService.renderFollowRelay(relay, relayActor);
 		const activity = this.apRendererService.addContext(follow);
-		this.queueService.deliver(relayActor, activity, relay.inbox);
+		this.queueService.deliver(relayActor, activity, relay.inbox, false);
 	
 		return relay;
 	}
@@ -76,7 +76,7 @@ export class RelayService {
 		const follow = this.apRendererService.renderFollowRelay(relay, relayActor);
 		const undo = this.apRendererService.renderUndo(follow, relayActor);
 		const activity = this.apRendererService.addContext(undo);
-		this.queueService.deliver(relayActor, activity, relay.inbox);
+		this.queueService.deliver(relayActor, activity, relay.inbox, false);
 	
 		await this.relaysRepository.delete(relay.id);
 	}
@@ -120,7 +120,7 @@ export class RelayService {
 		const signed = await this.apRendererService.attachLdSignature(copy, user);
 	
 		for (const relay of relays) {
-			this.queueService.deliver(user, signed, relay.inbox);
+			this.queueService.deliver(user, signed, relay.inbox, false);
 		}
 	}
 }
diff --git a/packages/backend/src/core/UserBlockingService.ts b/packages/backend/src/core/UserBlockingService.ts
index be37bad52e..92408da342 100644
--- a/packages/backend/src/core/UserBlockingService.ts
+++ b/packages/backend/src/core/UserBlockingService.ts
@@ -118,7 +118,7 @@ export class UserBlockingService implements OnApplicationShutdown {
 
 		if (this.userEntityService.isLocalUser(blocker) && this.userEntityService.isRemoteUser(blockee)) {
 			const content = this.apRendererService.addContext(this.apRendererService.renderBlock(blocking));
-			this.queueService.deliver(blocker, content, blockee.inbox);
+			this.queueService.deliver(blocker, content, blockee.inbox, false);
 		}
 	}
 
@@ -163,13 +163,13 @@ export class UserBlockingService implements OnApplicationShutdown {
 		// リモートにフォローリクエストをしていたらUndoFollow送信
 		if (this.userEntityService.isLocalUser(follower) && this.userEntityService.isRemoteUser(followee)) {
 			const content = this.apRendererService.addContext(this.apRendererService.renderUndo(this.apRendererService.renderFollow(follower, followee), follower));
-			this.queueService.deliver(follower, content, followee.inbox);
+			this.queueService.deliver(follower, content, followee.inbox, false);
 		}
 
 		// リモートからフォローリクエストを受けていたらReject送信
 		if (this.userEntityService.isRemoteUser(follower) && this.userEntityService.isLocalUser(followee)) {
 			const content = this.apRendererService.addContext(this.apRendererService.renderReject(this.apRendererService.renderFollow(follower, followee, request.requestId!), followee));
-			this.queueService.deliver(followee, content, follower.inbox);
+			this.queueService.deliver(followee, content, follower.inbox, false);
 		}
 	}
 
@@ -211,13 +211,13 @@ export class UserBlockingService implements OnApplicationShutdown {
 		// リモートにフォローをしていたらUndoFollow送信
 		if (this.userEntityService.isLocalUser(follower) && this.userEntityService.isRemoteUser(followee)) {
 			const content = this.apRendererService.addContext(this.apRendererService.renderUndo(this.apRendererService.renderFollow(follower, followee), follower));
-			this.queueService.deliver(follower, content, followee.inbox);
+			this.queueService.deliver(follower, content, followee.inbox, false);
 		}
 
 		// リモートからフォローをされていたらRejectFollow送信
 		if (this.userEntityService.isLocalUser(followee) && this.userEntityService.isRemoteUser(follower)) {
 			const content = this.apRendererService.addContext(this.apRendererService.renderReject(this.apRendererService.renderFollow(follower, followee), followee));
-			this.queueService.deliver(followee, content, follower.inbox);
+			this.queueService.deliver(followee, content, follower.inbox, false);
 		}
 	}
 
@@ -262,7 +262,7 @@ export class UserBlockingService implements OnApplicationShutdown {
 		// deliver if remote bloking
 		if (this.userEntityService.isLocalUser(blocker) && this.userEntityService.isRemoteUser(blockee)) {
 			const content = this.apRendererService.addContext(this.apRendererService.renderUndo(this.apRendererService.renderBlock(blocking), blocker));
-			this.queueService.deliver(blocker, content, blockee.inbox);
+			this.queueService.deliver(blocker, content, blockee.inbox, false);
 		}
 	}
 
diff --git a/packages/backend/src/core/UserFollowingService.ts b/packages/backend/src/core/UserFollowingService.ts
index a612c9eb95..9f09c34d4b 100644
--- a/packages/backend/src/core/UserFollowingService.ts
+++ b/packages/backend/src/core/UserFollowingService.ts
@@ -82,7 +82,7 @@ export class UserFollowingService {
 		if (this.userEntityService.isRemoteUser(follower) && this.userEntityService.isLocalUser(followee) && blocked) {
 			// リモートフォローを受けてブロックしていた場合は、エラーにするのではなくRejectを送り返しておしまい。
 			const content = this.apRendererService.addContext(this.apRendererService.renderReject(this.apRendererService.renderFollow(follower, followee, requestId), followee));
-			this.queueService.deliver(followee, content, follower.inbox);
+			this.queueService.deliver(followee, content, follower.inbox, false);
 			return;
 		} else if (this.userEntityService.isRemoteUser(follower) && this.userEntityService.isLocalUser(followee) && blocking) {
 			// リモートフォローを受けてブロックされているはずの場合だったら、ブロック解除しておく。
@@ -131,7 +131,7 @@ export class UserFollowingService {
 
 		if (this.userEntityService.isRemoteUser(follower) && this.userEntityService.isLocalUser(followee)) {
 			const content = this.apRendererService.addContext(this.apRendererService.renderAccept(this.apRendererService.renderFollow(follower, followee, requestId), followee));
-			this.queueService.deliver(followee, content, follower.inbox);
+			this.queueService.deliver(followee, content, follower.inbox, false);
 		}
 	}
 
@@ -294,13 +294,13 @@ export class UserFollowingService {
 	
 		if (this.userEntityService.isLocalUser(follower) && this.userEntityService.isRemoteUser(followee)) {
 			const content = this.apRendererService.addContext(this.apRendererService.renderUndo(this.apRendererService.renderFollow(follower, followee), follower));
-			this.queueService.deliver(follower, content, followee.inbox);
+			this.queueService.deliver(follower, content, followee.inbox, false);
 		}
 	
 		if (this.userEntityService.isLocalUser(followee) && this.userEntityService.isRemoteUser(follower)) {
 			// local user has null host
 			const content = this.apRendererService.addContext(this.apRendererService.renderReject(this.apRendererService.renderFollow(follower, followee), followee));
-			this.queueService.deliver(followee, content, follower.inbox);
+			this.queueService.deliver(followee, content, follower.inbox, false);
 		}
 	}
 	
@@ -389,7 +389,7 @@ export class UserFollowingService {
 	
 		if (this.userEntityService.isLocalUser(follower) && this.userEntityService.isRemoteUser(followee)) {
 			const content = this.apRendererService.addContext(this.apRendererService.renderFollow(follower, followee));
-			this.queueService.deliver(follower, content, followee.inbox);
+			this.queueService.deliver(follower, content, followee.inbox, false);
 		}
 	}
 
@@ -406,7 +406,7 @@ export class UserFollowingService {
 			const content = this.apRendererService.addContext(this.apRendererService.renderUndo(this.apRendererService.renderFollow(follower, followee), follower));
 	
 			if (this.userEntityService.isLocalUser(follower)) { // 本来このチェックは不要だけどTSに怒られるので
-				this.queueService.deliver(follower, content, followee.inbox);
+				this.queueService.deliver(follower, content, followee.inbox, false);
 			}
 		}
 	
@@ -449,7 +449,7 @@ export class UserFollowingService {
 	
 		if (this.userEntityService.isRemoteUser(follower) && this.userEntityService.isLocalUser(followee)) {
 			const content = this.apRendererService.addContext(this.apRendererService.renderAccept(this.apRendererService.renderFollow(follower, followee, request.requestId!), followee));
-			this.queueService.deliver(followee, content, follower.inbox);
+			this.queueService.deliver(followee, content, follower.inbox, false);
 		}
 	
 		this.userEntityService.pack(followee.id, followee, {
@@ -557,7 +557,7 @@ export class UserFollowingService {
 		});
 
 		const content = this.apRendererService.addContext(this.apRendererService.renderReject(this.apRendererService.renderFollow(follower, followee, request?.requestId ?? undefined), followee));
-		this.queueService.deliver(followee, content, follower.inbox);
+		this.queueService.deliver(followee, content, follower.inbox, false);
 	}
 
 	/**
diff --git a/packages/backend/src/core/UserSuspendService.ts b/packages/backend/src/core/UserSuspendService.ts
index 02903a0590..d00bb89c76 100644
--- a/packages/backend/src/core/UserSuspendService.ts
+++ b/packages/backend/src/core/UserSuspendService.ts
@@ -54,7 +54,7 @@ export class UserSuspendService {
 			}
 	
 			for (const inbox of queue) {
-				this.queueService.deliver(user, content, inbox);
+				this.queueService.deliver(user, content, inbox, true);
 			}
 		}
 	}
@@ -84,7 +84,7 @@ export class UserSuspendService {
 			}
 	
 			for (const inbox of queue) {
-				this.queueService.deliver(user as any, content, inbox);
+				this.queueService.deliver(user as any, content, inbox, true);
 			}
 		}
 	}
diff --git a/packages/backend/src/core/activitypub/ApDeliverManagerService.ts b/packages/backend/src/core/activitypub/ApDeliverManagerService.ts
index 5e6ea69846..70a6d32fe2 100644
--- a/packages/backend/src/core/activitypub/ApDeliverManagerService.ts
+++ b/packages/backend/src/core/activitypub/ApDeliverManagerService.ts
@@ -157,7 +157,8 @@ class DeliverManager {
 	public async execute() {
 		if (!this.userEntityService.isLocalUser(this.actor)) return;
 
-		const inboxes = new Set<string>();
+		// The value flags whether it is shared or not.
+		const inboxes = new Map<string, boolean>();
 
 		/*
 		build inbox list
@@ -185,7 +186,7 @@ class DeliverManager {
 
 			for (const following of followers) {
 				const inbox = following.followerSharedInbox ?? following.followerInbox;
-				inboxes.add(inbox);
+				inboxes.set(inbox, following.followerSharedInbox === null);
 			}
 		}
 
@@ -197,11 +198,12 @@ class DeliverManager {
 			// check that they actually have an inbox
 			&& recipe.to.inbox != null,
 		)
-			.forEach(recipe => inboxes.add(recipe.to.inbox!));
+			.forEach(recipe => inboxes.set(recipe.to.inbox!, false));
 
 		// deliver
 		for (const inbox of inboxes) {
-			this.queueService.deliver(this.actor, this.activity, inbox);
+			// inbox[0]: inbox, inbox[1]: whether it is sharedInbox
+			this.queueService.deliver(this.actor, this.activity, inbox[0], inbox[1]);
 		}
 	}
 }
diff --git a/packages/backend/src/queue/processors/DeliverProcessorService.ts b/packages/backend/src/queue/processors/DeliverProcessorService.ts
index 065501fe21..43a92bb267 100644
--- a/packages/backend/src/queue/processors/DeliverProcessorService.ts
+++ b/packages/backend/src/queue/processors/DeliverProcessorService.ts
@@ -116,7 +116,7 @@ export class DeliverProcessorService {
 				// 4xx
 				if (res.isClientError) {
 					// 相手が閉鎖していることを明示しているため、配送停止する
-					if (res.statusCode === 410) {
+					if (job.data.isSharedInbox && res.statusCode === 410) {
 						this.federatedInstanceService.fetch(host).then(i => {
 							this.instancesRepository.update(i.id, {
 								isSuspended: true,
diff --git a/packages/backend/src/queue/types.ts b/packages/backend/src/queue/types.ts
index 1214c9eb95..5d650c6864 100644
--- a/packages/backend/src/queue/types.ts
+++ b/packages/backend/src/queue/types.ts
@@ -12,6 +12,8 @@ export type DeliverJobData = {
 	content: unknown;
 	/** inbox URL to deliver */
 	to: string;
+	/** whether it is sharedInbox */
+	isSharedInbox: boolean;
 };
 
 export type InboxJobData = {
diff --git a/packages/backend/src/server/api/endpoints/admin/resolve-abuse-user-report.ts b/packages/backend/src/server/api/endpoints/admin/resolve-abuse-user-report.ts
index d0d52089e6..aead894611 100644
--- a/packages/backend/src/server/api/endpoints/admin/resolve-abuse-user-report.ts
+++ b/packages/backend/src/server/api/endpoints/admin/resolve-abuse-user-report.ts
@@ -49,7 +49,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> {
 				const actor = await this.instanceActorService.getInstanceActor();
 				const targetUser = await this.usersRepository.findOneByOrFail({ id: report.targetUserId });
 
-				this.queueService.deliver(actor, this.apRendererService.addContext(this.apRendererService.renderFlag(actor, targetUser.uri!, report.comment)), targetUser.inbox);
+				this.queueService.deliver(actor, this.apRendererService.addContext(this.apRendererService.renderFlag(actor, targetUser.uri!, report.comment)), targetUser.inbox, false);
 			}
 
 			await this.abuseUserReportsRepository.update(report.id, {
diff --git a/packages/backend/src/server/api/endpoints/notes/polls/vote.ts b/packages/backend/src/server/api/endpoints/notes/polls/vote.ts
index b9e06a7834..1bbd79fe1e 100644
--- a/packages/backend/src/server/api/endpoints/notes/polls/vote.ts
+++ b/packages/backend/src/server/api/endpoints/notes/polls/vote.ts
@@ -161,7 +161,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> {
 			if (note.userHost != null) {
 				const pollOwner = await this.usersRepository.findOneByOrFail({ id: note.userId }) as RemoteUser;
 
-				this.queueService.deliver(me, this.apRendererService.addContext(await this.apRendererService.renderVote(me, vote, note, poll, pollOwner)), pollOwner.inbox);
+				this.queueService.deliver(me, this.apRendererService.addContext(await this.apRendererService.renderVote(me, vote, note, poll, pollOwner)), pollOwner.inbox, false);
 			}
 
 			// リモートフォロワーにUpdate配信