Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

@tus/server: add lastPath arg to getFileIdFromRequest #626

Merged
merged 2 commits into from
Jun 12, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/smooth-kiwis-grow.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@tus/server': minor
---

Add `lastPath` argument to `getFileIdFromRequest` to simplify a common use case.
30 changes: 15 additions & 15 deletions packages/server/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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).
Expand Down Expand Up @@ -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<string, string | number>, body?: string }>`).
returned to the client
(`(req, res, upload) => Promise<{ res: http.ServerResponse, status_code?: number, headers?: Record<string, string | number>, 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
Expand Down Expand Up @@ -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.

Expand Down Expand Up @@ -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
Expand Down
6 changes: 4 additions & 2 deletions packages/server/src/handlers/BaseHandler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion packages/server/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
9 changes: 3 additions & 6 deletions packages/server/test/Server.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Expand Down
Loading