From ea2bf075953c6c5f4ba0ab0e36679790e2a7f56b Mon Sep 17 00:00:00 2001 From: Merlijn Vos Date: Wed, 12 Jun 2024 14:01:33 +0200 Subject: [PATCH] @tus/server: add `lastPath` arg to `getFileIdFromRequest` (#626) * @tus/server: add `lastPath` arg to `getFileIdFromRequest` * Changeset --- .changeset/smooth-kiwis-grow.md | 5 ++++ packages/server/README.md | 30 ++++++++++----------- packages/server/src/handlers/BaseHandler.ts | 6 +++-- packages/server/src/types.ts | 2 +- packages/server/test/Server.test.ts | 9 +++---- 5 files changed, 28 insertions(+), 24 deletions(-) create mode 100644 .changeset/smooth-kiwis-grow.md diff --git a/.changeset/smooth-kiwis-grow.md b/.changeset/smooth-kiwis-grow.md new file mode 100644 index 00000000..77899bb3 --- /dev/null +++ b/.changeset/smooth-kiwis-grow.md @@ -0,0 +1,5 @@ +--- +'@tus/server': minor +--- + +Add `lastPath` argument to `getFileIdFromRequest` to simplify a common use case. diff --git a/packages/server/README.md b/packages/server/README.md index 4ca9f2ee..491d72d1 100644 --- a/packages/server/README.md +++ b/packages/server/README.md @@ -106,8 +106,11 @@ Checkout the example how to #### `options.getFileIdFromRequest` -Control how the Upload-ID is extracted from the request (`(req) => string | void`) By -default, it expects everything in the path after the last `/` to be the upload id. +Control how the Upload-ID is extracted from the request +(`(req, lastPath) => string | void`) + +By default, it expects everything in the path after the last `/` to be the upload id. +`lastPath` is everything after the last `/`. Checkout the example how to [store files in custom nested directories](#example-store-files-in-custom-nested-directories). @@ -157,7 +160,8 @@ This can be used to implement validation of upload metadata or add headers. #### `options.onUploadFinish` `onUploadFinish` will be invoked after an upload is completed but before a response is -returned to the client (`(req, res, upload) => Promise<{ res: http.ServerResponse, status_code?: number, headers?: Record, body?: string }>`). +returned to the client +(`(req, res, upload) => Promise<{ res: http.ServerResponse, status_code?: number, headers?: Record, body?: string }>`). - You can optionally return `status_code`, `headers` and `body` to modify the response. Note that the tus specification does not allow sending response body nor status code @@ -254,8 +258,9 @@ Called every [`postReceiveInterval`](#optionspostreceiveinterval) milliseconds f upload while it‘s being written to the store. This means you are not guaranteed to get (all) events for an upload. For instance if -`postReceiveInterval` is set to 1000ms and an PATCH request takes 500ms, no event is emitted. -If the PATCH request takes 2500ms, you would get the offset at 2000ms, but not at 2500ms. +`postReceiveInterval` is set to 1000ms and an PATCH request takes 500ms, no event is +emitted. If the PATCH request takes 2500ms, you would get the offset at 2000ms, but not at +2500ms. Use `POST_FINISH` if you need to know when an upload is done. @@ -543,18 +548,13 @@ const server = new Server({ id = Buffer.from(id, 'utf-8').toString('base64url') return `${proto}://${host}${path}/${id}` }, - getFileIdFromRequest(req) { - const reExtractFileID = /([^/]+)\/?$/ - const match = reExtractFileID.exec(req.url as string) - - if (!match || path.includes(match[1])) { - return - } - - return Buffer.from(match[1], 'base64url').toString('utf-8') + getFileIdFromRequest(req, lastPath) { + // lastPath is everything after the last `/` + // If your custom URL is different, this might be undefined + // and you need to extract the ID yourself + return Buffer.from(lastPath, 'base64url').toString('utf-8') }, }) - ``` ### Example: use with Nginx diff --git a/packages/server/src/handlers/BaseHandler.ts b/packages/server/src/handlers/BaseHandler.ts index 10212f97..9973341d 100644 --- a/packages/server/src/handlers/BaseHandler.ts +++ b/packages/server/src/handlers/BaseHandler.ts @@ -63,10 +63,12 @@ export class BaseHandler extends EventEmitter { } getFileIdFromRequest(req: http.IncomingMessage) { + const match = reExtractFileID.exec(req.url as string) + if (this.options.getFileIdFromRequest) { - return this.options.getFileIdFromRequest(req) + const lastPath = match ? decodeURIComponent(match[1]) : undefined + return this.options.getFileIdFromRequest(req, lastPath) } - const match = reExtractFileID.exec(req.url as string) if (!match || this.options.path.includes(match[1])) { return diff --git a/packages/server/src/types.ts b/packages/server/src/types.ts index 83b01ca8..405408f5 100644 --- a/packages/server/src/types.ts +++ b/packages/server/src/types.ts @@ -53,7 +53,7 @@ export type ServerOptions = { * Control how the Upload-ID is extracted from the request. * @param req - The incoming HTTP request. */ - getFileIdFromRequest?: (req: http.IncomingMessage) => string | void + getFileIdFromRequest?: (req: http.IncomingMessage, lastPath?: string) => string | void /** * Control how you want to name files. diff --git a/packages/server/test/Server.test.ts b/packages/server/test/Server.test.ts index 491b7aa3..2b77f126 100644 --- a/packages/server/test/Server.test.ts +++ b/packages/server/test/Server.test.ts @@ -295,15 +295,12 @@ describe('Server', () => { id = Buffer.from(id, 'utf-8').toString('base64url') return `${proto}://${host}${path}/${id}` }, - getFileIdFromRequest(req) { - const reExtractFileID = /([^/]+)\/?$/ - const match = reExtractFileID.exec(req.url as string) - - if (!match || route.includes(match[1])) { + getFileIdFromRequest(req, lastPath) { + if (!lastPath) { return } - return Buffer.from(match[1], 'base64url').toString('utf-8') + return Buffer.from(lastPath, 'base64url').toString('utf-8') }, }) const length = Buffer.byteLength('test', 'utf8').toString()