Add env var for max file upload size (#18735)

* Add limit to file size

* Delete temp db record on file upload failure

* Emit empty value on upload failure

* Rename assets->files

* Update reference

* Finalize rename

* Add changeset

* Add proper exception
This commit is contained in:
Rijk van Zanten
2023-05-25 15:24:14 -04:00
committed by GitHub
parent b920b72e79
commit 3363ca6422
8 changed files with 43 additions and 1 deletions

View File

@@ -0,0 +1,5 @@
---
'@directus/api': minor
---
Added a new `FILES_MAX_UPLOAD_SIZE` environment variable for setting a max value system-wide

View File

@@ -1,11 +1,13 @@
import formatTitle from '@directus/format-title';
import { toArray } from '@directus/utils';
import Busboy from 'busboy';
import bytes from 'bytes';
import type { RequestHandler } from 'express';
import express from 'express';
import Joi from 'joi';
import path from 'path';
import env from '../env.js';
import { ContentTooLargeException } from '../exceptions/content-too-large.js';
import { ForbiddenException, InvalidPayloadException } from '../exceptions/index.js';
import { respond } from '../middleware/respond.js';
import useCollection from '../middleware/use-collection.js';
@@ -34,7 +36,14 @@ export const multipartHandler: RequestHandler = (req, res, next) => {
};
}
const busboy = Busboy({ headers, defParamCharset: 'utf8' });
const busboy = Busboy({
headers,
defParamCharset: 'utf8',
limits: {
fileSize: env['FILES_MAX_UPLOAD_SIZE'] ? bytes(env['FILES_MAX_UPLOAD_SIZE'] as string) : undefined,
},
});
const savedFiles: PrimaryKey[] = [];
const service = new FilesService({ accountability: req.accountability, schema: req.schema });
@@ -88,6 +97,12 @@ export const multipartHandler: RequestHandler = (req, res, next) => {
// Clear the payload for the next to-be-uploaded file
payload = {};
fileStream.on('limit', () => {
const error = new ContentTooLargeException(`Uploaded file is too large`);
fileStream.emit('error', error);
next(error);
});
try {
const primaryKey = await service.uploadOne(fileStream, payloadWithRequiredFields, existingPrimaryKey);
savedFiles.push(primaryKey);

View File

@@ -107,6 +107,10 @@ const allowedEnvironmentVars = [
'STORAGE_.+_HEALTHCHECK_THRESHOLD',
// metadata
'FILE_METADATA_ALLOW_LIST',
// files
'FILES_MAX_UPLOAD_SIZE',
// assets
'ASSETS_CACHE_TTL',
'ASSETS_TRANSFORM_MAX_CONCURRENT',

View File

@@ -0,0 +1,7 @@
import { BaseException } from '@directus/exceptions';
export class ContentTooLargeException extends BaseException {
constructor(message: string) {
super(message, 413, 'CONTENT_TOO_LARGE');
}
}

View File

@@ -87,6 +87,9 @@ export class FilesService extends ItemsService {
} catch (err: any) {
logger.warn(`Couldn't save file ${payload.filename_disk}`);
logger.warn(err);
await this.deleteOne(primaryKey);
throw new ServiceUnavailableException(`Couldn't save file ${payload.filename_disk}`, { service: 'files' });
}

View File

@@ -197,6 +197,7 @@ function useUpload() {
}
} catch (err: any) {
unexpectedError(err);
emit('input', null);
} finally {
uploading.value = false;
done.value = 0;

View File

@@ -626,6 +626,7 @@ errors:
RANGE_NOT_SATISFIABLE: Invalid range
METHOD_NOT_ALLOWED: Method not allowed
REQUESTS_EXCEEDED: Requests limit reached
CONTENT_TOO_LARGE: Submitted item/file is too large
security: Security
value_hashed: Value Securely Hashed
bookmark_name: Bookmark name...

View File

@@ -680,6 +680,12 @@ purposes, collection of additional metadata must be configured:
<sup>[1]</sup>: Extracting all metadata might cause memory issues when the file has an unusually large set of metadata
### Upload Limits
| Variable | Description | Default Value |
| ----------------------- | ------------------------------------------------------------------- | ------------- |
| `FILES_MAX_UPLOAD_SIZE` | Maximum file upload size allowed. For example `10mb`, `1gb`, `10kb` | |
## Assets
| Variable | Description | Default Value |