From cfcbcd83799a1a2888ff13c9121588db0e79364f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=95=84=EC=8A=A4=ED=8A=B8=EB=A1=9C?= Date: Sat, 28 Dec 2024 14:36:49 +0900 Subject: [PATCH] wip: postVisibilityLimit --- locales/ko-KR.yml | 2 +- .../backend/src/core/NoteCreateService.ts | 27 +++++++++--- packages/backend/src/core/RoleService.ts | 6 +-- .../src/core/entities/UserEntityService.ts | 18 +++++--- .../backend/src/models/json-schema/role.ts | 4 +- .../server/api/endpoints/admin/show-user.ts | 10 ++++- packages/backend/test/e2e/antennas.ts | 2 +- packages/backend/test/e2e/users.ts | 2 +- packages/frontend/src/const.ts | 2 +- .../frontend/src/pages/admin/roles.editor.vue | 43 ++++++++++--------- packages/frontend/src/pages/admin/roles.vue | 16 +++---- 11 files changed, 80 insertions(+), 52 deletions(-) diff --git a/locales/ko-KR.yml b/locales/ko-KR.yml index 63c4d7501..9b17bb7df 100644 --- a/locales/ko-KR.yml +++ b/locales/ko-KR.yml @@ -1848,7 +1848,7 @@ _role: _options: gtlAvailable: "글로벌 타임라인 보이기" ltlAvailable: "로컬 타임라인 보이기" - canPublicNote: "공개 노트 허용" + postVisibilityLimit: "사용할 수 있는 노트 공개 범위" canInitiateConversation: "멘션, 답글, 인용 허용" mentionLimit: "노트에 넣을 수 있는 멘션 수" canCreateContent: "컨텐츠 생성 허용" diff --git a/packages/backend/src/core/NoteCreateService.ts b/packages/backend/src/core/NoteCreateService.ts index 20c7f3e2b..37499ba1e 100644 --- a/packages/backend/src/core/NoteCreateService.ts +++ b/packages/backend/src/core/NoteCreateService.ts @@ -261,13 +261,26 @@ export class NoteCreateService implements OnApplicationShutdown { throw new IdentifiableError('5b1c2b67-50a6-4a8a-a59c-0ede40890de3', 'User has no permission to create content.'); } - if (data.visibility === 'public' && data.channel == null) { - const sensitiveWords = meta.sensitiveWords; - if (this.utilityService.isKeyWordIncluded(data.cw ?? this.utilityService.concatNoteContentsForKeyWordCheck({ text: data.text, pollChoices: data.poll?.choices }), sensitiveWords)) { - data.visibility = 'home'; - this.logger.warn('Visibility changed to home because sensitive words are included', { userId: user.id, note: data }); - } else if (!policies.canPublicNote) { - data.visibility = 'home'; + if (data.channel == null) { + if (policies.postVisibilityLimit < 3) { + data.visibility = ['specified', 'followers', 'home'][policies.postVisibilityLimit]; + this.logger.warn(`Visibility changed to ${data.visibility} because of role policies`, { + userId: user.id, + note: data, + }); + } + if (data.visibility === 'public') { + const sensitiveWords = meta.sensitiveWords; + if (this.utilityService.isKeyWordIncluded(data.cw ?? this.utilityService.concatNoteContentsForKeyWordCheck({ + text: data.text, + pollChoices: data.poll?.choices, + }), sensitiveWords)) { + data.visibility = 'home'; + this.logger.warn('Visibility changed to home because sensitive words are included', { + userId: user.id, + note: data, + }); + } } } diff --git a/packages/backend/src/core/RoleService.ts b/packages/backend/src/core/RoleService.ts index c0e2a14d8..eba05b47c 100644 --- a/packages/backend/src/core/RoleService.ts +++ b/packages/backend/src/core/RoleService.ts @@ -35,7 +35,7 @@ import type { OnApplicationShutdown, OnModuleInit } from '@nestjs/common'; export type RolePolicies = { gtlAvailable: boolean; ltlAvailable: boolean; - canPublicNote: boolean; + postVisibilityLimit: number; canInitiateConversation: boolean; canCreateContent: boolean; canUpdateContent: boolean; @@ -79,7 +79,7 @@ export type RolePolicies = { export const DEFAULT_POLICIES: RolePolicies = { gtlAvailable: true, ltlAvailable: true, - canPublicNote: true, + postVisibilityLimit: 3, canInitiateConversation: true, canCreateContent: true, canUpdateContent: true, @@ -396,7 +396,7 @@ export class RoleService implements OnApplicationShutdown, OnModuleInit { return { gtlAvailable: calc('gtlAvailable', vs => vs.some(v => v === true)), ltlAvailable: calc('ltlAvailable', vs => vs.some(v => v === true)), - canPublicNote: calc('canPublicNote', vs => vs.some(v => v === true)), + postVisibilityLimit: calc('postVisibilityLimit', vs => Math.max(...vs)), canInitiateConversation: calc('canInitiateConversation', vs => vs.some(v => v === true)), canCreateContent: calc('canCreateContent', vs => vs.some(v => v === true)), canUpdateContent: calc('canUpdateContent', vs => vs.some(v => v === true)), diff --git a/packages/backend/src/core/entities/UserEntityService.ts b/packages/backend/src/core/entities/UserEntityService.ts index 403925b4d..f4588f93e 100644 --- a/packages/backend/src/core/entities/UserEntityService.ts +++ b/packages/backend/src/core/entities/UserEntityService.ts @@ -543,8 +543,14 @@ export class UserEntityService implements OnModuleInit { bannerUrl: user.bannerUrl, bannerBlurhash: user.bannerBlurhash, isLocked: user.isLocked, - isSilenced: !policies?.canPublicNote, - isLimited: !(policies?.canCreateContent && policies.canUpdateContent && policies.canDeleteContent && policies.canInitiateConversation), + isSilenced: (policies?.postVisibilityLimit ?? 0) < 3, + isLimited: !( + policies?.canCreateContent && + policies.canUpdateContent && + policies.canDeleteContent && + policies.canInitiateConversation && + policies.canUseAccountRemoval + ), isSuspended: user.isSuspended, description: profile?.description, location: profile?.location, @@ -630,9 +636,9 @@ export class UserEntityService implements OnModuleInit { achievements: profile?.achievements, autoRemovalCondition: { active: user.autoRemoval, - deleteAfter: autoRemovalCondition?.deleteAfter, - noPiningNotes: autoRemovalCondition?.noPiningNotes, - noSpecifiedNotes: autoRemovalCondition?.noSpecifiedNotes, + deleteAfter: autoRemovalCondition.deleteAfter, + noPiningNotes: autoRemovalCondition.noPiningNotes, + noSpecifiedNotes: autoRemovalCondition.noSpecifiedNotes, }, loggedInDays: profile?.loggedInDates.length, policies: policies, @@ -731,7 +737,7 @@ export class UserEntityService implements OnModuleInit { } } - return (await Promise.allSettled(_users.map(u => this.pack(u, me, { ...options, userProfile: profilesMap?.get(u.id), userRelations: userRelations, userMemos: userMemos, pinNotes: pinNotes })))) + return (await Promise.allSettled(_users.map(u => this.pack(u, me, { ...options, userProfile: profilesMap.get(u.id), userRelations: userRelations, userMemos: userMemos, pinNotes: pinNotes })))) .filter(result => result.status === 'fulfilled') .map(result => (result as PromiseFulfilledResult>).value); } diff --git a/packages/backend/src/models/json-schema/role.ts b/packages/backend/src/models/json-schema/role.ts index ff651c514..144dbfda8 100644 --- a/packages/backend/src/models/json-schema/role.ts +++ b/packages/backend/src/models/json-schema/role.ts @@ -176,8 +176,8 @@ export const packedRolePoliciesSchema = { type: 'boolean', optional: false, nullable: false, }, - canPublicNote: { - type: 'boolean', + postVisibilityLimit: { + type: 'integer', optional: false, nullable: false, }, canInitiateConversation: { diff --git a/packages/backend/src/server/api/endpoints/admin/show-user.ts b/packages/backend/src/server/api/endpoints/admin/show-user.ts index f1818086a..177a74a80 100644 --- a/packages/backend/src/server/api/endpoints/admin/show-user.ts +++ b/packages/backend/src/server/api/endpoints/admin/show-user.ts @@ -220,8 +220,14 @@ export default class extends Endpoint { // eslint- const policies = await this.roleService.getUserPolicies(user.id); const isModerator = await this.roleService.isModerator(user); - const isLimited = !(policies.canCreateContent && policies.canUpdateContent && policies.canDeleteContent && policies.canInitiateConversation); - const isSilenced = !policies.canPublicNote; + const isLimited = !( + policies.canCreateContent && + policies.canUpdateContent && + policies.canDeleteContent && + policies.canInitiateConversation && + policies.canUseAccountRemoval + ); + const isSilenced = policies.postVisibilityLimit < 3; const _me = await this.usersRepository.findOneByOrFail({ id: me.id }); if (!await this.roleService.isAdministrator(_me) && await this.roleService.isAdministrator(user)) { diff --git a/packages/backend/test/e2e/antennas.ts b/packages/backend/test/e2e/antennas.ts index 05352916a..0464dcc70 100644 --- a/packages/backend/test/e2e/antennas.ts +++ b/packages/backend/test/e2e/antennas.ts @@ -94,7 +94,7 @@ describe('アンテナ', () => { await api('i/update', { isLocked: true }, userLocking); userSilenced = await signup({ username: 'userSilenced' }); await post(userSilenced, { text: 'test' }); - const roleSilenced = await role(root, {}, { canPublicNote: { priority: 0, useDefault: false, value: false } }); + const roleSilenced = await role(root, {}, { postVisibilityLimit: { priority: 0, useDefault: false, value: 2 } }); await api('admin/roles/assign', { userId: userSilenced.id, roleId: roleSilenced.id }, root); userSuspended = await signup({ username: 'userSuspended' }); await post(userSuspended, { text: 'test' }); diff --git a/packages/backend/test/e2e/users.ts b/packages/backend/test/e2e/users.ts index 85c68d42b..9922a9256 100644 --- a/packages/backend/test/e2e/users.ts +++ b/packages/backend/test/e2e/users.ts @@ -238,7 +238,7 @@ describe('ユーザー', () => { await api('admin/roles/assign', { userId: userRoleBadge.id, roleId: roleBadge.id }, root); userSilenced = await signup({ username: 'userSilenced' }); await post(userSilenced, { text: 'test' }); - roleSilenced = await role(root, {}, { canPublicNote: { priority: 0, useDefault: false, value: false } }); + roleSilenced = await role(root, {}, { postVisibilityLimit: { priority: 0, useDefault: false, value: 2 } }); await api('admin/roles/assign', { userId: userSilenced.id, roleId: roleSilenced.id }, root); userSuspended = await signup({ username: 'userSuspended' }); await post(userSuspended, { text: 'test' }); diff --git a/packages/frontend/src/const.ts b/packages/frontend/src/const.ts index 71ce53e8e..14d692c4d 100644 --- a/packages/frontend/src/const.ts +++ b/packages/frontend/src/const.ts @@ -74,7 +74,7 @@ export const obsoleteNotificationTypes = ['pollVote', 'groupInvited'] as const; export const ROLE_POLICIES = [ 'gtlAvailable', 'ltlAvailable', - 'canPublicNote', + 'postVisibilityLimit', 'canInitiateConversation', 'canCreateContent', 'canUpdateContent', diff --git a/packages/frontend/src/pages/admin/roles.editor.vue b/packages/frontend/src/pages/admin/roles.editor.vue index de231ae1e..3425342e5 100644 --- a/packages/frontend/src/pages/admin/roles.editor.vue +++ b/packages/frontend/src/pages/admin/roles.editor.vue @@ -105,6 +105,29 @@ SPDX-License-Identifier: AGPL-3.0-only + +