This commit is contained in:
parent
e6112c092b
commit
e48590f53e
|
@ -6,7 +6,7 @@
|
|||
import { Inject, Injectable } from '@nestjs/common';
|
||||
import { DI } from '@/di-symbols.js';
|
||||
import type { UsersRepository } from '@/models/_.js';
|
||||
import type { MiUser } from '@/models/User.js';
|
||||
import type { MiLocalUser, MiUser } from '@/models/User.js';
|
||||
import { ApRendererService } from '@/core/activitypub/ApRendererService.js';
|
||||
import { RelayService } from '@/core/RelayService.js';
|
||||
import { ApDeliverManagerService } from '@/core/activitypub/ApDeliverManagerService.js';
|
||||
|
@ -26,16 +26,43 @@ export class AccountUpdateService {
|
|||
) {
|
||||
}
|
||||
|
||||
private async createUpdatePersonActivity(user: MiLocalUser) {
|
||||
return this.apRendererService.addContext(
|
||||
this.apRendererService.renderUpdate(
|
||||
await this.apRendererService.renderPerson(user), user
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
@bindThis
|
||||
public async publishToFollowers(userId: MiUser['id']) {
|
||||
const user = await this.usersRepository.findOneBy({ id: userId });
|
||||
if (user == null) throw new Error('user not found');
|
||||
|
||||
// フォロワーがリモートユーザーかつ投稿者がローカルユーザーならUpdateを配信
|
||||
// 投稿者がローカルユーザーならUpdateを配信
|
||||
if (this.userEntityService.isLocalUser(user)) {
|
||||
const content = this.apRendererService.addContext(this.apRendererService.renderUpdate(await this.apRendererService.renderPerson(user), user));
|
||||
const content = await this.createUpdatePersonActivity(user);
|
||||
this.apDeliverManagerService.deliverToFollowers(user, content);
|
||||
this.relayService.deliverToRelays(user, content);
|
||||
}
|
||||
}
|
||||
|
||||
@bindThis
|
||||
async publishToFollowersAndSharedInboxAndRelays(userId: MiUser['id']) {
|
||||
const user = await this.usersRepository.findOneBy({ id: userId });
|
||||
if (user == null) throw new Error('user not found');
|
||||
|
||||
// 投稿者がローカルユーザーならUpdateを配信
|
||||
if (this.userEntityService.isLocalUser(user)) {
|
||||
const content = await this.createUpdatePersonActivity(user);
|
||||
const manager = this.apDeliverManagerService.createDeliverManager(user, content);
|
||||
manager.addAllKnowingSharedInboxRecipe();
|
||||
manager.addFollowersRecipe();
|
||||
|
||||
await Promise.allSettled([
|
||||
manager.execute(),
|
||||
this.relayService.deliverToRelays(user, content),
|
||||
]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,14 +4,12 @@
|
|||
*/
|
||||
|
||||
import { Inject, Injectable } from '@nestjs/common';
|
||||
import { Not, IsNull } from 'typeorm';
|
||||
import type { FollowingsRepository, FollowRequestsRepository, UsersRepository } from '@/models/_.js';
|
||||
import type { MiUser } from '@/models/User.js';
|
||||
import { GlobalEventService } from '@/core/GlobalEventService.js';
|
||||
import { DI } from '@/di-symbols.js';
|
||||
import { UserEntityService } from '@/core/entities/UserEntityService.js';
|
||||
import { bindThis } from '@/decorators.js';
|
||||
import { ApDeliverManagerService } from './activitypub/ApDeliverManagerService.js';
|
||||
import { ModerationLogService } from '@/core/ModerationLogService.js';
|
||||
import { AccountUpdateService } from '@/core/AccountUpdateService.js';
|
||||
|
||||
|
@ -29,9 +27,7 @@ export class UserSuspendService {
|
|||
|
||||
private userEntityService: UserEntityService,
|
||||
private globalEventService: GlobalEventService,
|
||||
private accountUpadateService: AccountUpdateService,
|
||||
private apRendererService: ApRendererService,
|
||||
private apDeliverManagerService: ApDeliverManagerService,
|
||||
private accountUpdateService: AccountUpdateService,
|
||||
private moderationLogService: ModerationLogService,
|
||||
) {
|
||||
}
|
||||
|
@ -84,12 +80,7 @@ export class UserSuspendService {
|
|||
});
|
||||
|
||||
if (this.userEntityService.isLocalUser(user)) {
|
||||
this.accountUpadateService.publishToFollowers(user.id);
|
||||
const content = this.apRendererService.addContext(this.apRendererService.renderDelete(this.userEntityService.genLocalUserUri(user.id), user));
|
||||
const manager = this.apDeliverManagerService.createDeliverManager(user, content);
|
||||
manager.addAllKnowingSharedInboxRecipe();
|
||||
manager.addFollowersRecipe();
|
||||
manager.execute();
|
||||
this.accountUpdateService.publishToFollowersAndSharedInboxAndRelays(user.id);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -98,12 +89,7 @@ export class UserSuspendService {
|
|||
this.globalEventService.publishInternalEvent('userChangeSuspendedState', { id: user.id, isSuspended: false });
|
||||
|
||||
if (this.userEntityService.isLocalUser(user)) {
|
||||
this.accountUpadateService.publishToFollowers(user.id);
|
||||
const content = this.apRendererService.addContext(this.apRendererService.renderUndo(this.apRendererService.renderDelete(this.userEntityService.genLocalUserUri(user.id), user), user));
|
||||
const manager = this.apDeliverManagerService.createDeliverManager(user, content);
|
||||
manager.addAllKnowingSharedInboxRecipe();
|
||||
manager.addFollowersRecipe();
|
||||
manager.execute();
|
||||
this.accountUpdateService.publishToFollowersAndSharedInboxAndRelays(user.id);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -26,6 +26,10 @@ import { ApRendererService } from '@/core/activitypub/ApRendererService.js';
|
|||
import { ModerationLogService } from '@/core/ModerationLogService.js';
|
||||
import { secureRndstr } from '@/misc/secure-rndstr.js';
|
||||
import { randomString } from '../utils.js';
|
||||
import { AccountUpdateService } from '@/core/AccountUpdateService.js';
|
||||
import { ApDeliverManagerService } from '@/core/activitypub/ApDeliverManagerService.js';
|
||||
import { RelayService } from '@/core/RelayService.js';
|
||||
import { ApLoggerService } from '@/core/activitypub/ApLoggerService.js';
|
||||
|
||||
function genHost() {
|
||||
return randomString() + '.example.com';
|
||||
|
@ -42,6 +46,9 @@ describe('UserSuspendService', () => {
|
|||
let globalEventService: jest.Mocked<GlobalEventService>;
|
||||
let apRendererService: jest.Mocked<ApRendererService>;
|
||||
let moderationLogService: jest.Mocked<ModerationLogService>;
|
||||
let accountUpdateService: jest.Mocked<AccountUpdateService>;
|
||||
let apDeliverManagerService: jest.Mocked<ApDeliverManagerService>;
|
||||
let relayService: jest.Mocked<RelayService>;
|
||||
|
||||
async function createUser(data: Partial<MiUser> = {}): Promise<MiUser> {
|
||||
const user = {
|
||||
|
@ -84,6 +91,8 @@ describe('UserSuspendService', () => {
|
|||
imports: [GlobalModule],
|
||||
providers: [
|
||||
UserSuspendService,
|
||||
AccountUpdateService,
|
||||
ApDeliverManagerService,
|
||||
{
|
||||
provide: UserEntityService,
|
||||
useFactory: () => ({
|
||||
|
@ -94,6 +103,7 @@ describe('UserSuspendService', () => {
|
|||
{
|
||||
provide: QueueService,
|
||||
useFactory: () => ({
|
||||
deliverMany: jest.fn(),
|
||||
deliver: jest.fn(),
|
||||
}),
|
||||
},
|
||||
|
@ -103,20 +113,38 @@ describe('UserSuspendService', () => {
|
|||
publishInternalEvent: jest.fn(),
|
||||
}),
|
||||
},
|
||||
{
|
||||
provide: ApRendererService,
|
||||
useFactory: () => ({
|
||||
addContext: jest.fn(),
|
||||
renderDelete: jest.fn(),
|
||||
renderUndo: jest.fn(),
|
||||
}),
|
||||
},
|
||||
{
|
||||
provide: ModerationLogService,
|
||||
useFactory: () => ({
|
||||
log: jest.fn(),
|
||||
}),
|
||||
},
|
||||
{
|
||||
provide: RelayService,
|
||||
useFactory: () => ({
|
||||
deliverToRelays: jest.fn(),
|
||||
}),
|
||||
},
|
||||
{
|
||||
provide: ApRendererService,
|
||||
useFactory: () => ({
|
||||
renderDelete: jest.fn(),
|
||||
renderUndo: jest.fn(),
|
||||
renderPerson: jest.fn(),
|
||||
renderUpdate: jest.fn(),
|
||||
addContext: jest.fn(),
|
||||
}),
|
||||
},
|
||||
{
|
||||
provide: ApLoggerService,
|
||||
useFactory: () => ({
|
||||
logger: {
|
||||
createSubLogger: jest.fn().mockReturnValue({
|
||||
info: jest.fn(), error: jest.fn(), warn: jest.fn(), debug: jest.fn(),
|
||||
}),
|
||||
},
|
||||
}),
|
||||
},
|
||||
],
|
||||
}).compile();
|
||||
|
||||
|
@ -131,6 +159,9 @@ describe('UserSuspendService', () => {
|
|||
globalEventService = app.get<GlobalEventService>(GlobalEventService) as jest.Mocked<GlobalEventService>;
|
||||
apRendererService = app.get<ApRendererService>(ApRendererService) as jest.Mocked<ApRendererService>;
|
||||
moderationLogService = app.get<ModerationLogService>(ModerationLogService) as jest.Mocked<ModerationLogService>;
|
||||
accountUpdateService = app.get<AccountUpdateService>(AccountUpdateService) as jest.Mocked<AccountUpdateService>;
|
||||
apDeliverManagerService = app.get<ApDeliverManagerService>(ApDeliverManagerService) as jest.Mocked<ApDeliverManagerService>;
|
||||
relayService = app.get<RelayService>(RelayService) as jest.Mocked<RelayService>;
|
||||
|
||||
// Reset mocks
|
||||
jest.clearAllMocks();
|
||||
|
@ -311,42 +342,46 @@ describe('UserSuspendService', () => {
|
|||
});
|
||||
|
||||
describe('ActivityPub delivery', () => {
|
||||
test('should deliver Delete activity on suspend of local user', async () => {
|
||||
test('should deliver Update Person activity on suspend of local user', async () => {
|
||||
const localUser = await createUser({ host: null });
|
||||
const moderator = await createUser();
|
||||
|
||||
userEntityService.isLocalUser.mockReturnValue(true);
|
||||
userEntityService.genLocalUserUri.mockReturnValue(`https://example.com/users/${localUser.id}`);
|
||||
apRendererService.renderDelete.mockReturnValue({ type: 'Delete' } as any);
|
||||
apRendererService.addContext.mockReturnValue({ '@context': '...', type: 'Delete' } as any);
|
||||
apRendererService.renderUpdate.mockReturnValue({ type: 'Update' } as any);
|
||||
apRendererService.renderPerson.mockReturnValue({ type: 'Person' } as any);
|
||||
apRendererService.addContext.mockReturnValue({ '@context': '...', type: 'Update' } as any);
|
||||
|
||||
await userSuspendService.suspend(localUser, moderator);
|
||||
await setTimeout(250);
|
||||
|
||||
// ActivityPub配信が呼ばれているかチェック
|
||||
expect(userEntityService.isLocalUser).toHaveBeenCalledWith(localUser);
|
||||
expect(apRendererService.renderDelete).toHaveBeenCalled();
|
||||
expect(apRendererService.renderUpdate).toHaveBeenCalled();
|
||||
expect(apRendererService.renderPerson).toHaveBeenCalled();
|
||||
expect(apRendererService.addContext).toHaveBeenCalled();
|
||||
expect(queueService.deliverMany).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
test('should deliver Undo Delete activity on unsuspend of local user', async () => {
|
||||
test('should deliver Update Person activity on unsuspend of local user', async () => {
|
||||
const localUser = await createUser({ host: null, isSuspended: true });
|
||||
const moderator = await createUser();
|
||||
|
||||
userEntityService.isLocalUser.mockReturnValue(true);
|
||||
userEntityService.genLocalUserUri.mockReturnValue(`https://example.com/users/${localUser.id}`);
|
||||
apRendererService.renderDelete.mockReturnValue({ type: 'Delete' } as any);
|
||||
apRendererService.renderUndo.mockReturnValue({ type: 'Undo' } as any);
|
||||
apRendererService.addContext.mockReturnValue({ '@context': '...', type: 'Undo' } as any);
|
||||
apRendererService.renderUpdate.mockReturnValue({ type: 'Update' } as any);
|
||||
apRendererService.renderPerson.mockReturnValue({ type: 'Person' } as any);
|
||||
apRendererService.addContext.mockReturnValue({ '@context': '...', type: 'Update' } as any);
|
||||
|
||||
await userSuspendService.unsuspend(localUser, moderator);
|
||||
await userSuspendService.suspend(localUser, moderator);
|
||||
await setTimeout(250);
|
||||
|
||||
// ActivityPub配信が呼ばれているかチェック
|
||||
expect(userEntityService.isLocalUser).toHaveBeenCalledWith(localUser);
|
||||
expect(apRendererService.renderDelete).toHaveBeenCalled();
|
||||
expect(apRendererService.renderUndo).toHaveBeenCalled();
|
||||
expect(apRendererService.renderUpdate).toHaveBeenCalled();
|
||||
expect(apRendererService.renderPerson).toHaveBeenCalled();
|
||||
expect(apRendererService.addContext).toHaveBeenCalled();
|
||||
expect(queueService.deliverMany).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
test('should not deliver any activity on suspend of remote user', async () => {
|
||||
|
|
Loading…
Reference in New Issue