From 4bcdc6639de80e38447d998f5ee55d4eb858688d Mon Sep 17 00:00:00 2001 From: syuilo <4439005+syuilo@users.noreply.github.com> Date: Thu, 22 May 2025 16:16:06 +0900 Subject: [PATCH] wip --- CHANGELOG.md | 2 ++ locales/index.d.ts | 12 ++++++++++ locales/ja-JP.yml | 3 +++ packages/backend/src/core/DriveService.ts | 22 ++++++++++++++----- packages/backend/src/core/RoleService.ts | 16 ++++++++++++++ .../backend/src/models/json-schema/role.ts | 8 +++++++ packages/frontend-shared/js/const.ts | 1 + .../frontend/src/pages/admin/roles.editor.vue | 20 +++++++++++++++++ packages/frontend/src/pages/admin/roles.vue | 8 +++++++ packages/frontend/src/utility/drive.ts | 21 ++++++++++++++++++ packages/misskey-js/src/autogen/types.ts | 1 + 11 files changed, 109 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 21ce5932b1..3362650a3d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,8 @@ - モデレーションが行き届きにくい不適切なリモートコンテンツなどが、自サーバー経由で図らずもインターネットに公開されてしまうことによるトラブル防止などに役立ちます - 「全て公開(今までの挙動)」「ローカルのコンテンツだけ公開(=サーバー内で受信されたリモートのコンテンツは公開しない)」「何も公開しない」から選択できます - デフォルト値は「ローカルのコンテンツだけ公開」になっています +- Feat: ロールでアップロード可能なファイル種別を設定可能になりました + - デフォルトは**テキスト、画像、動画、音声ファイル**になっています。zipなど、その他の種別のファイルは含まれていないため、必要に応じて設定を変更してください。 ### Client - Feat: ドライブのUIが強化されました diff --git a/locales/index.d.ts b/locales/index.d.ts index 0a0e28e02e..06dbdf8395 100644 --- a/locales/index.d.ts +++ b/locales/index.d.ts @@ -4022,6 +4022,10 @@ export interface Locale extends ILocale { * ファイルサイズの制限を超えているためアップロードできません。 */ "cannotUploadBecauseExceedsFileSizeLimit": string; + /** + * 許可されていないファイル種別のためアップロードできません。 + */ + "cannotUploadBecauseUnallowedFileType": string; /** * ベータ */ @@ -7729,6 +7733,14 @@ export interface Locale extends ILocale { * チャットを許可 */ "chatAvailability": string; + /** + * アップロード可能なファイル種別 + */ + "uploadableFileTypes": string; + /** + * MIMEタイプを指定します。改行で区切って複数指定できるほか、アスタリスク(*)でワイルドカード指定できます。(例: image/*) + */ + "uploadableFileTypes_caption": string; }; "_condition": { /** diff --git a/locales/ja-JP.yml b/locales/ja-JP.yml index b9e778741c..d3da3dfd22 100644 --- a/locales/ja-JP.yml +++ b/locales/ja-JP.yml @@ -1001,6 +1001,7 @@ failedToUpload: "アップロード失敗" cannotUploadBecauseInappropriate: "不適切な内容を含む可能性があると判定されたためアップロードできません。" cannotUploadBecauseNoFreeSpace: "ドライブの空き容量が無いためアップロードできません。" cannotUploadBecauseExceedsFileSizeLimit: "ファイルサイズの制限を超えているためアップロードできません。" +cannotUploadBecauseUnallowedFileType: "許可されていないファイル種別のためアップロードできません。" beta: "ベータ" enableAutoSensitive: "自動センシティブ判定" enableAutoSensitiveDescription: "利用可能な場合は、機械学習を利用して自動でメディアにセンシティブフラグを設定します。この機能をオフにしても、サーバーによっては自動で設定されることがあります。" @@ -2001,6 +2002,8 @@ _role: canImportMuting: "ミュートのインポートを許可" canImportUserLists: "リストのインポートを許可" chatAvailability: "チャットを許可" + uploadableFileTypes: "アップロード可能なファイル種別" + uploadableFileTypes_caption: "MIMEタイプを指定します。改行で区切って複数指定できるほか、アスタリスク(*)でワイルドカード指定できます。(例: image/*)" _condition: roleAssignedTo: "マニュアルロールにアサイン済み" isLocal: "ローカルユーザー" diff --git a/packages/backend/src/core/DriveService.ts b/packages/backend/src/core/DriveService.ts index 0d5ac022aa..0c7c06d92f 100644 --- a/packages/backend/src/core/DriveService.ts +++ b/packages/backend/src/core/DriveService.ts @@ -515,16 +515,23 @@ export class DriveService { this.registerLogger.debug(`ADD DRIVE FILE: user ${user?.id ?? 'not set'}, name ${detectedName}, tmp ${path}`); - //#region Check drive usage + //#region Check drive usage and mime type if (user && !isLink) { - const usage = await this.driveFileEntityService.calcDriveUsageOf(user); const isLocalUser = this.userEntityService.isLocalUser(user); - const policies = await this.roleService.getUserPolicies(user.id); + + const allowedMimeTypes = policies.uploadableFileTypes; + const isAllowed = allowedMimeTypes.some((mimeType) => { + if (mimeType === '*' || mimeType === '*/*') return true; + if (mimeType.endsWith('/*')) return info.type.mime.startsWith(mimeType.slice(0, -1)); + return info.type.mime === mimeType; + }); + if (!isAllowed) { + throw new IdentifiableError('bd71c601-f9b0-4808-9137-a330647ced9b', 'Unallowed file type.'); + } + const driveCapacity = 1024 * 1024 * policies.driveCapacityMb; const maxFileSize = 1024 * 1024 * policies.maxFileSizeMb; - this.registerLogger.debug('drive capacity override applied'); - this.registerLogger.debug(`overrideCap: ${driveCapacity}bytes, usage: ${usage}bytes, u+s: ${usage + info.size}bytes`); if (maxFileSize < info.size) { if (isLocalUser) { @@ -532,6 +539,11 @@ export class DriveService { } } + const usage = await this.driveFileEntityService.calcDriveUsageOf(user); + + this.registerLogger.debug('drive capacity override applied'); + this.registerLogger.debug(`overrideCap: ${driveCapacity}bytes, usage: ${usage}bytes, u+s: ${usage + info.size}bytes`); + // If usage limit exceeded if (driveCapacity < usage + info.size) { if (isLocalUser) { diff --git a/packages/backend/src/core/RoleService.ts b/packages/backend/src/core/RoleService.ts index fc97780ba3..46ab6478f6 100644 --- a/packages/backend/src/core/RoleService.ts +++ b/packages/backend/src/core/RoleService.ts @@ -65,6 +65,7 @@ export type RolePolicies = { canImportMuting: boolean; canImportUserLists: boolean; chatAvailability: 'available' | 'readonly' | 'unavailable'; + uploadableFileTypes: string[]; }; export const DEFAULT_POLICIES: RolePolicies = { @@ -101,6 +102,12 @@ export const DEFAULT_POLICIES: RolePolicies = { canImportMuting: true, canImportUserLists: true, chatAvailability: 'available', + uploadableFileTypes: [ + 'text/plain', + 'image/*', + 'video/*', + 'audio/*', + ], }; @Injectable() @@ -412,6 +419,15 @@ export class RoleService implements OnApplicationShutdown, OnModuleInit { canImportMuting: calc('canImportMuting', vs => vs.some(v => v === true)), canImportUserLists: calc('canImportUserLists', vs => vs.some(v => v === true)), chatAvailability: calc('chatAvailability', aggregateChatAvailability), + uploadableFileTypes: calc('uploadableFileTypes', vs => { + const set = new Set(); + for (const v of vs) { + for (const type of v) { + set.add(type); + } + } + return [...set]; + }), }; } diff --git a/packages/backend/src/models/json-schema/role.ts b/packages/backend/src/models/json-schema/role.ts index e67704e8d3..8bd01c92a3 100644 --- a/packages/backend/src/models/json-schema/role.ts +++ b/packages/backend/src/models/json-schema/role.ts @@ -228,6 +228,14 @@ export const packedRolePoliciesSchema = { type: 'integer', optional: false, nullable: false, }, + uploadableFileTypes: { + type: 'array', + optional: false, nullable: false, + items: { + type: 'string', + optional: false, nullable: false, + }, + }, alwaysMarkNsfw: { type: 'boolean', optional: false, nullable: false, diff --git a/packages/frontend-shared/js/const.ts b/packages/frontend-shared/js/const.ts index 8c49b41f4d..c4c4a25d74 100644 --- a/packages/frontend-shared/js/const.ts +++ b/packages/frontend-shared/js/const.ts @@ -110,6 +110,7 @@ export const ROLE_POLICIES = [ 'canImportMuting', 'canImportUserLists', 'chatAvailability', + 'uploadableFileTypes', ] as const; export const MFM_TAGS = ['tada', 'jelly', 'twitch', 'shake', 'spin', 'jump', 'bounce', 'flip', 'x2', 'x3', 'x4', 'scale', 'position', 'fg', 'bg', 'border', 'font', 'blur', 'rainbow', 'sparkle', 'rotate', 'ruby', 'unixtime']; diff --git a/packages/frontend/src/pages/admin/roles.editor.vue b/packages/frontend/src/pages/admin/roles.editor.vue index 2473d4e90d..5da969b835 100644 --- a/packages/frontend/src/pages/admin/roles.editor.vue +++ b/packages/frontend/src/pages/admin/roles.editor.vue @@ -406,6 +406,26 @@ SPDX-License-Identifier: AGPL-3.0-only + + + +
+ + + + + + + + + +
+
+