diff --git a/src/client/app/common/views/components/uploader.vue b/src/client/app/common/views/components/uploader.vue index 779c61e7b5b5..19f01bdbe42b 100644 --- a/src/client/app/common/views/components/uploader.vue +++ b/src/client/app/common/views/components/uploader.vue @@ -77,9 +77,22 @@ export default Vue.extend({ xhr.onload = (e: any) => { if (xhr.status !== 200) { this.uploads = (this.uploads as any[]).filter(x => x.id !== id); + + let errMsg = `${xhr.status} ${xhr.statusText}`; + if (xhr.status === 413) { + errMsg = 'File too large'; + } else { + try { + const errObj = JSON.parse(xhr.responseText); + if (errObj.error?.message) errMsg = errObj.error.message; + } catch (err) { + console.error(err); + } + } + this.$root.dialog({ type: 'error', - text: xhr.status === 413 ? `File to large` : `${xhr.status} ${xhr.statusText}` + text: errMsg }); return; } diff --git a/src/misc/get-file-info.ts b/src/misc/get-file-info.ts index 47308258c36f..ac6b6a583516 100644 --- a/src/misc/get-file-info.ts +++ b/src/misc/get-file-info.ts @@ -44,6 +44,8 @@ const FILE_TYPE_DETECTS = [ 'audio/aac', 'audio/x-flac', 'audio/vnd.wave', + + 'image/heif', 'image/heif-sequence', 'image/heic', 'image/heic-sequence', ]; export const FILE_TYPE_BROWSERSAFE = [ diff --git a/src/server/api/endpoints/drive/files/create.ts b/src/server/api/endpoints/drive/files/create.ts index 80e20431547a..5cad45a4d896 100644 --- a/src/server/api/endpoints/drive/files/create.ts +++ b/src/server/api/endpoints/drive/files/create.ts @@ -2,7 +2,7 @@ import * as ms from 'ms'; import $ from 'cafy'; import ID, { transform } from '../../../../../misc/cafy-id'; import { validateFileName, pack } from '../../../../../models/drive-file'; -import { addFile } from '../../../../../services/drive/add-file'; +import { FileTypeError, addFile } from '../../../../../services/drive/add-file'; import define from '../../../define'; import { apiLogger } from '../../../logger'; import { ApiError } from '../../../error'; @@ -71,7 +71,13 @@ export const meta = { message: 'Invalid file name.', code: 'INVALID_FILE_NAME', id: 'f449b209-0c60-4e51-84d5-29486263bfd4' - } + }, + + unsupportedFileTypeHeic: { + message: 'Unsupported file type. HEIC', + code: 'UNSUPPORTED_FILE_TYPE_HEIC', + id: 'c5384862-bc35-4d0f-9493-ba45ec96115d' + }, } }; @@ -95,6 +101,9 @@ export default define(meta, async (ps, user, app, file, cleanup) => { const driveFile = await addFile({ user, path: file.path, name, folderId: ps.folderId, force: ps.force, sensitive: ps.isSensitive }); return pack(driveFile, { detail: true, self: true }); } catch (e) { + if (e instanceof FileTypeError) { + if (e.type === 'denyHeic') throw new ApiError(meta.errors.unsupportedFileTypeHeic); + } apiLogger.error(e); throw new ApiError(); } finally { diff --git a/src/services/drive/add-file.ts b/src/services/drive/add-file.ts index 793ef027f690..e7d877153942 100644 --- a/src/services/drive/add-file.ts +++ b/src/services/drive/add-file.ts @@ -30,6 +30,18 @@ import { InternalStorage } from './internal-storage'; const logger = driveLogger.createSubLogger('register', 'yellow'); +type FileTypeErrorType = 'denyHeic'; + +export class FileTypeError extends Error { + public type?: FileTypeErrorType; + constructor(type?: FileTypeErrorType) { + super('file type error'); + this.name = 'FileTypeError'; + this.type = type; + } +} + + /*** * Save file * @param path Path for original @@ -41,6 +53,10 @@ async function save(path: string, name: string, info: FileInfo, metadata: IMetad // thunbnail, webpublic を必要なら生成 let animation = info.type.mime === 'image/apng' ? 'yes' : info.type.mime === 'image/png' ? 'no' : undefined; + if (!metadata.uri && ['image/heif', 'image/heif-sequence', 'image/heic', 'image/heic-sequence'].includes(info.type.mime)) { + throw new FileTypeError('denyHeic'); + } + const alts = await generateAlts(path, info.type.mime, !metadata.uri).catch(err => { if (err === 'ANIMATED') { animation = 'yes';