Skip to content

Commit 590b9ac

Browse files
committed
fix: tus-metadata
1 parent 830ea71 commit 590b9ac

File tree

9 files changed

+853
-125
lines changed

9 files changed

+853
-125
lines changed

src/http/routes/tus/index.ts

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
11
import { FastifyBaseLogger, FastifyInstance } from 'fastify'
2-
import { Server } from '@tus/server'
2+
import * as http from 'http'
3+
import { Server, ServerOptions, DataStore } from '@tus/server'
34
import { jwt, storage, db, dbSuperUser } from '../../plugins'
45
import { getConfig } from '../../../config'
5-
import * as http from 'http'
66
import { Storage } from '../../../storage'
7+
import { FileStore, LockNotifier, PgLocker, S3Store, UploadId } from '../../../storage/tus'
8+
import { getFileSizeLimit } from '../../../storage/limits'
79
import {
810
namingFunction,
911
onCreate,
@@ -13,15 +15,10 @@ import {
1315
generateUrl,
1416
getFileIdFromRequest,
1517
} from './lifecycle'
16-
import { ServerOptions, DataStore } from '@tus/server'
17-
import { getFileSizeLimit } from '../../../storage/limits'
18-
import { UploadId } from './upload-id'
19-
import { FileStore } from './file-store'
2018
import { TenantConnection } from '../../../database/connection'
21-
import { PgLocker, LockNotifier } from './postgres-locker'
2219
import { PubSub } from '../../../database/pubsub'
23-
import { S3Store } from './s3-store'
2420
import { DeleteHandler } from './handlers'
21+
import { RequestCacheMetadata } from '../../../storage/tus/request-cache-metadata'
2522

2623
const {
2724
storageS3Bucket,
@@ -49,6 +46,7 @@ function createTusStore() {
4946
return new S3Store({
5047
partSize: 6 * 1024 * 1024, // Each uploaded part will have ~6MB,
5148
expirationPeriodInMilliseconds: tusUrlExpiryMs,
49+
cache: new RequestCacheMetadata(),
5250
s3ClientConfig: {
5351
bucket: storageS3Bucket,
5452
region: storageS3Region,
@@ -126,6 +124,12 @@ export default async function routes(fastify: FastifyInstance) {
126124
fastify.register(db)
127125
fastify.register(storage)
128126

127+
fastify.addHook('onRequest', (req, res, done) => {
128+
RequestCacheMetadata.localStorage.run(new Map(), () => {
129+
done()
130+
})
131+
})
132+
129133
fastify.addHook('preHandler', async (req) => {
130134
;(req.raw as MultiPartRequest).log = req.log
131135
;(req.raw as MultiPartRequest).upload = {

src/http/routes/tus/lifecycle.ts

Lines changed: 27 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
11
import http from 'http'
2-
import { isRenderableError, Storage } from '../../../storage'
2+
import { BaseLogger } from 'pino'
33
import { Metadata, Upload } from '@tus/server'
4-
import { getConfig } from '../../../config'
54
import { randomUUID } from 'crypto'
6-
import { UploadId } from './upload-id'
5+
import { isRenderableError, Storage, StorageBackendError } from '../../../storage'
6+
import { getConfig } from '../../../config'
77
import { Uploader } from '../../../storage/uploader'
88
import { TenantConnection } from '../../../database/connection'
9-
import { BaseLogger } from 'pino'
9+
import { UploadId } from '../../../storage/tus'
1010

1111
const { storageS3Bucket, tusPath } = getConfig()
1212
const reExtractFileID = /([^/]+)\/?$/
@@ -22,6 +22,9 @@ export type MultiPartRequest = http.IncomingMessage & {
2222
}
2323
}
2424

25+
/**
26+
* Runs on every TUS incoming request
27+
*/
2528
export async function onIncomingRequest(
2629
rawReq: http.IncomingMessage,
2730
res: http.ServerResponse,
@@ -51,6 +54,9 @@ export async function onIncomingRequest(
5154
})
5255
}
5356

57+
/**
58+
* Generate URL for TUS upload, it encodes the uploadID to base64url
59+
*/
5460
export function generateUrl(
5561
_: http.IncomingMessage,
5662
{
@@ -67,6 +73,9 @@ export function generateUrl(
6773
return `${proto}://${host}${path}/${id}`
6874
}
6975

76+
/**
77+
* Extract the uploadId from the request and decodes it from base64url
78+
*/
7079
export function getFileIdFromRequest(rawRwq: http.IncomingMessage) {
7180
const req = rawRwq as MultiPartRequest
7281
const match = reExtractFileID.exec(req.url as string)
@@ -79,6 +88,9 @@ export function getFileIdFromRequest(rawRwq: http.IncomingMessage) {
7988
return req.upload.tenantId + '/' + idMatch
8089
}
8190

91+
/**
92+
* Generate the uploadId for the TUS upload
93+
*/
8294
export function namingFunction(rawReq: http.IncomingMessage) {
8395
const req = rawReq as MultiPartRequest
8496

@@ -89,7 +101,7 @@ export function namingFunction(rawReq: http.IncomingMessage) {
89101
const metadataHeader = req.headers['upload-metadata']
90102

91103
if (typeof metadataHeader !== 'string') {
92-
throw new Error('no metadata')
104+
throw new StorageBackendError('metadata_header_invalid', 400, 'metadata header invalid')
93105
}
94106

95107
try {
@@ -108,6 +120,9 @@ export function namingFunction(rawReq: http.IncomingMessage) {
108120
}
109121
}
110122

123+
/**
124+
* Runs before the upload URL is created
125+
*/
111126
export async function onCreate(
112127
rawReq: http.IncomingMessage,
113128
res: http.ServerResponse,
@@ -137,6 +152,9 @@ export async function onCreate(
137152
return res
138153
}
139154

155+
/**
156+
* Runs when the upload to the underline store is completed
157+
*/
140158
export async function onUploadFinish(
141159
rawReq: http.IncomingMessage,
142160
res: http.ServerResponse,
@@ -178,11 +196,15 @@ export async function onUploadFinish(
178196

179197
type TusError = { status_code: number; body: string }
180198

199+
/**
200+
* Runs when there is an error on the TUS upload
201+
*/
181202
export function onResponseError(
182203
req: http.IncomingMessage,
183204
res: http.ServerResponse,
184205
e: TusError | Error
185206
) {
207+
console.log(e)
186208
if (e instanceof Error) {
187209
;(res as any).executionError = e
188210
}

src/http/routes/tus/s3-store.ts

Lines changed: 0 additions & 103 deletions
This file was deleted.

src/http/routes/tus/file-store.ts renamed to src/storage/tus/file-store.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,8 @@ import { FileStore as TusFileStore } from '@tus/file-store'
22
import { Upload } from '@tus/server'
33
import fsExtra from 'fs-extra'
44
import path from 'path'
5-
import { FileBackend } from '../../../storage/backend'
65
import { Configstore } from '@tus/file-store'
6+
import { FileBackend } from '../backend'
77

88
type FileStoreOptions = {
99
directory: string

src/storage/tus/index.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
export * from './file-store'
2+
export * from './s3-store'
3+
export * from './postgres-locker'
4+
export * from './upload-id'

src/http/routes/tus/postgres-locker.ts renamed to src/storage/tus/postgres-locker.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
import { Lock, Locker, RequestRelease } from '@tus/server'
2-
import { Database, DBError } from '../../../storage/database'
3-
import { PubSubAdapter } from '../../../pubsub'
4-
import { UploadId } from './upload-id'
52
import { clearTimeout } from 'timers'
63
import EventEmitter from 'events'
4+
import { Database, DBError } from '../database'
5+
import { PubSubAdapter } from '../../pubsub'
6+
import { UploadId } from './upload-id'
77

88
const REQUEST_LOCK_RELEASE_MESSAGE = 'REQUEST_LOCK_RELEASE'
99

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
import { AsyncLocalStorage } from 'async_hooks'
2+
import { MetadataCache } from './s3-store'
3+
import { Upload } from '@tus/server'
4+
5+
type MetadataValue = {
6+
file: Upload
7+
'upload-id': string
8+
'tus-version': string
9+
}
10+
11+
export class RequestCacheMetadata implements MetadataCache {
12+
static localStorage = new AsyncLocalStorage<Map<string, MetadataValue>>()
13+
14+
delete(value: string): Promise<void> | void {
15+
RequestCacheMetadata.localStorage.getStore()?.delete(value)
16+
}
17+
18+
get(value: string): Promise<MetadataValue | void> | MetadataValue | void {
19+
return RequestCacheMetadata.localStorage.getStore()?.get(value)
20+
}
21+
22+
set(key: string, value: MetadataValue): Promise<void> | void {
23+
RequestCacheMetadata.localStorage.getStore()?.set(key, value)
24+
}
25+
}

0 commit comments

Comments
 (0)