From 37412f0e1bad31389dcc920b845477afcfc1ceb0 Mon Sep 17 00:00:00 2001 From: Copilot <198982749+Copilot@users.noreply.github.com> Date: Sun, 3 May 2026 17:10:17 +0900 Subject: [PATCH] enhance: Add `canCreateChannel` role policy (#17121) * Initial plan * Add canCreateChannel role policy to control channel creation Co-authored-by: syuilo <4439005+syuilo@users.noreply.github.com> * Add canCreateChannel to getUserPolicies return value Co-authored-by: syuilo <4439005+syuilo@users.noreply.github.com> * Add canCreateChannel translations for en-US and ja-JP Co-authored-by: syuilo <4439005+syuilo@users.noreply.github.com> * Add canCreateChannel to misskey-js rolePolicies array Co-authored-by: syuilo <4439005+syuilo@users.noreply.github.com> * Add frontend UI for canCreateChannel policy configuration Co-authored-by: syuilo <4439005+syuilo@users.noreply.github.com> * fix: build autogen files * :art: * migrate * fix: unnecessary changes to non-Japanese locales --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: syuilo <4439005+syuilo@users.noreply.github.com> Co-authored-by: kakkokari-gtyih <67428053+kakkokari-gtyih@users.noreply.github.com> --- locales/ja-JP.yml | 1 + packages/backend/src/core/RoleService.ts | 3 +++ packages/backend/src/models/json-schema/role.ts | 4 ++++ .../src/server/api/endpoints/channels/create.ts | 2 ++ .../frontend/src/pages/admin/roles.policy-editor.vue | 10 ++++++++++ packages/frontend/src/pages/channels.vue | 3 ++- packages/i18n/src/autogen/locale.ts | 4 ++++ packages/misskey-js/etc/misskey-js.api.md | 2 +- packages/misskey-js/src/autogen/types.ts | 1 + packages/misskey-js/src/consts.ts | 1 + 10 files changed, 29 insertions(+), 2 deletions(-) diff --git a/locales/ja-JP.yml b/locales/ja-JP.yml index 93679aa24b..5512deb972 100644 --- a/locales/ja-JP.yml +++ b/locales/ja-JP.yml @@ -2122,6 +2122,7 @@ _role: canSearchNotes: "ノート検索の利用" canSearchUsers: "ユーザー検索の利用" canUseTranslator: "翻訳機能の利用" + canCreateChannel: "チャンネルの作成" avatarDecorationLimit: "アイコンデコレーションの最大取付個数" canImportAntennas: "アンテナのインポートを許可" canImportBlocking: "ブロックのインポートを許可" diff --git a/packages/backend/src/core/RoleService.ts b/packages/backend/src/core/RoleService.ts index 4515cfd29c..a57449157b 100644 --- a/packages/backend/src/core/RoleService.ts +++ b/packages/backend/src/core/RoleService.ts @@ -47,6 +47,7 @@ export type RolePolicies = { canSearchUsers: boolean; canUseTranslator: boolean; canHideAds: boolean; + canCreateChannel: boolean; driveCapacityMb: number; maxFileSizeMb: number; alwaysMarkNsfw: boolean; @@ -88,6 +89,7 @@ export const DEFAULT_POLICIES: RolePolicies = { canSearchUsers: true, canUseTranslator: true, canHideAds: false, + canCreateChannel: true, driveCapacityMb: 100, maxFileSizeMb: 30, alwaysMarkNsfw: false, @@ -410,6 +412,7 @@ export class RoleService implements OnApplicationShutdown, OnModuleInit { canSearchUsers: calc('canSearchUsers', vs => vs.some(v => v === true)), canUseTranslator: calc('canUseTranslator', vs => vs.some(v => v === true)), canHideAds: calc('canHideAds', vs => vs.some(v => v === true)), + canCreateChannel: calc('canCreateChannel', vs => vs.some(v => v === true)), driveCapacityMb: calc('driveCapacityMb', vs => Math.max(...vs)), maxFileSizeMb: calc('maxFileSizeMb', vs => Math.max(...vs)), alwaysMarkNsfw: calc('alwaysMarkNsfw', vs => vs.some(v => v === true)), diff --git a/packages/backend/src/models/json-schema/role.ts b/packages/backend/src/models/json-schema/role.ts index b9000152d4..eaed3ac710 100644 --- a/packages/backend/src/models/json-schema/role.ts +++ b/packages/backend/src/models/json-schema/role.ts @@ -224,6 +224,10 @@ export const packedRolePoliciesSchema = { type: 'boolean', optional: false, nullable: false, }, + canCreateChannel: { + type: 'boolean', + optional: false, nullable: false, + }, driveCapacityMb: { type: 'integer', optional: false, nullable: false, diff --git a/packages/backend/src/server/api/endpoints/channels/create.ts b/packages/backend/src/server/api/endpoints/channels/create.ts index 8d49b6fd0f..0536f7a775 100644 --- a/packages/backend/src/server/api/endpoints/channels/create.ts +++ b/packages/backend/src/server/api/endpoints/channels/create.ts @@ -22,6 +22,8 @@ export const meta = { kind: 'write:channels', + requiredRolePolicy: 'canCreateChannel', + limit: { duration: ms('1hour'), max: 10, diff --git a/packages/frontend/src/pages/admin/roles.policy-editor.vue b/packages/frontend/src/pages/admin/roles.policy-editor.vue index f93cb703e6..c6b06b30d1 100644 --- a/packages/frontend/src/pages/admin/roles.policy-editor.vue +++ b/packages/frontend/src/pages/admin/roles.policy-editor.vue @@ -161,6 +161,16 @@ SPDX-License-Identifier: AGPL-3.0-only + + + + + + diff --git a/packages/frontend/src/pages/channels.vue b/packages/frontend/src/pages/channels.vue index fbe1d3ff87..0cd8528dbe 100644 --- a/packages/frontend/src/pages/channels.vue +++ b/packages/frontend/src/pages/channels.vue @@ -50,7 +50,7 @@ SPDX-License-Identifier: AGPL-3.0-only
- {{ i18n.ts.createNew }} + {{ i18n.ts.createNew }}
@@ -74,6 +74,7 @@ import { definePage } from '@/page.js'; import { i18n } from '@/i18n.js'; import { useRouter } from '@/router.js'; import { Paginator } from '@/utility/paginator.js'; +import { $i } from '@/i.js'; const router = useRouter(); diff --git a/packages/i18n/src/autogen/locale.ts b/packages/i18n/src/autogen/locale.ts index 69e7346a59..07e9a3afc1 100644 --- a/packages/i18n/src/autogen/locale.ts +++ b/packages/i18n/src/autogen/locale.ts @@ -8177,6 +8177,10 @@ export interface Locale extends ILocale { * 翻訳機能の利用 */ "canUseTranslator": string; + /** + * チャンネルの作成 + */ + "canCreateChannel": string; /** * アイコンデコレーションの最大取付個数 */ diff --git a/packages/misskey-js/etc/misskey-js.api.md b/packages/misskey-js/etc/misskey-js.api.md index 760d5c319b..1a026dc059 100644 --- a/packages/misskey-js/etc/misskey-js.api.md +++ b/packages/misskey-js/etc/misskey-js.api.md @@ -3466,7 +3466,7 @@ type RoleLite = components['schemas']['RoleLite']; type RolePolicies = components['schemas']['RolePolicies']; // @public (undocumented) -export const rolePolicies: readonly ["gtlAvailable", "ltlAvailable", "canPublicNote", "mentionLimit", "canInvite", "inviteLimit", "inviteLimitCycle", "inviteExpirationTime", "canManageCustomEmojis", "canManageAvatarDecorations", "canSearchNotes", "canSearchUsers", "canUseTranslator", "canHideAds", "driveCapacityMb", "maxFileSizeMb", "alwaysMarkNsfw", "canUpdateBioMedia", "pinLimit", "antennaLimit", "wordMuteLimit", "webhookLimit", "clipLimit", "noteEachClipsLimit", "userListLimit", "userEachUserListsLimit", "rateLimitFactor", "avatarDecorationLimit", "canImportAntennas", "canImportBlocking", "canImportFollowing", "canImportMuting", "canImportUserLists", "chatAvailability", "uploadableFileTypes", "noteDraftLimit", "scheduledNoteLimit", "watermarkAvailable"]; +export const rolePolicies: readonly ["gtlAvailable", "ltlAvailable", "canPublicNote", "mentionLimit", "canInvite", "inviteLimit", "inviteLimitCycle", "inviteExpirationTime", "canManageCustomEmojis", "canManageAvatarDecorations", "canSearchNotes", "canSearchUsers", "canUseTranslator", "canHideAds", "canCreateChannel", "driveCapacityMb", "maxFileSizeMb", "alwaysMarkNsfw", "canUpdateBioMedia", "pinLimit", "antennaLimit", "wordMuteLimit", "webhookLimit", "clipLimit", "noteEachClipsLimit", "userListLimit", "userEachUserListsLimit", "rateLimitFactor", "avatarDecorationLimit", "canImportAntennas", "canImportBlocking", "canImportFollowing", "canImportMuting", "canImportUserLists", "chatAvailability", "uploadableFileTypes", "noteDraftLimit", "scheduledNoteLimit", "watermarkAvailable"]; // @public (undocumented) type RolesListResponse = operations['roles___list']['responses']['200']['content']['application/json']; diff --git a/packages/misskey-js/src/autogen/types.ts b/packages/misskey-js/src/autogen/types.ts index df08b3f804..dcb37b1718 100644 --- a/packages/misskey-js/src/autogen/types.ts +++ b/packages/misskey-js/src/autogen/types.ts @@ -5322,6 +5322,7 @@ export type components = { canSearchUsers: boolean; canUseTranslator: boolean; canHideAds: boolean; + canCreateChannel: boolean; driveCapacityMb: number; maxFileSizeMb: number; uploadableFileTypes: string[]; diff --git a/packages/misskey-js/src/consts.ts b/packages/misskey-js/src/consts.ts index 8789ee056d..dc12bc7d56 100644 --- a/packages/misskey-js/src/consts.ts +++ b/packages/misskey-js/src/consts.ts @@ -205,6 +205,7 @@ export const rolePolicies = [ 'canSearchUsers', 'canUseTranslator', 'canHideAds', + 'canCreateChannel', 'driveCapacityMb', 'maxFileSizeMb', 'alwaysMarkNsfw',