From 3dd5af3003731a299501e2b9a994efad826e4d18 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=B4=87=E5=B3=B0=20=E6=9C=94=E8=8F=AF?= Date: Mon, 18 Nov 2024 15:30:53 +0900 Subject: [PATCH 01/16] =?UTF-8?q?Add:=20Blocking=E3=83=86=E3=83=BC?= =?UTF-8?q?=E3=83=96=E3=83=AB=E3=81=ABisReactionBlock=E3=82=AB=E3=83=A9?= =?UTF-8?q?=E3=83=A0=E3=82=92=E8=BF=BD=E5=8A=A0=E3=81=97=E3=80=81blocking-?= =?UTF-8?q?reaction-user=E3=82=A8=E3=83=B3=E3=83=89=E3=83=9D=E3=82=A4?= =?UTF-8?q?=E3=83=B3=E3=83=88=E3=82=92=E8=BF=BD=E5=8A=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ユーザー単位でリアクションをブロックするため、blocking-reaction-userエンドポイントを追加。 ロジックは別途実装する。 --- .../1731898598469-addBlockingReactionUser.js | 20 ++ packages/backend/src/core/CacheService.ts | 22 +- packages/backend/src/core/CoreModule.ts | 6 + .../backend/src/core/GlobalEventService.ts | 2 + .../backend/src/core/UserBlockingService.ts | 21 +- .../src/core/UserReactionBlockingService.ts | 111 ++++++++++ .../src/core/entities/UserEntityService.ts | 42 ++++ packages/backend/src/models/Blocking.ts | 7 + .../src/models/json-schema/blocking.ts | 4 + .../backend/src/models/json-schema/user.ts | 8 + .../backend/src/server/api/EndpointsModule.ts | 9 + packages/backend/src/server/api/endpoints.ts | 6 + .../blocking-reaction-user/create.ts | 110 +++++++++ .../blocking-reaction-user/delete.ts | 111 ++++++++++ .../endpoints/blocking-reaction-user/list.ts | 62 ++++++ .../src/server/api/endpoints/blocking/list.ts | 3 +- .../test/unit/entities/UserEntityService.ts | 59 +++++ packages/misskey-js/etc/misskey-js.api.md | 24 ++ .../misskey-js/src/autogen/apiClientJSDoc.ts | 33 +++ packages/misskey-js/src/autogen/endpoint.ts | 9 + packages/misskey-js/src/autogen/entities.ts | 6 + packages/misskey-js/src/autogen/types.ts | 208 ++++++++++++++++++ 22 files changed, 868 insertions(+), 15 deletions(-) create mode 100644 packages/backend/migration/1731898598469-addBlockingReactionUser.js create mode 100644 packages/backend/src/core/UserReactionBlockingService.ts create mode 100644 packages/backend/src/server/api/endpoints/blocking-reaction-user/create.ts create mode 100644 packages/backend/src/server/api/endpoints/blocking-reaction-user/delete.ts create mode 100644 packages/backend/src/server/api/endpoints/blocking-reaction-user/list.ts diff --git a/packages/backend/migration/1731898598469-addBlockingReactionUser.js b/packages/backend/migration/1731898598469-addBlockingReactionUser.js new file mode 100644 index 0000000000..6cf9d4ca87 --- /dev/null +++ b/packages/backend/migration/1731898598469-addBlockingReactionUser.js @@ -0,0 +1,20 @@ +/* + * SPDX-FileCopyrightText: syuilo and misskey-project + * SPDX-License-Identifier: AGPL-3.0-only + */ + +export class AddBlockingReactionUser1731898598469 { + name = 'AddBlockingReactionUser1731898598469' + + async up(queryRunner) { + await queryRunner.query(`ALTER TABLE "blocking" ADD "isReactionBlock" boolean NOT NULL DEFAULT false`); + await queryRunner.query(`COMMENT ON COLUMN "blocking"."isReactionBlock" IS 'Whether the blockee is a reaction block.'`); + await queryRunner.query(`CREATE INDEX "IDX_7b0698c38d27a5554bed4858bd" ON "blocking" ("isReactionBlock") `); + } + + async down(queryRunner) { + await queryRunner.query(`DELETE FROM blocking WHERE "isReactionBlock" = 'true'`); // blockingテーブルのisReactionBlockカラムがtrueの行を削除する + await queryRunner.query(`DROP INDEX "IDX_7b0698c38d27a5554bed4858bd"`); + await queryRunner.query(`ALTER TABLE "blocking" DROP COLUMN "isReactionBlock"`); + } +} diff --git a/packages/backend/src/core/CacheService.ts b/packages/backend/src/core/CacheService.ts index 6725ebe75b..db8c3c372d 100644 --- a/packages/backend/src/core/CacheService.ts +++ b/packages/backend/src/core/CacheService.ts @@ -24,6 +24,8 @@ export class CacheService implements OnApplicationShutdown { public userMutingsCache: RedisKVCache>; public userBlockingCache: RedisKVCache>; public userBlockedCache: RedisKVCache>; // NOTE: 「被」Blockキャッシュ + public userReactionBlockingCache: RedisKVCache>; // NOTE: リアクションBlockキャッシュ + public userReactionBlockedCache: RedisKVCache>; // NOTE: 「被」リアクションBlockキャッシュ public renoteMutingsCache: RedisKVCache>; public userFollowingsCache: RedisKVCache | undefined>>; @@ -80,7 +82,7 @@ export class CacheService implements OnApplicationShutdown { this.userBlockingCache = new RedisKVCache>(this.redisClient, 'userBlocking', { lifetime: 1000 * 60 * 30, // 30m memoryCacheLifetime: 1000 * 60, // 1m - fetcher: (key) => this.blockingsRepository.find({ where: { blockerId: key }, select: ['blockeeId'] }).then(xs => new Set(xs.map(x => x.blockeeId))), + fetcher: (key) => this.blockingsRepository.find({ where: { blockerId: key, isReactionBlock: false }, select: ['blockeeId'] }).then(xs => new Set(xs.map(x => x.blockeeId))), toRedisConverter: (value) => JSON.stringify(Array.from(value)), fromRedisConverter: (value) => new Set(JSON.parse(value)), }); @@ -88,7 +90,23 @@ export class CacheService implements OnApplicationShutdown { this.userBlockedCache = new RedisKVCache>(this.redisClient, 'userBlocked', { lifetime: 1000 * 60 * 30, // 30m memoryCacheLifetime: 1000 * 60, // 1m - fetcher: (key) => this.blockingsRepository.find({ where: { blockeeId: key }, select: ['blockerId'] }).then(xs => new Set(xs.map(x => x.blockerId))), + fetcher: (key) => this.blockingsRepository.find({ where: { blockeeId: key, isReactionBlock: false }, select: ['blockerId'] }).then(xs => new Set(xs.map(x => x.blockerId))), + toRedisConverter: (value) => JSON.stringify(Array.from(value)), + fromRedisConverter: (value) => new Set(JSON.parse(value)), + }); + + this.userReactionBlockingCache = new RedisKVCache>(this.redisClient, 'userReactionBlocking', { + lifetime: 1000 * 60 * 30, // 30m + memoryCacheLifetime: 1000 * 60, // 1m + fetcher: (key) => this.blockingsRepository.find({ where: { blockerId: key, isReactionBlock: true }, select: ['blockeeId'] }).then(xs => new Set(xs.map(x => x.blockeeId))), + toRedisConverter: (value) => JSON.stringify(Array.from(value)), + fromRedisConverter: (value) => new Set(JSON.parse(value)), + }); + + this.userReactionBlockedCache = new RedisKVCache>(this.redisClient, 'userReactionBlocked', { + lifetime: 1000 * 60 * 30, // 30m + memoryCacheLifetime: 1000 * 60, // 1m + fetcher: (key) => this.blockingsRepository.find({ where: { blockeeId: key, isReactionBlock: true }, select: ['blockerId'] }).then(xs => new Set(xs.map(x => x.blockerId))), toRedisConverter: (value) => JSON.stringify(Array.from(value)), fromRedisConverter: (value) => new Set(JSON.parse(value)), }); diff --git a/packages/backend/src/core/CoreModule.ts b/packages/backend/src/core/CoreModule.ts index 734d135648..393bdf26f7 100644 --- a/packages/backend/src/core/CoreModule.ts +++ b/packages/backend/src/core/CoreModule.ts @@ -58,6 +58,7 @@ import { S3Service } from './S3Service.js'; import { SignupService } from './SignupService.js'; import { WebAuthnService } from './WebAuthnService.js'; import { UserBlockingService } from './UserBlockingService.js'; +import { UserReactionBlockingService } from './UserReactionBlockingService.js'; import { CacheService } from './CacheService.js'; import { UserService } from './UserService.js'; import { UserFollowingService } from './UserFollowingService.js'; @@ -202,6 +203,7 @@ const $S3Service: Provider = { provide: 'S3Service', useExisting: S3Service }; const $SignupService: Provider = { provide: 'SignupService', useExisting: SignupService }; const $WebAuthnService: Provider = { provide: 'WebAuthnService', useExisting: WebAuthnService }; const $UserBlockingService: Provider = { provide: 'UserBlockingService', useExisting: UserBlockingService }; +const $UserReactionBlockingService: Provider = { provide: 'UserReactionBlockingService', useExisting: UserReactionBlockingService }; const $CacheService: Provider = { provide: 'CacheService', useExisting: CacheService }; const $UserService: Provider = { provide: 'UserService', useExisting: UserService }; const $UserFollowingService: Provider = { provide: 'UserFollowingService', useExisting: UserFollowingService }; @@ -353,6 +355,7 @@ const $ApQuestionService: Provider = { provide: 'ApQuestionService', useExisting SignupService, WebAuthnService, UserBlockingService, + UserReactionBlockingService, CacheService, UserService, UserFollowingService, @@ -500,6 +503,7 @@ const $ApQuestionService: Provider = { provide: 'ApQuestionService', useExisting $SignupService, $WebAuthnService, $UserBlockingService, + $UserReactionBlockingService, $CacheService, $UserService, $UserFollowingService, @@ -648,6 +652,7 @@ const $ApQuestionService: Provider = { provide: 'ApQuestionService', useExisting SignupService, WebAuthnService, UserBlockingService, + UserReactionBlockingService, CacheService, UserService, UserFollowingService, @@ -794,6 +799,7 @@ const $ApQuestionService: Provider = { provide: 'ApQuestionService', useExisting $SignupService, $WebAuthnService, $UserBlockingService, + $UserReactionBlockingService, $CacheService, $UserService, $UserFollowingService, diff --git a/packages/backend/src/core/GlobalEventService.ts b/packages/backend/src/core/GlobalEventService.ts index 03646ff566..d113848c20 100644 --- a/packages/backend/src/core/GlobalEventService.ts +++ b/packages/backend/src/core/GlobalEventService.ts @@ -223,6 +223,8 @@ export interface InternalEventTypes { unfollow: { followerId: MiUser['id']; followeeId: MiUser['id']; }; blockingCreated: { blockerId: MiUser['id']; blockeeId: MiUser['id']; }; blockingDeleted: { blockerId: MiUser['id']; blockeeId: MiUser['id']; }; + blockingReactionCreated: { blockerId: MiUser['id']; blockeeId: MiUser['id']; }; + blockingReactionDeleted: { blockerId: MiUser['id']; blockeeId: MiUser['id']; }; policiesUpdated: MiRole['policies']; roleCreated: MiRole; roleDeleted: MiRole; diff --git a/packages/backend/src/core/UserBlockingService.ts b/packages/backend/src/core/UserBlockingService.ts index 2f1310b8ef..0234042903 100644 --- a/packages/backend/src/core/UserBlockingService.ts +++ b/packages/backend/src/core/UserBlockingService.ts @@ -73,6 +73,7 @@ export class UserBlockingService implements OnModuleInit { blockerId: blocker.id, blockee, blockeeId: blockee.id, + isReactionBlock: false, } as MiBlocking; await this.blockingsRepository.insert(blocking); @@ -160,6 +161,7 @@ export class UserBlockingService implements OnModuleInit { const blocking = await this.blockingsRepository.findOneBy({ blockerId: blocker.id, blockeeId: blockee.id, + isReactionBlock: false, }); if (blocking == null) { @@ -169,28 +171,23 @@ export class UserBlockingService implements OnModuleInit { // Since we already have the blocker and blockee, we do not need to fetch // them in the query above and can just manually insert them here. - blocking.blocker = blocker; - blocking.blockee = blockee; + // But we don't need to do this because we are not using them in this function. + // blocking.blocker = blocker; + // blocking.blockee = blockee; await this.blockingsRepository.delete(blocking.id); - this.cacheService.userBlockingCache.refresh(blocker.id); - this.cacheService.userBlockedCache.refresh(blockee.id); + this.cacheService.userReactionBlockedCache.refresh(blocker.id); + this.cacheService.userReactionBlockedCache.refresh(blockee.id); - this.globalEventService.publishInternalEvent('blockingDeleted', { + this.globalEventService.publishInternalEvent('blockingReactionDeleted', { blockerId: blocker.id, blockeeId: blockee.id, }); - - // 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, false); - } } @bindThis public async checkBlocked(blockerId: MiUser['id'], blockeeId: MiUser['id']): Promise { - return (await this.cacheService.userBlockingCache.fetch(blockerId)).has(blockeeId); + return (await this.cacheService.userReactionBlockingCache.fetch(blockerId)).has(blockeeId); } } diff --git a/packages/backend/src/core/UserReactionBlockingService.ts b/packages/backend/src/core/UserReactionBlockingService.ts new file mode 100644 index 0000000000..99e807bc89 --- /dev/null +++ b/packages/backend/src/core/UserReactionBlockingService.ts @@ -0,0 +1,111 @@ +/* + * SPDX-FileCopyrightText: syuilo and misskey-project + * SPDX-License-Identifier: AGPL-3.0-only + */ + +import { Inject, Injectable, OnModuleInit } from '@nestjs/common'; +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 { QueueService } from '@/core/QueueService.js'; +import { GlobalEventService } from '@/core/GlobalEventService.js'; +import { DI } from '@/di-symbols.js'; +import type { BlockingsRepository, UserListsRepository } from '@/models/_.js'; +import Logger from '@/logger.js'; +import { UserEntityService } from '@/core/entities/UserEntityService.js'; +import { ApRendererService } from '@/core/activitypub/ApRendererService.js'; +import { LoggerService } from '@/core/LoggerService.js'; +import { UserWebhookService } from '@/core/UserWebhookService.js'; +import { bindThis } from '@/decorators.js'; +import { CacheService } from '@/core/CacheService.js'; +import { UserFollowingService } from '@/core/UserFollowingService.js'; + +@Injectable() +export class UserReactionBlockingService implements OnModuleInit { + private logger: Logger; + private userFollowingService: UserFollowingService; + + constructor( + private moduleRef: ModuleRef, + + @Inject(DI.blockingsRepository) + private blockingsRepository: BlockingsRepository, + + @Inject(DI.userListsRepository) + private userListsRepository: UserListsRepository, + + private cacheService: CacheService, + private userEntityService: UserEntityService, + private idService: IdService, + private queueService: QueueService, + private globalEventService: GlobalEventService, + private webhookService: UserWebhookService, + private apRendererService: ApRendererService, + private loggerService: LoggerService, + ) { + this.logger = this.loggerService.getLogger('user-block'); + } + + @bindThis + public async block(blocker: MiUser, blockee: MiUser, silent = false) { + const blocking = { + id: this.idService.gen(), + blocker, + blockerId: blocker.id, + blockee, + blockeeId: blockee.id, + isReactionBlock: true, + } as MiBlocking; + + await this.blockingsRepository.insert(blocking); + + this.cacheService.userReactionBlockingCache.refresh(blocker.id); + this.cacheService.userReactionBlockedCache.refresh(blockee.id); + + this.globalEventService.publishInternalEvent('blockingReactionCreated', { + blockerId: blocker.id, + blockeeId: blockee.id, + }); + } + + @bindThis + public async unblock(blocker: MiUser, blockee: MiUser) { + const blocking = await this.blockingsRepository.findOneBy({ + blockerId: blocker.id, + blockeeId: blockee.id, + isReactionBlock: true, + }); + + if (blocking == null) { + this.logger.warn('ブロック解除がリクエストされましたがブロックしていませんでした'); + return; + } + + // Since we already have the blocker and blockee, we do not need to fetch + // them in the query above and can just manually insert them here. + blocking.blocker = blocker; + blocking.blockee = blockee; + + await this.blockingsRepository.delete(blocking.id); + + this.cacheService.userBlockingCache.refresh(blocker.id); + this.cacheService.userBlockedCache.refresh(blockee.id); + + this.globalEventService.publishInternalEvent('blockingDeleted', { + blockerId: blocker.id, + blockeeId: blockee.id, + }); + + // 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, false); + } + } + + @bindThis + public async checkBlocked(blockerId: MiUser['id'], blockeeId: MiUser['id']): Promise { + return (await this.cacheService.userBlockingCache.fetch(blockerId)).has(blockeeId); + } +} diff --git a/packages/backend/src/core/entities/UserEntityService.ts b/packages/backend/src/core/entities/UserEntityService.ts index d3c087a153..a9dc7fb65b 100644 --- a/packages/backend/src/core/entities/UserEntityService.ts +++ b/packages/backend/src/core/entities/UserEntityService.ts @@ -76,6 +76,8 @@ export type UserRelation = { hasPendingFollowRequestToYou: boolean isBlocking: boolean isBlocked: boolean + isReactionBlocking: boolean + isReactionBlocked: boolean isMuted: boolean isRenoteMuted: boolean } @@ -169,6 +171,8 @@ export class UserEntityService implements OnModuleInit { hasPendingFollowRequestToYou, isBlocking, isBlocked, + isReactionBlocking, + isReactionBlocked, isMuted, isRenoteMuted, ] = await Promise.all([ @@ -198,12 +202,28 @@ export class UserEntityService implements OnModuleInit { where: { blockerId: me, blockeeId: target, + isReactionBlock: false, }, }), this.blockingsRepository.exists({ where: { blockerId: target, blockeeId: me, + isReactionBlock: false, + }, + }), + this.blockingsRepository.exists({ + where: { + blockerId: me, + blockeeId: target, + isReactionBlock: true, + }, + }), + this.blockingsRepository.exists({ + where: { + blockerId: target, + blockeeId: me, + isReactionBlock: true, }, }), this.mutingsRepository.exists({ @@ -229,6 +249,8 @@ export class UserEntityService implements OnModuleInit { hasPendingFollowRequestToYou, isBlocking, isBlocked, + isReactionBlocking, + isReactionBlocked, isMuted, isRenoteMuted, }; @@ -243,6 +265,8 @@ export class UserEntityService implements OnModuleInit { followeesRequests, blockers, blockees, + reactionBlockers, + reactionBlockees, muters, renoteMuters, ] = await Promise.all([ @@ -266,11 +290,25 @@ export class UserEntityService implements OnModuleInit { this.blockingsRepository.createQueryBuilder('b') .select('b.blockeeId') .where('b.blockerId = :me', { me }) + .andWhere('b.isReactionBlock = false') .getRawMany<{ b_blockeeId: string }>() .then(it => it.map(it => it.b_blockeeId)), this.blockingsRepository.createQueryBuilder('b') .select('b.blockerId') .where('b.blockeeId = :me', { me }) + .andWhere('b.isReactionBlock = false') + .getRawMany<{ b_blockerId: string }>() + .then(it => it.map(it => it.b_blockerId)), + this.blockingsRepository.createQueryBuilder('b') + .select('b.blockeeId') + .where('b.blockerId = :me', { me }) + .andWhere('b.isReactionBlock = true') + .getRawMany<{ b_blockeeId: string }>() + .then(it => it.map(it => it.b_blockeeId)), + this.blockingsRepository.createQueryBuilder('b') + .select('b.blockerId') + .where('b.blockeeId = :me', { me }) + .andWhere('b.isReactionBlock = true') .getRawMany<{ b_blockerId: string }>() .then(it => it.map(it => it.b_blockerId)), this.mutingsRepository.createQueryBuilder('m') @@ -300,6 +338,8 @@ export class UserEntityService implements OnModuleInit { hasPendingFollowRequestToYou: followeesRequests.includes(target), isBlocking: blockers.includes(target), isBlocked: blockees.includes(target), + isReactionBlocking: reactionBlockers.includes(target), + isReactionBlocked: reactionBlockees.includes(target), isMuted: muters.includes(target), isRenoteMuted: renoteMuters.includes(target), }, @@ -638,6 +678,8 @@ export class UserEntityService implements OnModuleInit { hasPendingFollowRequestToYou: relation.hasPendingFollowRequestToYou, isBlocking: relation.isBlocking, isBlocked: relation.isBlocked, + isReactionBlocking: relation.isReactionBlocking, + isReactionBlocked: relation.isReactionBlocked, isMuted: relation.isMuted, isRenoteMuted: relation.isRenoteMuted, notify: relation.following?.notify ?? 'none', diff --git a/packages/backend/src/models/Blocking.ts b/packages/backend/src/models/Blocking.ts index 34a6efe5a6..8989154e7e 100644 --- a/packages/backend/src/models/Blocking.ts +++ b/packages/backend/src/models/Blocking.ts @@ -38,4 +38,11 @@ export class MiBlocking { }) @JoinColumn() public blocker: MiUser | null; + + @Index() + @Column({ + comment: 'Whether the blockee is a reaction block.', + default: false, + }) + public isReactionBlock: boolean; } diff --git a/packages/backend/src/models/json-schema/blocking.ts b/packages/backend/src/models/json-schema/blocking.ts index 2d02ba6a70..ccaf18cd8a 100644 --- a/packages/backend/src/models/json-schema/blocking.ts +++ b/packages/backend/src/models/json-schema/blocking.ts @@ -27,5 +27,9 @@ export const packedBlockingSchema = { optional: false, nullable: false, ref: 'UserDetailedNotMe', }, + isReactionBlock: { + type: 'boolean', + optional: false, nullable: false, + }, }, } as const; diff --git a/packages/backend/src/models/json-schema/user.ts b/packages/backend/src/models/json-schema/user.ts index 38631f907d..18b80155c8 100644 --- a/packages/backend/src/models/json-schema/user.ts +++ b/packages/backend/src/models/json-schema/user.ts @@ -416,6 +416,14 @@ export const packedUserDetailedNotMeOnlySchema = { type: 'boolean', nullable: false, optional: true, }, + isReactionBlocking: { + type: 'boolean', + nullable: false, optional: true, + }, + isReactionBlocked: { + type: 'boolean', + nullable: false, optional: true, + }, isMuted: { type: 'boolean', nullable: false, optional: true, diff --git a/packages/backend/src/server/api/EndpointsModule.ts b/packages/backend/src/server/api/EndpointsModule.ts index 5bb194313d..512f27e6a9 100644 --- a/packages/backend/src/server/api/EndpointsModule.ts +++ b/packages/backend/src/server/api/EndpointsModule.ts @@ -231,6 +231,9 @@ import * as ep___i_favorites from './endpoints/i/favorites.js'; import * as ep___i_gallery_likes from './endpoints/i/gallery/likes.js'; import * as ep___i_gallery_posts from './endpoints/i/gallery/posts.js'; import * as ep___i_importBlocking from './endpoints/i/import-blocking.js'; +import * as ep___blocking_reaction_user_create from './endpoints/blocking-reaction-user/create.js'; +import * as ep___blocking_reaction_user_delete from './endpoints/blocking-reaction-user/delete.js'; +import * as ep___blocking_reaction_user_list from './endpoints/blocking-reaction-user/list.js'; import * as ep___i_importFollowing from './endpoints/i/import-following.js'; import * as ep___i_importMuting from './endpoints/i/import-muting.js'; import * as ep___i_importUserLists from './endpoints/i/import-user-lists.js'; @@ -502,6 +505,9 @@ const $auth_session_userkey: Provider = { provide: 'ep:auth/session/userkey', us const $blocking_create: Provider = { provide: 'ep:blocking/create', useClass: ep___blocking_create.default }; const $blocking_delete: Provider = { provide: 'ep:blocking/delete', useClass: ep___blocking_delete.default }; const $blocking_list: Provider = { provide: 'ep:blocking/list', useClass: ep___blocking_list.default }; +const $blocking_reaction_user_create: Provider = { provide: 'ep:blocking-reaction-user/create', useClass: ep___blocking_reaction_user_create.default }; +const $blocking_reaction_user_delete: Provider = { provide: 'ep:blocking-reaction-user/delete', useClass: ep___blocking_reaction_user_delete.default }; +const $blocking_reaction_user_list: Provider = { provide: 'ep:blocking-reaction-user/list', useClass: ep___blocking_reaction_user_list.default }; const $channels_create: Provider = { provide: 'ep:channels/create', useClass: ep___channels_create.default }; const $channels_featured: Provider = { provide: 'ep:channels/featured', useClass: ep___channels_featured.default }; const $channels_follow: Provider = { provide: 'ep:channels/follow', useClass: ep___channels_follow.default }; @@ -894,6 +900,9 @@ const $reversi_verify: Provider = { provide: 'ep:reversi/verify', useClass: ep__ $blocking_create, $blocking_delete, $blocking_list, + $blocking_reaction_user_create, + $blocking_reaction_user_delete, + $blocking_reaction_user_list, $channels_create, $channels_featured, $channels_follow, diff --git a/packages/backend/src/server/api/endpoints.ts b/packages/backend/src/server/api/endpoints.ts index 15809b2678..d015536610 100644 --- a/packages/backend/src/server/api/endpoints.ts +++ b/packages/backend/src/server/api/endpoints.ts @@ -120,6 +120,9 @@ import * as ep___auth_session_userkey from './endpoints/auth/session/userkey.js' import * as ep___blocking_create from './endpoints/blocking/create.js'; import * as ep___blocking_delete from './endpoints/blocking/delete.js'; import * as ep___blocking_list from './endpoints/blocking/list.js'; +import * as ep___blocking_reaction_user_create from './endpoints/blocking-reaction-user/create.js'; +import * as ep___blocking_reaction_user_delete from './endpoints/blocking-reaction-user/delete.js'; +import * as ep___blocking_reaction_user_list from './endpoints/blocking-reaction-user/list.js'; import * as ep___channels_create from './endpoints/channels/create.js'; import * as ep___channels_featured from './endpoints/channels/featured.js'; import * as ep___channels_follow from './endpoints/channels/follow.js'; @@ -506,6 +509,9 @@ const eps = [ ['blocking/create', ep___blocking_create], ['blocking/delete', ep___blocking_delete], ['blocking/list', ep___blocking_list], + ['blocking-reaction-user/create', ep___blocking_reaction_user_create], + ['blocking-reaction-user/delete', ep___blocking_reaction_user_delete], + ['blocking-reaction-user/list', ep___blocking_reaction_user_list], ['channels/create', ep___channels_create], ['channels/featured', ep___channels_featured], ['channels/follow', ep___channels_follow], diff --git a/packages/backend/src/server/api/endpoints/blocking-reaction-user/create.ts b/packages/backend/src/server/api/endpoints/blocking-reaction-user/create.ts new file mode 100644 index 0000000000..1c75de0796 --- /dev/null +++ b/packages/backend/src/server/api/endpoints/blocking-reaction-user/create.ts @@ -0,0 +1,110 @@ +/* + * SPDX-FileCopyrightText: syuilo and misskey-project + * SPDX-License-Identifier: AGPL-3.0-only + */ + +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 { UserEntityService } from '@/core/entities/UserEntityService.js'; +import { DI } from '@/di-symbols.js'; +import { GetterService } from '@/server/api/GetterService.js'; +import { ApiError } from '../../error.js'; +import { UserReactionBlockingService } from '@/core/UserReactionBlockingService.js'; + +export const meta = { + tags: ['account'], + + limit: { + duration: ms('1hour'), + max: 20, + }, + + requireCredential: true, + + kind: 'write:blocks', + + errors: { + noSuchUser: { + message: 'No such user.', + code: 'NO_SUCH_USER', + id: '7cc4f851-e2f1-4621-9633-ec9e1d00c01e', + }, + + blockeeIsYourself: { + message: 'Blockee is yourself.', + code: 'BLOCKEE_IS_YOURSELF', + id: '88b19138-f28d-42c0-8499-6a31bbd0fdc6', + }, + + alreadyBlocking: { + message: 'You are already blocking that user.', + code: 'ALREADY_BLOCKING', + id: '787fed64-acb9-464a-82eb-afbd745b9614', + }, + }, + + res: { + type: 'object', + optional: false, nullable: false, + ref: 'UserDetailedNotMe', + }, +} as const; + +export const paramDef = { + type: 'object', + properties: { + userId: { type: 'string', format: 'misskey:id' }, + }, + required: ['userId'], +} as const; + +@Injectable() +export default class extends Endpoint { // eslint-disable-line import/no-default-export + constructor( + @Inject(DI.usersRepository) + private usersRepository: UsersRepository, + + @Inject(DI.blockingsRepository) + private blockingsRepository: BlockingsRepository, + + private userEntityService: UserEntityService, + private getterService: GetterService, + private userReactionBlockingService: UserReactionBlockingService, + ) { + super(meta, paramDef, async (ps, me) => { + const blocker = await this.usersRepository.findOneByOrFail({ id: me.id }); + + // 自分自身 + if (me.id === ps.userId) { + throw new ApiError(meta.errors.blockeeIsYourself); + } + + // 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); + throw err; + }); + + // Check if already blocking + const exist = await this.blockingsRepository.exists({ + where: { + blockerId: blocker.id, + blockeeId: blockee.id, + isReactionBlock: true, + }, + }); + + if (exist) { + throw new ApiError(meta.errors.alreadyBlocking); + } + + await this.userReactionBlockingService.block(blocker, blockee); + + return await this.userEntityService.pack(blockee.id, blocker, { + schema: 'UserDetailedNotMe', + }); + }); + } +} diff --git a/packages/backend/src/server/api/endpoints/blocking-reaction-user/delete.ts b/packages/backend/src/server/api/endpoints/blocking-reaction-user/delete.ts new file mode 100644 index 0000000000..f35fc9ab9e --- /dev/null +++ b/packages/backend/src/server/api/endpoints/blocking-reaction-user/delete.ts @@ -0,0 +1,111 @@ +/* + * SPDX-FileCopyrightText: syuilo and misskey-project + * SPDX-License-Identifier: AGPL-3.0-only + */ + +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 { UserEntityService } from '@/core/entities/UserEntityService.js'; +import { UserReactionBlockingService } from '@/core/UserReactionBlockingService.js'; +import { DI } from '@/di-symbols.js'; +import { GetterService } from '@/server/api/GetterService.js'; +import { ApiError } from '../../error.js'; + +export const meta = { + tags: ['account'], + + limit: { + duration: ms('1hour'), + max: 100, + }, + + requireCredential: true, + + kind: 'write:blocks', + + errors: { + noSuchUser: { + message: 'No such user.', + code: 'NO_SUCH_USER', + id: '8621d8bf-c358-4303-a066-5ea78610eb3f', + }, + + blockeeIsYourself: { + message: 'Blockee is yourself.', + code: 'BLOCKEE_IS_YOURSELF', + id: '06f6fac6-524b-473c-a354-e97a40ae6eac', + }, + + notBlocking: { + message: 'You are not blocking that user.', + code: 'NOT_BLOCKING', + id: '291b2efa-60c6-45c0-9f6a-045c8f9b02cd', + }, + }, + + res: { + type: 'object', + optional: false, nullable: false, + ref: 'UserDetailedNotMe', + }, +} as const; + +export const paramDef = { + type: 'object', + properties: { + userId: { type: 'string', format: 'misskey:id' }, + }, + required: ['userId'], +} as const; + +@Injectable() +export default class extends Endpoint { // eslint-disable-line import/no-default-export + constructor( + @Inject(DI.usersRepository) + private usersRepository: UsersRepository, + + @Inject(DI.blockingsRepository) + private blockingsRepository: BlockingsRepository, + + private userEntityService: UserEntityService, + private getterService: GetterService, + private userReactionBlockingService: UserReactionBlockingService, + ) { + super(meta, paramDef, async (ps, me) => { + const blocker = await this.usersRepository.findOneByOrFail({ id: me.id }); + + // Check if the blockee is yourself + if (me.id === ps.userId) { + throw new ApiError(meta.errors.blockeeIsYourself); + } + + // 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); + throw err; + }); + + // Check not blocking + const exist = await this.blockingsRepository.exists({ + where: { + blockerId: blocker.id, + blockeeId: blockee.id, + isReactionBlock: true, + }, + }); + + if (!exist) { + throw new ApiError(meta.errors.notBlocking); + } + + // Delete blocking + await this.userReactionBlockingService.unblock(blocker, blockee); + + return await this.userEntityService.pack(blockee.id, blocker, { + schema: 'UserDetailedNotMe', + }); + }); + } +} diff --git a/packages/backend/src/server/api/endpoints/blocking-reaction-user/list.ts b/packages/backend/src/server/api/endpoints/blocking-reaction-user/list.ts new file mode 100644 index 0000000000..385266b1e4 --- /dev/null +++ b/packages/backend/src/server/api/endpoints/blocking-reaction-user/list.ts @@ -0,0 +1,62 @@ +/* + * SPDX-FileCopyrightText: syuilo and misskey-project + * SPDX-License-Identifier: AGPL-3.0-only + */ + +import { Inject, Injectable } from '@nestjs/common'; +import { Endpoint } from '@/server/api/endpoint-base.js'; +import type { BlockingsRepository } from '@/models/_.js'; +import { QueryService } from '@/core/QueryService.js'; +import { BlockingEntityService } from '@/core/entities/BlockingEntityService.js'; +import { DI } from '@/di-symbols.js'; + +export const meta = { + tags: ['account'], + + requireCredential: true, + + kind: 'read:blocks', + + res: { + type: 'array', + optional: false, nullable: false, + items: { + type: 'object', + optional: false, nullable: false, + ref: 'Blocking', + }, + }, +} as const; + +export const paramDef = { + type: 'object', + properties: { + limit: { type: 'integer', minimum: 1, maximum: 100, default: 30 }, + sinceId: { type: 'string', format: 'misskey:id' }, + untilId: { type: 'string', format: 'misskey:id' }, + }, + required: [], +} as const; + +@Injectable() +export default class extends Endpoint { // eslint-disable-line import/no-default-export + constructor( + @Inject(DI.blockingsRepository) + private blockingsRepository: BlockingsRepository, + + private blockingEntityService: BlockingEntityService, + private queryService: QueryService, + ) { + super(meta, paramDef, async (ps, me) => { + const query = this.queryService.makePaginationQuery(this.blockingsRepository.createQueryBuilder('blocking'), ps.sinceId, ps.untilId) + .andWhere('blocking.blockerId = :meId', { meId: me.id }) + .andWhere('blocking.isReactionBlock = true'); + + const blockings = await query + .limit(ps.limit) + .getMany(); + + return await this.blockingEntityService.packMany(blockings, me); + }); + } +} diff --git a/packages/backend/src/server/api/endpoints/blocking/list.ts b/packages/backend/src/server/api/endpoints/blocking/list.ts index 8431fa6b34..21f8b4815b 100644 --- a/packages/backend/src/server/api/endpoints/blocking/list.ts +++ b/packages/backend/src/server/api/endpoints/blocking/list.ts @@ -49,7 +49,8 @@ export default class extends Endpoint { // eslint- ) { super(meta, paramDef, async (ps, me) => { const query = this.queryService.makePaginationQuery(this.blockingsRepository.createQueryBuilder('blocking'), ps.sinceId, ps.untilId) - .andWhere('blocking.blockerId = :meId', { meId: me.id }); + .andWhere('blocking.blockerId = :meId', { meId: me.id }) + .andWhere('blocking.isReactionBlock = false'); const blockings = await query .limit(ps.limit) diff --git a/packages/backend/test/unit/entities/UserEntityService.ts b/packages/backend/test/unit/entities/UserEntityService.ts index e4f42809f8..b28d386b43 100644 --- a/packages/backend/test/unit/entities/UserEntityService.ts +++ b/packages/backend/test/unit/entities/UserEntityService.ts @@ -115,6 +115,16 @@ describe('UserEntityService', () => { id: genAidx(Date.now()), blockerId: blocker.id, blockeeId: blockee.id, + isReactionBlock: false, + }); + } + + async function blockReaction(blocker: MiUser, blockee: MiUser) { + await blockingRepository.insert({ + id: genAidx(Date.now()), + blockerId: blocker.id, + blockeeId: blockee.id, + isReactionBlock: true, }); } @@ -260,6 +270,8 @@ describe('UserEntityService', () => { expect(actual.hasPendingFollowRequestToYou).toBe(false); expect(actual.isBlocking).toBe(false); expect(actual.isBlocked).toBe(false); + expect(actual.isReactionBlocking).toBe(false); + expect(actual.isReactionBlocked).toBe(false); expect(actual.isMuted).toBe(false); expect(actual.isRenoteMuted).toBe(false); } @@ -275,6 +287,8 @@ describe('UserEntityService', () => { expect(actual.hasPendingFollowRequestToYou).toBe(false); expect(actual.isBlocking).toBe(false); expect(actual.isBlocked).toBe(false); + expect(actual.isReactionBlocking).toBe(false); + expect(actual.isReactionBlocked).toBe(false); expect(actual.isMuted).toBe(false); expect(actual.isRenoteMuted).toBe(false); } @@ -290,6 +304,8 @@ describe('UserEntityService', () => { expect(actual.hasPendingFollowRequestToYou).toBe(false); expect(actual.isBlocking).toBe(false); expect(actual.isBlocked).toBe(false); + expect(actual.isReactionBlocking).toBe(false); + expect(actual.isReactionBlocked).toBe(false); expect(actual.isMuted).toBe(false); expect(actual.isRenoteMuted).toBe(false); } @@ -305,6 +321,8 @@ describe('UserEntityService', () => { expect(actual.hasPendingFollowRequestToYou).toBe(true); expect(actual.isBlocking).toBe(false); expect(actual.isBlocked).toBe(false); + expect(actual.isReactionBlocking).toBe(false); + expect(actual.isReactionBlocked).toBe(false); expect(actual.isMuted).toBe(false); expect(actual.isRenoteMuted).toBe(false); } @@ -320,6 +338,8 @@ describe('UserEntityService', () => { expect(actual.hasPendingFollowRequestToYou).toBe(false); expect(actual.isBlocking).toBe(true); expect(actual.isBlocked).toBe(false); + expect(actual.isReactionBlocking).toBe(false); + expect(actual.isReactionBlocked).toBe(false); expect(actual.isMuted).toBe(false); expect(actual.isRenoteMuted).toBe(false); } @@ -339,6 +359,41 @@ describe('UserEntityService', () => { expect(actual.isRenoteMuted).toBe(false); } + + // meがリアクションをブロックしてる人たち + const reactionBlockingYou = await Promise.all(randomIntRange().map(() => createUser())); + for (const who of reactionBlockingYou) { + await blockReaction(me, who); + const actual = await service.pack(who, me, { schema: 'UserDetailed' }) as any; + expect(actual.isFollowing).toBe(false); + expect(actual.isFollowed).toBe(false); + expect(actual.hasPendingFollowRequestFromYou).toBe(false); + expect(actual.hasPendingFollowRequestToYou).toBe(false); + expect(actual.isBlocking).toBe(false); + expect(actual.isBlocked).toBe(false); + expect(actual.isReactionBlocking).toBe(true); + expect(actual.isReactionBlocked).toBe(false); + expect(actual.isMuted).toBe(false); + expect(actual.isRenoteMuted).toBe(false); + } + + // meのリアクションをブロックしてる人たち + const reactionBlockingMe = await Promise.all(randomIntRange().map(() => createUser())); + for (const who of reactionBlockingMe) { + await blockReaction(who, me); + const actual = await service.pack(who, me, { schema: 'UserDetailed' }) as any; + expect(actual.isFollowing).toBe(false); + expect(actual.isFollowed).toBe(false); + expect(actual.hasPendingFollowRequestFromYou).toBe(false); + expect(actual.hasPendingFollowRequestToYou).toBe(false); + expect(actual.isBlocking).toBe(false); + expect(actual.isBlocked).toBe(false); + expect(actual.isReactionBlocking).toBe(false); + expect(actual.isReactionBlocked).toBe(true); + expect(actual.isMuted).toBe(false); + expect(actual.isRenoteMuted).toBe(false); + } + // meがミュートしてる人たち const muters = await Promise.all(randomIntRange().map(() => createUser())); for (const who of muters) { @@ -350,6 +405,8 @@ describe('UserEntityService', () => { expect(actual.hasPendingFollowRequestToYou).toBe(false); expect(actual.isBlocking).toBe(false); expect(actual.isBlocked).toBe(false); + expect(actual.isReactionBlocking).toBe(false); + expect(actual.isReactionBlocked).toBe(false); expect(actual.isMuted).toBe(true); expect(actual.isRenoteMuted).toBe(false); } @@ -365,6 +422,8 @@ describe('UserEntityService', () => { expect(actual.hasPendingFollowRequestToYou).toBe(false); expect(actual.isBlocking).toBe(false); expect(actual.isBlocked).toBe(false); + expect(actual.isReactionBlocking).toBe(false); + expect(actual.isReactionBlocked).toBe(false); expect(actual.isMuted).toBe(false); expect(actual.isRenoteMuted).toBe(true); } diff --git a/packages/misskey-js/etc/misskey-js.api.md b/packages/misskey-js/etc/misskey-js.api.md index 01a3dbbb30..35cd112b6d 100644 --- a/packages/misskey-js/etc/misskey-js.api.md +++ b/packages/misskey-js/etc/misskey-js.api.md @@ -551,6 +551,24 @@ type BlockingListRequest = operations['blocking___list']['requestBody']['content // @public (undocumented) type BlockingListResponse = operations['blocking___list']['responses']['200']['content']['application/json']; +// @public (undocumented) +type BlockingReactionUserCreateRequest = operations['blocking-reaction-user___create']['requestBody']['content']['application/json']; + +// @public (undocumented) +type BlockingReactionUserCreateResponse = operations['blocking-reaction-user___create']['responses']['200']['content']['application/json']; + +// @public (undocumented) +type BlockingReactionUserDeleteRequest = operations['blocking-reaction-user___delete']['requestBody']['content']['application/json']; + +// @public (undocumented) +type BlockingReactionUserDeleteResponse = operations['blocking-reaction-user___delete']['responses']['200']['content']['application/json']; + +// @public (undocumented) +type BlockingReactionUserListRequest = operations['blocking-reaction-user___list']['requestBody']['content']['application/json']; + +// @public (undocumented) +type BlockingReactionUserListResponse = operations['blocking-reaction-user___list']['responses']['200']['content']['application/json']; + // @public (undocumented) type BubbleGameRankingRequest = operations['bubble-game___ranking']['requestBody']['content']['application/json']; @@ -1381,6 +1399,12 @@ declare namespace entities { BlockingDeleteResponse, BlockingListRequest, BlockingListResponse, + BlockingReactionUserCreateRequest, + BlockingReactionUserCreateResponse, + BlockingReactionUserDeleteRequest, + BlockingReactionUserDeleteResponse, + BlockingReactionUserListRequest, + BlockingReactionUserListResponse, ChannelsCreateRequest, ChannelsCreateResponse, ChannelsFeaturedResponse, diff --git a/packages/misskey-js/src/autogen/apiClientJSDoc.ts b/packages/misskey-js/src/autogen/apiClientJSDoc.ts index 1837f3db4f..314aa6adee 100644 --- a/packages/misskey-js/src/autogen/apiClientJSDoc.ts +++ b/packages/misskey-js/src/autogen/apiClientJSDoc.ts @@ -1204,6 +1204,39 @@ declare module '../api.js' { credential?: string | null, ): Promise>; + /** + * No description provided. + * + * **Credential required**: *Yes* / **Permission**: *write:blocks* + */ + request( + endpoint: E, + params: P, + credential?: string | null, + ): Promise>; + + /** + * No description provided. + * + * **Credential required**: *Yes* / **Permission**: *write:blocks* + */ + request( + endpoint: E, + params: P, + credential?: string | null, + ): Promise>; + + /** + * No description provided. + * + * **Credential required**: *Yes* / **Permission**: *read:blocks* + */ + request( + endpoint: E, + params: P, + credential?: string | null, + ): Promise>; + /** * No description provided. * diff --git a/packages/misskey-js/src/autogen/endpoint.ts b/packages/misskey-js/src/autogen/endpoint.ts index cb1f4dbe96..e9e01fbc61 100644 --- a/packages/misskey-js/src/autogen/endpoint.ts +++ b/packages/misskey-js/src/autogen/endpoint.ts @@ -156,6 +156,12 @@ import type { BlockingDeleteResponse, BlockingListRequest, BlockingListResponse, + BlockingReactionUserCreateRequest, + BlockingReactionUserCreateResponse, + BlockingReactionUserDeleteRequest, + BlockingReactionUserDeleteResponse, + BlockingReactionUserListRequest, + BlockingReactionUserListResponse, ChannelsCreateRequest, ChannelsCreateResponse, ChannelsFeaturedResponse, @@ -690,6 +696,9 @@ export type Endpoints = { 'blocking/create': { req: BlockingCreateRequest; res: BlockingCreateResponse }; 'blocking/delete': { req: BlockingDeleteRequest; res: BlockingDeleteResponse }; 'blocking/list': { req: BlockingListRequest; res: BlockingListResponse }; + 'blocking-reaction-user/create': { req: BlockingReactionUserCreateRequest; res: BlockingReactionUserCreateResponse }; + 'blocking-reaction-user/delete': { req: BlockingReactionUserDeleteRequest; res: BlockingReactionUserDeleteResponse }; + 'blocking-reaction-user/list': { req: BlockingReactionUserListRequest; res: BlockingReactionUserListResponse }; 'channels/create': { req: ChannelsCreateRequest; res: ChannelsCreateResponse }; 'channels/featured': { req: EmptyRequest; res: ChannelsFeaturedResponse }; 'channels/follow': { req: ChannelsFollowRequest; res: EmptyResponse }; diff --git a/packages/misskey-js/src/autogen/entities.ts b/packages/misskey-js/src/autogen/entities.ts index a8f474c25c..e01c6addf5 100644 --- a/packages/misskey-js/src/autogen/entities.ts +++ b/packages/misskey-js/src/autogen/entities.ts @@ -159,6 +159,12 @@ export type BlockingDeleteRequest = operations['blocking___delete']['requestBody export type BlockingDeleteResponse = operations['blocking___delete']['responses']['200']['content']['application/json']; export type BlockingListRequest = operations['blocking___list']['requestBody']['content']['application/json']; export type BlockingListResponse = operations['blocking___list']['responses']['200']['content']['application/json']; +export type BlockingReactionUserCreateRequest = operations['blocking-reaction-user___create']['requestBody']['content']['application/json']; +export type BlockingReactionUserCreateResponse = operations['blocking-reaction-user___create']['responses']['200']['content']['application/json']; +export type BlockingReactionUserDeleteRequest = operations['blocking-reaction-user___delete']['requestBody']['content']['application/json']; +export type BlockingReactionUserDeleteResponse = operations['blocking-reaction-user___delete']['responses']['200']['content']['application/json']; +export type BlockingReactionUserListRequest = operations['blocking-reaction-user___list']['requestBody']['content']['application/json']; +export type BlockingReactionUserListResponse = operations['blocking-reaction-user___list']['responses']['200']['content']['application/json']; export type ChannelsCreateRequest = operations['channels___create']['requestBody']['content']['application/json']; export type ChannelsCreateResponse = operations['channels___create']['responses']['200']['content']['application/json']; export type ChannelsFeaturedResponse = operations['channels___featured']['responses']['200']['content']['application/json']; diff --git a/packages/misskey-js/src/autogen/types.ts b/packages/misskey-js/src/autogen/types.ts index 280abba727..4a574781ab 100644 --- a/packages/misskey-js/src/autogen/types.ts +++ b/packages/misskey-js/src/autogen/types.ts @@ -997,6 +997,33 @@ export type paths = { */ post: operations['blocking___list']; }; + '/blocking-reaction-user/create': { + /** + * blocking-reaction-user/create + * @description No description provided. + * + * **Credential required**: *Yes* / **Permission**: *write:blocks* + */ + post: operations['blocking-reaction-user___create']; + }; + '/blocking-reaction-user/delete': { + /** + * blocking-reaction-user/delete + * @description No description provided. + * + * **Credential required**: *Yes* / **Permission**: *write:blocks* + */ + post: operations['blocking-reaction-user___delete']; + }; + '/blocking-reaction-user/list': { + /** + * blocking-reaction-user/list + * @description No description provided. + * + * **Credential required**: *Yes* / **Permission**: *read:blocks* + */ + post: operations['blocking-reaction-user___list']; + }; '/channels/create': { /** * channels/create @@ -3825,6 +3852,8 @@ export type components = { hasPendingFollowRequestToYou?: boolean; isBlocking?: boolean; isBlocked?: boolean; + isReactionBlocking?: boolean; + isReactionBlocked?: boolean; isMuted?: boolean; isRenoteMuted?: boolean; /** @enum {string} */ @@ -4486,6 +4515,7 @@ export type components = { /** Format: id */ blockeeId: string; blockee: components['schemas']['UserDetailedNotMe']; + isReactionBlock: boolean; }; Hashtag: { /** @example misskey */ @@ -11705,6 +11735,184 @@ export type operations = { }; }; }; + /** + * blocking-reaction-user/create + * @description No description provided. + * + * **Credential required**: *Yes* / **Permission**: *write:blocks* + */ + 'blocking-reaction-user___create': { + requestBody: { + content: { + 'application/json': { + /** Format: misskey:id */ + userId: string; + }; + }; + }; + responses: { + /** @description OK (with results) */ + 200: { + content: { + 'application/json': components['schemas']['UserDetailedNotMe']; + }; + }; + /** @description Client error */ + 400: { + content: { + 'application/json': components['schemas']['Error']; + }; + }; + /** @description Authentication error */ + 401: { + content: { + 'application/json': components['schemas']['Error']; + }; + }; + /** @description Forbidden error */ + 403: { + content: { + 'application/json': components['schemas']['Error']; + }; + }; + /** @description I'm Ai */ + 418: { + content: { + 'application/json': components['schemas']['Error']; + }; + }; + /** @description To many requests */ + 429: { + content: { + 'application/json': components['schemas']['Error']; + }; + }; + /** @description Internal server error */ + 500: { + content: { + 'application/json': components['schemas']['Error']; + }; + }; + }; + }; + /** + * blocking-reaction-user/delete + * @description No description provided. + * + * **Credential required**: *Yes* / **Permission**: *write:blocks* + */ + 'blocking-reaction-user___delete': { + requestBody: { + content: { + 'application/json': { + /** Format: misskey:id */ + userId: string; + }; + }; + }; + responses: { + /** @description OK (with results) */ + 200: { + content: { + 'application/json': components['schemas']['UserDetailedNotMe']; + }; + }; + /** @description Client error */ + 400: { + content: { + 'application/json': components['schemas']['Error']; + }; + }; + /** @description Authentication error */ + 401: { + content: { + 'application/json': components['schemas']['Error']; + }; + }; + /** @description Forbidden error */ + 403: { + content: { + 'application/json': components['schemas']['Error']; + }; + }; + /** @description I'm Ai */ + 418: { + content: { + 'application/json': components['schemas']['Error']; + }; + }; + /** @description To many requests */ + 429: { + content: { + 'application/json': components['schemas']['Error']; + }; + }; + /** @description Internal server error */ + 500: { + content: { + 'application/json': components['schemas']['Error']; + }; + }; + }; + }; + /** + * blocking-reaction-user/list + * @description No description provided. + * + * **Credential required**: *Yes* / **Permission**: *read:blocks* + */ + 'blocking-reaction-user___list': { + requestBody: { + content: { + 'application/json': { + /** @default 30 */ + limit?: number; + /** Format: misskey:id */ + sinceId?: string; + /** Format: misskey:id */ + untilId?: string; + }; + }; + }; + responses: { + /** @description OK (with results) */ + 200: { + content: { + 'application/json': components['schemas']['Blocking'][]; + }; + }; + /** @description Client error */ + 400: { + content: { + 'application/json': components['schemas']['Error']; + }; + }; + /** @description Authentication error */ + 401: { + content: { + 'application/json': components['schemas']['Error']; + }; + }; + /** @description Forbidden error */ + 403: { + content: { + 'application/json': components['schemas']['Error']; + }; + }; + /** @description I'm Ai */ + 418: { + content: { + 'application/json': components['schemas']['Error']; + }; + }; + /** @description Internal server error */ + 500: { + content: { + 'application/json': components['schemas']['Error']; + }; + }; + }; + }; /** * channels/create * @description No description provided. From 37627bb0e6234196cb2a9fade5c1c11536844044 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=B4=87=E5=B3=B0=20=E6=9C=94=E8=8F=AF?= Date: Mon, 18 Nov 2024 15:36:23 +0900 Subject: [PATCH 02/16] =?UTF-8?q?Add:=20=E3=83=AA=E3=82=A2=E3=82=AF?= =?UTF-8?q?=E3=82=B7=E3=83=A7=E3=83=B3=E3=83=96=E3=83=AD=E3=83=83=E3=82=AF?= =?UTF-8?q?=E3=81=AE=E8=A8=AD=E5=AE=9A=E7=94=BB=E9=9D=A2=E3=82=92=E8=BF=BD?= =?UTF-8?q?=E5=8A=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- locales/index.d.ts | 4 ++ locales/ja-JP.yml | 1 + .../src/pages/settings/mute-block.vue | 48 +++++++++++++++++++ 3 files changed, 53 insertions(+) diff --git a/locales/index.d.ts b/locales/index.d.ts index 24613419ce..1d2f28d4bc 100644 --- a/locales/index.d.ts +++ b/locales/index.d.ts @@ -994,6 +994,10 @@ export interface Locale extends ILocale { * ブロックしたユーザー */ "blockedUsers": string; + /** + * リアクションをブロックしたユーザー + */ + "reactionBlockedUsers": string; /** * ユーザーはいません */ diff --git a/locales/ja-JP.yml b/locales/ja-JP.yml index 9f32969a79..1f1e1b571e 100644 --- a/locales/ja-JP.yml +++ b/locales/ja-JP.yml @@ -244,6 +244,7 @@ federationAllowedHostsDescription: "連合を許可するサーバーのホス muteAndBlock: "ミュートとブロック" mutedUsers: "ミュートしたユーザー" blockedUsers: "ブロックしたユーザー" +reactionBlockedUsers: "リアクションをブロックしたユーザー" noUsers: "ユーザーはいません" editProfile: "プロフィールを編集" noteDeleteConfirm: "このノートを削除しますか?" diff --git a/packages/frontend/src/pages/settings/mute-block.vue b/packages/frontend/src/pages/settings/mute-block.vue index 4d413d53ab..74e69e1d92 100644 --- a/packages/frontend/src/pages/settings/mute-block.vue +++ b/packages/frontend/src/pages/settings/mute-block.vue @@ -122,6 +122,39 @@ SPDX-License-Identifier: AGPL-3.0-only + + + + + + + + + + + @@ -157,6 +190,11 @@ const blockingPagination = { limit: 10, }; +const blockingReactionUserPagination = { + endpoint: 'blocking-reaction-user/list' as const, + limit: 10, +}; + const expandedRenoteMuteItems = ref([]); const expandedMuteItems = ref([]); const expandedBlockItems = ref([]); @@ -194,6 +232,16 @@ async function unblock(user, ev) { }], ev.currentTarget ?? ev.target); } +async function unblockReactionUser(user, ev) { + os.popupMenu([{ + text: i18n.ts.unblock, + icon: 'ti ti-x', + action: async () => { + await os.apiWithDialog('blocking-reaction-user/delete', { userId: user.id }); + }, + }], ev.currentTarget ?? ev.target); +} + async function toggleRenoteMuteItem(item) { if (expandedRenoteMuteItems.value.includes(item.id)) { expandedRenoteMuteItems.value = expandedRenoteMuteItems.value.filter(x => x !== item.id); From 51a2a7d81c6701cffb5fad76fa8d6305eb95259f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=B4=87=E5=B3=B0=20=E6=9C=94=E8=8F=AF?= Date: Mon, 18 Nov 2024 15:43:36 +0900 Subject: [PATCH 03/16] =?UTF-8?q?Add:=20=E3=83=95=E3=83=AD=E3=83=B3?= =?UTF-8?q?=E3=83=88=E3=82=A8=E3=83=B3=E3=83=89=E3=81=AE=E3=83=A6=E3=83=BC?= =?UTF-8?q?=E3=82=B6=E3=83=BC=E3=83=A1=E3=83=8B=E3=83=A5=E3=83=BC=E3=81=AB?= =?UTF-8?q?=E3=83=AA=E3=82=A2=E3=82=AF=E3=82=B7=E3=83=A7=E3=83=B3=E3=83=96?= =?UTF-8?q?=E3=83=AD=E3=83=83=E3=82=AF=E3=82=92=E8=BF=BD=E5=8A=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- locales/index.d.ts | 16 ++++++++++++++++ locales/ja-JP.yml | 4 ++++ packages/frontend/src/scripts/get-user-menu.ts | 14 ++++++++++++++ 3 files changed, 34 insertions(+) diff --git a/locales/index.d.ts b/locales/index.d.ts index 1d2f28d4bc..b52c313208 100644 --- a/locales/index.d.ts +++ b/locales/index.d.ts @@ -606,6 +606,14 @@ export interface Locale extends ILocale { * ブロック解除 */ "unblock": string; + /** + * リアクションをブロック + */ + "blockReactionUser": string; + /** + * リアクションのブロックを解除 + */ + "unblockReactionUser": string; /** * 凍結 */ @@ -622,6 +630,14 @@ export interface Locale extends ILocale { * ブロック解除しますか? */ "unblockConfirm": string; + /** + * リアクションをブロックしますか? + */ + "blockReactionUserConfirm": string; + /** + * リアクションのブロックを解除しますか? + */ + "unblockReactionUserConfirm": string; /** * 凍結しますか? */ diff --git a/locales/ja-JP.yml b/locales/ja-JP.yml index 1f1e1b571e..4c48ed3c3d 100644 --- a/locales/ja-JP.yml +++ b/locales/ja-JP.yml @@ -147,10 +147,14 @@ renoteMute: "リノートをミュート" renoteUnmute: "リノートのミュートを解除" block: "ブロック" unblock: "ブロック解除" +blockReactionUser: "リアクションをブロック" +unblockReactionUser: "リアクションのブロックを解除" suspend: "凍結" unsuspend: "解凍" blockConfirm: "ブロックしますか?" unblockConfirm: "ブロック解除しますか?" +blockReactionUserConfirm: "リアクションをブロックしますか?" +unblockReactionUserConfirm: "リアクションのブロックを解除しますか?" suspendConfirm: "凍結しますか?" unsuspendConfirm: "解凍しますか?" selectList: "リストを選択" diff --git a/packages/frontend/src/scripts/get-user-menu.ts b/packages/frontend/src/scripts/get-user-menu.ts index d15279d633..c9039bb927 100644 --- a/packages/frontend/src/scripts/get-user-menu.ts +++ b/packages/frontend/src/scripts/get-user-menu.ts @@ -84,6 +84,16 @@ export function getUserMenu(user: Misskey.entities.UserDetailed, router: IRouter }); } + async function toggleReactionBlock() { + if (!await getConfirmed(user.isReactionBlocking ? i18n.ts.unblockReactionUserConfirm : i18n.ts.blockReactionUserConfirm)) return; + + os.apiWithDialog(user.isReactionBlocking ? 'blocking-reaction-user/delete' : 'blocking-reaction-user/create', { + userId: user.id, + }).then(() => { + user.isReactionBlocking = !user.isReactionBlocking; + }); + } + async function toggleNotify() { os.apiWithDialog('following/update', { userId: user.id, @@ -373,6 +383,10 @@ export function getUserMenu(user: Misskey.entities.UserDetailed, router: IRouter icon: 'ti ti-ban', text: user.isBlocking ? i18n.ts.unblock : i18n.ts.block, action: toggleBlock, + }, { + icon: 'ti ti-ban', + text: user.isReactionBlocking ? i18n.ts.unblockReactionUser : i18n.ts.blockReactionUser, + action: toggleReactionBlock, }); if (user.isFollowed) { From 1b0ac2882554e2c46562edf704f772a91309e72f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=B4=87=E5=B3=B0=20=E6=9C=94=E8=8F=AF?= Date: Mon, 18 Nov 2024 16:10:42 +0900 Subject: [PATCH 04/16] fix MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 不要なonModuleInit Imprementsを除去 --- packages/backend/src/core/UserReactionBlockingService.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/backend/src/core/UserReactionBlockingService.ts b/packages/backend/src/core/UserReactionBlockingService.ts index 99e807bc89..01083c29e3 100644 --- a/packages/backend/src/core/UserReactionBlockingService.ts +++ b/packages/backend/src/core/UserReactionBlockingService.ts @@ -22,7 +22,7 @@ import { CacheService } from '@/core/CacheService.js'; import { UserFollowingService } from '@/core/UserFollowingService.js'; @Injectable() -export class UserReactionBlockingService implements OnModuleInit { +export class UserReactionBlockingService { private logger: Logger; private userFollowingService: UserFollowingService; From 9ef2dbbd303460b7387b6ff11799137d39d2e72a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=B4=87=E5=B3=B0=20=E6=9C=94=E8=8F=AF?= Date: Mon, 18 Nov 2024 16:21:40 +0900 Subject: [PATCH 05/16] =?UTF-8?q?Mod:=20=E3=83=AD=E3=82=B0=E5=87=BA?= =?UTF-8?q?=E5=8A=9B=E3=82=92=E8=8B=B1=E8=AA=9E=E3=81=AB=E5=A4=89=E6=9B=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/backend/src/core/UserReactionBlockingService.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/backend/src/core/UserReactionBlockingService.ts b/packages/backend/src/core/UserReactionBlockingService.ts index 01083c29e3..7ee2e90b24 100644 --- a/packages/backend/src/core/UserReactionBlockingService.ts +++ b/packages/backend/src/core/UserReactionBlockingService.ts @@ -78,7 +78,7 @@ export class UserReactionBlockingService { }); if (blocking == null) { - this.logger.warn('ブロック解除がリクエストされましたがブロックしていませんでした'); + this.logger.warn('Unblock requested, but the target was not blocked.'); return; } From 202fceed22f3c48f54a65a59b065d1732afec249 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=B4=87=E5=B3=B0=20=E6=9C=94=E8=8F=AF?= Date: Mon, 18 Nov 2024 16:23:35 +0900 Subject: [PATCH 06/16] fix: as -> satisfies --- packages/backend/src/core/UserReactionBlockingService.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/backend/src/core/UserReactionBlockingService.ts b/packages/backend/src/core/UserReactionBlockingService.ts index 7ee2e90b24..1b17f82fe3 100644 --- a/packages/backend/src/core/UserReactionBlockingService.ts +++ b/packages/backend/src/core/UserReactionBlockingService.ts @@ -56,7 +56,7 @@ export class UserReactionBlockingService { blockee, blockeeId: blockee.id, isReactionBlock: true, - } as MiBlocking; + } satisfies MiBlocking; await this.blockingsRepository.insert(blocking); From 3ea69b6203ba4062c929c30b0d306358c1e381fe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=B4=87=E5=B3=B0=20=E6=9C=94=E8=8F=AF?= Date: Mon, 18 Nov 2024 21:03:33 +0900 Subject: [PATCH 07/16] =?UTF-8?q?Mod:=20isReactionBlock=E3=81=8B=E3=82=89e?= =?UTF-8?q?num=E3=81=AB=E5=A4=89=E6=9B=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/backend/src/core/QueryService.ts | 6 +- .../backend/src/core/UserBlockingService.ts | 66 +++++++++++++------ .../src/core/UserReactionBlockingService.ts | 43 +++++++----- .../src/core/entities/UserEntityService.ts | 17 ++--- packages/backend/src/models/Blocking.ts | 11 +++- packages/backend/src/models/_.ts | 3 +- .../src/models/json-schema/blocking.ts | 5 +- .../blocking-reaction-user/create.ts | 19 +++--- .../blocking-reaction-user/delete.ts | 3 +- .../endpoints/blocking-reaction-user/list.ts | 2 +- .../server/api/endpoints/blocking/create.ts | 18 ++--- .../server/api/endpoints/blocking/delete.ts | 18 ++--- .../src/server/api/endpoints/blocking/list.ts | 2 +- 13 files changed, 132 insertions(+), 81 deletions(-) diff --git a/packages/backend/src/core/QueryService.ts b/packages/backend/src/core/QueryService.ts index c4feeaf971..65b6bc2ef4 100644 --- a/packages/backend/src/core/QueryService.ts +++ b/packages/backend/src/core/QueryService.ts @@ -72,7 +72,8 @@ export class QueryService { public generateBlockedUserQuery(q: SelectQueryBuilder, me: { id: MiUser['id'] }): void { const blockingQuery = this.blockingsRepository.createQueryBuilder('blocking') .select('blocking.blockerId') - .where('blocking.blockeeId = :blockeeId', { blockeeId: me.id }); + .where('blocking.blockeeId = :blockeeId', { blockeeId: me.id }) + .andWhere('blocking.blockType = "user"'); // 投稿の作者にブロックされていない かつ // 投稿の返信先の作者にブロックされていない かつ @@ -97,7 +98,8 @@ export class QueryService { public generateBlockQueryForUsers(q: SelectQueryBuilder, me: { id: MiUser['id'] }): void { const blockingQuery = this.blockingsRepository.createQueryBuilder('blocking') .select('blocking.blockeeId') - .where('blocking.blockerId = :blockerId', { blockerId: me.id }); + .where('blocking.blockerId = :blockerId', { blockerId: me.id }) + .andWhere('blocking.blockType = "user"'); const blockedQuery = this.blockingsRepository.createQueryBuilder('blocking') .select('blocking.blockerId') diff --git a/packages/backend/src/core/UserBlockingService.ts b/packages/backend/src/core/UserBlockingService.ts index 0234042903..9b5c40f267 100644 --- a/packages/backend/src/core/UserBlockingService.ts +++ b/packages/backend/src/core/UserBlockingService.ts @@ -3,15 +3,21 @@ * SPDX-License-Identifier: AGPL-3.0-only */ -import { Inject, Injectable, OnModuleInit } from '@nestjs/common'; -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 { QueueService } from '@/core/QueueService.js'; -import { GlobalEventService } from '@/core/GlobalEventService.js'; -import { DI } from '@/di-symbols.js'; -import type { FollowRequestsRepository, BlockingsRepository, UserListsRepository, UserListMembershipsRepository } from '@/models/_.js'; +import {Inject, Injectable, OnModuleInit} from '@nestjs/common'; +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 {MiBlockingType} from '@/models/Blocking.js'; +import {QueueService} from '@/core/QueueService.js'; +import {GlobalEventService} from '@/core/GlobalEventService.js'; +import {DI} from '@/di-symbols.js'; +import type { + BlockingsRepository, + FollowRequestsRepository, + UserListMembershipsRepository, + UserListsRepository, +} from '@/models/_.js'; import Logger from '@/logger.js'; import { UserEntityService } from '@/core/entities/UserEntityService.js'; import { ApRendererService } from '@/core/activitypub/ApRendererService.js'; @@ -20,6 +26,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 { UserReactionBlockingService } from '@/core/UserReactionBlockingService.js'; @Injectable() export class UserBlockingService implements OnModuleInit { @@ -43,6 +50,7 @@ export class UserBlockingService implements OnModuleInit { private cacheService: CacheService, private userEntityService: UserEntityService, + private userReactionBlockingService: UserReactionBlockingService, private idService: IdService, private queueService: QueueService, private globalEventService: GlobalEventService, @@ -67,15 +75,27 @@ export class UserBlockingService implements OnModuleInit { this.removeFromList(blockee, blocker), ]); - const blocking = { - id: this.idService.gen(), - blocker, + const blocking = await this.blockingsRepository.findOneBy({ blockerId: blocker.id, - blockee, blockeeId: blockee.id, - isReactionBlock: false, - } as MiBlocking; + }).then(blocking => { + if (blocking) { + return blocking; + } + return { + id: this.idService.gen(), + blocker, + blockerId: blocker.id, + blockee, + blockeeId: blockee.id, + blockType: MiBlockingType.User, + } as MiBlocking; + }); + if (blocking.blockType === MiBlockingType.Reaction) { + await this.userReactionBlockingService.unblock(blocker, blockee); + } + blocking.blockType = MiBlockingType.User; await this.blockingsRepository.insert(blocking); this.cacheService.userBlockingCache.refresh(blocker.id); @@ -161,7 +181,7 @@ export class UserBlockingService implements OnModuleInit { const blocking = await this.blockingsRepository.findOneBy({ blockerId: blocker.id, blockeeId: blockee.id, - isReactionBlock: false, + blockType: MiBlockingType.User, }); if (blocking == null) { @@ -177,17 +197,23 @@ export class UserBlockingService implements OnModuleInit { await this.blockingsRepository.delete(blocking.id); - this.cacheService.userReactionBlockedCache.refresh(blocker.id); - this.cacheService.userReactionBlockedCache.refresh(blockee.id); + this.cacheService.userBlockingCache.refresh(blocker.id); + this.cacheService.userBlockedCache.refresh(blockee.id); - this.globalEventService.publishInternalEvent('blockingReactionDeleted', { + this.globalEventService.publishInternalEvent('blockingDeleted', { blockerId: blocker.id, blockeeId: blockee.id, }); + + // 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, false); + } } @bindThis public async checkBlocked(blockerId: MiUser['id'], blockeeId: MiUser['id']): Promise { - return (await this.cacheService.userReactionBlockingCache.fetch(blockerId)).has(blockeeId); + return (await this.cacheService.userBlockingCache.fetch(blockerId)).has(blockeeId); } } diff --git a/packages/backend/src/core/UserReactionBlockingService.ts b/packages/backend/src/core/UserReactionBlockingService.ts index 1b17f82fe3..cf3de8f331 100644 --- a/packages/backend/src/core/UserReactionBlockingService.ts +++ b/packages/backend/src/core/UserReactionBlockingService.ts @@ -20,6 +20,8 @@ import { UserWebhookService } from '@/core/UserWebhookService.js'; import { bindThis } from '@/decorators.js'; import { CacheService } from '@/core/CacheService.js'; import { UserFollowingService } from '@/core/UserFollowingService.js'; +import {MiBlockingType} from "@/models/Blocking.js"; +import {UserBlockingService} from "@/core/UserBlockingService.js"; @Injectable() export class UserReactionBlockingService { @@ -37,6 +39,7 @@ export class UserReactionBlockingService { private cacheService: CacheService, private userEntityService: UserEntityService, + private userBlockingService: UserBlockingService, private idService: IdService, private queueService: QueueService, private globalEventService: GlobalEventService, @@ -49,15 +52,27 @@ export class UserReactionBlockingService { @bindThis public async block(blocker: MiUser, blockee: MiUser, silent = false) { - const blocking = { - id: this.idService.gen(), - blocker, + const blocking = await this.blockingsRepository.findOneBy({ blockerId: blocker.id, - blockee, blockeeId: blockee.id, - isReactionBlock: true, - } satisfies MiBlocking; + }).then(blocking => { + if (blocking) { + return blocking; + } + return { + id: this.idService.gen(), + blocker, + blockerId: blocker.id, + blockee, + blockeeId: blockee.id, + blockType: MiBlockingType.Reaction, + } as MiBlocking; + }); + if (blocking.blockType === MiBlockingType.User) { + await this.userBlockingService.unblock(blocker, blockee); + } + blocking.blockType = MiBlockingType.Reaction; await this.blockingsRepository.insert(blocking); this.cacheService.userReactionBlockingCache.refresh(blocker.id); @@ -74,7 +89,7 @@ export class UserReactionBlockingService { const blocking = await this.blockingsRepository.findOneBy({ blockerId: blocker.id, blockeeId: blockee.id, - isReactionBlock: true, + blockType: MiBlockingType.Reaction, }); if (blocking == null) { @@ -89,23 +104,17 @@ export class UserReactionBlockingService { await this.blockingsRepository.delete(blocking.id); - this.cacheService.userBlockingCache.refresh(blocker.id); - this.cacheService.userBlockedCache.refresh(blockee.id); + this.cacheService.userReactionBlockingCache.refresh(blocker.id); + this.cacheService.userReactionBlockedCache.refresh(blockee.id); - this.globalEventService.publishInternalEvent('blockingDeleted', { + this.globalEventService.publishInternalEvent('blockingReactionDeleted', { blockerId: blocker.id, blockeeId: blockee.id, }); - - // 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, false); - } } @bindThis public async checkBlocked(blockerId: MiUser['id'], blockeeId: MiUser['id']): Promise { - return (await this.cacheService.userBlockingCache.fetch(blockerId)).has(blockeeId); + return (await this.cacheService.userReactionBlockingCache.fetch(blockerId)).has(blockeeId); } } diff --git a/packages/backend/src/core/entities/UserEntityService.ts b/packages/backend/src/core/entities/UserEntityService.ts index a9dc7fb65b..22d178dd74 100644 --- a/packages/backend/src/core/entities/UserEntityService.ts +++ b/packages/backend/src/core/entities/UserEntityService.ts @@ -27,6 +27,7 @@ import type { BlockingsRepository, FollowingsRepository, FollowRequestsRepository, + MiBlockingType, MiFollowing, MiUserNotePining, MiUserProfile, @@ -202,28 +203,28 @@ export class UserEntityService implements OnModuleInit { where: { blockerId: me, blockeeId: target, - isReactionBlock: false, + blockType: MiBlockingType.User, }, }), this.blockingsRepository.exists({ where: { blockerId: target, blockeeId: me, - isReactionBlock: false, + blockType: MiBlockingType.User, }, }), this.blockingsRepository.exists({ where: { blockerId: me, blockeeId: target, - isReactionBlock: true, + blockType: MiBlockingType.Reaction, }, }), this.blockingsRepository.exists({ where: { blockerId: target, blockeeId: me, - isReactionBlock: true, + blockType: MiBlockingType.Reaction, }, }), this.mutingsRepository.exists({ @@ -290,25 +291,25 @@ export class UserEntityService implements OnModuleInit { this.blockingsRepository.createQueryBuilder('b') .select('b.blockeeId') .where('b.blockerId = :me', { me }) - .andWhere('b.isReactionBlock = false') + .andWhere('b.blockType = "user"') .getRawMany<{ b_blockeeId: string }>() .then(it => it.map(it => it.b_blockeeId)), this.blockingsRepository.createQueryBuilder('b') .select('b.blockerId') .where('b.blockeeId = :me', { me }) - .andWhere('b.isReactionBlock = false') + .andWhere('b.blockType = "user"') .getRawMany<{ b_blockerId: string }>() .then(it => it.map(it => it.b_blockerId)), this.blockingsRepository.createQueryBuilder('b') .select('b.blockeeId') .where('b.blockerId = :me', { me }) - .andWhere('b.isReactionBlock = true') + .andWhere('b.blockType = "reaction"') .getRawMany<{ b_blockeeId: string }>() .then(it => it.map(it => it.b_blockeeId)), this.blockingsRepository.createQueryBuilder('b') .select('b.blockerId') .where('b.blockeeId = :me', { me }) - .andWhere('b.isReactionBlock = true') + .andWhere('b.blockType = "reaction"') .getRawMany<{ b_blockerId: string }>() .then(it => it.map(it => it.b_blockerId)), this.mutingsRepository.createQueryBuilder('m') diff --git a/packages/backend/src/models/Blocking.ts b/packages/backend/src/models/Blocking.ts index 8989154e7e..e19edd7935 100644 --- a/packages/backend/src/models/Blocking.ts +++ b/packages/backend/src/models/Blocking.ts @@ -7,6 +7,11 @@ import { PrimaryColumn, Entity, Index, JoinColumn, Column, ManyToOne } from 'typ import { id } from './util/id.js'; import { MiUser } from './User.js'; +export enum MiBlockingType { + User = 'user', + Reaction = 'reaction', +} + @Entity('blocking') @Index(['blockerId', 'blockeeId'], { unique: true }) export class MiBlocking { @@ -41,8 +46,8 @@ export class MiBlocking { @Index() @Column({ - comment: 'Whether the blockee is a reaction block.', - default: false, + comment: 'Block type.', + default: MiBlockingType.User, }) - public isReactionBlock: boolean; + public blockType: MiBlockingType; } diff --git a/packages/backend/src/models/_.ts b/packages/backend/src/models/_.ts index c72bdaa727..5a9769760f 100644 --- a/packages/backend/src/models/_.ts +++ b/packages/backend/src/models/_.ts @@ -20,7 +20,7 @@ import { MiAntenna } from '@/models/Antenna.js'; import { MiApp } from '@/models/App.js'; import { MiAvatarDecoration } from '@/models/AvatarDecoration.js'; import { MiAuthSession } from '@/models/AuthSession.js'; -import { MiBlocking } from '@/models/Blocking.js'; +import { MiBlocking, MiBlockingType } from '@/models/Blocking.js'; import { MiChannelFollowing } from '@/models/ChannelFollowing.js'; import { MiChannelFavorite } from '@/models/ChannelFavorite.js'; import { MiClip } from '@/models/Clip.js'; @@ -136,6 +136,7 @@ export { MiAvatarDecoration, MiAuthSession, MiBlocking, + MiBlockingType, MiChannelFollowing, MiChannelFavorite, MiClip, diff --git a/packages/backend/src/models/json-schema/blocking.ts b/packages/backend/src/models/json-schema/blocking.ts index ccaf18cd8a..431832b28b 100644 --- a/packages/backend/src/models/json-schema/blocking.ts +++ b/packages/backend/src/models/json-schema/blocking.ts @@ -27,9 +27,10 @@ export const packedBlockingSchema = { optional: false, nullable: false, ref: 'UserDetailedNotMe', }, - isReactionBlock: { - type: 'boolean', + blockType: { + type: 'string', optional: false, nullable: false, + enum: ['user', 'reaction'], }, }, } as const; diff --git a/packages/backend/src/server/api/endpoints/blocking-reaction-user/create.ts b/packages/backend/src/server/api/endpoints/blocking-reaction-user/create.ts index 1c75de0796..0df2acd69c 100644 --- a/packages/backend/src/server/api/endpoints/blocking-reaction-user/create.ts +++ b/packages/backend/src/server/api/endpoints/blocking-reaction-user/create.ts @@ -4,14 +4,15 @@ */ 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 { UserEntityService } from '@/core/entities/UserEntityService.js'; -import { DI } from '@/di-symbols.js'; -import { GetterService } from '@/server/api/GetterService.js'; -import { ApiError } from '../../error.js'; -import { UserReactionBlockingService } from '@/core/UserReactionBlockingService.js'; +import {Inject, Injectable} from '@nestjs/common'; +import {Endpoint} from '@/server/api/endpoint-base.js'; +import type {BlockingsRepository, UsersRepository} from '@/models/_.js'; +import {MiBlockingType} from "@/models/_.js"; +import {UserEntityService} from '@/core/entities/UserEntityService.js'; +import {DI} from '@/di-symbols.js'; +import {GetterService} from '@/server/api/GetterService.js'; +import {ApiError} from '../../error.js'; +import {UserReactionBlockingService} from '@/core/UserReactionBlockingService.js'; export const meta = { tags: ['account'], @@ -92,7 +93,7 @@ export default class extends Endpoint { // eslint- where: { blockerId: blocker.id, blockeeId: blockee.id, - isReactionBlock: true, + blockType: MiBlockingType.Reaction, }, }); diff --git a/packages/backend/src/server/api/endpoints/blocking-reaction-user/delete.ts b/packages/backend/src/server/api/endpoints/blocking-reaction-user/delete.ts index f35fc9ab9e..393d05f900 100644 --- a/packages/backend/src/server/api/endpoints/blocking-reaction-user/delete.ts +++ b/packages/backend/src/server/api/endpoints/blocking-reaction-user/delete.ts @@ -12,6 +12,7 @@ import { UserReactionBlockingService } from '@/core/UserReactionBlockingService. import { DI } from '@/di-symbols.js'; import { GetterService } from '@/server/api/GetterService.js'; import { ApiError } from '../../error.js'; +import {MiBlockingType} from "@/models/_.js"; export const meta = { tags: ['account'], @@ -92,7 +93,7 @@ export default class extends Endpoint { // eslint- where: { blockerId: blocker.id, blockeeId: blockee.id, - isReactionBlock: true, + blockType: MiBlockingType.Reaction, }, }); diff --git a/packages/backend/src/server/api/endpoints/blocking-reaction-user/list.ts b/packages/backend/src/server/api/endpoints/blocking-reaction-user/list.ts index 385266b1e4..5055bd596b 100644 --- a/packages/backend/src/server/api/endpoints/blocking-reaction-user/list.ts +++ b/packages/backend/src/server/api/endpoints/blocking-reaction-user/list.ts @@ -50,7 +50,7 @@ export default class extends Endpoint { // eslint- super(meta, paramDef, async (ps, me) => { const query = this.queryService.makePaginationQuery(this.blockingsRepository.createQueryBuilder('blocking'), ps.sinceId, ps.untilId) .andWhere('blocking.blockerId = :meId', { meId: me.id }) - .andWhere('blocking.isReactionBlock = true'); + .andWhere('blocking.blockType = "reaction"'); const blockings = await query .limit(ps.limit) diff --git a/packages/backend/src/server/api/endpoints/blocking/create.ts b/packages/backend/src/server/api/endpoints/blocking/create.ts index 5066215749..b9adbb2deb 100644 --- a/packages/backend/src/server/api/endpoints/blocking/create.ts +++ b/packages/backend/src/server/api/endpoints/blocking/create.ts @@ -4,14 +4,15 @@ */ 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 { UserEntityService } from '@/core/entities/UserEntityService.js'; -import { UserBlockingService } from '@/core/UserBlockingService.js'; -import { DI } from '@/di-symbols.js'; -import { GetterService } from '@/server/api/GetterService.js'; -import { ApiError } from '../../error.js'; +import {Inject, Injectable} from '@nestjs/common'; +import {Endpoint} from '@/server/api/endpoint-base.js'; +import type {BlockingsRepository, UsersRepository} from '@/models/_.js'; +import {MiBlockingType} from "@/models/_.js"; +import {UserEntityService} from '@/core/entities/UserEntityService.js'; +import {UserBlockingService} from '@/core/UserBlockingService.js'; +import {DI} from '@/di-symbols.js'; +import {GetterService} from '@/server/api/GetterService.js'; +import {ApiError} from '../../error.js'; export const meta = { tags: ['account'], @@ -92,6 +93,7 @@ export default class extends Endpoint { // eslint- where: { blockerId: blocker.id, blockeeId: blockee.id, + blockType: MiBlockingType.User, }, }); diff --git a/packages/backend/src/server/api/endpoints/blocking/delete.ts b/packages/backend/src/server/api/endpoints/blocking/delete.ts index cebb307338..e035cb9823 100644 --- a/packages/backend/src/server/api/endpoints/blocking/delete.ts +++ b/packages/backend/src/server/api/endpoints/blocking/delete.ts @@ -4,14 +4,15 @@ */ 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 { UserEntityService } from '@/core/entities/UserEntityService.js'; -import { UserBlockingService } from '@/core/UserBlockingService.js'; -import { DI } from '@/di-symbols.js'; -import { GetterService } from '@/server/api/GetterService.js'; -import { ApiError } from '../../error.js'; +import {Inject, Injectable} from '@nestjs/common'; +import {Endpoint} from '@/server/api/endpoint-base.js'; +import type {BlockingsRepository, UsersRepository} from '@/models/_.js'; +import {MiBlockingType} from "@/models/_.js"; +import {UserEntityService} from '@/core/entities/UserEntityService.js'; +import {UserBlockingService} from '@/core/UserBlockingService.js'; +import {DI} from '@/di-symbols.js'; +import {GetterService} from '@/server/api/GetterService.js'; +import {ApiError} from '../../error.js'; export const meta = { tags: ['account'], @@ -92,6 +93,7 @@ export default class extends Endpoint { // eslint- where: { blockerId: blocker.id, blockeeId: blockee.id, + blockType: MiBlockingType.User, }, }); diff --git a/packages/backend/src/server/api/endpoints/blocking/list.ts b/packages/backend/src/server/api/endpoints/blocking/list.ts index 21f8b4815b..f7a5fa1412 100644 --- a/packages/backend/src/server/api/endpoints/blocking/list.ts +++ b/packages/backend/src/server/api/endpoints/blocking/list.ts @@ -50,7 +50,7 @@ export default class extends Endpoint { // eslint- super(meta, paramDef, async (ps, me) => { const query = this.queryService.makePaginationQuery(this.blockingsRepository.createQueryBuilder('blocking'), ps.sinceId, ps.untilId) .andWhere('blocking.blockerId = :meId', { meId: me.id }) - .andWhere('blocking.isReactionBlock = false'); + .andWhere('blocking.blockType = "user"'); const blockings = await query .limit(ps.limit) From 24792e09b551a1f20e50400daf7ebc20067bda41 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=B4=87=E5=B3=B0=20=E6=9C=94=E8=8F=AF?= Date: Sun, 17 Nov 2024 18:01:47 +0900 Subject: [PATCH 08/16] =?UTF-8?q?Add:=20=E3=83=AA=E3=82=A2=E3=82=AF?= =?UTF-8?q?=E3=82=B7=E3=83=A7=E3=83=B3=E3=81=AE=E3=83=96=E3=83=AD=E3=83=83?= =?UTF-8?q?=E3=82=AF=E5=88=A4=E5=AE=9A=E3=81=ABblockingReactionUserService?= =?UTF-8?q?.checkBlocked=E3=82=92=E8=BF=BD=E5=8A=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/backend/src/core/ReactionService.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/packages/backend/src/core/ReactionService.ts b/packages/backend/src/core/ReactionService.ts index 6f9fe53937..72c027233a 100644 --- a/packages/backend/src/core/ReactionService.ts +++ b/packages/backend/src/core/ReactionService.ts @@ -30,6 +30,7 @@ import { trackPromise } from '@/misc/promise-tracker.js'; import { isQuote, isRenote } from '@/misc/is-renote.js'; import { ReactionsBufferingService } from '@/core/ReactionsBufferingService.js'; import { PER_NOTE_REACTION_USER_PAIR_CACHE_MAX } from '@/const.js'; +import { BlockingReactionUserService } from '@/core/BlockingReactionUserService.js'; const FALLBACK = '\u2764'; @@ -91,6 +92,7 @@ export class ReactionService { private userEntityService: UserEntityService, private noteEntityService: NoteEntityService, private userBlockingService: UserBlockingService, + private blockingReactionUserService: BlockingReactionUserService, private reactionsBufferingService: ReactionsBufferingService, private idService: IdService, private featuredService: FeaturedService, @@ -107,7 +109,8 @@ export class ReactionService { // Check blocking if (note.userId !== user.id) { const blocked = await this.userBlockingService.checkBlocked(note.userId, user.id); - if (blocked) { + const reactionBlocked = await this.blockingReactionUserService.checkBlocked(note.userId, user.id); + if (blocked || reactionBlocked) { throw new IdentifiableError('e70412a4-7197-4726-8e74-f3e0deb92aa7'); } } From 26652949cb1d869747ba4871939ad3fb8d117493 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=B4=87=E5=B3=B0=20=E6=9C=94=E8=8F=AF?= Date: Mon, 18 Nov 2024 21:12:34 +0900 Subject: [PATCH 09/16] =?UTF-8?q?fix:=20code=20style=E3=81=AE=E4=BF=AE?= =?UTF-8?q?=E6=AD=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../backend/src/core/UserBlockingService.ts | 18 +++++++++--------- .../src/core/UserReactionBlockingService.ts | 6 +++--- .../endpoints/blocking-reaction-user/create.ts | 18 +++++++++--------- .../endpoints/blocking-reaction-user/delete.ts | 3 +-- .../server/api/endpoints/blocking/create.ts | 18 +++++++++--------- .../server/api/endpoints/blocking/delete.ts | 18 +++++++++--------- 6 files changed, 40 insertions(+), 41 deletions(-) diff --git a/packages/backend/src/core/UserBlockingService.ts b/packages/backend/src/core/UserBlockingService.ts index 9b5c40f267..3de99baf0c 100644 --- a/packages/backend/src/core/UserBlockingService.ts +++ b/packages/backend/src/core/UserBlockingService.ts @@ -3,15 +3,15 @@ * SPDX-License-Identifier: AGPL-3.0-only */ -import {Inject, Injectable, OnModuleInit} from '@nestjs/common'; -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 {MiBlockingType} from '@/models/Blocking.js'; -import {QueueService} from '@/core/QueueService.js'; -import {GlobalEventService} from '@/core/GlobalEventService.js'; -import {DI} from '@/di-symbols.js'; +import { Inject, Injectable, OnModuleInit } from '@nestjs/common'; +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 { MiBlockingType } from '@/models/Blocking.js'; +import { QueueService } from '@/core/QueueService.js'; +import { GlobalEventService } from '@/core/GlobalEventService.js'; +import { DI } from '@/di-symbols.js'; import type { BlockingsRepository, FollowRequestsRepository, diff --git a/packages/backend/src/core/UserReactionBlockingService.ts b/packages/backend/src/core/UserReactionBlockingService.ts index cf3de8f331..750f77a85a 100644 --- a/packages/backend/src/core/UserReactionBlockingService.ts +++ b/packages/backend/src/core/UserReactionBlockingService.ts @@ -3,7 +3,7 @@ * SPDX-License-Identifier: AGPL-3.0-only */ -import { Inject, Injectable, OnModuleInit } from '@nestjs/common'; +import { Inject, Injectable } from '@nestjs/common'; import { ModuleRef } from '@nestjs/core'; import { IdService } from '@/core/IdService.js'; import type { MiUser } from '@/models/User.js'; @@ -20,8 +20,8 @@ import { UserWebhookService } from '@/core/UserWebhookService.js'; import { bindThis } from '@/decorators.js'; import { CacheService } from '@/core/CacheService.js'; import { UserFollowingService } from '@/core/UserFollowingService.js'; -import {MiBlockingType} from "@/models/Blocking.js"; -import {UserBlockingService} from "@/core/UserBlockingService.js"; +import { MiBlockingType } from '@/models/Blocking.js'; +import { UserBlockingService } from '@/core/UserBlockingService.js'; @Injectable() export class UserReactionBlockingService { diff --git a/packages/backend/src/server/api/endpoints/blocking-reaction-user/create.ts b/packages/backend/src/server/api/endpoints/blocking-reaction-user/create.ts index 0df2acd69c..d9ec2f0503 100644 --- a/packages/backend/src/server/api/endpoints/blocking-reaction-user/create.ts +++ b/packages/backend/src/server/api/endpoints/blocking-reaction-user/create.ts @@ -4,15 +4,15 @@ */ import ms from 'ms'; -import {Inject, Injectable} from '@nestjs/common'; -import {Endpoint} from '@/server/api/endpoint-base.js'; -import type {BlockingsRepository, UsersRepository} from '@/models/_.js'; -import {MiBlockingType} from "@/models/_.js"; -import {UserEntityService} from '@/core/entities/UserEntityService.js'; -import {DI} from '@/di-symbols.js'; -import {GetterService} from '@/server/api/GetterService.js'; -import {ApiError} from '../../error.js'; -import {UserReactionBlockingService} from '@/core/UserReactionBlockingService.js'; +import { Inject, Injectable } from '@nestjs/common'; +import { Endpoint } from '@/server/api/endpoint-base.js'; +import type { BlockingsRepository, UsersRepository } from '@/models/_.js'; +import { MiBlockingType } from '@/models/_.js'; +import { UserEntityService } from '@/core/entities/UserEntityService.js'; +import { DI } from '@/di-symbols.js'; +import { GetterService } from '@/server/api/GetterService.js'; +import { UserReactionBlockingService } from '@/core/UserReactionBlockingService.js'; +import { ApiError } from '../../error.js'; export const meta = { tags: ['account'], diff --git a/packages/backend/src/server/api/endpoints/blocking-reaction-user/delete.ts b/packages/backend/src/server/api/endpoints/blocking-reaction-user/delete.ts index 393d05f900..c608fa486d 100644 --- a/packages/backend/src/server/api/endpoints/blocking-reaction-user/delete.ts +++ b/packages/backend/src/server/api/endpoints/blocking-reaction-user/delete.ts @@ -6,13 +6,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 { UsersRepository, BlockingsRepository, MiBlockingType } from '@/models/_.js'; import { UserEntityService } from '@/core/entities/UserEntityService.js'; import { UserReactionBlockingService } from '@/core/UserReactionBlockingService.js'; import { DI } from '@/di-symbols.js'; import { GetterService } from '@/server/api/GetterService.js'; import { ApiError } from '../../error.js'; -import {MiBlockingType} from "@/models/_.js"; export const meta = { tags: ['account'], diff --git a/packages/backend/src/server/api/endpoints/blocking/create.ts b/packages/backend/src/server/api/endpoints/blocking/create.ts index b9adbb2deb..06dae5177c 100644 --- a/packages/backend/src/server/api/endpoints/blocking/create.ts +++ b/packages/backend/src/server/api/endpoints/blocking/create.ts @@ -4,15 +4,15 @@ */ import ms from 'ms'; -import {Inject, Injectable} from '@nestjs/common'; -import {Endpoint} from '@/server/api/endpoint-base.js'; -import type {BlockingsRepository, UsersRepository} from '@/models/_.js'; -import {MiBlockingType} from "@/models/_.js"; -import {UserEntityService} from '@/core/entities/UserEntityService.js'; -import {UserBlockingService} from '@/core/UserBlockingService.js'; -import {DI} from '@/di-symbols.js'; -import {GetterService} from '@/server/api/GetterService.js'; -import {ApiError} from '../../error.js'; +import { Inject, Injectable } from '@nestjs/common'; +import { Endpoint } from '@/server/api/endpoint-base.js'; +import type { BlockingsRepository, UsersRepository } from '@/models/_.js'; +import { MiBlockingType } from '@/models/_.js'; +import { UserEntityService } from '@/core/entities/UserEntityService.js'; +import { UserBlockingService } from '@/core/UserBlockingService.js'; +import { DI } from '@/di-symbols.js'; +import { GetterService } from '@/server/api/GetterService.js'; +import { ApiError } from '../../error.js'; export const meta = { tags: ['account'], diff --git a/packages/backend/src/server/api/endpoints/blocking/delete.ts b/packages/backend/src/server/api/endpoints/blocking/delete.ts index e035cb9823..1608329f8b 100644 --- a/packages/backend/src/server/api/endpoints/blocking/delete.ts +++ b/packages/backend/src/server/api/endpoints/blocking/delete.ts @@ -4,15 +4,15 @@ */ import ms from 'ms'; -import {Inject, Injectable} from '@nestjs/common'; -import {Endpoint} from '@/server/api/endpoint-base.js'; -import type {BlockingsRepository, UsersRepository} from '@/models/_.js'; -import {MiBlockingType} from "@/models/_.js"; -import {UserEntityService} from '@/core/entities/UserEntityService.js'; -import {UserBlockingService} from '@/core/UserBlockingService.js'; -import {DI} from '@/di-symbols.js'; -import {GetterService} from '@/server/api/GetterService.js'; -import {ApiError} from '../../error.js'; +import { Inject, Injectable } from '@nestjs/common'; +import { Endpoint } from '@/server/api/endpoint-base.js'; +import type { BlockingsRepository, UsersRepository } from '@/models/_.js'; +import { MiBlockingType } from '@/models/_.js'; +import { UserEntityService } from '@/core/entities/UserEntityService.js'; +import { UserBlockingService } from '@/core/UserBlockingService.js'; +import { DI } from '@/di-symbols.js'; +import { GetterService } from '@/server/api/GetterService.js'; +import { ApiError } from '../../error.js'; export const meta = { tags: ['account'], From 0301e86aff45d6011ac933955dee6d6de62f06ee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=B4=87=E5=B3=B0=20=E6=9C=94=E8=8F=AF?= Date: Mon, 18 Nov 2024 21:20:27 +0900 Subject: [PATCH 10/16] =?UTF-8?q?Mod:=20Migration=E3=83=95=E3=82=A1?= =?UTF-8?q?=E3=82=A4=E3=83=AB=E3=82=92=E5=86=8D=E4=BD=9C=E6=88=90?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../1731898598469-addBlockingReactionUser.js | 20 ------------------- .../1731932268436-addBlockingReactionUser.js | 16 +++++++++++++++ 2 files changed, 16 insertions(+), 20 deletions(-) delete mode 100644 packages/backend/migration/1731898598469-addBlockingReactionUser.js create mode 100644 packages/backend/migration/1731932268436-addBlockingReactionUser.js diff --git a/packages/backend/migration/1731898598469-addBlockingReactionUser.js b/packages/backend/migration/1731898598469-addBlockingReactionUser.js deleted file mode 100644 index 6cf9d4ca87..0000000000 --- a/packages/backend/migration/1731898598469-addBlockingReactionUser.js +++ /dev/null @@ -1,20 +0,0 @@ -/* - * SPDX-FileCopyrightText: syuilo and misskey-project - * SPDX-License-Identifier: AGPL-3.0-only - */ - -export class AddBlockingReactionUser1731898598469 { - name = 'AddBlockingReactionUser1731898598469' - - async up(queryRunner) { - await queryRunner.query(`ALTER TABLE "blocking" ADD "isReactionBlock" boolean NOT NULL DEFAULT false`); - await queryRunner.query(`COMMENT ON COLUMN "blocking"."isReactionBlock" IS 'Whether the blockee is a reaction block.'`); - await queryRunner.query(`CREATE INDEX "IDX_7b0698c38d27a5554bed4858bd" ON "blocking" ("isReactionBlock") `); - } - - async down(queryRunner) { - await queryRunner.query(`DELETE FROM blocking WHERE "isReactionBlock" = 'true'`); // blockingテーブルのisReactionBlockカラムがtrueの行を削除する - await queryRunner.query(`DROP INDEX "IDX_7b0698c38d27a5554bed4858bd"`); - await queryRunner.query(`ALTER TABLE "blocking" DROP COLUMN "isReactionBlock"`); - } -} diff --git a/packages/backend/migration/1731932268436-addBlockingReactionUser.js b/packages/backend/migration/1731932268436-addBlockingReactionUser.js new file mode 100644 index 0000000000..62033fb434 --- /dev/null +++ b/packages/backend/migration/1731932268436-addBlockingReactionUser.js @@ -0,0 +1,16 @@ +export class AddBlockingReactionUser1731932268436 { + name = 'AddBlockingReactionUser1731932268436' + + async up(queryRunner) { + await queryRunner.query(`ALTER TABLE "blocking" ADD "blockType" character varying NOT NULL DEFAULT 'user'`); + await queryRunner.query(`COMMENT ON COLUMN "blocking"."blockType" IS 'Block type.'`); + await queryRunner.query(`CREATE INDEX "IDX_cd38e7ea08163899a2d1f4427d" ON "blocking" ("blockType") `); + } + + async down(queryRunner) { + await queryRunner.query(`DELETE FROM blocking WHERE "blockType" = 'reaction'`); // blockingテーブルのblockTypeがreactionの行を削除 + await queryRunner.query(`DROP INDEX "public"."IDX_cd38e7ea08163899a2d1f4427d"`); + await queryRunner.query(`COMMENT ON COLUMN "blocking"."blockType" IS 'Block type.'`); + await queryRunner.query(`ALTER TABLE "blocking" DROP COLUMN "blockType"`); + } +} From da94dbee0030fb8ae60370a04ab542721f3df253 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=B4=87=E5=B3=B0=20=E6=9C=94=E8=8F=AF?= Date: Mon, 18 Nov 2024 21:46:28 +0900 Subject: [PATCH 11/16] =?UTF-8?q?Mod:=20UserReactionBlockingService?= =?UTF-8?q?=E3=81=A8UserBlockingService=E3=82=92=E7=B5=B1=E5=90=88?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/backend/src/core/CoreModule.ts | 6 - packages/backend/src/core/ReactionService.ts | 5 +- .../backend/src/core/UserBlockingService.ts | 74 ++++++++++- .../src/core/UserReactionBlockingService.ts | 120 ------------------ .../blocking-reaction-user/create.ts | 6 +- .../blocking-reaction-user/delete.ts | 6 +- packages/misskey-js/src/autogen/types.ts | 3 +- 7 files changed, 81 insertions(+), 139 deletions(-) delete mode 100644 packages/backend/src/core/UserReactionBlockingService.ts diff --git a/packages/backend/src/core/CoreModule.ts b/packages/backend/src/core/CoreModule.ts index 393bdf26f7..734d135648 100644 --- a/packages/backend/src/core/CoreModule.ts +++ b/packages/backend/src/core/CoreModule.ts @@ -58,7 +58,6 @@ import { S3Service } from './S3Service.js'; import { SignupService } from './SignupService.js'; import { WebAuthnService } from './WebAuthnService.js'; import { UserBlockingService } from './UserBlockingService.js'; -import { UserReactionBlockingService } from './UserReactionBlockingService.js'; import { CacheService } from './CacheService.js'; import { UserService } from './UserService.js'; import { UserFollowingService } from './UserFollowingService.js'; @@ -203,7 +202,6 @@ const $S3Service: Provider = { provide: 'S3Service', useExisting: S3Service }; const $SignupService: Provider = { provide: 'SignupService', useExisting: SignupService }; const $WebAuthnService: Provider = { provide: 'WebAuthnService', useExisting: WebAuthnService }; const $UserBlockingService: Provider = { provide: 'UserBlockingService', useExisting: UserBlockingService }; -const $UserReactionBlockingService: Provider = { provide: 'UserReactionBlockingService', useExisting: UserReactionBlockingService }; const $CacheService: Provider = { provide: 'CacheService', useExisting: CacheService }; const $UserService: Provider = { provide: 'UserService', useExisting: UserService }; const $UserFollowingService: Provider = { provide: 'UserFollowingService', useExisting: UserFollowingService }; @@ -355,7 +353,6 @@ const $ApQuestionService: Provider = { provide: 'ApQuestionService', useExisting SignupService, WebAuthnService, UserBlockingService, - UserReactionBlockingService, CacheService, UserService, UserFollowingService, @@ -503,7 +500,6 @@ const $ApQuestionService: Provider = { provide: 'ApQuestionService', useExisting $SignupService, $WebAuthnService, $UserBlockingService, - $UserReactionBlockingService, $CacheService, $UserService, $UserFollowingService, @@ -652,7 +648,6 @@ const $ApQuestionService: Provider = { provide: 'ApQuestionService', useExisting SignupService, WebAuthnService, UserBlockingService, - UserReactionBlockingService, CacheService, UserService, UserFollowingService, @@ -799,7 +794,6 @@ const $ApQuestionService: Provider = { provide: 'ApQuestionService', useExisting $SignupService, $WebAuthnService, $UserBlockingService, - $UserReactionBlockingService, $CacheService, $UserService, $UserFollowingService, diff --git a/packages/backend/src/core/ReactionService.ts b/packages/backend/src/core/ReactionService.ts index 72c027233a..6924841eca 100644 --- a/packages/backend/src/core/ReactionService.ts +++ b/packages/backend/src/core/ReactionService.ts @@ -30,7 +30,6 @@ import { trackPromise } from '@/misc/promise-tracker.js'; import { isQuote, isRenote } from '@/misc/is-renote.js'; import { ReactionsBufferingService } from '@/core/ReactionsBufferingService.js'; import { PER_NOTE_REACTION_USER_PAIR_CACHE_MAX } from '@/const.js'; -import { BlockingReactionUserService } from '@/core/BlockingReactionUserService.js'; const FALLBACK = '\u2764'; @@ -92,7 +91,7 @@ export class ReactionService { private userEntityService: UserEntityService, private noteEntityService: NoteEntityService, private userBlockingService: UserBlockingService, - private blockingReactionUserService: BlockingReactionUserService, + private userReactionBlockingService: UserBlockingService, private reactionsBufferingService: ReactionsBufferingService, private idService: IdService, private featuredService: FeaturedService, @@ -109,7 +108,7 @@ export class ReactionService { // Check blocking if (note.userId !== user.id) { const blocked = await this.userBlockingService.checkBlocked(note.userId, user.id); - const reactionBlocked = await this.blockingReactionUserService.checkBlocked(note.userId, user.id); + const reactionBlocked = await this.userReactionBlockingService.checkBlocked(note.userId, user.id); if (blocked || reactionBlocked) { throw new IdentifiableError('e70412a4-7197-4726-8e74-f3e0deb92aa7'); } diff --git a/packages/backend/src/core/UserBlockingService.ts b/packages/backend/src/core/UserBlockingService.ts index 3de99baf0c..328205d645 100644 --- a/packages/backend/src/core/UserBlockingService.ts +++ b/packages/backend/src/core/UserBlockingService.ts @@ -26,7 +26,6 @@ import { UserWebhookService } from '@/core/UserWebhookService.js'; import { bindThis } from '@/decorators.js'; import { CacheService } from '@/core/CacheService.js'; import { UserFollowingService } from '@/core/UserFollowingService.js'; -import { UserReactionBlockingService } from '@/core/UserReactionBlockingService.js'; @Injectable() export class UserBlockingService implements OnModuleInit { @@ -50,7 +49,6 @@ export class UserBlockingService implements OnModuleInit { private cacheService: CacheService, private userEntityService: UserEntityService, - private userReactionBlockingService: UserReactionBlockingService, private idService: IdService, private queueService: QueueService, private globalEventService: GlobalEventService, @@ -93,7 +91,7 @@ export class UserBlockingService implements OnModuleInit { }); if (blocking.blockType === MiBlockingType.Reaction) { - await this.userReactionBlockingService.unblock(blocker, blockee); + await this.reactionUnblock(blocker, blockee); } blocking.blockType = MiBlockingType.User; await this.blockingsRepository.insert(blocking); @@ -216,4 +214,74 @@ export class UserBlockingService implements OnModuleInit { public async checkBlocked(blockerId: MiUser['id'], blockeeId: MiUser['id']): Promise { return (await this.cacheService.userBlockingCache.fetch(blockerId)).has(blockeeId); } + + + @bindThis + public async reactionBlock(blocker: MiUser, blockee: MiUser, silent = false) { + const blocking = await this.blockingsRepository.findOneBy({ + blockerId: blocker.id, + blockeeId: blockee.id, + }).then(blocking => { + if (blocking) { + return blocking; + } + return { + id: this.idService.gen(), + blocker, + blockerId: blocker.id, + blockee, + blockeeId: blockee.id, + blockType: MiBlockingType.Reaction, + } as MiBlocking; + }); + + if (blocking.blockType === MiBlockingType.User) { + await this.unblock(blocker, blockee); + } + blocking.blockType = MiBlockingType.Reaction; + await this.blockingsRepository.insert(blocking); + + this.cacheService.userReactionBlockingCache.refresh(blocker.id); + this.cacheService.userReactionBlockedCache.refresh(blockee.id); + + this.globalEventService.publishInternalEvent('blockingReactionCreated', { + blockerId: blocker.id, + blockeeId: blockee.id, + }); + } + + @bindThis + public async reactionUnblock(blocker: MiUser, blockee: MiUser) { + const blocking = await this.blockingsRepository.findOneBy({ + blockerId: blocker.id, + blockeeId: blockee.id, + blockType: MiBlockingType.Reaction, + }); + + if (blocking == null) { + this.logger.warn('Unblock requested, but the target was not blocked.'); + return; + } + + // Since we already have the blocker and blockee, we do not need to fetch + // them in the query above and can just manually insert them here. + blocking.blocker = blocker; + blocking.blockee = blockee; + + await this.blockingsRepository.delete(blocking.id); + + this.cacheService.userReactionBlockingCache.refresh(blocker.id); + this.cacheService.userReactionBlockedCache.refresh(blockee.id); + + this.globalEventService.publishInternalEvent('blockingReactionDeleted', { + blockerId: blocker.id, + blockeeId: blockee.id, + }); + } + + @bindThis + public async checkReactionBlocked(blockerId: MiUser['id'], blockeeId: MiUser['id']): Promise { + return (await this.cacheService.userReactionBlockingCache.fetch(blockerId)).has(blockeeId); + } } + diff --git a/packages/backend/src/core/UserReactionBlockingService.ts b/packages/backend/src/core/UserReactionBlockingService.ts deleted file mode 100644 index 750f77a85a..0000000000 --- a/packages/backend/src/core/UserReactionBlockingService.ts +++ /dev/null @@ -1,120 +0,0 @@ -/* - * SPDX-FileCopyrightText: syuilo and misskey-project - * SPDX-License-Identifier: AGPL-3.0-only - */ - -import { Inject, Injectable } from '@nestjs/common'; -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 { QueueService } from '@/core/QueueService.js'; -import { GlobalEventService } from '@/core/GlobalEventService.js'; -import { DI } from '@/di-symbols.js'; -import type { BlockingsRepository, UserListsRepository } from '@/models/_.js'; -import Logger from '@/logger.js'; -import { UserEntityService } from '@/core/entities/UserEntityService.js'; -import { ApRendererService } from '@/core/activitypub/ApRendererService.js'; -import { LoggerService } from '@/core/LoggerService.js'; -import { UserWebhookService } from '@/core/UserWebhookService.js'; -import { bindThis } from '@/decorators.js'; -import { CacheService } from '@/core/CacheService.js'; -import { UserFollowingService } from '@/core/UserFollowingService.js'; -import { MiBlockingType } from '@/models/Blocking.js'; -import { UserBlockingService } from '@/core/UserBlockingService.js'; - -@Injectable() -export class UserReactionBlockingService { - private logger: Logger; - private userFollowingService: UserFollowingService; - - constructor( - private moduleRef: ModuleRef, - - @Inject(DI.blockingsRepository) - private blockingsRepository: BlockingsRepository, - - @Inject(DI.userListsRepository) - private userListsRepository: UserListsRepository, - - private cacheService: CacheService, - private userEntityService: UserEntityService, - private userBlockingService: UserBlockingService, - private idService: IdService, - private queueService: QueueService, - private globalEventService: GlobalEventService, - private webhookService: UserWebhookService, - private apRendererService: ApRendererService, - private loggerService: LoggerService, - ) { - this.logger = this.loggerService.getLogger('user-block'); - } - - @bindThis - public async block(blocker: MiUser, blockee: MiUser, silent = false) { - const blocking = await this.blockingsRepository.findOneBy({ - blockerId: blocker.id, - blockeeId: blockee.id, - }).then(blocking => { - if (blocking) { - return blocking; - } - return { - id: this.idService.gen(), - blocker, - blockerId: blocker.id, - blockee, - blockeeId: blockee.id, - blockType: MiBlockingType.Reaction, - } as MiBlocking; - }); - - if (blocking.blockType === MiBlockingType.User) { - await this.userBlockingService.unblock(blocker, blockee); - } - blocking.blockType = MiBlockingType.Reaction; - await this.blockingsRepository.insert(blocking); - - this.cacheService.userReactionBlockingCache.refresh(blocker.id); - this.cacheService.userReactionBlockedCache.refresh(blockee.id); - - this.globalEventService.publishInternalEvent('blockingReactionCreated', { - blockerId: blocker.id, - blockeeId: blockee.id, - }); - } - - @bindThis - public async unblock(blocker: MiUser, blockee: MiUser) { - const blocking = await this.blockingsRepository.findOneBy({ - blockerId: blocker.id, - blockeeId: blockee.id, - blockType: MiBlockingType.Reaction, - }); - - if (blocking == null) { - this.logger.warn('Unblock requested, but the target was not blocked.'); - return; - } - - // Since we already have the blocker and blockee, we do not need to fetch - // them in the query above and can just manually insert them here. - blocking.blocker = blocker; - blocking.blockee = blockee; - - await this.blockingsRepository.delete(blocking.id); - - this.cacheService.userReactionBlockingCache.refresh(blocker.id); - this.cacheService.userReactionBlockedCache.refresh(blockee.id); - - this.globalEventService.publishInternalEvent('blockingReactionDeleted', { - blockerId: blocker.id, - blockeeId: blockee.id, - }); - } - - @bindThis - public async checkBlocked(blockerId: MiUser['id'], blockeeId: MiUser['id']): Promise { - return (await this.cacheService.userReactionBlockingCache.fetch(blockerId)).has(blockeeId); - } -} diff --git a/packages/backend/src/server/api/endpoints/blocking-reaction-user/create.ts b/packages/backend/src/server/api/endpoints/blocking-reaction-user/create.ts index d9ec2f0503..8074bd0a1d 100644 --- a/packages/backend/src/server/api/endpoints/blocking-reaction-user/create.ts +++ b/packages/backend/src/server/api/endpoints/blocking-reaction-user/create.ts @@ -11,7 +11,7 @@ import { MiBlockingType } from '@/models/_.js'; import { UserEntityService } from '@/core/entities/UserEntityService.js'; import { DI } from '@/di-symbols.js'; import { GetterService } from '@/server/api/GetterService.js'; -import { UserReactionBlockingService } from '@/core/UserReactionBlockingService.js'; +import { UserBlockingService } from '@/core/UserBlockingService.js'; import { ApiError } from '../../error.js'; export const meta = { @@ -72,7 +72,7 @@ export default class extends Endpoint { // eslint- private userEntityService: UserEntityService, private getterService: GetterService, - private userReactionBlockingService: UserReactionBlockingService, + private userBlockingService: UserBlockingService, ) { super(meta, paramDef, async (ps, me) => { const blocker = await this.usersRepository.findOneByOrFail({ id: me.id }); @@ -101,7 +101,7 @@ export default class extends Endpoint { // eslint- throw new ApiError(meta.errors.alreadyBlocking); } - await this.userReactionBlockingService.block(blocker, blockee); + await this.userBlockingService.reactionBlock(blocker, blockee); return await this.userEntityService.pack(blockee.id, blocker, { schema: 'UserDetailedNotMe', diff --git a/packages/backend/src/server/api/endpoints/blocking-reaction-user/delete.ts b/packages/backend/src/server/api/endpoints/blocking-reaction-user/delete.ts index c608fa486d..02c89fe0ae 100644 --- a/packages/backend/src/server/api/endpoints/blocking-reaction-user/delete.ts +++ b/packages/backend/src/server/api/endpoints/blocking-reaction-user/delete.ts @@ -8,9 +8,9 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; import type { UsersRepository, BlockingsRepository, MiBlockingType } from '@/models/_.js'; import { UserEntityService } from '@/core/entities/UserEntityService.js'; -import { UserReactionBlockingService } from '@/core/UserReactionBlockingService.js'; import { DI } from '@/di-symbols.js'; import { GetterService } from '@/server/api/GetterService.js'; +import { UserBlockingService } from '@/core/UserBlockingService.js'; import { ApiError } from '../../error.js'; export const meta = { @@ -71,7 +71,7 @@ export default class extends Endpoint { // eslint- private userEntityService: UserEntityService, private getterService: GetterService, - private userReactionBlockingService: UserReactionBlockingService, + private userBlockingService: UserBlockingService, ) { super(meta, paramDef, async (ps, me) => { const blocker = await this.usersRepository.findOneByOrFail({ id: me.id }); @@ -101,7 +101,7 @@ export default class extends Endpoint { // eslint- } // Delete blocking - await this.userReactionBlockingService.unblock(blocker, blockee); + await this.userBlockingService.reactionUnblock(blocker, blockee); return await this.userEntityService.pack(blockee.id, blocker, { schema: 'UserDetailedNotMe', diff --git a/packages/misskey-js/src/autogen/types.ts b/packages/misskey-js/src/autogen/types.ts index 4a574781ab..4d2126a149 100644 --- a/packages/misskey-js/src/autogen/types.ts +++ b/packages/misskey-js/src/autogen/types.ts @@ -4515,7 +4515,8 @@ export type components = { /** Format: id */ blockeeId: string; blockee: components['schemas']['UserDetailedNotMe']; - isReactionBlock: boolean; + /** @enum {string} */ + blockType: 'user' | 'reaction'; }; Hashtag: { /** @example misskey */ From 34da11f371fb3350448ec6678caf9a0bb5b5c01d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=B4=87=E5=B3=B0=20=E6=9C=94=E8=8F=AF?= Date: Mon, 18 Nov 2024 22:06:32 +0900 Subject: [PATCH 12/16] =?UTF-8?q?fix:=20import=E5=91=A8=E3=82=8A=E3=81=AE?= =?UTF-8?q?=E8=AB=B8=E3=80=85=E4=BF=AE=E6=AD=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/backend/src/core/CacheService.ts | 9 +++++---- packages/backend/src/core/entities/UserEntityService.ts | 2 +- .../api/endpoints/blocking-reaction-user/create.ts | 2 +- .../api/endpoints/blocking-reaction-user/delete.ts | 3 ++- .../backend/src/server/api/endpoints/blocking/create.ts | 2 +- .../backend/src/server/api/endpoints/blocking/delete.ts | 2 +- 6 files changed, 11 insertions(+), 9 deletions(-) diff --git a/packages/backend/src/core/CacheService.ts b/packages/backend/src/core/CacheService.ts index db8c3c372d..9d0ac56615 100644 --- a/packages/backend/src/core/CacheService.ts +++ b/packages/backend/src/core/CacheService.ts @@ -8,6 +8,7 @@ import * as Redis from 'ioredis'; import type { BlockingsRepository, FollowingsRepository, MutingsRepository, RenoteMutingsRepository, MiUserProfile, UserProfilesRepository, UsersRepository, MiFollowing } from '@/models/_.js'; import { MemoryKVCache, RedisKVCache } from '@/misc/cache.js'; import type { MiLocalUser, MiUser } from '@/models/User.js'; +import { MiBlockingType } from '@/models/Blocking.js'; import { DI } from '@/di-symbols.js'; import { UserEntityService } from '@/core/entities/UserEntityService.js'; import { bindThis } from '@/decorators.js'; @@ -82,7 +83,7 @@ export class CacheService implements OnApplicationShutdown { this.userBlockingCache = new RedisKVCache>(this.redisClient, 'userBlocking', { lifetime: 1000 * 60 * 30, // 30m memoryCacheLifetime: 1000 * 60, // 1m - fetcher: (key) => this.blockingsRepository.find({ where: { blockerId: key, isReactionBlock: false }, select: ['blockeeId'] }).then(xs => new Set(xs.map(x => x.blockeeId))), + fetcher: (key) => this.blockingsRepository.find({ where: { blockerId: key, blockType: MiBlockingType.User }, select: ['blockeeId'] }).then(xs => new Set(xs.map(x => x.blockeeId))), toRedisConverter: (value) => JSON.stringify(Array.from(value)), fromRedisConverter: (value) => new Set(JSON.parse(value)), }); @@ -90,7 +91,7 @@ export class CacheService implements OnApplicationShutdown { this.userBlockedCache = new RedisKVCache>(this.redisClient, 'userBlocked', { lifetime: 1000 * 60 * 30, // 30m memoryCacheLifetime: 1000 * 60, // 1m - fetcher: (key) => this.blockingsRepository.find({ where: { blockeeId: key, isReactionBlock: false }, select: ['blockerId'] }).then(xs => new Set(xs.map(x => x.blockerId))), + fetcher: (key) => this.blockingsRepository.find({ where: { blockeeId: key, blockType: MiBlockingType.User }, select: ['blockerId'] }).then(xs => new Set(xs.map(x => x.blockerId))), toRedisConverter: (value) => JSON.stringify(Array.from(value)), fromRedisConverter: (value) => new Set(JSON.parse(value)), }); @@ -98,7 +99,7 @@ export class CacheService implements OnApplicationShutdown { this.userReactionBlockingCache = new RedisKVCache>(this.redisClient, 'userReactionBlocking', { lifetime: 1000 * 60 * 30, // 30m memoryCacheLifetime: 1000 * 60, // 1m - fetcher: (key) => this.blockingsRepository.find({ where: { blockerId: key, isReactionBlock: true }, select: ['blockeeId'] }).then(xs => new Set(xs.map(x => x.blockeeId))), + fetcher: (key) => this.blockingsRepository.find({ where: { blockerId: key, blockType: MiBlockingType.Reaction }, select: ['blockeeId'] }).then(xs => new Set(xs.map(x => x.blockeeId))), toRedisConverter: (value) => JSON.stringify(Array.from(value)), fromRedisConverter: (value) => new Set(JSON.parse(value)), }); @@ -106,7 +107,7 @@ export class CacheService implements OnApplicationShutdown { this.userReactionBlockedCache = new RedisKVCache>(this.redisClient, 'userReactionBlocked', { lifetime: 1000 * 60 * 30, // 30m memoryCacheLifetime: 1000 * 60, // 1m - fetcher: (key) => this.blockingsRepository.find({ where: { blockeeId: key, isReactionBlock: true }, select: ['blockerId'] }).then(xs => new Set(xs.map(x => x.blockerId))), + fetcher: (key) => this.blockingsRepository.find({ where: { blockeeId: key, blockType: MiBlockingType.Reaction }, select: ['blockerId'] }).then(xs => new Set(xs.map(x => x.blockerId))), toRedisConverter: (value) => JSON.stringify(Array.from(value)), fromRedisConverter: (value) => new Set(JSON.parse(value)), }); diff --git a/packages/backend/src/core/entities/UserEntityService.ts b/packages/backend/src/core/entities/UserEntityService.ts index 22d178dd74..c61587c595 100644 --- a/packages/backend/src/core/entities/UserEntityService.ts +++ b/packages/backend/src/core/entities/UserEntityService.ts @@ -27,7 +27,6 @@ import type { BlockingsRepository, FollowingsRepository, FollowRequestsRepository, - MiBlockingType, MiFollowing, MiUserNotePining, MiUserProfile, @@ -40,6 +39,7 @@ import type { UserSecurityKeysRepository, UsersRepository, } from '@/models/_.js'; +import { MiBlockingType } from '@/models/Blocking.js'; import { bindThis } from '@/decorators.js'; import { RoleService } from '@/core/RoleService.js'; import { ApPersonService } from '@/core/activitypub/models/ApPersonService.js'; diff --git a/packages/backend/src/server/api/endpoints/blocking-reaction-user/create.ts b/packages/backend/src/server/api/endpoints/blocking-reaction-user/create.ts index 8074bd0a1d..cfc0e90b02 100644 --- a/packages/backend/src/server/api/endpoints/blocking-reaction-user/create.ts +++ b/packages/backend/src/server/api/endpoints/blocking-reaction-user/create.ts @@ -7,7 +7,7 @@ import ms from 'ms'; import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; import type { BlockingsRepository, UsersRepository } from '@/models/_.js'; -import { MiBlockingType } from '@/models/_.js'; +import { MiBlockingType } from '@/models/Blocking.js'; import { UserEntityService } from '@/core/entities/UserEntityService.js'; import { DI } from '@/di-symbols.js'; import { GetterService } from '@/server/api/GetterService.js'; diff --git a/packages/backend/src/server/api/endpoints/blocking-reaction-user/delete.ts b/packages/backend/src/server/api/endpoints/blocking-reaction-user/delete.ts index 02c89fe0ae..45a8f6e5f1 100644 --- a/packages/backend/src/server/api/endpoints/blocking-reaction-user/delete.ts +++ b/packages/backend/src/server/api/endpoints/blocking-reaction-user/delete.ts @@ -6,7 +6,8 @@ import ms from 'ms'; import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import type { UsersRepository, BlockingsRepository, MiBlockingType } from '@/models/_.js'; +import type { UsersRepository, BlockingsRepository } from '@/models/_.js'; +import { MiBlockingType } from '@/models/Blocking.js'; import { UserEntityService } from '@/core/entities/UserEntityService.js'; import { DI } from '@/di-symbols.js'; import { GetterService } from '@/server/api/GetterService.js'; diff --git a/packages/backend/src/server/api/endpoints/blocking/create.ts b/packages/backend/src/server/api/endpoints/blocking/create.ts index 06dae5177c..4f775b24bb 100644 --- a/packages/backend/src/server/api/endpoints/blocking/create.ts +++ b/packages/backend/src/server/api/endpoints/blocking/create.ts @@ -7,7 +7,7 @@ import ms from 'ms'; import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; import type { BlockingsRepository, UsersRepository } from '@/models/_.js'; -import { MiBlockingType } from '@/models/_.js'; +import { MiBlockingType } from '@/models/Blocking.js'; import { UserEntityService } from '@/core/entities/UserEntityService.js'; import { UserBlockingService } from '@/core/UserBlockingService.js'; import { DI } from '@/di-symbols.js'; diff --git a/packages/backend/src/server/api/endpoints/blocking/delete.ts b/packages/backend/src/server/api/endpoints/blocking/delete.ts index 1608329f8b..e0d9f0939e 100644 --- a/packages/backend/src/server/api/endpoints/blocking/delete.ts +++ b/packages/backend/src/server/api/endpoints/blocking/delete.ts @@ -7,7 +7,7 @@ import ms from 'ms'; import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; import type { BlockingsRepository, UsersRepository } from '@/models/_.js'; -import { MiBlockingType } from '@/models/_.js'; +import { MiBlockingType } from '@/models/Blocking.js'; import { UserEntityService } from '@/core/entities/UserEntityService.js'; import { UserBlockingService } from '@/core/UserBlockingService.js'; import { DI } from '@/di-symbols.js'; From 292809a3248f15577a6f23422e505a581006363d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=B4=87=E5=B3=B0=20=E6=9C=94=E8=8F=AF?= Date: Mon, 18 Nov 2024 22:08:05 +0900 Subject: [PATCH 13/16] =?UTF-8?q?fix:=20SPDX=E3=81=A4=E3=81=91=E5=BF=98?= =?UTF-8?q?=E3=82=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../migration/1731932268436-addBlockingReactionUser.js | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/packages/backend/migration/1731932268436-addBlockingReactionUser.js b/packages/backend/migration/1731932268436-addBlockingReactionUser.js index 62033fb434..72067694fa 100644 --- a/packages/backend/migration/1731932268436-addBlockingReactionUser.js +++ b/packages/backend/migration/1731932268436-addBlockingReactionUser.js @@ -1,3 +1,8 @@ +/* + * SPDX-FileCopyrightText: syuilo and misskey-project + * SPDX-License-Identifier: AGPL-3.0-only + */ + export class AddBlockingReactionUser1731932268436 { name = 'AddBlockingReactionUser1731932268436' From a96ae92f479a3436c2d5d932b6c7bf8cd3c15c61 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=B4=87=E5=B3=B0=20=E6=9C=94=E8=8F=AF?= Date: Mon, 18 Nov 2024 22:11:13 +0900 Subject: [PATCH 14/16] fix: ReactionService --- packages/backend/src/core/ReactionService.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/packages/backend/src/core/ReactionService.ts b/packages/backend/src/core/ReactionService.ts index 6924841eca..04cbc72107 100644 --- a/packages/backend/src/core/ReactionService.ts +++ b/packages/backend/src/core/ReactionService.ts @@ -91,7 +91,6 @@ export class ReactionService { private userEntityService: UserEntityService, private noteEntityService: NoteEntityService, private userBlockingService: UserBlockingService, - private userReactionBlockingService: UserBlockingService, private reactionsBufferingService: ReactionsBufferingService, private idService: IdService, private featuredService: FeaturedService, @@ -108,7 +107,7 @@ export class ReactionService { // Check blocking if (note.userId !== user.id) { const blocked = await this.userBlockingService.checkBlocked(note.userId, user.id); - const reactionBlocked = await this.userReactionBlockingService.checkBlocked(note.userId, user.id); + const reactionBlocked = await this.userBlockingService.checkReactionBlocked(note.userId, user.id); if (blocked || reactionBlocked) { throw new IdentifiableError('e70412a4-7197-4726-8e74-f3e0deb92aa7'); } From fd9b7edeff0f289b5ab21654a23bae0049291e60 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=B4=87=E5=B3=B0=20=E6=9C=94=E8=8F=AF?= Date: Tue, 19 Nov 2024 11:08:34 +0900 Subject: [PATCH 15/16] =?UTF-8?q?fix:=20=E3=82=AF=E3=82=A8=E3=83=AA?= =?UTF-8?q?=E3=81=A8=E3=81=8B=E4=BF=AE=E6=AD=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/backend/src/core/QueryService.ts | 5 +++-- packages/backend/src/core/entities/UserEntityService.ts | 8 ++++---- .../server/api/endpoints/blocking-reaction-user/list.ts | 3 ++- .../backend/src/server/api/endpoints/blocking/list.ts | 3 ++- packages/backend/test/unit/entities/UserEntityService.ts | 6 +++--- 5 files changed, 14 insertions(+), 11 deletions(-) diff --git a/packages/backend/src/core/QueryService.ts b/packages/backend/src/core/QueryService.ts index 65b6bc2ef4..8cdfb20bb9 100644 --- a/packages/backend/src/core/QueryService.ts +++ b/packages/backend/src/core/QueryService.ts @@ -11,6 +11,7 @@ import type { UserProfilesRepository, FollowingsRepository, ChannelFollowingsRep import { bindThis } from '@/decorators.js'; import { IdService } from '@/core/IdService.js'; import type { SelectQueryBuilder } from 'typeorm'; +import {MiBlockingType} from "@/models/_.js"; @Injectable() export class QueryService { @@ -73,7 +74,7 @@ export class QueryService { const blockingQuery = this.blockingsRepository.createQueryBuilder('blocking') .select('blocking.blockerId') .where('blocking.blockeeId = :blockeeId', { blockeeId: me.id }) - .andWhere('blocking.blockType = "user"'); + .andWhere('blocking.blockType = :blockType', { blockType: MiBlockingType.User }); // 投稿の作者にブロックされていない かつ // 投稿の返信先の作者にブロックされていない かつ @@ -99,7 +100,7 @@ export class QueryService { const blockingQuery = this.blockingsRepository.createQueryBuilder('blocking') .select('blocking.blockeeId') .where('blocking.blockerId = :blockerId', { blockerId: me.id }) - .andWhere('blocking.blockType = "user"'); + .andWhere('blocking.blockType = :blockType', { blockType: MiBlockingType.User }); const blockedQuery = this.blockingsRepository.createQueryBuilder('blocking') .select('blocking.blockerId') diff --git a/packages/backend/src/core/entities/UserEntityService.ts b/packages/backend/src/core/entities/UserEntityService.ts index c61587c595..df2c803691 100644 --- a/packages/backend/src/core/entities/UserEntityService.ts +++ b/packages/backend/src/core/entities/UserEntityService.ts @@ -291,25 +291,25 @@ export class UserEntityService implements OnModuleInit { this.blockingsRepository.createQueryBuilder('b') .select('b.blockeeId') .where('b.blockerId = :me', { me }) - .andWhere('b.blockType = "user"') + .andWhere('b.blockType = :type', { type: MiBlockingType.User }) .getRawMany<{ b_blockeeId: string }>() .then(it => it.map(it => it.b_blockeeId)), this.blockingsRepository.createQueryBuilder('b') .select('b.blockerId') .where('b.blockeeId = :me', { me }) - .andWhere('b.blockType = "user"') + .andWhere('b.blockType = :type', { type: MiBlockingType.User }) .getRawMany<{ b_blockerId: string }>() .then(it => it.map(it => it.b_blockerId)), this.blockingsRepository.createQueryBuilder('b') .select('b.blockeeId') .where('b.blockerId = :me', { me }) - .andWhere('b.blockType = "reaction"') + .andWhere('b.blockType = :type', { type: MiBlockingType.Reaction }) .getRawMany<{ b_blockeeId: string }>() .then(it => it.map(it => it.b_blockeeId)), this.blockingsRepository.createQueryBuilder('b') .select('b.blockerId') .where('b.blockeeId = :me', { me }) - .andWhere('b.blockType = "reaction"') + .andWhere('b.blockType = :type', { type: MiBlockingType.Reaction }) .getRawMany<{ b_blockerId: string }>() .then(it => it.map(it => it.b_blockerId)), this.mutingsRepository.createQueryBuilder('m') diff --git a/packages/backend/src/server/api/endpoints/blocking-reaction-user/list.ts b/packages/backend/src/server/api/endpoints/blocking-reaction-user/list.ts index 5055bd596b..f4cb38ec1e 100644 --- a/packages/backend/src/server/api/endpoints/blocking-reaction-user/list.ts +++ b/packages/backend/src/server/api/endpoints/blocking-reaction-user/list.ts @@ -9,6 +9,7 @@ import type { BlockingsRepository } from '@/models/_.js'; import { QueryService } from '@/core/QueryService.js'; import { BlockingEntityService } from '@/core/entities/BlockingEntityService.js'; import { DI } from '@/di-symbols.js'; +import { MiBlockingType } from '@/models/Blocking.js'; export const meta = { tags: ['account'], @@ -50,7 +51,7 @@ export default class extends Endpoint { // eslint- super(meta, paramDef, async (ps, me) => { const query = this.queryService.makePaginationQuery(this.blockingsRepository.createQueryBuilder('blocking'), ps.sinceId, ps.untilId) .andWhere('blocking.blockerId = :meId', { meId: me.id }) - .andWhere('blocking.blockType = "reaction"'); + .andWhere('blocking.blockType = :blockType', { blockType: MiBlockingType.Reaction }); const blockings = await query .limit(ps.limit) diff --git a/packages/backend/src/server/api/endpoints/blocking/list.ts b/packages/backend/src/server/api/endpoints/blocking/list.ts index f7a5fa1412..292bc0417e 100644 --- a/packages/backend/src/server/api/endpoints/blocking/list.ts +++ b/packages/backend/src/server/api/endpoints/blocking/list.ts @@ -9,6 +9,7 @@ import type { BlockingsRepository } from '@/models/_.js'; import { QueryService } from '@/core/QueryService.js'; import { BlockingEntityService } from '@/core/entities/BlockingEntityService.js'; import { DI } from '@/di-symbols.js'; +import { MiBlockingType } from '@/models/Blocking.js'; export const meta = { tags: ['account'], @@ -50,7 +51,7 @@ export default class extends Endpoint { // eslint- super(meta, paramDef, async (ps, me) => { const query = this.queryService.makePaginationQuery(this.blockingsRepository.createQueryBuilder('blocking'), ps.sinceId, ps.untilId) .andWhere('blocking.blockerId = :meId', { meId: me.id }) - .andWhere('blocking.blockType = "user"'); + .andWhere('blocking.blockType = :blockType', { blockType: MiBlockingType.User }); const blockings = await query .limit(ps.limit) diff --git a/packages/backend/test/unit/entities/UserEntityService.ts b/packages/backend/test/unit/entities/UserEntityService.ts index b28d386b43..c5c69cdb74 100644 --- a/packages/backend/test/unit/entities/UserEntityService.ts +++ b/packages/backend/test/unit/entities/UserEntityService.ts @@ -12,7 +12,7 @@ import { secureRndstr } from '@/misc/secure-rndstr.js'; import { genAidx } from '@/misc/id/aidx.js'; import { BlockingsRepository, - FollowingsRepository, FollowRequestsRepository, + FollowingsRepository, FollowRequestsRepository, MiBlockingType, MiUserProfile, MutingsRepository, RenoteMutingsRepository, UserMemoRepository, UserProfilesRepository, @@ -115,7 +115,7 @@ describe('UserEntityService', () => { id: genAidx(Date.now()), blockerId: blocker.id, blockeeId: blockee.id, - isReactionBlock: false, + blockType: MiBlockingType.User, }); } @@ -124,7 +124,7 @@ describe('UserEntityService', () => { id: genAidx(Date.now()), blockerId: blocker.id, blockeeId: blockee.id, - isReactionBlock: true, + blockType: MiBlockingType.Reaction, }); } From 6236c936f121570fdbcb86198e677fff63f0f85f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=B4=87=E5=B3=B0=20=E6=9C=94=E8=8F=AF?= Date: Tue, 19 Nov 2024 11:16:14 +0900 Subject: [PATCH 16/16] =?UTF-8?q?fix:=20code=20style=20=E3=81=AE=E4=BF=AE?= =?UTF-8?q?=E6=AD=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/backend/src/core/QueryService.ts | 2 +- packages/backend/src/core/UserBlockingService.ts | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/packages/backend/src/core/QueryService.ts b/packages/backend/src/core/QueryService.ts index 8cdfb20bb9..a4b9db061c 100644 --- a/packages/backend/src/core/QueryService.ts +++ b/packages/backend/src/core/QueryService.ts @@ -10,8 +10,8 @@ import type { MiUser } from '@/models/User.js'; import type { UserProfilesRepository, FollowingsRepository, ChannelFollowingsRepository, BlockingsRepository, NoteThreadMutingsRepository, MutingsRepository, RenoteMutingsRepository } from '@/models/_.js'; import { bindThis } from '@/decorators.js'; import { IdService } from '@/core/IdService.js'; +import { MiBlockingType } from '@/models/Blocking.js'; import type { SelectQueryBuilder } from 'typeorm'; -import {MiBlockingType} from "@/models/_.js"; @Injectable() export class QueryService { diff --git a/packages/backend/src/core/UserBlockingService.ts b/packages/backend/src/core/UserBlockingService.ts index 328205d645..7611c7584c 100644 --- a/packages/backend/src/core/UserBlockingService.ts +++ b/packages/backend/src/core/UserBlockingService.ts @@ -214,8 +214,7 @@ export class UserBlockingService implements OnModuleInit { public async checkBlocked(blockerId: MiUser['id'], blockeeId: MiUser['id']): Promise { return (await this.cacheService.userBlockingCache.fetch(blockerId)).has(blockeeId); } - - + @bindThis public async reactionBlock(blocker: MiUser, blockee: MiUser, silent = false) { const blocking = await this.blockingsRepository.findOneBy({