fix: incorrect config.upload types (#7874)
Fixes https://github.com/payloadcms/payload/issues/7698 Now exporting `FetchAPIFileUploadOptions` from payload, as that type is now used in `config.upload`.
This commit is contained in:
@@ -90,7 +90,7 @@
|
||||
"@babel/preset-typescript": "^7.24.1",
|
||||
"@next/eslint-plugin-next": "15.0.0-canary.104",
|
||||
"@payloadcms/eslint-config": "workspace:*",
|
||||
"@types/busboy": "^1.5.3",
|
||||
"@types/busboy": "1.5.4",
|
||||
"@types/react": "npm:types-react@19.0.0-rc.0",
|
||||
"@types/react-dom": "npm:types-react-dom@19.0.0-rc.0",
|
||||
"@types/uuid": "10.0.0",
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
import type { FetchAPIFileUploadOptions, FileShape } from './index.js'
|
||||
import type { FetchAPIFileUploadOptions } from 'payload'
|
||||
|
||||
import type { FileShape } from './index.js'
|
||||
|
||||
import {
|
||||
checkAndMakeDir,
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import type { FetchAPIFileUploadOptions } from 'payload'
|
||||
|
||||
import crypto from 'crypto'
|
||||
import fs, { WriteStream } from 'fs'
|
||||
import path from 'path'
|
||||
|
||||
import type { FetchAPIFileUploadOptions } from './index.js'
|
||||
|
||||
import { checkAndMakeDir, debugLog, deleteFile, getTempFilename } from './utilities.js'
|
||||
|
||||
type Handler = (
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import type { BusboyConfig } from 'busboy'
|
||||
import type { FetchAPIFileUploadOptions } from 'payload'
|
||||
|
||||
import path from 'path'
|
||||
import { APIError } from 'payload'
|
||||
@@ -35,104 +35,6 @@ export type FileShape = {
|
||||
truncated: boolean
|
||||
}
|
||||
|
||||
export type FetchAPIFileUploadOptions = {
|
||||
/**
|
||||
* Returns a HTTP 413 when the file is bigger than the size limit if `true`.
|
||||
* Otherwise, it will add a `truncated = true` to the resulting file structure.
|
||||
* @default false
|
||||
*/
|
||||
abortOnLimit?: boolean | undefined
|
||||
/**
|
||||
* Automatically creates the directory path specified in `.mv(filePathName)`
|
||||
* @default false
|
||||
*/
|
||||
createParentPath?: boolean | undefined
|
||||
/**
|
||||
* Turn on/off upload process logging. Can be useful for troubleshooting.
|
||||
* @default false
|
||||
*/
|
||||
debug?: boolean | undefined
|
||||
/**
|
||||
* User defined limit handler which will be invoked if the file is bigger than configured limits.
|
||||
* @default false
|
||||
*/
|
||||
limitHandler?: ((args: { request: Request; size: number }) => void) | boolean | undefined
|
||||
/**
|
||||
* By default, `req.body` and `req.files` are flattened like this:
|
||||
* `{'name': 'John', 'hobbies[0]': 'Cinema', 'hobbies[1]': 'Bike'}
|
||||
*
|
||||
* When this option is enabled they are parsed in order to be nested like this:
|
||||
* `{'name': 'John', 'hobbies': ['Cinema', 'Bike']}`
|
||||
* @default false
|
||||
*/
|
||||
parseNested?: boolean | undefined
|
||||
/**
|
||||
* Preserves filename extension when using `safeFileNames` option.
|
||||
* If set to `true`, will default to an extension length of `3`.
|
||||
* If set to `number`, this will be the max allowable extension length.
|
||||
* If an extension is smaller than the extension length, it remains untouched. If the extension is longer,
|
||||
* it is shifted.
|
||||
* @default false
|
||||
*
|
||||
* @example
|
||||
* // true
|
||||
* app.use(fileUpload({ safeFileNames: true, preserveExtension: true }));
|
||||
* // myFileName.ext --> myFileName.ext
|
||||
*
|
||||
* @example
|
||||
* // max extension length 2, extension shifted
|
||||
* app.use(fileUpload({ safeFileNames: true, preserveExtension: 2 }));
|
||||
* // myFileName.ext --> myFileNamee.xt
|
||||
*/
|
||||
preserveExtension?: boolean | number | undefined
|
||||
/**
|
||||
* Response which will be send to client if file size limit exceeded when `abortOnLimit` set to `true`.
|
||||
* @default 'File size limit has been reached'
|
||||
*/
|
||||
responseOnLimit?: string | undefined
|
||||
/**
|
||||
* Strips characters from the upload's filename.
|
||||
* You can use custom regex to determine what to strip.
|
||||
* If set to `true`, non-alphanumeric characters _except_ dashes and underscores will be stripped.
|
||||
* This option is off by default.
|
||||
* @default false
|
||||
*
|
||||
* @example
|
||||
* // strip slashes from file names
|
||||
* app.use(fileUpload({ safeFileNames: /\\/g }))
|
||||
*
|
||||
* @example
|
||||
* app.use(fileUpload({ safeFileNames: true }))
|
||||
*/
|
||||
safeFileNames?: RegExp | boolean | undefined
|
||||
/**
|
||||
* Path to store temporary files.
|
||||
* Used along with the `useTempFiles` option. By default this module uses `'tmp'` folder
|
||||
* in the current working directory.
|
||||
* You can use trailing slash, but it is not necessary.
|
||||
* @default './tmp'
|
||||
*/
|
||||
tempFileDir?: string | undefined
|
||||
/**
|
||||
* This defines how long to wait for data before aborting. Set to `0` if you want to turn off timeout checks.
|
||||
* @default 60_000
|
||||
*/
|
||||
uploadTimeout?: number | undefined
|
||||
/**
|
||||
* Applies uri decoding to file names if set `true`.
|
||||
* @default false
|
||||
*/
|
||||
uriDecodeFileNames?: boolean | undefined
|
||||
/**
|
||||
* By default this module uploads files into RAM.
|
||||
* Setting this option to `true` turns on using temporary files instead of utilising RAM.
|
||||
* This avoids memory overflow issues when uploading large files or in case of uploading
|
||||
* lots of files at same time.
|
||||
* @default false
|
||||
*/
|
||||
useTempFiles?: boolean | undefined
|
||||
} & Partial<BusboyConfig>
|
||||
|
||||
type FetchAPIFileUploadResponseFile = {
|
||||
data: Buffer
|
||||
mimetype: string
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
import type { FetchAPIFileUploadOptions } from 'payload'
|
||||
import type { Readable } from 'stream'
|
||||
|
||||
import Busboy from 'busboy'
|
||||
import httpStatus from 'http-status'
|
||||
import { APIError } from 'payload'
|
||||
|
||||
import type { FetchAPIFileUploadOptions, FetchAPIFileUploadResponse } from './index.js'
|
||||
import type { FetchAPIFileUploadResponse } from './index.js'
|
||||
|
||||
import { fileFactory } from './fileFactory.js'
|
||||
import { memHandler, tempFileHandler } from './handlers.js'
|
||||
@@ -163,6 +164,8 @@ export const processMultipart: ProcessMultipart = async ({ options, request }) =
|
||||
uploadTimer.set()
|
||||
})
|
||||
|
||||
// TODO: Valid eslint error - this will likely be a floating promise. Evaluate if we need to handle this differently.
|
||||
// eslint-disable-next-line @typescript-eslint/no-misused-promises
|
||||
busboy.on('finish', async () => {
|
||||
debugLog(options, `Busboy finished parsing request.`)
|
||||
if (options.parseNested) {
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import type { FetchAPIFileUploadOptions } from 'payload'
|
||||
|
||||
import fs from 'fs'
|
||||
import path from 'path'
|
||||
import { Readable } from 'stream'
|
||||
|
||||
import type { FetchAPIFileUploadOptions } from './index.js'
|
||||
|
||||
// Parameters for safe file name parsing.
|
||||
const SAFE_FILE_NAME_REGEX = /[^\w-]/g
|
||||
const MAX_EXTENSION_LENGTH = 3
|
||||
|
||||
@@ -2,8 +2,6 @@ import type { PayloadRequest } from 'payload'
|
||||
|
||||
import { APIError } from 'payload'
|
||||
|
||||
import type { FetchAPIFileUploadOptions } from '../fetchAPI-multipart/index.js'
|
||||
|
||||
import { fetchAPIFileUpload } from '../fetchAPI-multipart/index.js'
|
||||
|
||||
type AddDataAndFileToRequest = (req: PayloadRequest) => Promise<void>
|
||||
@@ -30,7 +28,7 @@ export const addDataAndFileToRequest: AddDataAndFileToRequest = async (req) => {
|
||||
}
|
||||
} else if (bodyByteSize && contentType.includes('multipart/')) {
|
||||
const { error, fields, files } = await fetchAPIFileUpload({
|
||||
options: payload.config.upload as FetchAPIFileUploadOptions,
|
||||
options: payload.config.upload,
|
||||
request: req as Request,
|
||||
})
|
||||
|
||||
|
||||
@@ -86,6 +86,7 @@
|
||||
"dependencies": {
|
||||
"@next/env": "^15.0.0-canary.104",
|
||||
"@payloadcms/translations": "workspace:*",
|
||||
"@types/busboy": "1.5.4",
|
||||
"ajv": "8.14.0",
|
||||
"bson-objectid": "2.0.4",
|
||||
"ci-info": "^4.0.0",
|
||||
@@ -114,7 +115,6 @@
|
||||
"@hyrious/esbuild-plugin-commonjs": "^0.2.4",
|
||||
"@monaco-editor/react": "4.5.1",
|
||||
"@payloadcms/eslint-config": "workspace:*",
|
||||
"@types/express-fileupload": "1.4.1",
|
||||
"@types/json-schema": "7.0.15",
|
||||
"@types/jsonwebtoken": "8.5.9",
|
||||
"@types/minimist": "1.2.2",
|
||||
|
||||
@@ -4,7 +4,7 @@ import type {
|
||||
I18nOptions,
|
||||
TFunction,
|
||||
} from '@payloadcms/translations'
|
||||
import type { Options as ExpressFileUploadOptions } from 'express-fileupload'
|
||||
import type { BusboyConfig } from 'busboy'
|
||||
import type GraphQL from 'graphql'
|
||||
import type { JSONSchema4 } from 'json-schema'
|
||||
import type { DestinationStream, LoggerOptions } from 'pino'
|
||||
@@ -536,6 +536,104 @@ export interface AdminDependencies {
|
||||
[key: string]: AdminComponent | AdminFunction
|
||||
}
|
||||
|
||||
export type FetchAPIFileUploadOptions = {
|
||||
/**
|
||||
* Returns a HTTP 413 when the file is bigger than the size limit if `true`.
|
||||
* Otherwise, it will add a `truncated = true` to the resulting file structure.
|
||||
* @default false
|
||||
*/
|
||||
abortOnLimit?: boolean | undefined
|
||||
/**
|
||||
* Automatically creates the directory path specified in `.mv(filePathName)`
|
||||
* @default false
|
||||
*/
|
||||
createParentPath?: boolean | undefined
|
||||
/**
|
||||
* Turn on/off upload process logging. Can be useful for troubleshooting.
|
||||
* @default false
|
||||
*/
|
||||
debug?: boolean | undefined
|
||||
/**
|
||||
* User defined limit handler which will be invoked if the file is bigger than configured limits.
|
||||
* @default false
|
||||
*/
|
||||
limitHandler?: ((args: { request: Request; size: number }) => void) | boolean | undefined
|
||||
/**
|
||||
* By default, `req.body` and `req.files` are flattened like this:
|
||||
* `{'name': 'John', 'hobbies[0]': 'Cinema', 'hobbies[1]': 'Bike'}
|
||||
*
|
||||
* When this option is enabled they are parsed in order to be nested like this:
|
||||
* `{'name': 'John', 'hobbies': ['Cinema', 'Bike']}`
|
||||
* @default false
|
||||
*/
|
||||
parseNested?: boolean | undefined
|
||||
/**
|
||||
* Preserves filename extension when using `safeFileNames` option.
|
||||
* If set to `true`, will default to an extension length of `3`.
|
||||
* If set to `number`, this will be the max allowable extension length.
|
||||
* If an extension is smaller than the extension length, it remains untouched. If the extension is longer,
|
||||
* it is shifted.
|
||||
* @default false
|
||||
*
|
||||
* @example
|
||||
* // true
|
||||
* app.use(fileUpload({ safeFileNames: true, preserveExtension: true }));
|
||||
* // myFileName.ext --> myFileName.ext
|
||||
*
|
||||
* @example
|
||||
* // max extension length 2, extension shifted
|
||||
* app.use(fileUpload({ safeFileNames: true, preserveExtension: 2 }));
|
||||
* // myFileName.ext --> myFileNamee.xt
|
||||
*/
|
||||
preserveExtension?: boolean | number | undefined
|
||||
/**
|
||||
* Response which will be send to client if file size limit exceeded when `abortOnLimit` set to `true`.
|
||||
* @default 'File size limit has been reached'
|
||||
*/
|
||||
responseOnLimit?: string | undefined
|
||||
/**
|
||||
* Strips characters from the upload's filename.
|
||||
* You can use custom regex to determine what to strip.
|
||||
* If set to `true`, non-alphanumeric characters _except_ dashes and underscores will be stripped.
|
||||
* This option is off by default.
|
||||
* @default false
|
||||
*
|
||||
* @example
|
||||
* // strip slashes from file names
|
||||
* app.use(fileUpload({ safeFileNames: /\\/g }))
|
||||
*
|
||||
* @example
|
||||
* app.use(fileUpload({ safeFileNames: true }))
|
||||
*/
|
||||
safeFileNames?: RegExp | boolean | undefined
|
||||
/**
|
||||
* Path to store temporary files.
|
||||
* Used along with the `useTempFiles` option. By default this module uses `'tmp'` folder
|
||||
* in the current working directory.
|
||||
* You can use trailing slash, but it is not necessary.
|
||||
* @default './tmp'
|
||||
*/
|
||||
tempFileDir?: string | undefined
|
||||
/**
|
||||
* This defines how long to wait for data before aborting. Set to `0` if you want to turn off timeout checks.
|
||||
* @default 60_000
|
||||
*/
|
||||
uploadTimeout?: number | undefined
|
||||
/**
|
||||
* Applies uri decoding to file names if set `true`.
|
||||
* @default false
|
||||
*/
|
||||
uriDecodeFileNames?: boolean | undefined
|
||||
/**
|
||||
* By default this module uploads files into RAM.
|
||||
* Setting this option to `true` turns on using temporary files instead of utilising RAM.
|
||||
* This avoids memory overflow issues when uploading large files or in case of uploading
|
||||
* lots of files at same time.
|
||||
* @default false
|
||||
*/
|
||||
useTempFiles?: boolean | undefined
|
||||
} & Partial<BusboyConfig>
|
||||
|
||||
/**
|
||||
* This is the central configuration
|
||||
*
|
||||
@@ -885,7 +983,7 @@ export type Config = {
|
||||
/**
|
||||
* Customize the handling of incoming file uploads for collections that have uploads enabled.
|
||||
*/
|
||||
upload?: ExpressFileUploadOptions
|
||||
upload?: FetchAPIFileUploadOptions
|
||||
}
|
||||
|
||||
export type SanitizedConfig = {
|
||||
@@ -906,7 +1004,7 @@ export type SanitizedConfig = {
|
||||
* Deduped list of adapters used in the project
|
||||
*/
|
||||
adapters: string[]
|
||||
} & ExpressFileUploadOptions
|
||||
} & FetchAPIFileUploadOptions
|
||||
} & Omit<
|
||||
// TODO: DeepRequired breaks certain, advanced TypeScript types / certain type information is lost. We should remove it when possible.
|
||||
// E.g. in packages/ui/src/graphics/Account/index.tsx in getComponent, if avatar.Component is casted to what it's supposed to be,
|
||||
|
||||
16
pnpm-lock.yaml
generated
16
pnpm-lock.yaml
generated
@@ -759,7 +759,7 @@ importers:
|
||||
specifier: workspace:*
|
||||
version: link:../eslint-config
|
||||
'@types/busboy':
|
||||
specifier: ^1.5.3
|
||||
specifier: 1.5.4
|
||||
version: 1.5.4
|
||||
'@types/react':
|
||||
specifier: npm:types-react@19.0.0-rc.0
|
||||
@@ -800,6 +800,9 @@ importers:
|
||||
'@payloadcms/translations':
|
||||
specifier: workspace:*
|
||||
version: link:../translations
|
||||
'@types/busboy':
|
||||
specifier: 1.5.4
|
||||
version: 1.5.4
|
||||
ajv:
|
||||
specifier: 8.14.0
|
||||
version: 8.14.0
|
||||
@@ -882,9 +885,6 @@ importers:
|
||||
'@payloadcms/eslint-config':
|
||||
specifier: workspace:*
|
||||
version: link:../eslint-config
|
||||
'@types/express-fileupload':
|
||||
specifier: 1.4.1
|
||||
version: 1.4.1
|
||||
'@types/json-schema':
|
||||
specifier: 7.0.15
|
||||
version: 7.0.15
|
||||
@@ -4375,9 +4375,6 @@ packages:
|
||||
'@types/estree@1.0.5':
|
||||
resolution: {integrity: sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==}
|
||||
|
||||
'@types/express-fileupload@1.4.1':
|
||||
resolution: {integrity: sha512-sbl865h1Sser6SF+efpw2F/+roGISj+PRIbMcGXbtzgJQCBAeeBmoSo7sPge/mBa22ymCHfFPtHFsag/wUxwfg==}
|
||||
|
||||
'@types/express-serve-static-core@4.19.5':
|
||||
resolution: {integrity: sha512-y6W03tvrACO72aijJ5uF02FRq5cgDR9lUxddQ8vyF+GvmjJQqbzDcJngEjURc+ZsG31VI3hODNZJ2URj86pzmg==}
|
||||
|
||||
@@ -13030,11 +13027,6 @@ snapshots:
|
||||
|
||||
'@types/estree@1.0.5': {}
|
||||
|
||||
'@types/express-fileupload@1.4.1':
|
||||
dependencies:
|
||||
'@types/busboy': 1.5.4
|
||||
'@types/express': 4.17.21
|
||||
|
||||
'@types/express-serve-static-core@4.19.5':
|
||||
dependencies:
|
||||
'@types/node': 20.12.5
|
||||
|
||||
Reference in New Issue
Block a user