Skip to content

Commit 277a1ef

Browse files
sinorukakkokari-gtyihsyuilo
authored
fix(backend): handle relay-delivered Announce activities correctly (#17308)
* fix(backend): handle relay-delivered Announce activities correctly Relay Announce activities now use the target note URI instead of the Announce URI for federation allowlist checks, dedup locking, and existence lookups. Notes delivered via relay are published directly to the notes stream without creating a renote. Closes #11056 * Update packages/backend/src/core/RelayService.ts Co-authored-by: syuilo <[email protected]> --------- Co-authored-by: かっこかり <[email protected]> Co-authored-by: syuilo <[email protected]>
1 parent 8e536eb commit 277a1ef

3 files changed

Lines changed: 30 additions & 5 deletions

File tree

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
- Fix: ローカルに存在しないリモートアカウントに対するアカウント削除リクエストを受信した際に、そのユーザーを新規作成して削除する挙動を修正
1717
- Fix: Inboxでの特定のエラーによる失敗はDelayedにしない
1818
- Fix: ID生成アルゴリズムにULIDを使用している場合にMisskeyが正しく動作しない問題を修正
19+
- Fix: リレー経由で届いたノートがリノートとして表示される問題を修正
1920
- Fix: robots.txtの内容を調整
2021

2122
## 2026.3.2

packages/backend/src/core/RelayService.ts

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -91,13 +91,27 @@ export class RelayService {
9191
return JSON.stringify(result);
9292
}
9393

94+
@bindThis
95+
private getAcceptedRelays(): Promise<MiRelay[]> {
96+
return this.relaysCache.fetch(() => this.relaysRepository.findBy({
97+
status: 'accepted',
98+
}));
99+
}
100+
101+
@bindThis
102+
public async isRelayActor(actor: { inbox: string | null; sharedInbox: string | null }): Promise<boolean> {
103+
const relays = await this.getAcceptedRelays();
104+
return relays.some(relay =>
105+
(actor.inbox != null && relay.inbox === actor.inbox)
106+
|| (actor.sharedInbox != null && relay.inbox === actor.sharedInbox),
107+
);
108+
}
109+
94110
@bindThis
95111
public async deliverToRelays(user: { id: MiUser['id']; host: null; }, activity: any): Promise<void> {
96112
if (activity == null) return;
97113

98-
const relays = await this.relaysCache.fetch(() => this.relaysRepository.findBy({
99-
status: 'accepted',
100-
}));
114+
const relays = await this.getAcceptedRelays();
101115
if (relays.length === 0) return;
102116

103117
const copy = deepClone(activity);

packages/backend/src/core/activitypub/ApInboxService.ts

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -302,12 +302,14 @@ export class ApInboxService {
302302

303303
@bindThis
304304
private async announceNote(actor: MiRemoteUser, activity: IAnnounce, target: IPost, resolver?: Resolver): Promise<string | void> {
305-
const uri = getApId(activity);
306-
307305
if (actor.isSuspended) {
308306
return;
309307
}
310308

309+
// リレーからのAnnounceかチェック
310+
const fromRelay = await this.relayService.isRelayActor(actor);
311+
const uri = getApId(fromRelay ? target : activity);
312+
311313
// アナウンス先が許可されているかチェック
312314
if (!this.utilityService.isFederationAllowedUri(uri)) return;
313315

@@ -336,6 +338,14 @@ export class ApInboxService {
336338
throw err;
337339
}
338340

341+
// リレーからのAnnounceはリノートを作成せず、ノートを直接公開する
342+
if (fromRelay) {
343+
this.logger.info(`Publishing relay-delivered note: ${uri}`);
344+
const noteObj = await this.noteEntityService.pack(renote, null, { skipHide: true, withReactionAndUserPairCache: true });
345+
this.globalEventService.publishNotesStream(noteObj);
346+
return;
347+
}
348+
339349
if (!await this.noteEntityService.isVisibleForMe(renote, actor.id)) {
340350
return 'skip: invalid actor for this activity';
341351
}

0 commit comments

Comments
 (0)