63 lines
2.1 KiB
JavaScript
63 lines
2.1 KiB
JavaScript
import { mkdir, writeFile } from 'node:fs/promises';
|
|
import { extname, resolve } from 'node:path';
|
|
import { requestLimits } from '../../config/limits.mjs';
|
|
import { paths } from '../../config/paths.mjs';
|
|
import { httpError } from '../../shared/http/httpError.mjs';
|
|
import { readJsonBody } from '../../shared/http/readJsonBody.mjs';
|
|
import { sendJson } from '../../shared/http/sendJson.mjs';
|
|
import { assetExtensions, imageExtensions } from './assetTypes.mjs';
|
|
import { getUniqueFileName } from './getUniqueFileName.mjs';
|
|
import { parseUploadPayload } from './parseUploadPayload.mjs';
|
|
import { sanitizeFileName } from './sanitizeFileName.mjs';
|
|
|
|
export async function handleUploadAsset(request, response) {
|
|
const payload = await readJsonBody(request);
|
|
const fileName = sanitizeFileName(readRequiredString(payload, 'fileName'));
|
|
const extension = extname(fileName).toLowerCase();
|
|
|
|
validateExtension(extension);
|
|
const fileBuffer = parseUploadPayload(payload);
|
|
validateUploadSize(fileBuffer.length);
|
|
|
|
await mkdir(paths.uploadRoot, { recursive: true });
|
|
const uniqueFileName = await getUniqueFileName(paths.uploadRoot, fileName);
|
|
await writeFile(resolve(paths.uploadRoot, uniqueFileName), fileBuffer);
|
|
|
|
return sendJson(response, { asset: createAssetResponse(uniqueFileName, extension, fileBuffer.length) }, 201);
|
|
}
|
|
|
|
function createAssetResponse(fileName, extension, size) {
|
|
return {
|
|
fileName,
|
|
isImage: imageExtensions.has(extension),
|
|
path: `/assets/uploads/${fileName}`,
|
|
size,
|
|
};
|
|
}
|
|
|
|
function readRequiredString(input, key) {
|
|
const value = input[key];
|
|
|
|
if (typeof value !== 'string' || !value.trim()) {
|
|
throw httpError(400, `"${key}" is required`);
|
|
}
|
|
|
|
return value.trim();
|
|
}
|
|
|
|
function validateExtension(extension) {
|
|
if (!assetExtensions.has(extension)) {
|
|
throw httpError(400, `Files with "${extension || 'no'}" extension are not allowed`);
|
|
}
|
|
}
|
|
|
|
function validateUploadSize(size) {
|
|
if (size === 0) {
|
|
throw httpError(400, 'Uploaded file is empty');
|
|
}
|
|
|
|
if (size > requestLimits.maxUploadSize) {
|
|
throw httpError(413, `Uploaded file must be ${Math.round(requestLimits.maxUploadSize / 1024 / 1024)} MB or smaller`);
|
|
}
|
|
}
|