Compare commits
5 Commits
9c98c13743
...
fe1b2b00f5
| Author | SHA1 | Date |
|---|---|---|
|
|
fe1b2b00f5 | |
|
|
4fcb80bcf2 | |
|
|
836ed98c54 | |
|
|
64791a7160 | |
|
|
90e39d22d2 |
|
|
@ -14,6 +14,8 @@
|
|||
- デフォルト値は「ローカルのコンテンツだけ公開」になっています
|
||||
- Feat: ロールでアップロード可能なファイル種別を設定可能になりました
|
||||
- デフォルトは**テキスト、JSON、画像、動画、音声ファイル**になっています。zipなど、その他の種別のファイルは含まれていないため、必要に応じて設定を変更してください。
|
||||
- 場合によってはファイル種別を正しく検出できないことがあります(特にテキストフォーマット)。その場合、ファイル種別は application/octet-stream と見做されます。
|
||||
- したがって、それらの種別不明ファイルを許可したい場合は application/octet-stream を指定に追加してください。
|
||||
- Enhance: UIのアイコンデータの読み込みを軽量化
|
||||
|
||||
### Client
|
||||
|
|
|
|||
|
|
@ -7753,6 +7753,10 @@ export interface Locale extends ILocale {
|
|||
* MIMEタイプを指定します。改行で区切って複数指定できるほか、アスタリスク(*)でワイルドカード指定できます。(例: image/*)
|
||||
*/
|
||||
"uploadableFileTypes_caption": string;
|
||||
/**
|
||||
* ファイルによっては種別を判定できないことがあります。そのようなファイルを許可する場合は {x} を指定に追加してください。
|
||||
*/
|
||||
"uploadableFileTypes_caption2": ParameterizedString<"x">;
|
||||
};
|
||||
"_condition": {
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -2007,6 +2007,7 @@ _role:
|
|||
chatAvailability: "チャットを許可"
|
||||
uploadableFileTypes: "アップロード可能なファイル種別"
|
||||
uploadableFileTypes_caption: "MIMEタイプを指定します。改行で区切って複数指定できるほか、アスタリスク(*)でワイルドカード指定できます。(例: image/*)"
|
||||
uploadableFileTypes_caption2: "ファイルによっては種別を判定できないことがあります。そのようなファイルを許可する場合は {x} を指定に追加してください。"
|
||||
_condition:
|
||||
roleAssignedTo: "マニュアルロールにアサイン済み"
|
||||
isLocal: "ローカルユーザー"
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "misskey",
|
||||
"version": "2025.5.1-beta.0",
|
||||
"version": "2025.5.1-beta.1",
|
||||
"codename": "nasubi",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
|
|
|
|||
|
|
@ -469,13 +469,14 @@ export class DriveService {
|
|||
if (user && this.meta.sensitiveMediaDetection === 'remote' && this.userEntityService.isLocalUser(user)) skipNsfwCheck = true;
|
||||
|
||||
const info = await this.fileInfoService.getFileInfo(path, {
|
||||
fileName: name,
|
||||
skipSensitiveDetection: skipNsfwCheck,
|
||||
sensitiveThreshold: // 感度が高いほどしきい値は低くすることになる
|
||||
this.meta.sensitiveMediaDetectionSensitivity === 'veryHigh' ? 0.1 :
|
||||
this.meta.sensitiveMediaDetectionSensitivity === 'high' ? 0.3 :
|
||||
this.meta.sensitiveMediaDetectionSensitivity === 'low' ? 0.7 :
|
||||
this.meta.sensitiveMediaDetectionSensitivity === 'veryLow' ? 0.9 :
|
||||
0.5,
|
||||
this.meta.sensitiveMediaDetectionSensitivity === 'veryHigh' ? 0.1 :
|
||||
this.meta.sensitiveMediaDetectionSensitivity === 'high' ? 0.3 :
|
||||
this.meta.sensitiveMediaDetectionSensitivity === 'low' ? 0.7 :
|
||||
this.meta.sensitiveMediaDetectionSensitivity === 'veryLow' ? 0.9 :
|
||||
0.5,
|
||||
sensitiveThresholdForPorn: 0.75,
|
||||
enableSensitiveMediaDetectionForVideos: this.meta.enableSensitiveMediaDetectionForVideos,
|
||||
});
|
||||
|
|
|
|||
|
|
@ -64,6 +64,7 @@ export class FileInfoService {
|
|||
*/
|
||||
@bindThis
|
||||
public async getFileInfo(path: string, opts: {
|
||||
fileName?: string | null;
|
||||
skipSensitiveDetection: boolean;
|
||||
sensitiveThreshold?: number;
|
||||
sensitiveThresholdForPorn?: number;
|
||||
|
|
@ -76,6 +77,26 @@ export class FileInfoService {
|
|||
|
||||
let type = await this.detectType(path);
|
||||
|
||||
if (type.mime === TYPE_OCTET_STREAM.mime && opts.fileName != null) {
|
||||
const ext = opts.fileName.split('.').pop();
|
||||
if (ext === 'txt') {
|
||||
type = {
|
||||
mime: 'text/plain',
|
||||
ext: 'txt',
|
||||
};
|
||||
} else if (ext === 'csv') {
|
||||
type = {
|
||||
mime: 'text/csv',
|
||||
ext: 'csv',
|
||||
};
|
||||
} else if (ext === 'json') {
|
||||
type = {
|
||||
mime: 'application/json',
|
||||
ext: 'json',
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
// image dimensions
|
||||
let width: number | undefined;
|
||||
let height: number | undefined;
|
||||
|
|
@ -438,12 +459,12 @@ export class FileInfoService {
|
|||
*/
|
||||
@bindThis
|
||||
private async detectImageSize(path: string): Promise<{
|
||||
width: number;
|
||||
height: number;
|
||||
wUnits: string;
|
||||
hUnits: string;
|
||||
orientation?: number;
|
||||
}> {
|
||||
width: number;
|
||||
height: number;
|
||||
wUnits: string;
|
||||
hUnits: string;
|
||||
orientation?: number;
|
||||
}> {
|
||||
const readable = fs.createReadStream(path);
|
||||
const imageSize = await probeImageSize(readable);
|
||||
readable.destroy();
|
||||
|
|
|
|||
|
|
@ -63,6 +63,12 @@ export const meta = {
|
|||
id: 'b9d8c348-33f0-4673-b9a9-5d4da058977a',
|
||||
httpStatusCode: 413,
|
||||
},
|
||||
|
||||
unallowedFileType: {
|
||||
message: 'Cannot upload the file because it is an unallowed file type.',
|
||||
code: 'UNALLOWED_FILE_TYPE',
|
||||
id: '4becd248-7f2c-48c4-a9f0-75edc4f9a1ea',
|
||||
},
|
||||
},
|
||||
} as const;
|
||||
|
||||
|
|
@ -123,6 +129,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
|
|||
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);
|
||||
if (err.id === 'bd71c601-f9b0-4808-9137-a330647ced9b') throw new ApiError(meta.errors.unallowedFileType);
|
||||
}
|
||||
throw new ApiError();
|
||||
} finally {
|
||||
|
|
|
|||
|
|
@ -73,7 +73,10 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||
</MkSelect>
|
||||
|
||||
<div>{{ i18n.tsx._uploader.maxFileSizeIsX({ x: $i.policies.maxFileSizeMb + 'MB' }) }}</div>
|
||||
<div>{{ i18n.ts._uploader.allowedTypes }}: {{ $i.policies.uploadableFileTypes.join(', ') }}</div>
|
||||
|
||||
<!-- クライアントで検出するMIME typeとサーバーで検出するMIME typeが異なる場合があり、混乱の元になるのでとりあえず隠しとく -->
|
||||
<!-- https://github.com/misskey-dev/misskey/issues/16091 -->
|
||||
<!--<div>{{ i18n.ts._uploader.allowedTypes }}: {{ $i.policies.uploadableFileTypes.join(', ') }}</div>-->
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
|
|
|||
|
|
@ -418,7 +418,10 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||
<template #label>{{ i18n.ts._role.useBaseValue }}</template>
|
||||
</MkSwitch>
|
||||
<MkTextarea :modelValue="role.policies.uploadableFileTypes.value.join('\n')" :disabled="role.policies.uploadableFileTypes.useDefault" :readonly="readonly" @update:modelValue="role.policies.uploadableFileTypes.value = $event.split('\n')">
|
||||
<template #caption>{{ i18n.ts._role._options.uploadableFileTypes_caption }}</template>
|
||||
<template #caption>
|
||||
<div>{{ i18n.ts._role._options.uploadableFileTypes_caption }}</div>
|
||||
<div><i class="ti ti-alert-triangle" style="color: var(--MI_THEME-warn);"></i> {{ i18n.tsx._role._options.uploadableFileTypes_caption2({ x: 'application/octet-stream' }) }}</div>
|
||||
</template>
|
||||
</MkTextarea>
|
||||
<MkRange v-model="role.policies.uploadableFileTypes.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>
|
||||
|
|
|
|||
|
|
@ -150,6 +150,10 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||
<template #label>{{ i18n.ts._role._options.uploadableFileTypes }}</template>
|
||||
<template #suffix>...</template>
|
||||
<MkTextarea :modelValue="policies.uploadableFileTypes.join('\n')">
|
||||
<template #caption>
|
||||
<div>{{ i18n.ts._role._options.uploadableFileTypes_caption }}</div>
|
||||
<div><i class="ti ti-alert-triangle" style="color: var(--MI_THEME-warn);"></i> {{ i18n.tsx._role._options.uploadableFileTypes_caption2({ x: 'application/octet-stream' }) }}</div>
|
||||
</template>
|
||||
</MkTextarea>
|
||||
</MkFolder>
|
||||
|
||||
|
|
|
|||
|
|
@ -39,20 +39,22 @@ export function uploadFile(file: File | Blob, options: {
|
|||
const filePromise = new Promise<Misskey.entities.DriveFile>((resolve, reject) => {
|
||||
if ($i == null) return reject();
|
||||
|
||||
const allowedMimeTypes = $i.policies.uploadableFileTypes;
|
||||
const isAllowedMimeType = allowedMimeTypes.some(mimeType => {
|
||||
if (mimeType === '*' || mimeType === '*/*') return true;
|
||||
if (mimeType.endsWith('/*')) return file.type.startsWith(mimeType.slice(0, -1));
|
||||
return file.type === mimeType;
|
||||
});
|
||||
if (!isAllowedMimeType) {
|
||||
os.alert({
|
||||
type: 'error',
|
||||
title: i18n.ts.failedToUpload,
|
||||
text: i18n.ts.cannotUploadBecauseUnallowedFileType,
|
||||
});
|
||||
return reject();
|
||||
}
|
||||
// こっち側で検出するMIME typeとサーバーで検出するMIME typeは異なる場合があるため、こっち側ではやらないことにする
|
||||
// https://github.com/misskey-dev/misskey/issues/16091
|
||||
//const allowedMimeTypes = $i.policies.uploadableFileTypes;
|
||||
//const isAllowedMimeType = allowedMimeTypes.some(mimeType => {
|
||||
// if (mimeType === '*' || mimeType === '*/*') return true;
|
||||
// if (mimeType.endsWith('/*')) return file.type.startsWith(mimeType.slice(0, -1));
|
||||
// return file.type === mimeType;
|
||||
//});
|
||||
//if (!isAllowedMimeType) {
|
||||
// os.alert({
|
||||
// type: 'error',
|
||||
// title: i18n.ts.failedToUpload,
|
||||
// text: i18n.ts.cannotUploadBecauseUnallowedFileType,
|
||||
// });
|
||||
// return reject();
|
||||
//}
|
||||
|
||||
if ((file.size > instance.maxFileSize) || (file.size > ($i.policies.maxFileSizeMb * 1024 * 1024))) {
|
||||
os.alert({
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"type": "module",
|
||||
"name": "misskey-js",
|
||||
"version": "2025.5.1-beta.0",
|
||||
"version": "2025.5.1-beta.1",
|
||||
"description": "Misskey SDK for JavaScript",
|
||||
"license": "MIT",
|
||||
"main": "./built/index.js",
|
||||
|
|
|
|||
Loading…
Reference in New Issue