From 81f11d8f860803cf01fbb2cfd106bd3344db98f2 Mon Sep 17 00:00:00 2001 From: syuilo <Syuilotan@yahoo.co.jp> Date: Sun, 15 Jan 2023 20:52:53 +0900 Subject: [PATCH] refactor: rename role.options -> role.policies --- locales/ja-JP.yml | 1 + .../migration/1673783015567-Policies.js | 13 ++ packages/backend/src/core/DriveService.ts | 4 +- .../backend/src/core/NoteCreateService.ts | 2 +- .../backend/src/core/NotePiningService.ts | 2 +- packages/backend/src/core/RoleService.ts | 26 ++-- packages/backend/src/core/UserListService.ts | 2 +- .../src/core/entities/RoleEntityService.ts | 10 +- .../src/core/entities/UserEntityService.ts | 4 +- packages/backend/src/models/entities/Meta.ts | 2 +- packages/backend/src/models/entities/Role.ts | 2 +- .../src/server/NodeinfoServerService.ts | 8 +- .../backend/src/server/api/ApiCallService.ts | 8 +- .../backend/src/server/api/EndpointsModule.ts | 8 +- packages/backend/src/server/api/endpoints.ts | 6 +- .../endpoints/admin/emoji/add-aliases-bulk.ts | 2 +- .../server/api/endpoints/admin/emoji/add.ts | 2 +- .../server/api/endpoints/admin/emoji/copy.ts | 2 +- .../api/endpoints/admin/emoji/delete-bulk.ts | 2 +- .../api/endpoints/admin/emoji/delete.ts | 2 +- .../api/endpoints/admin/emoji/import-zip.ts | 2 +- .../api/endpoints/admin/emoji/list-remote.ts | 2 +- .../server/api/endpoints/admin/emoji/list.ts | 2 +- .../admin/emoji/remove-aliases-bulk.ts | 2 +- .../endpoints/admin/emoji/set-aliases-bulk.ts | 2 +- .../admin/emoji/set-category-bulk.ts | 2 +- .../api/endpoints/admin/emoji/update.ts | 2 +- .../src/server/api/endpoints/admin/meta.ts | 4 +- .../api/endpoints/admin/roles/create.ts | 6 +- ...override.ts => update-default-policies.ts} | 8 +- .../api/endpoints/admin/roles/update.ts | 6 +- .../server/api/endpoints/admin/show-user.ts | 3 +- .../server/api/endpoints/antennas/create.ts | 2 +- .../server/api/endpoints/clips/add-note.ts | 2 +- .../src/server/api/endpoints/clips/create.ts | 2 +- .../backend/src/server/api/endpoints/drive.ts | 4 +- .../src/server/api/endpoints/i/update.ts | 2 +- .../server/api/endpoints/i/webhooks/create.ts | 2 +- .../src/server/api/endpoints/invite.ts | 2 +- .../backend/src/server/api/endpoints/meta.ts | 4 +- .../api/endpoints/notes/global-timeline.ts | 4 +- .../api/endpoints/notes/hybrid-timeline.ts | 4 +- .../api/endpoints/notes/local-timeline.ts | 4 +- .../api/endpoints/users/lists/create.ts | 2 +- .../api/stream/channels/global-timeline.ts | 4 +- .../api/stream/channels/hybrid-timeline.ts | 4 +- .../api/stream/channels/local-timeline.ts | 4 +- .../backend/src/server/api/stream/types.ts | 2 +- .../frontend/src/pages/admin/roles.editor.vue | 142 +++++++++--------- packages/frontend/src/pages/admin/roles.vue | 119 +++++++-------- packages/frontend/src/pages/timeline.vue | 4 +- packages/frontend/src/pages/user-info.vue | 20 ++- packages/frontend/src/ui/_common_/common.ts | 4 +- 53 files changed, 254 insertions(+), 232 deletions(-) create mode 100644 packages/backend/migration/1673783015567-Policies.js rename packages/backend/src/server/api/endpoints/admin/roles/{update-default-role-override.ts => update-default-policies.ts} (85%) diff --git a/locales/ja-JP.yml b/locales/ja-JP.yml index 79856447a0..786d41ca41 100644 --- a/locales/ja-JP.yml +++ b/locales/ja-JP.yml @@ -952,6 +952,7 @@ _role: isPublic: "ロールを公開" descriptionOfIsPublic: "ロールにアサインされたユーザーを誰でも見ることができます。また、ユーザーのプロフィールでこのロールが表示されます。" options: "オプション" + policies: "ポリシー" baseRole: "ベースロール" useBaseValue: "ベースロールの値を使用" chooseRoleToAssign: "アサインするロールを選択" diff --git a/packages/backend/migration/1673783015567-Policies.js b/packages/backend/migration/1673783015567-Policies.js new file mode 100644 index 0000000000..8b36921d41 --- /dev/null +++ b/packages/backend/migration/1673783015567-Policies.js @@ -0,0 +1,13 @@ +export class Policies1673783015567 { + name = 'Policies1673783015567' + + async up(queryRunner) { + await queryRunner.query(`ALTER TABLE "role" RENAME COLUMN "options" TO "policies"`); + await queryRunner.query(`ALTER TABLE "meta" RENAME COLUMN "defaultRoleOverride" TO "policies"`); + } + + async down(queryRunner) { + await queryRunner.query(`ALTER TABLE "meta" RENAME COLUMN "policies" TO "defaultRoleOverride"`); + await queryRunner.query(`ALTER TABLE "role" RENAME COLUMN "policies" TO "options"`); + } +} diff --git a/packages/backend/src/core/DriveService.ts b/packages/backend/src/core/DriveService.ts index 9002c96a65..598a457e83 100644 --- a/packages/backend/src/core/DriveService.ts +++ b/packages/backend/src/core/DriveService.ts @@ -479,8 +479,8 @@ export class DriveService { if (user && !isLink) { const usage = await this.driveFileEntityService.calcDriveUsageOf(user); - const role = await this.roleService.getUserRoleOptions(user.id); - const driveCapacity = 1024 * 1024 * role.driveCapacityMb; + const policies = await this.roleService.getUserPolicies(user.id); + const driveCapacity = 1024 * 1024 * policies.driveCapacityMb; this.registerLogger.debug('drive capacity override applied'); this.registerLogger.debug(`overrideCap: ${driveCapacity}bytes, usage: ${usage}bytes, u+s: ${usage + info.size}bytes`); diff --git a/packages/backend/src/core/NoteCreateService.ts b/packages/backend/src/core/NoteCreateService.ts index 112b84fdf9..3dc44a25fe 100644 --- a/packages/backend/src/core/NoteCreateService.ts +++ b/packages/backend/src/core/NoteCreateService.ts @@ -226,7 +226,7 @@ export class NoteCreateService { if (data.channel != null) data.localOnly = true; if (data.visibility === 'public' && data.channel == null) { - if ((await this.roleService.getUserRoleOptions(user.id)).canPublicNote === false) { + if ((await this.roleService.getUserPolicies(user.id)).canPublicNote === false) { data.visibility = 'home'; } } diff --git a/packages/backend/src/core/NotePiningService.ts b/packages/backend/src/core/NotePiningService.ts index bc038e17a7..bb6def1edb 100644 --- a/packages/backend/src/core/NotePiningService.ts +++ b/packages/backend/src/core/NotePiningService.ts @@ -57,7 +57,7 @@ export class NotePiningService { const pinings = await this.userNotePiningsRepository.findBy({ userId: user.id }); - if (pinings.length >= (await this.roleService.getUserRoleOptions(user.id)).pinLimit) { + if (pinings.length >= (await this.roleService.getUserPolicies(user.id)).pinLimit) { throw new IdentifiableError('15a018eb-58e5-4da1-93be-330fcc5e4e1a', 'You can not pin notes any more.'); } diff --git a/packages/backend/src/core/RoleService.ts b/packages/backend/src/core/RoleService.ts index 42b477d9ed..abad058303 100644 --- a/packages/backend/src/core/RoleService.ts +++ b/packages/backend/src/core/RoleService.ts @@ -13,7 +13,7 @@ import { UserEntityService } from '@/core/entities/UserEntityService.js'; import { StreamMessages } from '@/server/api/stream/types.js'; import type { OnApplicationShutdown } from '@nestjs/common'; -export type RoleOptions = { +export type RolePolicies = { gtlAvailable: boolean; ltlAvailable: boolean; canPublicNote: boolean; @@ -31,7 +31,7 @@ export type RoleOptions = { rateLimitFactor: number; }; -export const DEFAULT_ROLE: RoleOptions = { +export const DEFAULT_POLICIES: RolePolicies = { gtlAvailable: true, ltlAvailable: true, canPublicNote: true, @@ -195,26 +195,26 @@ export class RoleService implements OnApplicationShutdown { } @bindThis - public async getUserRoleOptions(userId: User['id'] | null): Promise<RoleOptions> { + public async getUserPolicies(userId: User['id'] | null): Promise<RolePolicies> { const meta = await this.metaService.fetch(); - const baseRoleOptions = { ...DEFAULT_ROLE, ...meta.defaultRoleOverride }; + const basePolicies = { ...DEFAULT_POLICIES, ...meta.policies }; - if (userId == null) return baseRoleOptions; + if (userId == null) return basePolicies; const roles = await this.getUserRoles(userId); - function calc<T extends keyof RoleOptions>(name: T, aggregate: (values: RoleOptions[T][]) => RoleOptions[T]) { - if (roles.length === 0) return baseRoleOptions[name]; + function calc<T extends keyof RolePolicies>(name: T, aggregate: (values: RolePolicies[T][]) => RolePolicies[T]) { + if (roles.length === 0) return basePolicies[name]; - const options = roles.map(role => role.options[name] ?? { priority: 0, useDefault: true }); + const policies = roles.map(role => role.policies[name] ?? { priority: 0, useDefault: true }); - const p2 = options.filter(option => option.priority === 2); - if (p2.length > 0) return aggregate(p2.map(option => option.useDefault ? baseRoleOptions[name] : option.value)); + const p2 = policies.filter(policy => policy.priority === 2); + if (p2.length > 0) return aggregate(p2.map(policy => policy.useDefault ? basePolicies[name] : policy.value)); - const p1 = options.filter(option => option.priority === 1); - if (p1.length > 0) return aggregate(p2.map(option => option.useDefault ? baseRoleOptions[name] : option.value)); + const p1 = policies.filter(policy => policy.priority === 1); + if (p1.length > 0) return aggregate(p2.map(policy => policy.useDefault ? basePolicies[name] : policy.value)); - return aggregate(options.map(option => option.useDefault ? baseRoleOptions[name] : option.value)); + return aggregate(policies.map(policy => policy.useDefault ? basePolicies[name] : policy.value)); } return { diff --git a/packages/backend/src/core/UserListService.ts b/packages/backend/src/core/UserListService.ts index 18c9787fa8..fc48738307 100644 --- a/packages/backend/src/core/UserListService.ts +++ b/packages/backend/src/core/UserListService.ts @@ -35,7 +35,7 @@ export class UserListService { const currentCount = await this.userListJoiningsRepository.countBy({ userListId: list.id, }); - if (currentCount > (await this.roleService.getUserRoleOptions(me.id)).userEachUserListsLimit) { + if (currentCount > (await this.roleService.getUserPolicies(me.id)).userEachUserListsLimit) { throw new Error('Too many users'); } diff --git a/packages/backend/src/core/entities/RoleEntityService.ts b/packages/backend/src/core/entities/RoleEntityService.ts index 6a14775653..52f3374468 100644 --- a/packages/backend/src/core/entities/RoleEntityService.ts +++ b/packages/backend/src/core/entities/RoleEntityService.ts @@ -6,7 +6,7 @@ import type { Packed } from '@/misc/schema.js'; import type { User } from '@/models/entities/User.js'; import type { Role } from '@/models/entities/Role.js'; import { bindThis } from '@/decorators.js'; -import { DEFAULT_ROLE } from '@/core/RoleService.js'; +import { DEFAULT_POLICIES } from '@/core/RoleService.js'; import { UserEntityService } from './UserEntityService.js'; @Injectable() @@ -40,9 +40,9 @@ export class RoleEntityService { roleId: role.id, }); - const roleOptions = { ...role.options }; - for (const [k, v] of Object.entries(DEFAULT_ROLE)) { - if (roleOptions[k] == null) roleOptions[k] = { + const policies = { ...role.policies }; + for (const [k, v] of Object.entries(DEFAULT_POLICIES)) { + if (policies[k] == null) policies[k] = { useDefault: true, priority: 0, value: v, @@ -62,7 +62,7 @@ export class RoleEntityService { isAdministrator: role.isAdministrator, isModerator: role.isModerator, canEditMembersByModerator: role.canEditMembersByModerator, - options: roleOptions, + policies: policies, usersCount: assigns.length, ...(opts.detail ? { users: this.userEntityService.packMany(assigns.map(x => x.userId), me), diff --git a/packages/backend/src/core/entities/UserEntityService.ts b/packages/backend/src/core/entities/UserEntityService.ts index 6b754150cf..bf6f6f4553 100644 --- a/packages/backend/src/core/entities/UserEntityService.ts +++ b/packages/backend/src/core/entities/UserEntityService.ts @@ -423,7 +423,7 @@ export class UserEntityService implements OnModuleInit { bannerUrl: user.banner ? this.driveFileEntityService.getPublicUrl(user.banner, false) : null, bannerBlurhash: user.banner?.blurhash ?? null, isLocked: user.isLocked, - isSilenced: this.roleService.getUserRoleOptions(user.id).then(r => !r.canPublicNote), + isSilenced: this.roleService.getUserPolicies(user.id).then(r => !r.canPublicNote), isSuspended: user.isSuspended ?? falsy, description: profile!.description, location: profile!.location, @@ -496,7 +496,7 @@ export class UserEntityService implements OnModuleInit { } : {}), ...(opts.includeSecrets ? { - role: this.roleService.getUserRoleOptions(user.id), + policies: this.roleService.getUserPolicies(user.id), email: profile!.email, emailVerified: profile!.emailVerified, securityKeysList: profile!.twoFactorEnabled diff --git a/packages/backend/src/models/entities/Meta.ts b/packages/backend/src/models/entities/Meta.ts index e724ba9a49..5d222a6da1 100644 --- a/packages/backend/src/models/entities/Meta.ts +++ b/packages/backend/src/models/entities/Meta.ts @@ -458,5 +458,5 @@ export class Meta { @Column('jsonb', { default: { }, }) - public defaultRoleOverride: Record<string, any>; + public policies: Record<string, any>; } diff --git a/packages/backend/src/models/entities/Role.ts b/packages/backend/src/models/entities/Role.ts index d8d203493b..abd5f864a2 100644 --- a/packages/backend/src/models/entities/Role.ts +++ b/packages/backend/src/models/entities/Role.ts @@ -136,7 +136,7 @@ export class Role { @Column('jsonb', { default: { }, }) - public options: Record<string, { + public policies: Record<string, { useDefault: boolean; priority: number; value: any; diff --git a/packages/backend/src/server/NodeinfoServerService.ts b/packages/backend/src/server/NodeinfoServerService.ts index 19380d13a4..024ddfe632 100644 --- a/packages/backend/src/server/NodeinfoServerService.ts +++ b/packages/backend/src/server/NodeinfoServerService.ts @@ -10,7 +10,7 @@ import { UserEntityService } from '@/core/entities/UserEntityService.js'; import { bindThis } from '@/decorators.js'; import NotesChart from '@/core/chart/charts/notes.js'; import UsersChart from '@/core/chart/charts/users.js'; -import { DEFAULT_ROLE } from '@/core/RoleService.js'; +import { DEFAULT_POLICIES } from '@/core/RoleService.js'; import type { FastifyInstance, FastifyPluginOptions } from 'fastify'; const nodeinfo2_1path = '/nodeinfo/2.1'; @@ -74,7 +74,7 @@ export class NodeinfoServerService { const proxyAccount = meta.proxyAccountId ? await this.userEntityService.pack(meta.proxyAccountId).catch(() => null) : null; - const baseRoleOptions = { ...DEFAULT_ROLE, ...meta.defaultRoleOverride }; + const basePolicies = { ...DEFAULT_POLICIES, ...meta.policies }; return { software: { @@ -105,8 +105,8 @@ export class NodeinfoServerService { repositoryUrl: meta.repositoryUrl, feedbackUrl: meta.feedbackUrl, disableRegistration: meta.disableRegistration, - disableLocalTimeline: !baseRoleOptions.ltlAvailable, - disableGlobalTimeline: !baseRoleOptions.gtlAvailable, + disableLocalTimeline: !basePolicies.ltlAvailable, + disableGlobalTimeline: !basePolicies.gtlAvailable, emailRequiredForSignup: meta.emailRequiredForSignup, enableHcaptcha: meta.enableHcaptcha, enableRecaptcha: meta.enableRecaptcha, diff --git a/packages/backend/src/server/api/ApiCallService.ts b/packages/backend/src/server/api/ApiCallService.ts index dcc9342a82..395a1c468a 100644 --- a/packages/backend/src/server/api/ApiCallService.ts +++ b/packages/backend/src/server/api/ApiCallService.ts @@ -225,7 +225,7 @@ export class ApiCallService implements OnApplicationShutdown { } // TODO: 毎リクエスト計算するのもあれだしキャッシュしたい - const factor = user ? (await this.roleService.getUserRoleOptions(user.id)).rateLimitFactor : 1; + const factor = user ? (await this.roleService.getUserPolicies(user.id)).rateLimitFactor : 1; // Rate limit await this.rateLimiterService.limit(limit as IEndpointMeta['limit'] & { key: NonNullable<string> }, limitActor, factor).catch(err => { @@ -274,9 +274,9 @@ export class ApiCallService implements OnApplicationShutdown { } } - if (ep.meta.requireRoleOption != null && !user!.isRoot) { - const myRole = await this.roleService.getUserRoleOptions(user!.id); - if (!myRole[ep.meta.requireRoleOption]) { + if (ep.meta.requireRolePolicy != null && !user!.isRoot) { + const policies = await this.roleService.getUserPolicies(user!.id); + if (!policies[ep.meta.requireRolePolicy]) { throw new ApiError({ message: 'You are not assigned to a required role.', code: 'ROLE_PERMISSION_DENIED', diff --git a/packages/backend/src/server/api/EndpointsModule.ts b/packages/backend/src/server/api/EndpointsModule.ts index aa88a9dd13..14927da7d6 100644 --- a/packages/backend/src/server/api/EndpointsModule.ts +++ b/packages/backend/src/server/api/EndpointsModule.ts @@ -65,7 +65,7 @@ import * as ep___admin_roles_show from './endpoints/admin/roles/show.js'; import * as ep___admin_roles_update from './endpoints/admin/roles/update.js'; import * as ep___admin_roles_assign from './endpoints/admin/roles/assign.js'; import * as ep___admin_roles_unassign from './endpoints/admin/roles/unassign.js'; -import * as ep___admin_roles_updateDefaultRoleOverride from './endpoints/admin/roles/update-default-role-override.js'; +import * as ep___admin_roles_updateDefaultPolicies from './endpoints/admin/roles/update-default-policies.js'; import * as ep___announcements from './endpoints/announcements.js'; import * as ep___antennas_create from './endpoints/antennas/create.js'; import * as ep___antennas_delete from './endpoints/antennas/delete.js'; @@ -399,7 +399,7 @@ const $admin_roles_show: Provider = { provide: 'ep:admin/roles/show', useClass: const $admin_roles_update: Provider = { provide: 'ep:admin/roles/update', useClass: ep___admin_roles_update.default }; const $admin_roles_assign: Provider = { provide: 'ep:admin/roles/assign', useClass: ep___admin_roles_assign.default }; const $admin_roles_unassign: Provider = { provide: 'ep:admin/roles/unassign', useClass: ep___admin_roles_unassign.default }; -const $admin_roles_updateDefaultRoleOverride: Provider = { provide: 'ep:admin/roles/update-default-role-override', useClass: ep___admin_roles_updateDefaultRoleOverride.default }; +const $admin_roles_updateDefaultPolicies: Provider = { provide: 'ep:admin/roles/update-default-policies', useClass: ep___admin_roles_updateDefaultPolicies.default }; const $announcements: Provider = { provide: 'ep:announcements', useClass: ep___announcements.default }; const $antennas_create: Provider = { provide: 'ep:antennas/create', useClass: ep___antennas_create.default }; const $antennas_delete: Provider = { provide: 'ep:antennas/delete', useClass: ep___antennas_delete.default }; @@ -737,7 +737,7 @@ const $retention: Provider = { provide: 'ep:retention', useClass: ep___retention $admin_roles_update, $admin_roles_assign, $admin_roles_unassign, - $admin_roles_updateDefaultRoleOverride, + $admin_roles_updateDefaultPolicies, $announcements, $antennas_create, $antennas_delete, @@ -1069,7 +1069,7 @@ const $retention: Provider = { provide: 'ep:retention', useClass: ep___retention $admin_roles_update, $admin_roles_assign, $admin_roles_unassign, - $admin_roles_updateDefaultRoleOverride, + $admin_roles_updateDefaultPolicies, $announcements, $antennas_create, $antennas_delete, diff --git a/packages/backend/src/server/api/endpoints.ts b/packages/backend/src/server/api/endpoints.ts index f50a3b5dd2..54c4206ea4 100644 --- a/packages/backend/src/server/api/endpoints.ts +++ b/packages/backend/src/server/api/endpoints.ts @@ -64,7 +64,7 @@ import * as ep___admin_roles_show from './endpoints/admin/roles/show.js'; import * as ep___admin_roles_update from './endpoints/admin/roles/update.js'; import * as ep___admin_roles_assign from './endpoints/admin/roles/assign.js'; import * as ep___admin_roles_unassign from './endpoints/admin/roles/unassign.js'; -import * as ep___admin_roles_updateDefaultRoleOverride from './endpoints/admin/roles/update-default-role-override.js'; +import * as ep___admin_roles_updateDefaultPolicies from './endpoints/admin/roles/update-default-policies.js'; import * as ep___announcements from './endpoints/announcements.js'; import * as ep___antennas_create from './endpoints/antennas/create.js'; import * as ep___antennas_delete from './endpoints/antennas/delete.js'; @@ -396,7 +396,7 @@ const eps = [ ['admin/roles/update', ep___admin_roles_update], ['admin/roles/assign', ep___admin_roles_assign], ['admin/roles/unassign', ep___admin_roles_unassign], - ['admin/roles/update-default-role-override', ep___admin_roles_updateDefaultRoleOverride], + ['admin/roles/update-default-policies', ep___admin_roles_updateDefaultPolicies], ['announcements', ep___announcements], ['antennas/create', ep___antennas_create], ['antennas/delete', ep___antennas_delete], @@ -695,7 +695,7 @@ export interface IEndpointMeta { */ readonly requireAdmin?: boolean; - readonly requireRoleOption?: string; + readonly requireRolePolicy?: string; /** * エンドポイントのリミテーションに関するやつ diff --git a/packages/backend/src/server/api/endpoints/admin/emoji/add-aliases-bulk.ts b/packages/backend/src/server/api/endpoints/admin/emoji/add-aliases-bulk.ts index d114fd3d55..9b6c774f0c 100644 --- a/packages/backend/src/server/api/endpoints/admin/emoji/add-aliases-bulk.ts +++ b/packages/backend/src/server/api/endpoints/admin/emoji/add-aliases-bulk.ts @@ -8,7 +8,7 @@ export const meta = { tags: ['admin'], requireCredential: true, - requireRoleOption: 'canManageCustomEmojis', + requireRolePolicy: 'canManageCustomEmojis', } as const; export const paramDef = { diff --git a/packages/backend/src/server/api/endpoints/admin/emoji/add.ts b/packages/backend/src/server/api/endpoints/admin/emoji/add.ts index 52ccb74447..abca1d169d 100644 --- a/packages/backend/src/server/api/endpoints/admin/emoji/add.ts +++ b/packages/backend/src/server/api/endpoints/admin/emoji/add.ts @@ -14,7 +14,7 @@ export const meta = { tags: ['admin'], requireCredential: true, - requireRoleOption: 'canManageCustomEmojis', + requireRolePolicy: 'canManageCustomEmojis', errors: { noSuchFile: { diff --git a/packages/backend/src/server/api/endpoints/admin/emoji/copy.ts b/packages/backend/src/server/api/endpoints/admin/emoji/copy.ts index 4d1fdd989d..b4fc7fd6f5 100644 --- a/packages/backend/src/server/api/endpoints/admin/emoji/copy.ts +++ b/packages/backend/src/server/api/endpoints/admin/emoji/copy.ts @@ -14,7 +14,7 @@ export const meta = { tags: ['admin'], requireCredential: true, - requireRoleOption: 'canManageCustomEmojis', + requireRolePolicy: 'canManageCustomEmojis', errors: { noSuchEmoji: { diff --git a/packages/backend/src/server/api/endpoints/admin/emoji/delete-bulk.ts b/packages/backend/src/server/api/endpoints/admin/emoji/delete-bulk.ts index 27aa4fb1b1..ae45105b28 100644 --- a/packages/backend/src/server/api/endpoints/admin/emoji/delete-bulk.ts +++ b/packages/backend/src/server/api/endpoints/admin/emoji/delete-bulk.ts @@ -9,7 +9,7 @@ export const meta = { tags: ['admin'], requireCredential: true, - requireRoleOption: 'canManageCustomEmojis', + requireRolePolicy: 'canManageCustomEmojis', } as const; export const paramDef = { diff --git a/packages/backend/src/server/api/endpoints/admin/emoji/delete.ts b/packages/backend/src/server/api/endpoints/admin/emoji/delete.ts index 2531246569..e237d87d34 100644 --- a/packages/backend/src/server/api/endpoints/admin/emoji/delete.ts +++ b/packages/backend/src/server/api/endpoints/admin/emoji/delete.ts @@ -10,7 +10,7 @@ export const meta = { tags: ['admin'], requireCredential: true, - requireRoleOption: 'canManageCustomEmojis', + requireRolePolicy: 'canManageCustomEmojis', errors: { noSuchEmoji: { diff --git a/packages/backend/src/server/api/endpoints/admin/emoji/import-zip.ts b/packages/backend/src/server/api/endpoints/admin/emoji/import-zip.ts index 15f468c180..b4a07324bb 100644 --- a/packages/backend/src/server/api/endpoints/admin/emoji/import-zip.ts +++ b/packages/backend/src/server/api/endpoints/admin/emoji/import-zip.ts @@ -5,7 +5,7 @@ import { QueueService } from '@/core/QueueService.js'; export const meta = { secure: true, requireCredential: true, - requireRoleOption: 'canManageCustomEmojis', + requireRolePolicy: 'canManageCustomEmojis', } as const; export const paramDef = { diff --git a/packages/backend/src/server/api/endpoints/admin/emoji/list-remote.ts b/packages/backend/src/server/api/endpoints/admin/emoji/list-remote.ts index 131c9ef223..d9ce97194a 100644 --- a/packages/backend/src/server/api/endpoints/admin/emoji/list-remote.ts +++ b/packages/backend/src/server/api/endpoints/admin/emoji/list-remote.ts @@ -11,7 +11,7 @@ export const meta = { tags: ['admin'], requireCredential: true, - requireRoleOption: 'canManageCustomEmojis', + requireRolePolicy: 'canManageCustomEmojis', res: { type: 'array', diff --git a/packages/backend/src/server/api/endpoints/admin/emoji/list.ts b/packages/backend/src/server/api/endpoints/admin/emoji/list.ts index ef2bc936c3..1a6096f36f 100644 --- a/packages/backend/src/server/api/endpoints/admin/emoji/list.ts +++ b/packages/backend/src/server/api/endpoints/admin/emoji/list.ts @@ -11,7 +11,7 @@ export const meta = { tags: ['admin'], requireCredential: true, - requireRoleOption: 'canManageCustomEmojis', + requireRolePolicy: 'canManageCustomEmojis', res: { type: 'array', diff --git a/packages/backend/src/server/api/endpoints/admin/emoji/remove-aliases-bulk.ts b/packages/backend/src/server/api/endpoints/admin/emoji/remove-aliases-bulk.ts index a70cd8d787..5fc9e024bf 100644 --- a/packages/backend/src/server/api/endpoints/admin/emoji/remove-aliases-bulk.ts +++ b/packages/backend/src/server/api/endpoints/admin/emoji/remove-aliases-bulk.ts @@ -8,7 +8,7 @@ export const meta = { tags: ['admin'], requireCredential: true, - requireRoleOption: 'canManageCustomEmojis', + requireRolePolicy: 'canManageCustomEmojis', } as const; export const paramDef = { diff --git a/packages/backend/src/server/api/endpoints/admin/emoji/set-aliases-bulk.ts b/packages/backend/src/server/api/endpoints/admin/emoji/set-aliases-bulk.ts index b33e5662bb..8b5ba8fbf4 100644 --- a/packages/backend/src/server/api/endpoints/admin/emoji/set-aliases-bulk.ts +++ b/packages/backend/src/server/api/endpoints/admin/emoji/set-aliases-bulk.ts @@ -8,7 +8,7 @@ export const meta = { tags: ['admin'], requireCredential: true, - requireRoleOption: 'canManageCustomEmojis', + requireRolePolicy: 'canManageCustomEmojis', } as const; export const paramDef = { diff --git a/packages/backend/src/server/api/endpoints/admin/emoji/set-category-bulk.ts b/packages/backend/src/server/api/endpoints/admin/emoji/set-category-bulk.ts index 05834bc572..827b5ace7a 100644 --- a/packages/backend/src/server/api/endpoints/admin/emoji/set-category-bulk.ts +++ b/packages/backend/src/server/api/endpoints/admin/emoji/set-category-bulk.ts @@ -8,7 +8,7 @@ export const meta = { tags: ['admin'], requireCredential: true, - requireRoleOption: 'canManageCustomEmojis', + requireRolePolicy: 'canManageCustomEmojis', } as const; export const paramDef = { diff --git a/packages/backend/src/server/api/endpoints/admin/emoji/update.ts b/packages/backend/src/server/api/endpoints/admin/emoji/update.ts index 19645cb515..fb0ef12878 100644 --- a/packages/backend/src/server/api/endpoints/admin/emoji/update.ts +++ b/packages/backend/src/server/api/endpoints/admin/emoji/update.ts @@ -9,7 +9,7 @@ export const meta = { tags: ['admin'], requireCredential: true, - requireRoleOption: 'canManageCustomEmojis', + requireRolePolicy: 'canManageCustomEmojis', errors: { noSuchEmoji: { diff --git a/packages/backend/src/server/api/endpoints/admin/meta.ts b/packages/backend/src/server/api/endpoints/admin/meta.ts index fd08a5f847..b393827054 100644 --- a/packages/backend/src/server/api/endpoints/admin/meta.ts +++ b/packages/backend/src/server/api/endpoints/admin/meta.ts @@ -4,7 +4,7 @@ import { Endpoint } from '@/server/api/endpoint-base.js'; import { MetaService } from '@/core/MetaService.js'; import type { Config } from '@/config.js'; import { DI } from '@/di-symbols.js'; -import { DEFAULT_ROLE } from '@/core/RoleService.js'; +import { DEFAULT_POLICIES } from '@/core/RoleService.js'; export const meta = { tags: ['meta'], @@ -440,7 +440,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { deeplIsPro: instance.deeplIsPro, enableIpLogging: instance.enableIpLogging, enableActiveEmailValidation: instance.enableActiveEmailValidation, - baseRole: { ...DEFAULT_ROLE, ...instance.defaultRoleOverride }, + policies: { ...DEFAULT_POLICIES, ...instance.policies }, }; }); } diff --git a/packages/backend/src/server/api/endpoints/admin/roles/create.ts b/packages/backend/src/server/api/endpoints/admin/roles/create.ts index a9216a6386..f136c6d624 100644 --- a/packages/backend/src/server/api/endpoints/admin/roles/create.ts +++ b/packages/backend/src/server/api/endpoints/admin/roles/create.ts @@ -25,7 +25,7 @@ export const paramDef = { isModerator: { type: 'boolean' }, isAdministrator: { type: 'boolean' }, canEditMembersByModerator: { type: 'boolean' }, - options: { + policies: { type: 'object', }, }, @@ -39,7 +39,7 @@ export const paramDef = { 'isModerator', 'isAdministrator', 'canEditMembersByModerator', - 'options', + 'policies', ], } as const; @@ -70,7 +70,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { isAdministrator: ps.isAdministrator, isModerator: ps.isModerator, canEditMembersByModerator: ps.canEditMembersByModerator, - options: ps.options, + policies: ps.policies, }).then(x => this.rolesRepository.findOneByOrFail(x.identifiers[0])); this.globalEventService.publishInternalEvent('roleCreated', created); diff --git a/packages/backend/src/server/api/endpoints/admin/roles/update-default-role-override.ts b/packages/backend/src/server/api/endpoints/admin/roles/update-default-policies.ts similarity index 85% rename from packages/backend/src/server/api/endpoints/admin/roles/update-default-role-override.ts rename to packages/backend/src/server/api/endpoints/admin/roles/update-default-policies.ts index 35da04efd2..6006816bcb 100644 --- a/packages/backend/src/server/api/endpoints/admin/roles/update-default-role-override.ts +++ b/packages/backend/src/server/api/endpoints/admin/roles/update-default-policies.ts @@ -16,12 +16,12 @@ export const meta = { export const paramDef = { type: 'object', properties: { - options: { + policies: { type: 'object', }, }, required: [ - 'options', + 'policies', ], } as const; @@ -34,9 +34,9 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { ) { super(meta, paramDef, async (ps) => { await this.metaService.update({ - defaultRoleOverride: ps.options, + policies: ps.policies, }); - this.globalEventService.publishInternalEvent('defaultRoleOverrideUpdated', ps.options); + this.globalEventService.publishInternalEvent('policiesUpdated', ps.policies); }); } } diff --git a/packages/backend/src/server/api/endpoints/admin/roles/update.ts b/packages/backend/src/server/api/endpoints/admin/roles/update.ts index 4ca5124eda..fc4c3d8f11 100644 --- a/packages/backend/src/server/api/endpoints/admin/roles/update.ts +++ b/packages/backend/src/server/api/endpoints/admin/roles/update.ts @@ -33,7 +33,7 @@ export const paramDef = { isModerator: { type: 'boolean' }, isAdministrator: { type: 'boolean' }, canEditMembersByModerator: { type: 'boolean' }, - options: { + policies: { type: 'object', }, }, @@ -48,7 +48,7 @@ export const paramDef = { 'isModerator', 'isAdministrator', 'canEditMembersByModerator', - 'options', + 'policies', ], } as const; @@ -79,7 +79,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { isModerator: ps.isModerator, isAdministrator: ps.isAdministrator, canEditMembersByModerator: ps.canEditMembersByModerator, - options: ps.options, + policies: ps.policies, }); const updated = await this.rolesRepository.findOneByOrFail({ id: ps.roleId }); this.globalEventService.publishInternalEvent('roleUpdated', updated); 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 3f4ec299af..94603cc91a 100644 --- a/packages/backend/src/server/api/endpoints/admin/show-user.ts +++ b/packages/backend/src/server/api/endpoints/admin/show-user.ts @@ -52,7 +52,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { } const isModerator = await this.roleService.isModerator(user); - const isSilenced = !(await this.roleService.getUserRoleOptions(user.id)).canPublicNote; + const isSilenced = !(await this.roleService.getUserPolicies(user.id)).canPublicNote; const _me = await this.usersRepository.findOneByOrFail({ id: me.id }); if (!await this.roleService.isAdministrator(_me) && await this.roleService.isAdministrator(user)) { @@ -94,6 +94,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { lastActiveDate: user.lastActiveDate, moderationNote: profile.moderationNote, signins, + policies: await this.roleService.getUserPolicies(user.id), roles: await this.roleEntityService.packMany(roles, me, { detail: false }), }; }); diff --git a/packages/backend/src/server/api/endpoints/antennas/create.ts b/packages/backend/src/server/api/endpoints/antennas/create.ts index 08625250c8..a1553b6a80 100644 --- a/packages/backend/src/server/api/endpoints/antennas/create.ts +++ b/packages/backend/src/server/api/endpoints/antennas/create.ts @@ -92,7 +92,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { const currentAntennasCount = await this.antennasRepository.countBy({ userId: me.id, }); - if (currentAntennasCount > (await this.roleService.getUserRoleOptions(me.id)).antennaLimit) { + if (currentAntennasCount > (await this.roleService.getUserPolicies(me.id)).antennaLimit) { throw new ApiError(meta.errors.tooManyAntennas); } diff --git a/packages/backend/src/server/api/endpoints/clips/add-note.ts b/packages/backend/src/server/api/endpoints/clips/add-note.ts index 3cf096c242..f3f9c3477f 100644 --- a/packages/backend/src/server/api/endpoints/clips/add-note.ts +++ b/packages/backend/src/server/api/endpoints/clips/add-note.ts @@ -97,7 +97,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { const currentCount = await this.clipNotesRepository.countBy({ clipId: clip.id, }); - if (currentCount > (await this.roleService.getUserRoleOptions(me.id)).noteEachClipsLimit) { + if (currentCount > (await this.roleService.getUserPolicies(me.id)).noteEachClipsLimit) { throw new ApiError(meta.errors.tooManyClipNotes); } diff --git a/packages/backend/src/server/api/endpoints/clips/create.ts b/packages/backend/src/server/api/endpoints/clips/create.ts index abc0288c89..c095de702c 100644 --- a/packages/backend/src/server/api/endpoints/clips/create.ts +++ b/packages/backend/src/server/api/endpoints/clips/create.ts @@ -54,7 +54,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { const currentCount = await this.clipsRepository.countBy({ userId: me.id, }); - if (currentCount > (await this.roleService.getUserRoleOptions(me.id)).clipLimit) { + if (currentCount > (await this.roleService.getUserPolicies(me.id)).clipLimit) { throw new ApiError(meta.errors.tooManyClips); } diff --git a/packages/backend/src/server/api/endpoints/drive.ts b/packages/backend/src/server/api/endpoints/drive.ts index 2a06792dcf..e5bbfecbcf 100644 --- a/packages/backend/src/server/api/endpoints/drive.ts +++ b/packages/backend/src/server/api/endpoints/drive.ts @@ -47,10 +47,10 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // Calculate drive usage const usage = await this.driveFileEntityService.calcDriveUsageOf(me.id); - const myRole = await this.roleService.getUserRoleOptions(me.id); + const policies = await this.roleService.getUserPolicies(me.id); return { - capacity: 1024 * 1024 * myRole.driveCapacityMb, + capacity: 1024 * 1024 * policies.driveCapacityMb, usage: usage, }; }); diff --git a/packages/backend/src/server/api/endpoints/i/update.ts b/packages/backend/src/server/api/endpoints/i/update.ts index fe09eca674..b1eaab3908 100644 --- a/packages/backend/src/server/api/endpoints/i/update.ts +++ b/packages/backend/src/server/api/endpoints/i/update.ts @@ -173,7 +173,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { if (ps.mutedWords !== undefined) { // TODO: ちゃんと数える const length = JSON.stringify(ps.mutedWords).length; - if (length > (await this.roleService.getUserRoleOptions(user.id)).wordMuteLimit) { + if (length > (await this.roleService.getUserPolicies(user.id)).wordMuteLimit) { throw new ApiError(meta.errors.tooManyMutedWords); } diff --git a/packages/backend/src/server/api/endpoints/i/webhooks/create.ts b/packages/backend/src/server/api/endpoints/i/webhooks/create.ts index 3d89b77a7b..51fcce6cf0 100644 --- a/packages/backend/src/server/api/endpoints/i/webhooks/create.ts +++ b/packages/backend/src/server/api/endpoints/i/webhooks/create.ts @@ -54,7 +54,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { const currentWebhooksCount = await this.webhooksRepository.countBy({ userId: me.id, }); - if (currentWebhooksCount > (await this.roleService.getUserRoleOptions(me.id)).webhookLimit) { + if (currentWebhooksCount > (await this.roleService.getUserPolicies(me.id)).webhookLimit) { throw new ApiError(meta.errors.tooManyWebhooks); } diff --git a/packages/backend/src/server/api/endpoints/invite.ts b/packages/backend/src/server/api/endpoints/invite.ts index 9b03cf4bb6..5d2c479e79 100644 --- a/packages/backend/src/server/api/endpoints/invite.ts +++ b/packages/backend/src/server/api/endpoints/invite.ts @@ -9,7 +9,7 @@ export const meta = { tags: ['meta'], requireCredential: true, - requireRoleOption: 'canInvite', + requireRolePolicy: 'canInvite', res: { type: 'object', diff --git a/packages/backend/src/server/api/endpoints/meta.ts b/packages/backend/src/server/api/endpoints/meta.ts index f46a32dfe7..89fa503173 100644 --- a/packages/backend/src/server/api/endpoints/meta.ts +++ b/packages/backend/src/server/api/endpoints/meta.ts @@ -7,7 +7,7 @@ import { UserEntityService } from '@/core/entities/UserEntityService.js'; import { MetaService } from '@/core/MetaService.js'; import type { Config } from '@/config.js'; import { DI } from '@/di-symbols.js'; -import { DEFAULT_ROLE } from '@/core/RoleService.js'; +import { DEFAULT_POLICIES } from '@/core/RoleService.js'; export const meta = { tags: ['meta'], @@ -334,7 +334,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { translatorAvailable: instance.deeplAuthKey != null, - baseRole: { ...DEFAULT_ROLE, ...instance.defaultRoleOverride }, + policies: { ...DEFAULT_POLICIES, ...instance.policies }, ...(ps.detail ? { pinnedPages: instance.pinnedPages, diff --git a/packages/backend/src/server/api/endpoints/notes/global-timeline.ts b/packages/backend/src/server/api/endpoints/notes/global-timeline.ts index 081563493d..5d0cdc3fca 100644 --- a/packages/backend/src/server/api/endpoints/notes/global-timeline.ts +++ b/packages/backend/src/server/api/endpoints/notes/global-timeline.ts @@ -62,8 +62,8 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { private activeUsersChart: ActiveUsersChart, ) { super(meta, paramDef, async (ps, me) => { - const role = await this.roleService.getUserRoleOptions(me ? me.id : null); - if (!role.gtlAvailable) { + const policies = await this.roleService.getUserPolicies(me ? me.id : null); + if (!policies.gtlAvailable) { throw new ApiError(meta.errors.gtlDisabled); } diff --git a/packages/backend/src/server/api/endpoints/notes/hybrid-timeline.ts b/packages/backend/src/server/api/endpoints/notes/hybrid-timeline.ts index b2c504448e..2819abb125 100644 --- a/packages/backend/src/server/api/endpoints/notes/hybrid-timeline.ts +++ b/packages/backend/src/server/api/endpoints/notes/hybrid-timeline.ts @@ -71,8 +71,8 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { private activeUsersChart: ActiveUsersChart, ) { super(meta, paramDef, async (ps, me) => { - const role = await this.roleService.getUserRoleOptions(me.id); - if (!role.ltlAvailable) { + const policies = await this.roleService.getUserPolicies(me.id); + if (!policies.ltlAvailable) { throw new ApiError(meta.errors.stlDisabled); } diff --git a/packages/backend/src/server/api/endpoints/notes/local-timeline.ts b/packages/backend/src/server/api/endpoints/notes/local-timeline.ts index 6361edc310..f396f7e584 100644 --- a/packages/backend/src/server/api/endpoints/notes/local-timeline.ts +++ b/packages/backend/src/server/api/endpoints/notes/local-timeline.ts @@ -67,8 +67,8 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { private activeUsersChart: ActiveUsersChart, ) { super(meta, paramDef, async (ps, me) => { - const role = await this.roleService.getUserRoleOptions(me ? me.id : null); - if (!role.ltlAvailable) { + const policies = await this.roleService.getUserPolicies(me ? me.id : null); + if (!policies.ltlAvailable) { throw new ApiError(meta.errors.ltlDisabled); } diff --git a/packages/backend/src/server/api/endpoints/users/lists/create.ts b/packages/backend/src/server/api/endpoints/users/lists/create.ts index 22e5e3ce78..a840c1a04e 100644 --- a/packages/backend/src/server/api/endpoints/users/lists/create.ts +++ b/packages/backend/src/server/api/endpoints/users/lists/create.ts @@ -55,7 +55,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { const currentCount = await this.userListsRepository.countBy({ userId: me.id, }); - if (currentCount > (await this.roleService.getUserRoleOptions(me.id)).userListLimit) { + if (currentCount > (await this.roleService.getUserPolicies(me.id)).userListLimit) { throw new ApiError(meta.errors.tooManyUserLists); } diff --git a/packages/backend/src/server/api/stream/channels/global-timeline.ts b/packages/backend/src/server/api/stream/channels/global-timeline.ts index 185c813869..43d8907fc9 100644 --- a/packages/backend/src/server/api/stream/channels/global-timeline.ts +++ b/packages/backend/src/server/api/stream/channels/global-timeline.ts @@ -29,8 +29,8 @@ class GlobalTimelineChannel extends Channel { @bindThis public async init(params: any) { - const role = await this.roleService.getUserRoleOptions(this.user ? this.user.id : null); - if (!role.gtlAvailable) return; + const policies = await this.roleService.getUserPolicies(this.user ? this.user.id : null); + if (!policies.gtlAvailable) return; // Subscribe events this.subscriber.on('notesStream', this.onNote); diff --git a/packages/backend/src/server/api/stream/channels/hybrid-timeline.ts b/packages/backend/src/server/api/stream/channels/hybrid-timeline.ts index a0f75f202c..340f677815 100644 --- a/packages/backend/src/server/api/stream/channels/hybrid-timeline.ts +++ b/packages/backend/src/server/api/stream/channels/hybrid-timeline.ts @@ -30,8 +30,8 @@ class HybridTimelineChannel extends Channel { @bindThis public async init(params: any): Promise<void> { - const role = await this.roleService.getUserRoleOptions(this.user ? this.user.id : null); - if (!role.ltlAvailable) return; + const policies = await this.roleService.getUserPolicies(this.user ? this.user.id : null); + if (!policies.ltlAvailable) return; // Subscribe events this.subscriber.on('notesStream', this.onNote); diff --git a/packages/backend/src/server/api/stream/channels/local-timeline.ts b/packages/backend/src/server/api/stream/channels/local-timeline.ts index 7d76f42fe7..ea29e30d63 100644 --- a/packages/backend/src/server/api/stream/channels/local-timeline.ts +++ b/packages/backend/src/server/api/stream/channels/local-timeline.ts @@ -28,8 +28,8 @@ class LocalTimelineChannel extends Channel { @bindThis public async init(params: any) { - const role = await this.roleService.getUserRoleOptions(this.user ? this.user.id : null); - if (!role.ltlAvailable) return; + const policies = await this.roleService.getUserPolicies(this.user ? this.user.id : null); + if (!policies.ltlAvailable) return; // Subscribe events this.subscriber.on('notesStream', this.onNote); diff --git a/packages/backend/src/server/api/stream/types.ts b/packages/backend/src/server/api/stream/types.ts index 03837baefb..a442529bb3 100644 --- a/packages/backend/src/server/api/stream/types.ts +++ b/packages/backend/src/server/api/stream/types.ts @@ -30,7 +30,7 @@ export interface InternalStreamTypes { remoteUserUpdated: Serialized<{ id: User['id']; }>; follow: Serialized<{ followerId: User['id']; followeeId: User['id']; }>; unfollow: Serialized<{ followerId: User['id']; followeeId: User['id']; }>; - defaultRoleOverrideUpdated: Serialized<Role['options']>; + policiesUpdated: Serialized<Role['options']>; roleCreated: Serialized<Role>; roleDeleted: Serialized<Role>; roleUpdated: Serialized<Role>; diff --git a/packages/frontend/src/pages/admin/roles.editor.vue b/packages/frontend/src/pages/admin/roles.editor.vue index 0f67cec0b2..d0be0a8c39 100644 --- a/packages/frontend/src/pages/admin/roles.editor.vue +++ b/packages/frontend/src/pages/admin/roles.editor.vue @@ -36,20 +36,20 @@ </MkFolder> <FormSlot> - <template #label>{{ i18n.ts._role.options }}</template> + <template #label>{{ i18n.ts._role.policies }}</template> <div class="_gaps_s"> <MkFolder> <template #label>{{ i18n.ts._role._options.rateLimitFactor }}</template> - <template #suffix>{{ options.rateLimitFactor.useDefault ? i18n.ts._role.useBaseValue : `${Math.floor(options.rateLimitFactor.value * 100)}%` }} <i :class="getPriorityIcon(options.rateLimitFactor)"></i></template> + <template #suffix>{{ policies.rateLimitFactor.useDefault ? i18n.ts._role.useBaseValue : `${Math.floor(policies.rateLimitFactor.value * 100)}%` }} <i :class="getPriorityIcon(policies.rateLimitFactor)"></i></template> <div class="_gaps"> - <MkSwitch v-model="options.rateLimitFactor.useDefault" :readonly="readonly"> + <MkSwitch v-model="policies.rateLimitFactor.useDefault" :readonly="readonly"> <template #label>{{ i18n.ts._role.useBaseValue }}</template> </MkSwitch> - <MkRange :model-value="options.rateLimitFactor.value * 100" :min="30" :max="300" :step="10" :text-converter="(v) => `${v}%`" @update:model-value="v => options.rateLimitFactor.value = (v / 100)"> + <MkRange :model-value="policies.rateLimitFactor.value * 100" :min="30" :max="300" :step="10" :text-converter="(v) => `${v}%`" @update:model-value="v => policies.rateLimitFactor.value = (v / 100)"> <template #label>{{ i18n.ts._role._options.rateLimitFactor }}</template> <template #caption>{{ i18n.ts._role._options.descriptionOfRateLimitFactor }}</template> </MkRange> - <MkRange v-model="options.rateLimitFactor.priority" :min="0" :max="2" :step="1" easing :text-converter="(v) => v === 0 ? i18n.ts._role._priority.low : v === 1 ? i18n.ts._role._priority.middle : v === 2 ? i18n.ts._role._priority.high : ''"> + <MkRange v-model="policies.rateLimitFactor.priority" :min="0" :max="2" :step="1" easing :text-converter="(v) => v === 0 ? i18n.ts._role._priority.low : v === 1 ? i18n.ts._role._priority.middle : v === 2 ? i18n.ts._role._priority.high : ''"> <template #label>{{ i18n.ts._role.priority }}</template> </MkRange> </div> @@ -57,15 +57,15 @@ <MkFolder> <template #label>{{ i18n.ts._role._options.gtlAvailable }}</template> - <template #suffix>{{ options.gtlAvailable.useDefault ? i18n.ts._role.useBaseValue : (options.gtlAvailable.value ? i18n.ts.yes : i18n.ts.no) }} <i :class="getPriorityIcon(options.gtlAvailable)"></i></template> + <template #suffix>{{ policies.gtlAvailable.useDefault ? i18n.ts._role.useBaseValue : (policies.gtlAvailable.value ? i18n.ts.yes : i18n.ts.no) }} <i :class="getPriorityIcon(policies.gtlAvailable)"></i></template> <div class="_gaps"> - <MkSwitch v-model="options.gtlAvailable.useDefault" :readonly="readonly"> + <MkSwitch v-model="policies.gtlAvailable.useDefault" :readonly="readonly"> <template #label>{{ i18n.ts._role.useBaseValue }}</template> </MkSwitch> - <MkSwitch v-model="options.gtlAvailable.value" :disabled="options.gtlAvailable.useDefault" :readonly="readonly"> + <MkSwitch v-model="policies.gtlAvailable.value" :disabled="policies.gtlAvailable.useDefault" :readonly="readonly"> <template #label>{{ i18n.ts.enable }}</template> </MkSwitch> - <MkRange v-model="options.gtlAvailable.priority" :min="0" :max="2" :step="1" easing :text-converter="(v) => v === 0 ? i18n.ts._role._priority.low : v === 1 ? i18n.ts._role._priority.middle : v === 2 ? i18n.ts._role._priority.high : ''"> + <MkRange v-model="policies.gtlAvailable.priority" :min="0" :max="2" :step="1" easing :text-converter="(v) => v === 0 ? i18n.ts._role._priority.low : v === 1 ? i18n.ts._role._priority.middle : v === 2 ? i18n.ts._role._priority.high : ''"> <template #label>{{ i18n.ts._role.priority }}</template> </MkRange> </div> @@ -73,15 +73,15 @@ <MkFolder> <template #label>{{ i18n.ts._role._options.ltlAvailable }}</template> - <template #suffix>{{ options.ltlAvailable.useDefault ? i18n.ts._role.useBaseValue : (options.ltlAvailable.value ? i18n.ts.yes : i18n.ts.no) }} <i :class="getPriorityIcon(options.ltlAvailable)"></i></template> + <template #suffix>{{ policies.ltlAvailable.useDefault ? i18n.ts._role.useBaseValue : (policies.ltlAvailable.value ? i18n.ts.yes : i18n.ts.no) }} <i :class="getPriorityIcon(policies.ltlAvailable)"></i></template> <div class="_gaps"> - <MkSwitch v-model="options.ltlAvailable.useDefault" :readonly="readonly"> + <MkSwitch v-model="policies.ltlAvailable.useDefault" :readonly="readonly"> <template #label>{{ i18n.ts._role.useBaseValue }}</template> </MkSwitch> - <MkSwitch v-model="options.ltlAvailable.value" :disabled="options.ltlAvailable.useDefault" :readonly="readonly"> + <MkSwitch v-model="policies.ltlAvailable.value" :disabled="policies.ltlAvailable.useDefault" :readonly="readonly"> <template #label>{{ i18n.ts.enable }}</template> </MkSwitch> - <MkRange v-model="options.ltlAvailable.priority" :min="0" :max="2" :step="1" easing :text-converter="(v) => v === 0 ? i18n.ts._role._priority.low : v === 1 ? i18n.ts._role._priority.middle : v === 2 ? i18n.ts._role._priority.high : ''"> + <MkRange v-model="policies.ltlAvailable.priority" :min="0" :max="2" :step="1" easing :text-converter="(v) => v === 0 ? i18n.ts._role._priority.low : v === 1 ? i18n.ts._role._priority.middle : v === 2 ? i18n.ts._role._priority.high : ''"> <template #label>{{ i18n.ts._role.priority }}</template> </MkRange> </div> @@ -89,15 +89,15 @@ <MkFolder> <template #label>{{ i18n.ts._role._options.canPublicNote }}</template> - <template #suffix>{{ options.canPublicNote.useDefault ? i18n.ts._role.useBaseValue : (options.canPublicNote.value ? i18n.ts.yes : i18n.ts.no) }} <i :class="getPriorityIcon(options.canPublicNote)"></i></template> + <template #suffix>{{ policies.canPublicNote.useDefault ? i18n.ts._role.useBaseValue : (policies.canPublicNote.value ? i18n.ts.yes : i18n.ts.no) }} <i :class="getPriorityIcon(policies.canPublicNote)"></i></template> <div class="_gaps"> - <MkSwitch v-model="options.canPublicNote.useDefault" :readonly="readonly"> + <MkSwitch v-model="policies.canPublicNote.useDefault" :readonly="readonly"> <template #label>{{ i18n.ts._role.useBaseValue }}</template> </MkSwitch> - <MkSwitch v-model="options.canPublicNote.value" :disabled="options.canPublicNote.useDefault" :readonly="readonly"> + <MkSwitch v-model="policies.canPublicNote.value" :disabled="policies.canPublicNote.useDefault" :readonly="readonly"> <template #label>{{ i18n.ts.enable }}</template> </MkSwitch> - <MkRange v-model="options.canPublicNote.priority" :min="0" :max="2" :step="1" easing :text-converter="(v) => v === 0 ? i18n.ts._role._priority.low : v === 1 ? i18n.ts._role._priority.middle : v === 2 ? i18n.ts._role._priority.high : ''"> + <MkRange v-model="policies.canPublicNote.priority" :min="0" :max="2" :step="1" easing :text-converter="(v) => v === 0 ? i18n.ts._role._priority.low : v === 1 ? i18n.ts._role._priority.middle : v === 2 ? i18n.ts._role._priority.high : ''"> <template #label>{{ i18n.ts._role.priority }}</template> </MkRange> </div> @@ -105,15 +105,15 @@ <MkFolder> <template #label>{{ i18n.ts._role._options.canInvite }}</template> - <template #suffix>{{ options.canInvite.useDefault ? i18n.ts._role.useBaseValue : (options.canInvite.value ? i18n.ts.yes : i18n.ts.no) }} <i :class="getPriorityIcon(options.canInvite)"></i></template> + <template #suffix>{{ policies.canInvite.useDefault ? i18n.ts._role.useBaseValue : (policies.canInvite.value ? i18n.ts.yes : i18n.ts.no) }} <i :class="getPriorityIcon(policies.canInvite)"></i></template> <div class="_gaps"> - <MkSwitch v-model="options.canInvite.useDefault" :readonly="readonly"> + <MkSwitch v-model="policies.canInvite.useDefault" :readonly="readonly"> <template #label>{{ i18n.ts._role.useBaseValue }}</template> </MkSwitch> - <MkSwitch v-model="options.canInvite.value" :disabled="options.canInvite.useDefault" :readonly="readonly"> + <MkSwitch v-model="policies.canInvite.value" :disabled="policies.canInvite.useDefault" :readonly="readonly"> <template #label>{{ i18n.ts.enable }}</template> </MkSwitch> - <MkRange v-model="options.canInvite.priority" :min="0" :max="2" :step="1" easing :text-converter="(v) => v === 0 ? i18n.ts._role._priority.low : v === 1 ? i18n.ts._role._priority.middle : v === 2 ? i18n.ts._role._priority.high : ''"> + <MkRange v-model="policies.canInvite.priority" :min="0" :max="2" :step="1" easing :text-converter="(v) => v === 0 ? i18n.ts._role._priority.low : v === 1 ? i18n.ts._role._priority.middle : v === 2 ? i18n.ts._role._priority.high : ''"> <template #label>{{ i18n.ts._role.priority }}</template> </MkRange> </div> @@ -121,15 +121,15 @@ <MkFolder> <template #label>{{ i18n.ts._role._options.canManageCustomEmojis }}</template> - <template #suffix>{{ options.canManageCustomEmojis.useDefault ? i18n.ts._role.useBaseValue : (options.canManageCustomEmojis.value ? i18n.ts.yes : i18n.ts.no) }} <i :class="getPriorityIcon(options.canManageCustomEmojis)"></i></template> + <template #suffix>{{ policies.canManageCustomEmojis.useDefault ? i18n.ts._role.useBaseValue : (policies.canManageCustomEmojis.value ? i18n.ts.yes : i18n.ts.no) }} <i :class="getPriorityIcon(policies.canManageCustomEmojis)"></i></template> <div class="_gaps"> - <MkSwitch v-model="options.canManageCustomEmojis.useDefault" :readonly="readonly"> + <MkSwitch v-model="policies.canManageCustomEmojis.useDefault" :readonly="readonly"> <template #label>{{ i18n.ts._role.useBaseValue }}</template> </MkSwitch> - <MkSwitch v-model="options.canManageCustomEmojis.value" :disabled="options.canManageCustomEmojis.useDefault" :readonly="readonly"> + <MkSwitch v-model="policies.canManageCustomEmojis.value" :disabled="policies.canManageCustomEmojis.useDefault" :readonly="readonly"> <template #label>{{ i18n.ts.enable }}</template> </MkSwitch> - <MkRange v-model="options.canManageCustomEmojis.priority" :min="0" :max="2" :step="1" easing :text-converter="(v) => v === 0 ? i18n.ts._role._priority.low : v === 1 ? i18n.ts._role._priority.middle : v === 2 ? i18n.ts._role._priority.high : ''"> + <MkRange v-model="policies.canManageCustomEmojis.priority" :min="0" :max="2" :step="1" easing :text-converter="(v) => v === 0 ? i18n.ts._role._priority.low : v === 1 ? i18n.ts._role._priority.middle : v === 2 ? i18n.ts._role._priority.high : ''"> <template #label>{{ i18n.ts._role.priority }}</template> </MkRange> </div> @@ -137,15 +137,15 @@ <MkFolder> <template #label>{{ i18n.ts._role._options.driveCapacity }}</template> - <template #suffix>{{ options.driveCapacityMb.useDefault ? i18n.ts._role.useBaseValue : (options.driveCapacityMb.value + 'MB') }} <i :class="getPriorityIcon(options.driveCapacityMb)"></i></template> + <template #suffix>{{ policies.driveCapacityMb.useDefault ? i18n.ts._role.useBaseValue : (policies.driveCapacityMb.value + 'MB') }} <i :class="getPriorityIcon(policies.driveCapacityMb)"></i></template> <div class="_gaps"> - <MkSwitch v-model="options.driveCapacityMb.useDefault" :readonly="readonly"> + <MkSwitch v-model="policies.driveCapacityMb.useDefault" :readonly="readonly"> <template #label>{{ i18n.ts._role.useBaseValue }}</template> </MkSwitch> - <MkInput v-model="options.driveCapacityMb.value" :disabled="options.driveCapacityMb.useDefault" type="number" :readonly="readonly"> + <MkInput v-model="policies.driveCapacityMb.value" :disabled="policies.driveCapacityMb.useDefault" type="number" :readonly="readonly"> <template #suffix>MB</template> </MkInput> - <MkRange v-model="options.driveCapacityMb.priority" :min="0" :max="2" :step="1" easing :text-converter="(v) => v === 0 ? i18n.ts._role._priority.low : v === 1 ? i18n.ts._role._priority.middle : v === 2 ? i18n.ts._role._priority.high : ''"> + <MkRange v-model="policies.driveCapacityMb.priority" :min="0" :max="2" :step="1" easing :text-converter="(v) => v === 0 ? i18n.ts._role._priority.low : v === 1 ? i18n.ts._role._priority.middle : v === 2 ? i18n.ts._role._priority.high : ''"> <template #label>{{ i18n.ts._role.priority }}</template> </MkRange> </div> @@ -153,14 +153,14 @@ <MkFolder> <template #label>{{ i18n.ts._role._options.pinMax }}</template> - <template #suffix>{{ options.pinLimit.useDefault ? i18n.ts._role.useBaseValue : (options.pinLimit.value) }} <i :class="getPriorityIcon(options.pinLimit)"></i></template> + <template #suffix>{{ policies.pinLimit.useDefault ? i18n.ts._role.useBaseValue : (policies.pinLimit.value) }} <i :class="getPriorityIcon(policies.pinLimit)"></i></template> <div class="_gaps"> - <MkSwitch v-model="options.pinLimit.useDefault" :readonly="readonly"> + <MkSwitch v-model="policies.pinLimit.useDefault" :readonly="readonly"> <template #label>{{ i18n.ts._role.useBaseValue }}</template> </MkSwitch> - <MkInput v-model="options.pinLimit.value" :disabled="options.pinLimit.useDefault" type="number" :readonly="readonly"> + <MkInput v-model="policies.pinLimit.value" :disabled="policies.pinLimit.useDefault" type="number" :readonly="readonly"> </MkInput> - <MkRange v-model="options.pinLimit.priority" :min="0" :max="2" :step="1" easing :text-converter="(v) => v === 0 ? i18n.ts._role._priority.low : v === 1 ? i18n.ts._role._priority.middle : v === 2 ? i18n.ts._role._priority.high : ''"> + <MkRange v-model="policies.pinLimit.priority" :min="0" :max="2" :step="1" easing :text-converter="(v) => v === 0 ? i18n.ts._role._priority.low : v === 1 ? i18n.ts._role._priority.middle : v === 2 ? i18n.ts._role._priority.high : ''"> <template #label>{{ i18n.ts._role.priority }}</template> </MkRange> </div> @@ -168,14 +168,14 @@ <MkFolder> <template #label>{{ i18n.ts._role._options.antennaMax }}</template> - <template #suffix>{{ options.antennaLimit.useDefault ? i18n.ts._role.useBaseValue : (options.antennaLimit.value) }} <i :class="getPriorityIcon(options.antennaLimit)"></i></template> + <template #suffix>{{ policies.antennaLimit.useDefault ? i18n.ts._role.useBaseValue : (policies.antennaLimit.value) }} <i :class="getPriorityIcon(policies.antennaLimit)"></i></template> <div class="_gaps"> - <MkSwitch v-model="options.antennaLimit.useDefault" :readonly="readonly"> + <MkSwitch v-model="policies.antennaLimit.useDefault" :readonly="readonly"> <template #label>{{ i18n.ts._role.useBaseValue }}</template> </MkSwitch> - <MkInput v-model="options.antennaLimit.value" :disabled="options.antennaLimit.useDefault" type="number" :readonly="readonly"> + <MkInput v-model="policies.antennaLimit.value" :disabled="policies.antennaLimit.useDefault" type="number" :readonly="readonly"> </MkInput> - <MkRange v-model="options.antennaLimit.priority" :min="0" :max="2" :step="1" easing :text-converter="(v) => v === 0 ? i18n.ts._role._priority.low : v === 1 ? i18n.ts._role._priority.middle : v === 2 ? i18n.ts._role._priority.high : ''"> + <MkRange v-model="policies.antennaLimit.priority" :min="0" :max="2" :step="1" easing :text-converter="(v) => v === 0 ? i18n.ts._role._priority.low : v === 1 ? i18n.ts._role._priority.middle : v === 2 ? i18n.ts._role._priority.high : ''"> <template #label>{{ i18n.ts._role.priority }}</template> </MkRange> </div> @@ -183,15 +183,15 @@ <MkFolder> <template #label>{{ i18n.ts._role._options.wordMuteMax }}</template> - <template #suffix>{{ options.wordMuteLimit.useDefault ? i18n.ts._role.useBaseValue : (options.wordMuteLimit.value) }} <i :class="getPriorityIcon(options.wordMuteLimit)"></i></template> + <template #suffix>{{ policies.wordMuteLimit.useDefault ? i18n.ts._role.useBaseValue : (policies.wordMuteLimit.value) }} <i :class="getPriorityIcon(policies.wordMuteLimit)"></i></template> <div class="_gaps"> - <MkSwitch v-model="options.wordMuteLimit.useDefault" :readonly="readonly"> + <MkSwitch v-model="policies.wordMuteLimit.useDefault" :readonly="readonly"> <template #label>{{ i18n.ts._role.useBaseValue }}</template> </MkSwitch> - <MkInput v-model="options.wordMuteLimit.value" :disabled="options.wordMuteLimit.useDefault" type="number" :readonly="readonly"> + <MkInput v-model="policies.wordMuteLimit.value" :disabled="policies.wordMuteLimit.useDefault" type="number" :readonly="readonly"> <template #suffix>chars</template> </MkInput> - <MkRange v-model="options.wordMuteLimit.priority" :min="0" :max="2" :step="1" easing :text-converter="(v) => v === 0 ? i18n.ts._role._priority.low : v === 1 ? i18n.ts._role._priority.middle : v === 2 ? i18n.ts._role._priority.high : ''"> + <MkRange v-model="policies.wordMuteLimit.priority" :min="0" :max="2" :step="1" easing :text-converter="(v) => v === 0 ? i18n.ts._role._priority.low : v === 1 ? i18n.ts._role._priority.middle : v === 2 ? i18n.ts._role._priority.high : ''"> <template #label>{{ i18n.ts._role.priority }}</template> </MkRange> </div> @@ -199,14 +199,14 @@ <MkFolder> <template #label>{{ i18n.ts._role._options.webhookMax }}</template> - <template #suffix>{{ options.webhookLimit.useDefault ? i18n.ts._role.useBaseValue : (options.webhookLimit.value) }} <i :class="getPriorityIcon(options.webhookLimit)"></i></template> + <template #suffix>{{ policies.webhookLimit.useDefault ? i18n.ts._role.useBaseValue : (policies.webhookLimit.value) }} <i :class="getPriorityIcon(policies.webhookLimit)"></i></template> <div class="_gaps"> - <MkSwitch v-model="options.webhookLimit.useDefault" :readonly="readonly"> + <MkSwitch v-model="policies.webhookLimit.useDefault" :readonly="readonly"> <template #label>{{ i18n.ts._role.useBaseValue }}</template> </MkSwitch> - <MkInput v-model="options.webhookLimit.value" :disabled="options.webhookLimit.useDefault" type="number" :readonly="readonly"> + <MkInput v-model="policies.webhookLimit.value" :disabled="policies.webhookLimit.useDefault" type="number" :readonly="readonly"> </MkInput> - <MkRange v-model="options.webhookLimit.priority" :min="0" :max="2" :step="1" easing :text-converter="(v) => v === 0 ? i18n.ts._role._priority.low : v === 1 ? i18n.ts._role._priority.middle : v === 2 ? i18n.ts._role._priority.high : ''"> + <MkRange v-model="policies.webhookLimit.priority" :min="0" :max="2" :step="1" easing :text-converter="(v) => v === 0 ? i18n.ts._role._priority.low : v === 1 ? i18n.ts._role._priority.middle : v === 2 ? i18n.ts._role._priority.high : ''"> <template #label>{{ i18n.ts._role.priority }}</template> </MkRange> </div> @@ -214,14 +214,14 @@ <MkFolder> <template #label>{{ i18n.ts._role._options.clipMax }}</template> - <template #suffix>{{ options.clipLimit.useDefault ? i18n.ts._role.useBaseValue : (options.clipLimit.value) }} <i :class="getPriorityIcon(options.clipLimit)"></i></template> + <template #suffix>{{ policies.clipLimit.useDefault ? i18n.ts._role.useBaseValue : (policies.clipLimit.value) }} <i :class="getPriorityIcon(policies.clipLimit)"></i></template> <div class="_gaps"> - <MkSwitch v-model="options.clipLimit.useDefault" :readonly="readonly"> + <MkSwitch v-model="policies.clipLimit.useDefault" :readonly="readonly"> <template #label>{{ i18n.ts._role.useBaseValue }}</template> </MkSwitch> - <MkInput v-model="options.clipLimit.value" :disabled="options.clipLimit.useDefault" type="number" :readonly="readonly"> + <MkInput v-model="policies.clipLimit.value" :disabled="policies.clipLimit.useDefault" type="number" :readonly="readonly"> </MkInput> - <MkRange v-model="options.clipLimit.priority" :min="0" :max="2" :step="1" easing :text-converter="(v) => v === 0 ? i18n.ts._role._priority.low : v === 1 ? i18n.ts._role._priority.middle : v === 2 ? i18n.ts._role._priority.high : ''"> + <MkRange v-model="policies.clipLimit.priority" :min="0" :max="2" :step="1" easing :text-converter="(v) => v === 0 ? i18n.ts._role._priority.low : v === 1 ? i18n.ts._role._priority.middle : v === 2 ? i18n.ts._role._priority.high : ''"> <template #label>{{ i18n.ts._role.priority }}</template> </MkRange> </div> @@ -229,14 +229,14 @@ <MkFolder> <template #label>{{ i18n.ts._role._options.noteEachClipsMax }}</template> - <template #suffix>{{ options.noteEachClipsLimit.useDefault ? i18n.ts._role.useBaseValue : (options.noteEachClipsLimit.value) }} <i :class="getPriorityIcon(options.noteEachClipsLimit)"></i></template> + <template #suffix>{{ policies.noteEachClipsLimit.useDefault ? i18n.ts._role.useBaseValue : (policies.noteEachClipsLimit.value) }} <i :class="getPriorityIcon(policies.noteEachClipsLimit)"></i></template> <div class="_gaps"> - <MkSwitch v-model="options.noteEachClipsLimit.useDefault" :readonly="readonly"> + <MkSwitch v-model="policies.noteEachClipsLimit.useDefault" :readonly="readonly"> <template #label>{{ i18n.ts._role.useBaseValue }}</template> </MkSwitch> - <MkInput v-model="options.noteEachClipsLimit.value" :disabled="options.noteEachClipsLimit.useDefault" type="number" :readonly="readonly"> + <MkInput v-model="policies.noteEachClipsLimit.value" :disabled="policies.noteEachClipsLimit.useDefault" type="number" :readonly="readonly"> </MkInput> - <MkRange v-model="options.noteEachClipsLimit.priority" :min="0" :max="2" :step="1" easing :text-converter="(v) => v === 0 ? i18n.ts._role._priority.low : v === 1 ? i18n.ts._role._priority.middle : v === 2 ? i18n.ts._role._priority.high : ''"> + <MkRange v-model="policies.noteEachClipsLimit.priority" :min="0" :max="2" :step="1" easing :text-converter="(v) => v === 0 ? i18n.ts._role._priority.low : v === 1 ? i18n.ts._role._priority.middle : v === 2 ? i18n.ts._role._priority.high : ''"> <template #label>{{ i18n.ts._role.priority }}</template> </MkRange> </div> @@ -244,14 +244,14 @@ <MkFolder> <template #label>{{ i18n.ts._role._options.userListMax }}</template> - <template #suffix>{{ options.userListLimit.useDefault ? i18n.ts._role.useBaseValue : (options.userListLimit.value) }} <i :class="getPriorityIcon(options.userListLimit)"></i></template> + <template #suffix>{{ policies.userListLimit.useDefault ? i18n.ts._role.useBaseValue : (policies.userListLimit.value) }} <i :class="getPriorityIcon(policies.userListLimit)"></i></template> <div class="_gaps"> - <MkSwitch v-model="options.userListLimit.useDefault" :readonly="readonly"> + <MkSwitch v-model="policies.userListLimit.useDefault" :readonly="readonly"> <template #label>{{ i18n.ts._role.useBaseValue }}</template> </MkSwitch> - <MkInput v-model="options.userListLimit.value" :disabled="options.userListLimit.useDefault" type="number" :readonly="readonly"> + <MkInput v-model="policies.userListLimit.value" :disabled="policies.userListLimit.useDefault" type="number" :readonly="readonly"> </MkInput> - <MkRange v-model="options.userListLimit.priority" :min="0" :max="2" :step="1" easing :text-converter="(v) => v === 0 ? i18n.ts._role._priority.low : v === 1 ? i18n.ts._role._priority.middle : v === 2 ? i18n.ts._role._priority.high : ''"> + <MkRange v-model="policies.userListLimit.priority" :min="0" :max="2" :step="1" easing :text-converter="(v) => v === 0 ? i18n.ts._role._priority.low : v === 1 ? i18n.ts._role._priority.middle : v === 2 ? i18n.ts._role._priority.high : ''"> <template #label>{{ i18n.ts._role.priority }}</template> </MkRange> </div> @@ -259,14 +259,14 @@ <MkFolder> <template #label>{{ i18n.ts._role._options.userEachUserListsMax }}</template> - <template #suffix>{{ options.userEachUserListsLimit.useDefault ? i18n.ts._role.useBaseValue : (options.userEachUserListsLimit.value) }} <i :class="getPriorityIcon(options.userEachUserListsLimit)"></i></template> + <template #suffix>{{ policies.userEachUserListsLimit.useDefault ? i18n.ts._role.useBaseValue : (policies.userEachUserListsLimit.value) }} <i :class="getPriorityIcon(policies.userEachUserListsLimit)"></i></template> <div class="_gaps"> - <MkSwitch v-model="options.userEachUserListsLimit.useDefault" :readonly="readonly"> + <MkSwitch v-model="policies.userEachUserListsLimit.useDefault" :readonly="readonly"> <template #label>{{ i18n.ts._role.useBaseValue }}</template> </MkSwitch> - <MkInput v-model="options.userEachUserListsLimit.value" :disabled="options.userEachUserListsLimit.useDefault" type="number" :readonly="readonly"> + <MkInput v-model="policies.userEachUserListsLimit.value" :disabled="policies.userEachUserListsLimit.useDefault" type="number" :readonly="readonly"> </MkInput> - <MkRange v-model="options.userEachUserListsLimit.priority" :min="0" :max="2" :step="1" easing :text-converter="(v) => v === 0 ? i18n.ts._role._priority.low : v === 1 ? i18n.ts._role._priority.middle : v === 2 ? i18n.ts._role._priority.high : ''"> + <MkRange v-model="policies.userEachUserListsLimit.priority" :min="0" :max="2" :step="1" easing :text-converter="(v) => v === 0 ? i18n.ts._role._priority.low : v === 1 ? i18n.ts._role._priority.middle : v === 2 ? i18n.ts._role._priority.high : ''"> <template #label>{{ i18n.ts._role.priority }}</template> </MkRange> </div> @@ -306,7 +306,7 @@ import * as os from '@/os'; import { i18n } from '@/i18n'; import { instance } from '@/instance'; -const ROLE_OPTIONS = [ +const ROLE_POLICIES = [ 'gtlAvailable', 'ltlAvailable', 'canPublicNote', @@ -345,13 +345,13 @@ let condFormula = $ref(role?.condFormula ?? { id: uuid(), type: 'isRemote' }); let isPublic = $ref(role?.isPublic ?? false); let canEditMembersByModerator = $ref(role?.canEditMembersByModerator ?? false); -const options = reactive<Record<typeof ROLE_OPTIONS[number], { useDefault: boolean; priority: number; value: any; }>>({}); -for (const ROLE_OPTION of ROLE_OPTIONS) { - const _options = role?.options ?? {}; - options[ROLE_OPTION] = { - useDefault: _options[ROLE_OPTION]?.useDefault ?? true, - priority: _options[ROLE_OPTION]?.priority ?? 0, - value: _options[ROLE_OPTION]?.value ?? instance.baseRole[ROLE_OPTION], +const policies = reactive<Record<typeof ROLE_POLICIES[number], { useDefault: boolean; priority: number; value: any; }>>({}); +for (const ROLE_POLICY of ROLE_POLICIES) { + const _policies = role?.policies ?? {}; + policies[ROLE_POLICY] = { + useDefault: _policies[ROLE_POLICY]?.useDefault ?? true, + priority: _policies[ROLE_POLICY]?.priority ?? 0, + value: _policies[ROLE_POLICY]?.value ?? instance.policies[ROLE_POLICY], }; } @@ -381,7 +381,7 @@ async function save() { isModerator: rolePermission === 'moderator', isPublic, canEditMembersByModerator, - options, + policies, }); emit('updated'); } else { @@ -395,7 +395,7 @@ async function save() { isModerator: rolePermission === 'moderator', isPublic, canEditMembersByModerator, - options, + policies, }); emit('created', created); } diff --git a/packages/frontend/src/pages/admin/roles.vue b/packages/frontend/src/pages/admin/roles.vue index 1ceead8b6c..f074069df0 100644 --- a/packages/frontend/src/pages/admin/roles.vue +++ b/packages/frontend/src/pages/admin/roles.vue @@ -10,114 +10,114 @@ <div class="_gaps"> <MkFolder> <template #label>{{ i18n.ts._role._options.rateLimitFactor }}</template> - <template #suffix>{{ Math.floor(options_rateLimitFactor * 100) }}%</template> - <MkRange :model-value="options_rateLimitFactor * 100" :min="30" :max="300" :step="10" :text-converter="(v) => `${v}%`" @update:model-value="v => options_rateLimitFactor = (v / 100)"> + <template #suffix>{{ Math.floor(policies.rateLimitFactor * 100) }}%</template> + <MkRange :model-value="policies.rateLimitFactor * 100" :min="30" :max="300" :step="10" :text-converter="(v) => `${v}%`" @update:model-value="v => policies.rateLimitFactor = (v / 100)"> <template #caption>{{ i18n.ts._role._options.descriptionOfRateLimitFactor }}</template> </MkRange> </MkFolder> <MkFolder> <template #label>{{ i18n.ts._role._options.gtlAvailable }}</template> - <template #suffix>{{ options_gtlAvailable ? i18n.ts.yes : i18n.ts.no }}</template> - <MkSwitch v-model="options_gtlAvailable"> + <template #suffix>{{ policies.gtlAvailable ? i18n.ts.yes : i18n.ts.no }}</template> + <MkSwitch v-model="policies.gtlAvailable"> <template #label>{{ i18n.ts.enable }}</template> </MkSwitch> </MkFolder> <MkFolder> <template #label>{{ i18n.ts._role._options.ltlAvailable }}</template> - <template #suffix>{{ options_ltlAvailable ? i18n.ts.yes : i18n.ts.no }}</template> - <MkSwitch v-model="options_ltlAvailable"> + <template #suffix>{{ policies.ltlAvailable ? i18n.ts.yes : i18n.ts.no }}</template> + <MkSwitch v-model="policies.ltlAvailable"> <template #label>{{ i18n.ts.enable }}</template> </MkSwitch> </MkFolder> <MkFolder> <template #label>{{ i18n.ts._role._options.canPublicNote }}</template> - <template #suffix>{{ options_canPublicNote ? i18n.ts.yes : i18n.ts.no }}</template> - <MkSwitch v-model="options_canPublicNote"> + <template #suffix>{{ policies.canPublicNote ? i18n.ts.yes : i18n.ts.no }}</template> + <MkSwitch v-model="policies.canPublicNote"> <template #label>{{ i18n.ts.enable }}</template> </MkSwitch> </MkFolder> <MkFolder> <template #label>{{ i18n.ts._role._options.canInvite }}</template> - <template #suffix>{{ options_canInvite ? i18n.ts.yes : i18n.ts.no }}</template> - <MkSwitch v-model="options_canInvite"> + <template #suffix>{{ policies.canInvite ? i18n.ts.yes : i18n.ts.no }}</template> + <MkSwitch v-model="policies.canInvite"> <template #label>{{ i18n.ts.enable }}</template> </MkSwitch> </MkFolder> <MkFolder> <template #label>{{ i18n.ts._role._options.canManageCustomEmojis }}</template> - <template #suffix>{{ options_canManageCustomEmojis ? i18n.ts.yes : i18n.ts.no }}</template> - <MkSwitch v-model="options_canManageCustomEmojis"> + <template #suffix>{{ policies.canManageCustomEmojis ? i18n.ts.yes : i18n.ts.no }}</template> + <MkSwitch v-model="policies.canManageCustomEmojis"> <template #label>{{ i18n.ts.enable }}</template> </MkSwitch> </MkFolder> <MkFolder> <template #label>{{ i18n.ts._role._options.driveCapacity }}</template> - <template #suffix>{{ options_driveCapacityMb }}MB</template> - <MkInput v-model="options_driveCapacityMb" type="number"> + <template #suffix>{{ policies.driveCapacityMb }}MB</template> + <MkInput v-model="policies.driveCapacityMb" type="number"> <template #suffix>MB</template> </MkInput> </MkFolder> <MkFolder> <template #label>{{ i18n.ts._role._options.pinMax }}</template> - <template #suffix>{{ options_pinLimit }}</template> - <MkInput v-model="options_pinLimit" type="number"> + <template #suffix>{{ policies.pinLimit }}</template> + <MkInput v-model="policies.pinLimit" type="number"> </MkInput> </MkFolder> <MkFolder> <template #label>{{ i18n.ts._role._options.antennaMax }}</template> - <template #suffix>{{ options_antennaLimit }}</template> - <MkInput v-model="options_antennaLimit" type="number"> + <template #suffix>{{ policies.antennaLimit }}</template> + <MkInput v-model="policies.antennaLimit" type="number"> </MkInput> </MkFolder> <MkFolder> <template #label>{{ i18n.ts._role._options.wordMuteMax }}</template> - <template #suffix>{{ options_wordMuteLimit }}</template> - <MkInput v-model="options_wordMuteLimit" type="number"> + <template #suffix>{{ policies.wordMuteLimit }}</template> + <MkInput v-model="policies.wordMuteLimit" type="number"> <template #suffix>chars</template> </MkInput> </MkFolder> <MkFolder> <template #label>{{ i18n.ts._role._options.webhookMax }}</template> - <template #suffix>{{ options_webhookLimit }}</template> - <MkInput v-model="options_webhookLimit" type="number"> + <template #suffix>{{ policies.webhookLimit }}</template> + <MkInput v-model="policies.webhookLimit" type="number"> </MkInput> </MkFolder> <MkFolder> <template #label>{{ i18n.ts._role._options.clipMax }}</template> - <template #suffix>{{ options_clipLimit }}</template> - <MkInput v-model="options_clipLimit" type="number"> + <template #suffix>{{ policies.clipLimit }}</template> + <MkInput v-model="policies.clipLimit" type="number"> </MkInput> </MkFolder> <MkFolder> <template #label>{{ i18n.ts._role._options.noteEachClipsMax }}</template> - <template #suffix>{{ options_noteEachClipsLimit }}</template> - <MkInput v-model="options_noteEachClipsLimit" type="number"> + <template #suffix>{{ policies.noteEachClipsLimit }}</template> + <MkInput v-model="policies.noteEachClipsLimit" type="number"> </MkInput> </MkFolder> <MkFolder> <template #label>{{ i18n.ts._role._options.userListMax }}</template> - <template #suffix>{{ options_userListLimit }}</template> - <MkInput v-model="options_userListLimit" type="number"> + <template #suffix>{{ policies.userListLimit }}</template> + <MkInput v-model="policies.userListLimit" type="number"> </MkInput> </MkFolder> <MkFolder> <template #label>{{ i18n.ts._role._options.userEachUserListsMax }}</template> - <template #suffix>{{ options_userEachUserListsLimit }}</template> - <MkInput v-model="options_userEachUserListsLimit" type="number"> + <template #suffix>{{ policies.userEachUserListsLimit }}</template> + <MkInput v-model="policies.userEachUserListsLimit" type="number"> </MkInput> </MkFolder> @@ -134,7 +134,7 @@ </template> <script lang="ts" setup> -import { computed } from 'vue'; +import { computed, reactive } from 'vue'; import XHeader from './_header_.vue'; import MkInput from '@/components/MkInput.vue'; import MkSelect from '@/components/MkSelect.vue'; @@ -150,45 +150,36 @@ import { definePageMetadata } from '@/scripts/page-metadata'; import { instance } from '@/instance'; import { useRouter } from '@/router'; +const ROLE_POLICIES = [ + 'gtlAvailable', + 'ltlAvailable', + 'canPublicNote', + 'canInvite', + 'canManageCustomEmojis', + 'driveCapacityMb', + 'pinLimit', + 'antennaLimit', + 'wordMuteLimit', + 'webhookLimit', + 'clipLimit', + 'noteEachClipsLimit', + 'userListLimit', + 'userEachUserListsLimit', + 'rateLimitFactor', +] as const; + const router = useRouter(); const roles = await os.api('admin/roles/list'); -let options_gtlAvailable = $ref(instance.baseRole.gtlAvailable); -let options_ltlAvailable = $ref(instance.baseRole.ltlAvailable); -let options_canPublicNote = $ref(instance.baseRole.canPublicNote); -let options_canInvite = $ref(instance.baseRole.canInvite); -let options_canManageCustomEmojis = $ref(instance.baseRole.canManageCustomEmojis); -let options_driveCapacityMb = $ref(instance.baseRole.driveCapacityMb); -let options_pinLimit = $ref(instance.baseRole.pinLimit); -let options_antennaLimit = $ref(instance.baseRole.antennaLimit); -let options_wordMuteLimit = $ref(instance.baseRole.wordMuteLimit); -let options_webhookLimit = $ref(instance.baseRole.webhookLimit); -let options_clipLimit = $ref(instance.baseRole.clipLimit); -let options_noteEachClipsLimit = $ref(instance.baseRole.noteEachClipsLimit); -let options_userListLimit = $ref(instance.baseRole.userListLimit); -let options_userEachUserListsLimit = $ref(instance.baseRole.userEachUserListsLimit); -let options_rateLimitFactor = $ref(instance.baseRole.rateLimitFactor); +const policies = reactive<Record<typeof ROLE_POLICIES[number], any>>({}); +for (const ROLE_POLICY of ROLE_POLICIES) { + policies[ROLE_POLICY] = instance.policies[ROLE_POLICY]; +} async function updateBaseRole() { - await os.apiWithDialog('admin/roles/update-default-role-override', { - options: { - gtlAvailable: options_gtlAvailable, - ltlAvailable: options_ltlAvailable, - canPublicNote: options_canPublicNote, - canInvite: options_canInvite, - canManageCustomEmojis: options_canManageCustomEmojis, - driveCapacityMb: options_driveCapacityMb, - pinLimit: options_pinLimit, - antennaLimit: options_antennaLimit, - wordMuteLimit: options_wordMuteLimit, - webhookLimit: options_webhookLimit, - clipLimit: options_clipLimit, - noteEachClipsLimit: options_noteEachClipsLimit, - userListLimit: options_userListLimit, - userEachUserListsLimit: options_userEachUserListsLimit, - rateLimitFactor: options_rateLimitFactor, - }, + await os.apiWithDialog('admin/roles/update-default-policies', { + policies, }); } diff --git a/packages/frontend/src/pages/timeline.vue b/packages/frontend/src/pages/timeline.vue index f5ee1be92c..59dc1114d1 100644 --- a/packages/frontend/src/pages/timeline.vue +++ b/packages/frontend/src/pages/timeline.vue @@ -35,8 +35,8 @@ import { definePageMetadata } from '@/scripts/page-metadata'; const XTutorial = defineAsyncComponent(() => import('./timeline.tutorial.vue')); -const isLocalTimelineAvailable = ($i == null && instance.baseRole.ltlAvailable) || ($i != null && $i.role.ltlAvailable); -const isGlobalTimelineAvailable = ($i == null && instance.baseRole.gtlAvailable) || ($i != null && $i.role.gtlAvailable); +const isLocalTimelineAvailable = ($i == null && instance.policies.ltlAvailable) || ($i != null && $i.policies.ltlAvailable); +const isGlobalTimelineAvailable = ($i == null && instance.policies.gtlAvailable) || ($i != null && $i.policies.gtlAvailable); const keymap = { 't': focus, }; diff --git a/packages/frontend/src/pages/user-info.vue b/packages/frontend/src/pages/user-info.vue index 5bc98a56a7..d7e8102d60 100644 --- a/packages/frontend/src/pages/user-info.vue +++ b/packages/frontend/src/pages/user-info.vue @@ -86,16 +86,28 @@ </div> </FormSection> </div> + <div v-else-if="tab === 'moderation'" class="_gaps_m"> <MkSwitch v-model="suspended" @update:model-value="toggleSuspend">{{ i18n.ts.suspend }}</MkSwitch> + <div> <MkButton v-if="user.host == null && iAmModerator" inline style="margin-right: 8px;" @click="resetPassword"><i class="ti ti-key"></i> {{ i18n.ts.resetPassword }}</MkButton> <MkButton v-if="$i.isAdmin" inline danger @click="deleteAccount">{{ i18n.ts.deleteAccount }}</MkButton> </div> + + <MkFolder> + <template #icon><i class="ti ti-license"></i></template> + <template #label>{{ i18n.ts._role.policies }}</template> + <div class="_gaps"> + <div v-for="policy in Object.keys(info.policies)" :key="policy"> + {{ policy }} ... {{ info.policies[policy] }} + </div> + </div> + </MkFolder> + <MkFolder> <template #icon><i class="ti ti-badges"></i></template> <template #label>{{ i18n.ts.roles }}</template> - <div class="_gaps"> <MkButton v-if="user.host == null && iAmModerator" primary rounded @click="assignRole"><i class="ti ti-plus"></i> {{ i18n.ts.assign }}</MkButton> @@ -105,6 +117,7 @@ </div> </div> </MkFolder> + <MkFolder> <template #icon><i class="ti ti-password"></i></template> <template #label>IP</template> @@ -117,16 +130,18 @@ </div> </template> </MkFolder> + <MkFolder> <template #icon><i class="ti ti-cloud"></i></template> <template #label>{{ i18n.ts.files }}</template> - <MkFileListForAdmin :pagination="filesPagination" view-mode="grid"/> </MkFolder> + <MkTextarea v-model="moderationNote" manual-save> <template #label>Moderation note</template> </MkTextarea> </div> + <div v-else-if="tab === 'chart'" class="_gaps_m"> <div class="cmhjzshm"> <div class="selects"> @@ -142,6 +157,7 @@ </div> </div> </div> + <div v-else-if="tab === 'raw'" class="_gaps_m"> <MkObjectView v-if="info && $i.isAdmin" tall :value="info"> </MkObjectView> diff --git a/packages/frontend/src/ui/_common_/common.ts b/packages/frontend/src/ui/_common_/common.ts index cc152f9c1c..a90ec6172f 100644 --- a/packages/frontend/src/ui/_common_/common.ts +++ b/packages/frontend/src/ui/_common_/common.ts @@ -47,7 +47,7 @@ export function openInstanceMenu(ev: MouseEvent) { to: '/clicker', text: '🍪👈', icon: 'ti ti-cookie', - }, ($i && ($i.isAdmin || $i.role.canInvite) && instance.disableRegistration) ? { + }, ($i && ($i.isAdmin || $i.policies.canInvite) && instance.disableRegistration) ? { text: i18n.ts.invite, icon: 'ti ti-user-plus', action: () => { @@ -63,7 +63,7 @@ export function openInstanceMenu(ev: MouseEvent) { }); }); }, - } : undefined, ($i && ($i.isAdmin || $i.role.canManageCustomEmojis)) ? { + } : undefined, ($i && ($i.isAdmin || $i.policies.canManageCustomEmojis)) ? { type: 'link', to: '/custom-emojis-manager', text: i18n.ts.manageCustomEmojis,