feat: アップロード可能な最大ファイルサイズをロールごとに設定可能に
This commit is contained in:
parent
effc84b9cc
commit
9481b5a6e8
|
@ -2,6 +2,7 @@
|
|||
|
||||
### General
|
||||
- Feat: bull-boardに代わるジョブキューの管理ツールが実装されました
|
||||
- Feat: アップロード可能な最大ファイルサイズをロールごとに設定可能に
|
||||
- Enhance: チャットの新規メッセージをプッシュ通知するように
|
||||
|
||||
### Client
|
||||
|
|
|
@ -7464,6 +7464,10 @@ export interface Locale extends ILocale {
|
|||
* ドライブ容量
|
||||
*/
|
||||
"driveCapacity": string;
|
||||
/**
|
||||
* アップロード可能な最大ファイルサイズ
|
||||
*/
|
||||
"maxFileSize": string;
|
||||
/**
|
||||
* ファイルにNSFWを常に付与
|
||||
*/
|
||||
|
|
|
@ -1934,6 +1934,7 @@ _role:
|
|||
canManageCustomEmojis: "カスタム絵文字の管理"
|
||||
canManageAvatarDecorations: "アバターデコレーションの管理"
|
||||
driveCapacity: "ドライブ容量"
|
||||
maxFileSize: "アップロード可能な最大ファイルサイズ"
|
||||
alwaysMarkNsfw: "ファイルにNSFWを常に付与"
|
||||
canUpdateBioMedia: "アイコンとバナーの更新を許可"
|
||||
pinMax: "ノートのピン留めの最大数"
|
||||
|
|
|
@ -522,9 +522,16 @@ export class DriveService {
|
|||
|
||||
const policies = await this.roleService.getUserPolicies(user.id);
|
||||
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) {
|
||||
throw new IdentifiableError('f9e4e5f3-4df4-40b5-b400-f236945f7073', 'Max file size exceeded.');
|
||||
}
|
||||
}
|
||||
|
||||
// If usage limit exceeded
|
||||
if (driveCapacity < usage + info.size) {
|
||||
if (isLocalUser) {
|
||||
|
|
|
@ -46,6 +46,7 @@ export type RolePolicies = {
|
|||
canUseTranslator: boolean;
|
||||
canHideAds: boolean;
|
||||
driveCapacityMb: number;
|
||||
maxFileSizeMb: number;
|
||||
alwaysMarkNsfw: boolean;
|
||||
canUpdateBioMedia: boolean;
|
||||
pinLimit: number;
|
||||
|
@ -81,6 +82,7 @@ export const DEFAULT_POLICIES: RolePolicies = {
|
|||
canUseTranslator: true,
|
||||
canHideAds: false,
|
||||
driveCapacityMb: 100,
|
||||
maxFileSizeMb: 10,
|
||||
alwaysMarkNsfw: false,
|
||||
canUpdateBioMedia: true,
|
||||
pinLimit: 5,
|
||||
|
@ -391,6 +393,7 @@ export class RoleService implements OnApplicationShutdown, OnModuleInit {
|
|||
canUseTranslator: calc('canUseTranslator', vs => vs.some(v => v === true)),
|
||||
canHideAds: calc('canHideAds', 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)),
|
||||
canUpdateBioMedia: calc('canUpdateBioMedia', vs => vs.some(v => v === true)),
|
||||
pinLimit: calc('pinLimit', vs => Math.max(...vs)),
|
||||
|
|
|
@ -224,6 +224,10 @@ export const packedRolePoliciesSchema = {
|
|||
type: 'integer',
|
||||
optional: false, nullable: false,
|
||||
},
|
||||
maxFileSizeMb: {
|
||||
type: 'integer',
|
||||
optional: false, nullable: false,
|
||||
},
|
||||
alwaysMarkNsfw: {
|
||||
type: 'boolean',
|
||||
optional: false, nullable: false,
|
||||
|
|
|
@ -10,9 +10,9 @@ import { IdentifiableError } from '@/misc/identifiable-error.js';
|
|||
import { Endpoint } from '@/server/api/endpoint-base.js';
|
||||
import { DriveFileEntityService } from '@/core/entities/DriveFileEntityService.js';
|
||||
import { DriveService } from '@/core/DriveService.js';
|
||||
import { ApiError } from '../../../error.js';
|
||||
import { MiMeta } from '@/models/_.js';
|
||||
import { DI } from '@/di-symbols.js';
|
||||
import { ApiError } from '../../../error.js';
|
||||
|
||||
export const meta = {
|
||||
tags: ['drive'],
|
||||
|
@ -56,6 +56,12 @@ export const meta = {
|
|||
code: 'NO_FREE_SPACE',
|
||||
id: 'd08dbc37-a6a9-463a-8c47-96c32ab5f064',
|
||||
},
|
||||
|
||||
maxFileSizeExceeded: {
|
||||
message: 'Cannot upload the file because it exceeds the maximum file size.',
|
||||
code: 'MAX_FILE_SIZE_EXCEEDED',
|
||||
id: 'b9d8c348-33f0-4673-b9a9-5d4da058977a',
|
||||
},
|
||||
},
|
||||
} as const;
|
||||
|
||||
|
@ -115,6 +121,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
|
|||
if (err instanceof IdentifiableError) {
|
||||
if (err.id === '282f77bf-5816-4f72-9264-aa14d8261a21') throw new ApiError(meta.errors.inappropriate);
|
||||
if (err.id === 'c6244ed2-a39a-4e1c-bf93-f0fbd7764fa6') throw new ApiError(meta.errors.noFreeSpace);
|
||||
if (err.id === 'f9e4e5f3-4df4-40b5-b400-f236945f7073') throw new ApiError(meta.errors.maxFileSizeExceeded);
|
||||
}
|
||||
throw new ApiError();
|
||||
} finally {
|
||||
|
|
|
@ -91,6 +91,7 @@ export const ROLE_POLICIES = [
|
|||
'canUseTranslator',
|
||||
'canHideAds',
|
||||
'driveCapacityMb',
|
||||
'maxFileSizeMb',
|
||||
'alwaysMarkNsfw',
|
||||
'canUpdateBioMedia',
|
||||
'pinLimit',
|
||||
|
|
|
@ -386,6 +386,26 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||
</div>
|
||||
</MkFolder>
|
||||
|
||||
<MkFolder v-if="matchQuery([i18n.ts._role._options.maxFileSize, 'maxFileSizeMb'])">
|
||||
<template #label>{{ i18n.ts._role._options.maxFileSize }}</template>
|
||||
<template #suffix>
|
||||
<span v-if="role.policies.maxFileSizeMb.useDefault" :class="$style.useDefaultLabel">{{ i18n.ts._role.useBaseValue }}</span>
|
||||
<span v-else>{{ role.policies.maxFileSizeMb.value + 'MB' }}</span>
|
||||
<span :class="$style.priorityIndicator"><i :class="getPriorityIcon(role.policies.maxFileSizeMb)"></i></span>
|
||||
</template>
|
||||
<div class="_gaps">
|
||||
<MkSwitch v-model="role.policies.maxFileSizeMb.useDefault" :readonly="readonly">
|
||||
<template #label>{{ i18n.ts._role.useBaseValue }}</template>
|
||||
</MkSwitch>
|
||||
<MkInput v-model="role.policies.maxFileSizeMb.value" :disabled="role.policies.maxFileSizeMb.useDefault" type="number" :readonly="readonly">
|
||||
<template #suffix>MB</template>
|
||||
</MkInput>
|
||||
<MkRange v-model="role.policies.maxFileSizeMb.priority" :min="0" :max="2" :step="1" easing :textConverter="(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>
|
||||
</MkFolder>
|
||||
|
||||
<MkFolder v-if="matchQuery([i18n.ts._role._options.alwaysMarkNsfw, 'alwaysMarkNsfw'])">
|
||||
<template #label>{{ i18n.ts._role._options.alwaysMarkNsfw }}</template>
|
||||
<template #suffix>
|
||||
|
|
|
@ -138,6 +138,14 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||
</MkInput>
|
||||
</MkFolder>
|
||||
|
||||
<MkFolder v-if="matchQuery([i18n.ts._role._options.maxFileSize, 'maxFileSizeMb'])">
|
||||
<template #label>{{ i18n.ts._role._options.maxFileSize }}</template>
|
||||
<template #suffix>{{ policies.maxFileSizeMb }}MB</template>
|
||||
<MkInput v-model="policies.maxFileSizeMb" type="number">
|
||||
<template #suffix>MB</template>
|
||||
</MkInput>
|
||||
</MkFolder>
|
||||
|
||||
<MkFolder v-if="matchQuery([i18n.ts._role._options.alwaysMarkNsfw, 'alwaysMarkNsfw'])">
|
||||
<template #label>{{ i18n.ts._role._options.alwaysMarkNsfw }}</template>
|
||||
<template #suffix>{{ policies.alwaysMarkNsfw ? i18n.ts.yes : i18n.ts.no }}</template>
|
||||
|
|
|
@ -40,7 +40,7 @@ export function uploadFile(
|
|||
|
||||
const _folder = typeof folder === 'string' ? folder : folder?.id;
|
||||
|
||||
if (file.size > instance.maxFileSize) {
|
||||
if ((file.size > instance.maxFileSize) || (file.size > ($i.policies.maxFileSizeMb * 1024 * 1024))) {
|
||||
alert({
|
||||
type: 'error',
|
||||
title: i18n.ts.failedToUpload,
|
||||
|
|
|
@ -5216,6 +5216,7 @@ export type components = {
|
|||
canUseTranslator: boolean;
|
||||
canHideAds: boolean;
|
||||
driveCapacityMb: number;
|
||||
maxFileSizeMb: number;
|
||||
alwaysMarkNsfw: boolean;
|
||||
canUpdateBioMedia: boolean;
|
||||
pinLimit: number;
|
||||
|
|
Loading…
Reference in New Issue