mirror of
https://github.com/misskey-dev/misskey.git
synced 2026-07-26 02:04:55 +02:00
* 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 <4439005+syuilo@users.noreply.github.com> --------- Co-authored-by: かっこかり <67428053+kakkokari-gtyih@users.noreply.github.com> Co-authored-by: syuilo <4439005+syuilo@users.noreply.github.com>
127 lines
3.8 KiB
TypeScript
127 lines
3.8 KiB
TypeScript
/*
|
|
* SPDX-FileCopyrightText: syuilo and misskey-project
|
|
* SPDX-License-Identifier: AGPL-3.0-only
|
|
*/
|
|
|
|
import { Inject, Injectable } from '@nestjs/common';
|
|
import type { MiUser } from '@/models/User.js';
|
|
import type { RelaysRepository } from '@/models/_.js';
|
|
import { IdService } from '@/core/IdService.js';
|
|
import { MemorySingleCache } from '@/misc/cache.js';
|
|
import type { MiRelay } from '@/models/Relay.js';
|
|
import { QueueService } from '@/core/QueueService.js';
|
|
import { ApRendererService } from '@/core/activitypub/ApRendererService.js';
|
|
import { DI } from '@/di-symbols.js';
|
|
import { deepClone } from '@/misc/clone.js';
|
|
import { bindThis } from '@/decorators.js';
|
|
import { SystemAccountService } from '@/core/SystemAccountService.js';
|
|
|
|
@Injectable()
|
|
export class RelayService {
|
|
private relaysCache: MemorySingleCache<MiRelay[]>;
|
|
|
|
constructor(
|
|
@Inject(DI.relaysRepository)
|
|
private relaysRepository: RelaysRepository,
|
|
|
|
private idService: IdService,
|
|
private queueService: QueueService,
|
|
private systemAccountService: SystemAccountService,
|
|
private apRendererService: ApRendererService,
|
|
) {
|
|
this.relaysCache = new MemorySingleCache<MiRelay[]>(1000 * 60 * 10); // 10m
|
|
}
|
|
|
|
@bindThis
|
|
public async addRelay(inbox: string): Promise<MiRelay> {
|
|
const relay = await this.relaysRepository.insertOne({
|
|
id: this.idService.gen(),
|
|
inbox,
|
|
status: 'requesting',
|
|
});
|
|
|
|
const relayActor = await this.systemAccountService.fetch('relay');
|
|
const follow = this.apRendererService.renderFollowRelay(relay, relayActor);
|
|
const activity = this.apRendererService.addContext(follow);
|
|
this.queueService.deliver(relayActor, activity, relay.inbox, false);
|
|
|
|
return relay;
|
|
}
|
|
|
|
@bindThis
|
|
public async removeRelay(inbox: string): Promise<void> {
|
|
const relay = await this.relaysRepository.findOneBy({
|
|
inbox,
|
|
});
|
|
|
|
if (relay == null) {
|
|
throw new Error('relay not found');
|
|
}
|
|
|
|
const relayActor = await this.systemAccountService.fetch('relay');
|
|
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, false);
|
|
|
|
await this.relaysRepository.delete(relay.id);
|
|
}
|
|
|
|
@bindThis
|
|
public async listRelay(): Promise<MiRelay[]> {
|
|
const relays = await this.relaysRepository.find();
|
|
return relays;
|
|
}
|
|
|
|
@bindThis
|
|
public async relayAccepted(id: string): Promise<string> {
|
|
const result = await this.relaysRepository.update(id, {
|
|
status: 'accepted',
|
|
});
|
|
|
|
return JSON.stringify(result);
|
|
}
|
|
|
|
@bindThis
|
|
public async relayRejected(id: string): Promise<string> {
|
|
const result = await this.relaysRepository.update(id, {
|
|
status: 'rejected',
|
|
});
|
|
|
|
return JSON.stringify(result);
|
|
}
|
|
|
|
@bindThis
|
|
private getAcceptedRelays(): Promise<MiRelay[]> {
|
|
return this.relaysCache.fetch(() => this.relaysRepository.findBy({
|
|
status: 'accepted',
|
|
}));
|
|
}
|
|
|
|
@bindThis
|
|
public async isRelayActor(actor: { inbox: string | null; sharedInbox: string | null }): Promise<boolean> {
|
|
const relays = await this.getAcceptedRelays();
|
|
return relays.some(relay =>
|
|
(actor.inbox != null && relay.inbox === actor.inbox)
|
|
|| (actor.sharedInbox != null && relay.inbox === actor.sharedInbox),
|
|
);
|
|
}
|
|
|
|
@bindThis
|
|
public async deliverToRelays(user: { id: MiUser['id']; host: null; }, activity: any): Promise<void> {
|
|
if (activity == null) return;
|
|
|
|
const relays = await this.getAcceptedRelays();
|
|
if (relays.length === 0) return;
|
|
|
|
const copy = deepClone(activity);
|
|
if (!copy.to) copy.to = ['https://www.w3.org/ns/activitystreams#Public'];
|
|
|
|
const signed = await this.apRendererService.attachLdSignature(copy, user);
|
|
|
|
for (const relay of relays) {
|
|
this.queueService.deliver(user, signed, relay.inbox, false);
|
|
}
|
|
}
|
|
}
|