chore: Separate maximum import size config for document/workspace (#6566)
* chore: Separate maximum import size config for document/workspace * Update .env.sample
This commit is contained in:
12
.env.sample
12
.env.sample
@@ -66,6 +66,14 @@ FILE_STORAGE_LOCAL_ROOT_DIR=/var/lib/outline/data
|
|||||||
# Maximum allowed size for the uploaded attachment.
|
# Maximum allowed size for the uploaded attachment.
|
||||||
FILE_STORAGE_UPLOAD_MAX_SIZE=262144000
|
FILE_STORAGE_UPLOAD_MAX_SIZE=262144000
|
||||||
|
|
||||||
|
# Override the maximum size of document imports, generally this should be lower
|
||||||
|
# than the document attachment maximum size.
|
||||||
|
FILE_STORAGE_IMPORT_MAX_SIZE=
|
||||||
|
|
||||||
|
# Override the maximum size of workspace imports, these can be especially large
|
||||||
|
# and the files are temporary being automatically deleted after a period of time.
|
||||||
|
FILE_STORAGE_WORKSPACE_IMPORT_MAX_SIZE=
|
||||||
|
|
||||||
# –––––––––––––– AUTHENTICATION ––––––––––––––
|
# –––––––––––––– AUTHENTICATION ––––––––––––––
|
||||||
|
|
||||||
# Third party signin credentials, at least ONE OF EITHER Google, Slack,
|
# Third party signin credentials, at least ONE OF EITHER Google, Slack,
|
||||||
@@ -142,10 +150,6 @@ ENABLE_UPDATES=true
|
|||||||
# available memory by 512 for a rough estimate
|
# available memory by 512 for a rough estimate
|
||||||
WEB_CONCURRENCY=1
|
WEB_CONCURRENCY=1
|
||||||
|
|
||||||
# Override the maximum size of document imports, could be required if you have
|
|
||||||
# especially large Word documents with embedded imagery
|
|
||||||
MAXIMUM_IMPORT_SIZE=5120000
|
|
||||||
|
|
||||||
# You can remove this line if your reverse proxy already logs incoming http
|
# You can remove this line if your reverse proxy already logs incoming http
|
||||||
# requests and this ends up being duplicative
|
# requests and this ends up being duplicative
|
||||||
DEBUG=http
|
DEBUG=http
|
||||||
|
|||||||
@@ -38,7 +38,7 @@ function DropToImport({ disabled, onSubmit, children, format }: Props) {
|
|||||||
try {
|
try {
|
||||||
const attachment = await uploadFile(file, {
|
const attachment = await uploadFile(file, {
|
||||||
name: file.name,
|
name: file.name,
|
||||||
preset: AttachmentPreset.Import,
|
preset: AttachmentPreset.WorkspaceImport,
|
||||||
});
|
});
|
||||||
await collections.import(attachment.id, format);
|
await collections.import(attachment.id, format);
|
||||||
onSubmit();
|
onSubmit();
|
||||||
|
|||||||
@@ -596,10 +596,10 @@ export default class DocumentsStore extends Store<Document> {
|
|||||||
throw new Error(`The selected file type is not supported (${file.type})`);
|
throw new Error(`The selected file type is not supported (${file.type})`);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (file.size > env.MAXIMUM_IMPORT_SIZE) {
|
if (file.size > env.FILE_STORAGE_IMPORT_MAX_SIZE) {
|
||||||
throw new Error(
|
throw new Error(
|
||||||
`The selected file was larger than the ${bytesToHumanReadable(
|
`The selected file was larger than the ${bytesToHumanReadable(
|
||||||
env.MAXIMUM_IMPORT_SIZE
|
env.FILE_STORAGE_IMPORT_MAX_SIZE
|
||||||
)} maximum size`
|
)} maximum size`
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -33,7 +33,7 @@ router.post(
|
|||||||
multipart({
|
multipart({
|
||||||
maximumFileSize: Math.max(
|
maximumFileSize: Math.max(
|
||||||
env.FILE_STORAGE_UPLOAD_MAX_SIZE,
|
env.FILE_STORAGE_UPLOAD_MAX_SIZE,
|
||||||
env.MAXIMUM_IMPORT_SIZE
|
env.FILE_STORAGE_IMPORT_MAX_SIZE
|
||||||
),
|
),
|
||||||
}),
|
}),
|
||||||
async (ctx: APIContext<T.FilesCreateReq>) => {
|
async (ctx: APIContext<T.FilesCreateReq>) => {
|
||||||
|
|||||||
@@ -582,7 +582,8 @@ export class Environment {
|
|||||||
this.toOptionalNumber(process.env.RATE_LIMITER_DURATION_WINDOW) ?? 60;
|
this.toOptionalNumber(process.env.RATE_LIMITER_DURATION_WINDOW) ?? 60;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @deprecated Set max allowed upload size for file attachments.
|
* Set max allowed upload size for file attachments.
|
||||||
|
* @deprecated Use FILE_STORAGE_UPLOAD_MAX_SIZE instead
|
||||||
*/
|
*/
|
||||||
@IsOptional()
|
@IsOptional()
|
||||||
@IsNumber()
|
@IsNumber()
|
||||||
@@ -671,17 +672,39 @@ export class Environment {
|
|||||||
public FILE_STORAGE_UPLOAD_MAX_SIZE =
|
public FILE_STORAGE_UPLOAD_MAX_SIZE =
|
||||||
this.toOptionalNumber(process.env.FILE_STORAGE_UPLOAD_MAX_SIZE) ??
|
this.toOptionalNumber(process.env.FILE_STORAGE_UPLOAD_MAX_SIZE) ??
|
||||||
this.toOptionalNumber(process.env.AWS_S3_UPLOAD_MAX_SIZE) ??
|
this.toOptionalNumber(process.env.AWS_S3_UPLOAD_MAX_SIZE) ??
|
||||||
100000000;
|
1000000;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set max allowed upload size for document imports.
|
||||||
|
*/
|
||||||
|
@IsNumber()
|
||||||
|
public FILE_STORAGE_IMPORT_MAX_SIZE =
|
||||||
|
this.toOptionalNumber(process.env.FILE_STORAGE_IMPORT_MAX_SIZE) ??
|
||||||
|
this.toOptionalNumber(process.env.MAXIMUM_IMPORT_SIZE) ??
|
||||||
|
this.toOptionalNumber(process.env.FILE_STORAGE_UPLOAD_MAX_SIZE) ??
|
||||||
|
1000000;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set max allowed upload size for imports at workspace level.
|
||||||
|
*/
|
||||||
|
@IsNumber()
|
||||||
|
public FILE_STORAGE_WORKSPACE_IMPORT_MAX_SIZE =
|
||||||
|
this.toOptionalNumber(process.env.FILE_STORAGE_WORKSPACE_IMPORT_MAX_SIZE) ??
|
||||||
|
this.toOptionalNumber(process.env.MAXIMUM_IMPORT_SIZE) ??
|
||||||
|
this.toOptionalNumber(process.env.FILE_STORAGE_UPLOAD_MAX_SIZE) ??
|
||||||
|
1000000;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Because imports can be much larger than regular file attachments and are
|
* Because imports can be much larger than regular file attachments and are
|
||||||
* deleted automatically we allow an optional separate limit on the size of
|
* deleted automatically we allow an optional separate limit on the size of
|
||||||
* imports.
|
* imports.
|
||||||
|
*
|
||||||
|
* @deprecated Use `FILE_STORAGE_IMPORT_MAX_SIZE` or `FILE_STORAGE_WORKSPACE_IMPORT_MAX_SIZE` instead
|
||||||
*/
|
*/
|
||||||
@IsNumber()
|
@IsNumber()
|
||||||
public MAXIMUM_IMPORT_SIZE = Math.max(
|
@Deprecated("Use FILE_STORAGE_IMPORT_MAX_SIZE instead")
|
||||||
this.toOptionalNumber(process.env.MAXIMUM_IMPORT_SIZE) ?? 100000000,
|
public MAXIMUM_IMPORT_SIZE = this.toOptionalNumber(
|
||||||
this.FILE_STORAGE_UPLOAD_MAX_SIZE
|
process.env.MAXIMUM_IMPORT_SIZE
|
||||||
);
|
);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -86,6 +86,7 @@ export default class AttachmentHelper {
|
|||||||
static presetToExpiry(preset: AttachmentPreset) {
|
static presetToExpiry(preset: AttachmentPreset) {
|
||||||
switch (preset) {
|
switch (preset) {
|
||||||
case AttachmentPreset.Import:
|
case AttachmentPreset.Import:
|
||||||
|
case AttachmentPreset.WorkspaceImport:
|
||||||
return addHours(new Date(), 24);
|
return addHours(new Date(), 24);
|
||||||
default:
|
default:
|
||||||
return undefined;
|
return undefined;
|
||||||
@@ -101,7 +102,9 @@ export default class AttachmentHelper {
|
|||||||
static presetToMaxUploadSize(preset: AttachmentPreset) {
|
static presetToMaxUploadSize(preset: AttachmentPreset) {
|
||||||
switch (preset) {
|
switch (preset) {
|
||||||
case AttachmentPreset.Import:
|
case AttachmentPreset.Import:
|
||||||
return env.MAXIMUM_IMPORT_SIZE;
|
return env.FILE_STORAGE_IMPORT_MAX_SIZE;
|
||||||
|
case AttachmentPreset.WorkspaceImport:
|
||||||
|
return env.FILE_STORAGE_WORKSPACE_IMPORT_MAX_SIZE;
|
||||||
case AttachmentPreset.Avatar:
|
case AttachmentPreset.Avatar:
|
||||||
case AttachmentPreset.DocumentAttachment:
|
case AttachmentPreset.DocumentAttachment:
|
||||||
default:
|
default:
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ export default function present(
|
|||||||
SENTRY_TUNNEL: env.SENTRY_TUNNEL,
|
SENTRY_TUNNEL: env.SENTRY_TUNNEL,
|
||||||
SLACK_CLIENT_ID: env.SLACK_CLIENT_ID,
|
SLACK_CLIENT_ID: env.SLACK_CLIENT_ID,
|
||||||
SLACK_APP_ID: env.SLACK_APP_ID,
|
SLACK_APP_ID: env.SLACK_APP_ID,
|
||||||
MAXIMUM_IMPORT_SIZE: env.MAXIMUM_IMPORT_SIZE,
|
FILE_STORAGE_IMPORT_MAX_SIZE: env.FILE_STORAGE_IMPORT_MAX_SIZE,
|
||||||
PDF_EXPORT_ENABLED: false,
|
PDF_EXPORT_ENABLED: false,
|
||||||
DEFAULT_LANGUAGE: env.DEFAULT_LANGUAGE,
|
DEFAULT_LANGUAGE: env.DEFAULT_LANGUAGE,
|
||||||
EMAIL_ENABLED: !!env.SMTP_HOST || env.isDevelopment,
|
EMAIL_ENABLED: !!env.SMTP_HOST || env.isDevelopment,
|
||||||
|
|||||||
@@ -66,7 +66,7 @@ describe("#attachments.create", () => {
|
|||||||
name: "test.zip",
|
name: "test.zip",
|
||||||
contentType: "application/zip",
|
contentType: "application/zip",
|
||||||
size: 10000,
|
size: 10000,
|
||||||
preset: AttachmentPreset.Import,
|
preset: AttachmentPreset.WorkspaceImport,
|
||||||
token: user.getJwtToken(),
|
token: user.getJwtToken(),
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1312,7 +1312,7 @@ router.post(
|
|||||||
auth(),
|
auth(),
|
||||||
rateLimiter(RateLimiterStrategy.TwentyFivePerMinute),
|
rateLimiter(RateLimiterStrategy.TwentyFivePerMinute),
|
||||||
validate(T.DocumentsImportSchema),
|
validate(T.DocumentsImportSchema),
|
||||||
multipart({ maximumFileSize: env.MAXIMUM_IMPORT_SIZE }),
|
multipart({ maximumFileSize: env.FILE_STORAGE_IMPORT_MAX_SIZE }),
|
||||||
transaction(),
|
transaction(),
|
||||||
async (ctx: APIContext<T.DocumentsImportReq>) => {
|
async (ctx: APIContext<T.DocumentsImportReq>) => {
|
||||||
const { collectionId, parentDocumentId, publish } = ctx.input.body;
|
const { collectionId, parentDocumentId, publish } = ctx.input.body;
|
||||||
|
|||||||
@@ -48,7 +48,7 @@ api.use(
|
|||||||
formidable: {
|
formidable: {
|
||||||
maxFileSize: Math.max(
|
maxFileSize: Math.max(
|
||||||
env.FILE_STORAGE_UPLOAD_MAX_SIZE,
|
env.FILE_STORAGE_UPLOAD_MAX_SIZE,
|
||||||
env.MAXIMUM_IMPORT_SIZE
|
env.FILE_STORAGE_IMPORT_MAX_SIZE
|
||||||
),
|
),
|
||||||
maxFieldsSize: 10 * 1024 * 1024,
|
maxFieldsSize: 10 * 1024 * 1024,
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -53,7 +53,7 @@ export type PublicEnv = {
|
|||||||
SENTRY_TUNNEL: string | undefined;
|
SENTRY_TUNNEL: string | undefined;
|
||||||
SLACK_CLIENT_ID: string | undefined;
|
SLACK_CLIENT_ID: string | undefined;
|
||||||
SLACK_APP_ID: string | undefined;
|
SLACK_APP_ID: string | undefined;
|
||||||
MAXIMUM_IMPORT_SIZE: number;
|
FILE_STORAGE_IMPORT_MAX_SIZE: number;
|
||||||
EMAIL_ENABLED: boolean;
|
EMAIL_ENABLED: boolean;
|
||||||
PDF_EXPORT_ENABLED: boolean;
|
PDF_EXPORT_ENABLED: boolean;
|
||||||
DEFAULT_LANGUAGE: string;
|
DEFAULT_LANGUAGE: string;
|
||||||
@@ -71,6 +71,7 @@ export type PublicEnv = {
|
|||||||
|
|
||||||
export enum AttachmentPreset {
|
export enum AttachmentPreset {
|
||||||
DocumentAttachment = "documentAttachment",
|
DocumentAttachment = "documentAttachment",
|
||||||
|
WorkspaceImport = "workspaceImport",
|
||||||
Import = "import",
|
Import = "import",
|
||||||
Avatar = "avatar",
|
Avatar = "avatar",
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user