操作ブロックの挙動をサービス側に移動

This commit is contained in:
kakkokari-gtyih 2024-10-18 17:41:51 +09:00
parent 0978aa429a
commit a3d77f3185
10 changed files with 110 additions and 61 deletions

View File

@ -8,6 +8,8 @@ import { ModuleRef } from '@nestjs/core';
import { IdService } from '@/core/IdService.js';
import type { MiUser } from '@/models/User.js';
import type { MiBlocking } from '@/models/Blocking.js';
import type { MiMeta } from '@/models/Meta.js';
import { IdentifiableError } from '@/misc/identifiable-error.js';
import { QueueService } from '@/core/QueueService.js';
import { GlobalEventService } from '@/core/GlobalEventService.js';
import { DI } from '@/di-symbols.js';
@ -20,6 +22,7 @@ import { UserWebhookService } from '@/core/UserWebhookService.js';
import { bindThis } from '@/decorators.js';
import { CacheService } from '@/core/CacheService.js';
import { UserFollowingService } from '@/core/UserFollowingService.js';
import { RoleService } from '@/core/RoleService.js';
@Injectable()
export class UserBlockingService implements OnModuleInit {
@ -29,6 +32,9 @@ export class UserBlockingService implements OnModuleInit {
constructor(
private moduleRef: ModuleRef,
@Inject(DI.meta)
private serverSettings: MiMeta,
@Inject(DI.followRequestsRepository)
private followRequestsRepository: FollowRequestsRepository,
@ -41,6 +47,7 @@ export class UserBlockingService implements OnModuleInit {
@Inject(DI.userListMembershipsRepository)
private userListMembershipsRepository: UserListMembershipsRepository,
private roleService: RoleService,
private cacheService: CacheService,
private userEntityService: UserEntityService,
private idService: IdService,
@ -59,6 +66,15 @@ export class UserBlockingService implements OnModuleInit {
@bindThis
public async block(blocker: MiUser, blockee: MiUser, silent = false) {
// フォロー解除できない(=ブロックもできない)ユーザーの場合
if (
this.serverSettings.forciblyFollowedUsers.includes(blockee.id) &&
!await this.roleService.isModerator(blocker)
) {
throw new IdentifiableError('e2f04d25-0d94-4ac3-a4d8-ba401062741b', 'You cannot block that user due to server policy.');
}
await Promise.all([
this.cancelRequest(blocker, blockee, silent),
this.cancelRequest(blockee, blocker, silent),

View File

@ -27,6 +27,7 @@ import { CacheService } from '@/core/CacheService.js';
import type { Config } from '@/config.js';
import { AccountMoveService } from '@/core/AccountMoveService.js';
import { UtilityService } from '@/core/UtilityService.js';
import { RoleService } from '@/core/RoleService.js';
import type { ThinUser } from '@/queue/types.js';
import Logger from '../logger.js';
@ -73,6 +74,7 @@ export class UserFollowingService implements OnModuleInit {
@Inject(DI.instancesRepository)
private instancesRepository: InstancesRepository,
private roleService: RoleService,
private cacheService: CacheService,
private utilityService: UtilityService,
private userEntityService: UserEntityService,
@ -365,13 +367,22 @@ export class UserFollowingService implements OnModuleInit {
@bindThis
public async unfollow(
follower: {
id: MiUser['id']; host: MiUser['host']; uri: MiUser['host']; inbox: MiUser['inbox']; sharedInbox: MiUser['sharedInbox'];
id: MiUser['id']; host: MiUser['host']; uri: MiUser['host']; isRoot: MiUser['isRoot']; inbox: MiUser['inbox']; sharedInbox: MiUser['sharedInbox'];
},
followee: {
id: MiUser['id']; host: MiUser['host']; uri: MiUser['host']; inbox: MiUser['inbox']; sharedInbox: MiUser['sharedInbox'];
},
silent = false,
): Promise<void> {
// フォロー解除できないユーザーの場合
if (
this.meta.forciblyFollowedUsers.includes(followee.id) &&
!await this.roleService.isModerator(follower)
) {
throw new IdentifiableError('19f25f61-0141-4683-99dc-217a88d633cb', 'You cannot unfollow that user due to server policy.');
}
const following = await this.followingsRepository.findOne({
relations: {
follower: true,

View File

@ -5,19 +5,26 @@
import { Inject, Injectable } from '@nestjs/common';
import { In } from 'typeorm';
import { IdentifiableError } from '@/misc/identifiable-error.js';
import type { MutingsRepository, MiMuting } from '@/models/_.js';
import { IdService } from '@/core/IdService.js';
import type { MiUser } from '@/models/User.js';
import type { MiMeta } from '@/models/Meta.js';
import { DI } from '@/di-symbols.js';
import { bindThis } from '@/decorators.js';
import { RoleService } from '@/core/RoleService.js';
import { CacheService } from '@/core/CacheService.js';
@Injectable()
export class UserMutingService {
constructor(
@Inject(DI.meta)
private serverSettings: MiMeta,
@Inject(DI.mutingsRepository)
private mutingsRepository: MutingsRepository,
private roleService: RoleService,
private idService: IdService,
private cacheService: CacheService,
) {
@ -25,6 +32,15 @@ export class UserMutingService {
@bindThis
public async mute(user: MiUser, target: MiUser, expiresAt: Date | null = null): Promise<void> {
// フォロー解除できない(=ミュートもできない)ユーザーの場合
if (
this.serverSettings.forciblyFollowedUsers.includes(target.id) &&
!await this.roleService.isModerator(user)
) {
throw new IdentifiableError('15273a89-374d-49fa-8df6-8bb3feeea455', 'You cannot mute that user due to server policy.');
}
await this.mutingsRepository.insert({
id: this.idService.gen(),
expiresAt: expiresAt ?? null,

View File

@ -10,16 +10,23 @@ import type { MiRenoteMuting } from '@/models/RenoteMuting.js';
import { IdService } from '@/core/IdService.js';
import type { MiUser } from '@/models/User.js';
import type { MiMeta } from '@/models/Meta.js';
import { DI } from '@/di-symbols.js';
import { bindThis } from '@/decorators.js';
import { RoleService } from '@/core/RoleService.js';
import { CacheService } from '@/core/CacheService.js';
import { IdentifiableError } from '@/misc/identifiable-error.js';
@Injectable()
export class UserRenoteMutingService {
constructor(
@Inject(DI.meta)
private serverSettings: MiMeta,
@Inject(DI.renoteMutingsRepository)
private renoteMutingsRepository: RenoteMutingsRepository,
private roleService: RoleService,
private idService: IdService,
private cacheService: CacheService,
) {
@ -27,6 +34,15 @@ export class UserRenoteMutingService {
@bindThis
public async mute(user: MiUser, target: MiUser, expiresAt: Date | null = null): Promise<void> {
// フォロー解除できない(=リノートミュートもできない)ユーザーの場合
if (
this.serverSettings.forciblyFollowedUsers.includes(target.id) &&
!await this.roleService.isModerator(user)
) {
throw new IdentifiableError('15273a89-374d-49fa-8df6-8bb3feeea455', 'You cannot mute that user due to server policy.');
}
await this.renoteMutingsRepository.insert({
id: this.idService.gen(),
muterId: user.id,

View File

@ -17,6 +17,7 @@ import { bindThis } from '@/decorators.js';
import { QueueLoggerService } from '../QueueLoggerService.js';
import type * as Bull from 'bullmq';
import type { DbUserImportJobData } from '../types.js';
import { IdentifiableError } from '@/misc/identifiable-error.js';
@Injectable()
export class ImportMutingProcessorService {
@ -90,7 +91,13 @@ export class ImportMutingProcessorService {
this.logger.info(`Mute[${linenum}] ${target.id} ...`);
await this.userMutingService.mute(user, target);
await this.userMutingService.mute(user, target).catch((err) => {
if (err instanceof IdentifiableError && err.id === '15273a89-374d-49fa-8df6-8bb3feeea455') {
// フォロー解除できない(=ミュートもできない)ユーザー。動作は正常のため、エラーを無視する
return;
}
throw err;
});
} catch (e) {
this.logger.warn(`Error in line:${linenum} ${e}`);
}

View File

@ -16,6 +16,7 @@ import { MiLocalUser, MiRemoteUser } from '@/models/User.js';
import { RelationshipJobData } from '../types.js';
import { QueueLoggerService } from '../QueueLoggerService.js';
import type * as Bull from 'bullmq';
import { IdentifiableError } from '@/misc/identifiable-error.js';
@Injectable()
export class RelationshipProcessorService {
@ -50,7 +51,13 @@ export class RelationshipProcessorService {
this.usersRepository.findOneByOrFail({ id: job.data.from.id }),
this.usersRepository.findOneByOrFail({ id: job.data.to.id }),
]) as [MiLocalUser | MiRemoteUser, MiLocalUser | MiRemoteUser];
await this.userFollowingService.unfollow(follower, followee, job.data.silent);
await this.userFollowingService.unfollow(follower, followee, job.data.silent).catch((err) => {
if (err instanceof IdentifiableError && err.id === '19f25f61-0141-4683-99dc-217a88d633cb') {
// フォロー解除できないユーザー。動作は正常のため、エラーを無視する
return;
}
throw err;
});
return 'ok';
}
@ -61,7 +68,13 @@ export class RelationshipProcessorService {
this.usersRepository.findOneByOrFail({ id: job.data.from.id }),
this.usersRepository.findOneByOrFail({ id: job.data.to.id }),
]);
await this.userBlockingService.block(blockee, blocker, job.data.silent);
await this.userBlockingService.block(blockee, blocker, job.data.silent).catch((err) => {
if (err instanceof IdentifiableError && err.id === 'e2f04d25-0d94-4ac3-a4d8-ba401062741b') {
// フォロー解除できない(=ブロックもできない)ユーザー。動作は正常のため、エラーを無視する
return;
}
throw err;
});
return 'ok';
}

View File

@ -7,13 +7,12 @@ import ms from 'ms';
import { Inject, Injectable } from '@nestjs/common';
import { Endpoint } from '@/server/api/endpoint-base.js';
import type { UsersRepository, BlockingsRepository } from '@/models/_.js';
import type { MiMeta } from '@/models/Meta.js';
import { UserEntityService } from '@/core/entities/UserEntityService.js';
import { UserBlockingService } from '@/core/UserBlockingService.js';
import { RoleService } from '@/core/RoleService.js';
import { DI } from '@/di-symbols.js';
import { GetterService } from '@/server/api/GetterService.js';
import { ApiError } from '../../error.js';
import { IdentifiableError } from '@/misc/identifiable-error.js';
export const meta = {
tags: ['account'],
@ -72,16 +71,12 @@ export const paramDef = {
@Injectable()
export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export
constructor(
@Inject(DI.meta)
private serverSettings: MiMeta,
@Inject(DI.usersRepository)
private usersRepository: UsersRepository,
@Inject(DI.blockingsRepository)
private blockingsRepository: BlockingsRepository,
private roleService: RoleService,
private userEntityService: UserEntityService,
private getterService: GetterService,
private userBlockingService: UserBlockingService,
@ -96,7 +91,9 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
// Get blockee
const blockee = await this.getterService.getUser(ps.userId).catch(err => {
if (err.id === '15348ddd-432d-49c2-8a5a-8069753becff') throw new ApiError(meta.errors.noSuchUser);
if (err instanceof IdentifiableError && err.id === '15348ddd-432d-49c2-8a5a-8069753becff') {
throw new ApiError(meta.errors.noSuchUser);
}
throw err;
});
@ -112,14 +109,11 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
throw new ApiError(meta.errors.alreadyBlocking);
}
if (
this.serverSettings.forciblyFollowedUsers.includes(blockee.id) &&
!await this.roleService.isModerator(blocker)
) {
throw new ApiError(meta.errors.cannotBlockDueToServerPolicy);
}
await this.userBlockingService.block(blocker, blockee);
await this.userBlockingService.block(blocker, blockee).catch((err) => {
if (err instanceof IdentifiableError && err.id === meta.errors.cannotBlockDueToServerPolicy.id) {
throw new ApiError(meta.errors.cannotBlockDueToServerPolicy);
}
});
return await this.userEntityService.pack(blockee.id, blocker, {
schema: 'UserDetailedNotMe',

View File

@ -7,10 +7,9 @@ import ms from 'ms';
import { Inject, Injectable } from '@nestjs/common';
import { Endpoint } from '@/server/api/endpoint-base.js';
import type { FollowingsRepository } from '@/models/_.js';
import type { MiMeta } from '@/models/Meta.js';
import { IdentifiableError } from '@/misc/identifiable-error.js';
import { UserEntityService } from '@/core/entities/UserEntityService.js';
import { UserFollowingService } from '@/core/UserFollowingService.js';
import { RoleService } from '@/core/RoleService.js';
import { DI } from '@/di-symbols.js';
import { GetterService } from '@/server/api/GetterService.js';
import { ApiError } from '../../error.js';
@ -72,13 +71,9 @@ export const paramDef = {
@Injectable()
export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export
constructor(
@Inject(DI.meta)
private serverSettings: MiMeta,
@Inject(DI.followingsRepository)
private followingsRepository: FollowingsRepository,
private roleService: RoleService,
private userEntityService: UserEntityService,
private getterService: GetterService,
private userFollowingService: UserFollowingService,
@ -109,14 +104,11 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
throw new ApiError(meta.errors.notFollowing);
}
if (
this.serverSettings.forciblyFollowedUsers.includes(followee.id) &&
!await this.roleService.isModerator(follower)
) {
throw new ApiError(meta.errors.cannotUnfollowDueToServerPolicy);
}
await this.userFollowingService.unfollow(follower, followee);
await this.userFollowingService.unfollow(follower, followee).catch((err) => {
if (err instanceof IdentifiableError && err.id === meta.errors.cannotUnfollowDueToServerPolicy.id) {
throw new ApiError(meta.errors.cannotUnfollowDueToServerPolicy);
}
});
return await this.userEntityService.pack(followee.id, me);
});

View File

@ -7,12 +7,11 @@ import { Inject, Injectable } from '@nestjs/common';
import ms from 'ms';
import { Endpoint } from '@/server/api/endpoint-base.js';
import type { MutingsRepository } from '@/models/_.js';
import type { MiMeta } from '@/models/Meta.js';
import { DI } from '@/di-symbols.js';
import { GetterService } from '@/server/api/GetterService.js';
import { UserMutingService } from '@/core/UserMutingService.js';
import { RoleService } from '@/core/RoleService.js';
import { ApiError } from '../../error.js';
import { IdentifiableError } from '@/misc/identifiable-error.js';
export const meta = {
tags: ['account'],
@ -71,13 +70,9 @@ export const paramDef = {
@Injectable()
export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export
constructor(
@Inject(DI.meta)
private serverSettings: MiMeta,
@Inject(DI.mutingsRepository)
private mutingsRepository: MutingsRepository,
private roleService: RoleService,
private getterService: GetterService,
private userMutingService: UserMutingService,
) {
@ -107,18 +102,15 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
throw new ApiError(meta.errors.alreadyMuting);
}
if (
this.serverSettings.forciblyFollowedUsers.includes(mutee.id) &&
!await this.roleService.isModerator(muter)
) {
throw new ApiError(meta.errors.cannotMuteDueToServerPolicy);
}
if (ps.expiresAt && ps.expiresAt <= Date.now()) {
return;
}
await this.userMutingService.mute(muter, mutee, ps.expiresAt ? new Date(ps.expiresAt) : null);
await this.userMutingService.mute(muter, mutee, ps.expiresAt ? new Date(ps.expiresAt) : null).catch((err) => {
if (err instanceof IdentifiableError && err.id === meta.errors.cannotMuteDueToServerPolicy.id) {
throw new ApiError(meta.errors.cannotMuteDueToServerPolicy);
}
});
});
}
}

View File

@ -9,10 +9,9 @@ import { Endpoint } from '@/server/api/endpoint-base.js';
import { DI } from '@/di-symbols.js';
import { GetterService } from '@/server/api/GetterService.js';
import { ApiError } from '../../error.js';
import { RoleService } from '@/core/RoleService.js';
import { UserRenoteMutingService } from '@/core/UserRenoteMutingService.js';
import type { RenoteMutingsRepository } from '@/models/_.js';
import type { MiMeta } from '@/models/Meta.js';
import { IdentifiableError } from '@/misc/identifiable-error.js';
export const meta = {
tags: ['account'],
@ -66,13 +65,9 @@ export const paramDef = {
@Injectable()
export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export
constructor(
@Inject(DI.meta)
private serverSettings: MiMeta,
@Inject(DI.renoteMutingsRepository)
private renoteMutingsRepository: RenoteMutingsRepository,
private roleService: RoleService,
private getterService: GetterService,
private userRenoteMutingService: UserRenoteMutingService,
) {
@ -102,15 +97,12 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
throw new ApiError(meta.errors.alreadyMuting);
}
if (
this.serverSettings.forciblyFollowedUsers.includes(mutee.id) &&
!await this.roleService.isModerator(muter)
) {
throw new ApiError(meta.errors.cannotMuteDueToServerPolicy);
}
// Create mute
await this.userRenoteMutingService.mute(muter, mutee);
await this.userRenoteMutingService.mute(muter, mutee).catch((err) => {
if (err instanceof IdentifiableError && err.id === meta.errors.cannotMuteDueToServerPolicy.id) {
throw new ApiError(meta.errors.cannotMuteDueToServerPolicy);
}
});
});
}
}