diff --git a/.env.sample b/.env.sample index 80ba377e7..4b8d84b63 100644 --- a/.env.sample +++ b/.env.sample @@ -66,6 +66,14 @@ FILE_STORAGE_LOCAL_ROOT_DIR=/var/lib/outline/data # Maximum allowed size for the uploaded attachment. 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 –––––––––––––– # 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 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 # requests and this ends up being duplicative DEBUG=http diff --git a/app/scenes/Settings/components/DropToImport.tsx b/app/scenes/Settings/components/DropToImport.tsx index 64d6cb2f7..b0663edb4 100644 --- a/app/scenes/Settings/components/DropToImport.tsx +++ b/app/scenes/Settings/components/DropToImport.tsx @@ -38,7 +38,7 @@ function DropToImport({ disabled, onSubmit, children, format }: Props) { try { const attachment = await uploadFile(file, { name: file.name, - preset: AttachmentPreset.Import, + preset: AttachmentPreset.WorkspaceImport, }); await collections.import(attachment.id, format); onSubmit(); diff --git a/app/stores/DocumentsStore.ts b/app/stores/DocumentsStore.ts index b29616494..71bf287e4 100644 --- a/app/stores/DocumentsStore.ts +++ b/app/stores/DocumentsStore.ts @@ -596,10 +596,10 @@ export default class DocumentsStore extends Store { 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( `The selected file was larger than the ${bytesToHumanReadable( - env.MAXIMUM_IMPORT_SIZE + env.FILE_STORAGE_IMPORT_MAX_SIZE )} maximum size` ); } diff --git a/plugins/storage/server/api/files.ts b/plugins/storage/server/api/files.ts index 2ab8d3aa6..3a35ad857 100644 --- a/plugins/storage/server/api/files.ts +++ b/plugins/storage/server/api/files.ts @@ -33,7 +33,7 @@ router.post( multipart({ maximumFileSize: Math.max( env.FILE_STORAGE_UPLOAD_MAX_SIZE, - env.MAXIMUM_IMPORT_SIZE + env.FILE_STORAGE_IMPORT_MAX_SIZE ), }), async (ctx: APIContext) => { diff --git a/server/env.ts b/server/env.ts index dec111b0f..d275e1149 100644 --- a/server/env.ts +++ b/server/env.ts @@ -582,7 +582,8 @@ export class Environment { 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() @IsNumber() @@ -671,17 +672,39 @@ export class Environment { public FILE_STORAGE_UPLOAD_MAX_SIZE = this.toOptionalNumber(process.env.FILE_STORAGE_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 * deleted automatically we allow an optional separate limit on the size of * imports. + * + * @deprecated Use `FILE_STORAGE_IMPORT_MAX_SIZE` or `FILE_STORAGE_WORKSPACE_IMPORT_MAX_SIZE` instead */ @IsNumber() - public MAXIMUM_IMPORT_SIZE = Math.max( - this.toOptionalNumber(process.env.MAXIMUM_IMPORT_SIZE) ?? 100000000, - this.FILE_STORAGE_UPLOAD_MAX_SIZE + @Deprecated("Use FILE_STORAGE_IMPORT_MAX_SIZE instead") + public MAXIMUM_IMPORT_SIZE = this.toOptionalNumber( + process.env.MAXIMUM_IMPORT_SIZE ); /** diff --git a/server/models/helpers/AttachmentHelper.ts b/server/models/helpers/AttachmentHelper.ts index 1a8fbbb78..88f197794 100644 --- a/server/models/helpers/AttachmentHelper.ts +++ b/server/models/helpers/AttachmentHelper.ts @@ -86,6 +86,7 @@ export default class AttachmentHelper { static presetToExpiry(preset: AttachmentPreset) { switch (preset) { case AttachmentPreset.Import: + case AttachmentPreset.WorkspaceImport: return addHours(new Date(), 24); default: return undefined; @@ -101,7 +102,9 @@ export default class AttachmentHelper { static presetToMaxUploadSize(preset: AttachmentPreset) { switch (preset) { 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.DocumentAttachment: default: diff --git a/server/presenters/env.ts b/server/presenters/env.ts index 0b98975b4..fe7095e97 100644 --- a/server/presenters/env.ts +++ b/server/presenters/env.ts @@ -24,7 +24,7 @@ export default function present( SENTRY_TUNNEL: env.SENTRY_TUNNEL, SLACK_CLIENT_ID: env.SLACK_CLIENT_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, DEFAULT_LANGUAGE: env.DEFAULT_LANGUAGE, EMAIL_ENABLED: !!env.SMTP_HOST || env.isDevelopment, diff --git a/server/routes/api/attachments/attachments.test.ts b/server/routes/api/attachments/attachments.test.ts index 022ece0b3..caeb12926 100644 --- a/server/routes/api/attachments/attachments.test.ts +++ b/server/routes/api/attachments/attachments.test.ts @@ -66,7 +66,7 @@ describe("#attachments.create", () => { name: "test.zip", contentType: "application/zip", size: 10000, - preset: AttachmentPreset.Import, + preset: AttachmentPreset.WorkspaceImport, token: user.getJwtToken(), }, }); diff --git a/server/routes/api/documents/documents.ts b/server/routes/api/documents/documents.ts index f47a6be0c..b85d610a9 100644 --- a/server/routes/api/documents/documents.ts +++ b/server/routes/api/documents/documents.ts @@ -1312,7 +1312,7 @@ router.post( auth(), rateLimiter(RateLimiterStrategy.TwentyFivePerMinute), validate(T.DocumentsImportSchema), - multipart({ maximumFileSize: env.MAXIMUM_IMPORT_SIZE }), + multipart({ maximumFileSize: env.FILE_STORAGE_IMPORT_MAX_SIZE }), transaction(), async (ctx: APIContext) => { const { collectionId, parentDocumentId, publish } = ctx.input.body; diff --git a/server/routes/api/index.ts b/server/routes/api/index.ts index 00ccb384f..90cce1451 100644 --- a/server/routes/api/index.ts +++ b/server/routes/api/index.ts @@ -48,7 +48,7 @@ api.use( formidable: { maxFileSize: Math.max( env.FILE_STORAGE_UPLOAD_MAX_SIZE, - env.MAXIMUM_IMPORT_SIZE + env.FILE_STORAGE_IMPORT_MAX_SIZE ), maxFieldsSize: 10 * 1024 * 1024, }, diff --git a/shared/types.ts b/shared/types.ts index f8c970ada..8f311fe7b 100644 --- a/shared/types.ts +++ b/shared/types.ts @@ -53,7 +53,7 @@ export type PublicEnv = { SENTRY_TUNNEL: string | undefined; SLACK_CLIENT_ID: string | undefined; SLACK_APP_ID: string | undefined; - MAXIMUM_IMPORT_SIZE: number; + FILE_STORAGE_IMPORT_MAX_SIZE: number; EMAIL_ENABLED: boolean; PDF_EXPORT_ENABLED: boolean; DEFAULT_LANGUAGE: string; @@ -71,6 +71,7 @@ export type PublicEnv = { export enum AttachmentPreset { DocumentAttachment = "documentAttachment", + WorkspaceImport = "workspaceImport", Import = "import", Avatar = "avatar", }