Skip to content

Commit

Permalink
feat: s3 store expiration extension
Browse files Browse the repository at this point in the history
  • Loading branch information
fenos committed Nov 14, 2023
1 parent 612ac24 commit 48dee94
Showing 1 changed file with 28 additions and 2 deletions.
30 changes: 28 additions & 2 deletions packages/s3-store/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ type Options = {
// The server calculates the optimal part size, which takes this size into account,
// but may increase it to not exceed the S3 10K parts limit.
partSize?: number
expirationPeriodInMilliseconds?: number
// Options to pass to the AWS S3 SDK.
s3ClientConfig: S3ClientConfig & {bucket: string}
}
Expand All @@ -30,6 +31,7 @@ type MetadataValue = {
file: Upload
'upload-id': string
'tus-version': string
'created-at': string
}
// Implementation (based on https://github.com/tus/tusd/blob/master/s3store/s3store.go)
//
Expand Down Expand Up @@ -69,6 +71,7 @@ export class S3Store extends DataStore {
private cache: Map<string, MetadataValue> = new Map()
private client: S3
private preferredPartSize: number
private expirationPeriodInMilliseconds = 0
public maxMultipartParts = 10_000 as const
public minPartSize = 5_242_880 as const // 5MiB
public maxUploadSize = 5_497_558_138_880 as const // 5TiB
Expand All @@ -82,9 +85,11 @@ export class S3Store extends DataStore {
'creation-with-upload',
'creation-defer-length',
'termination',
'expiration',
]
this.bucket = bucket
this.preferredPartSize = partSize || 8 * 1024 * 1024
this.expirationPeriodInMilliseconds = options.expirationPeriodInMilliseconds ?? 0
this.client = new S3(restS3ClientConfig)
}

Expand All @@ -103,6 +108,7 @@ export class S3Store extends DataStore {
Metadata: {
'upload-id': uploadId,
'tus-version': TUS_RESUMABLE,
'created-at': upload.creation_date || new Date().toISOString(),
},
})
log(`[${upload.id}] metadata file saved`)
Expand All @@ -127,11 +133,13 @@ export class S3Store extends DataStore {
this.cache.set(id, {
'tus-version': Metadata?.['tus-version'] as string,
'upload-id': Metadata?.['upload-id'] as string,
'created-at': Metadata?.['created-at'] as string,
file: new Upload({
id,
size: file.size ? Number.parseInt(file.size, 10) : undefined,
offset: Number.parseInt(file.offset, 10),
metadata: file.metadata,
creation_date: Metadata?.['created-at'] as string,
}),
})
return this.cache.get(id) as MetadataValue
Expand Down Expand Up @@ -167,12 +175,14 @@ export class S3Store extends DataStore {

private async uploadIncompletePart(
id: string,
readStream: fs.ReadStream | Readable
readStream: fs.ReadStream | Readable,
expires?: Date
): Promise<string> {
const data = await this.client.putObject({
Bucket: this.bucket,
Key: this.partKey(id, true),
Body: readStream,
Expires: expires,
})
log(`[${id}] finished uploading incomplete part`)
return data.ETag as string
Expand Down Expand Up @@ -314,7 +324,13 @@ export class S3Store extends DataStore {
if (partSize + incompletePartSize >= this.minPartSize || isFinalPart) {
await this.uploadPart(metadata, readable, partNumber)
} else {
await this.uploadIncompletePart(metadata.file.id, readable)
await this.uploadIncompletePart(
metadata.file.id,
readable,
metadata.file.creation_date
? this.getExpirationDate(metadata.file.creation_date)
: undefined
)
}

bytesUploaded += partSize
Expand Down Expand Up @@ -588,4 +604,14 @@ export class S3Store extends DataStore {

this.clearCache(id)
}

protected getExpirationDate(created_at: string) {
const date = new Date(created_at)

return new Date(date.getTime() + this.getExpiration())
}

getExpiration(): number {
return this.expirationPeriodInMilliseconds
}
}

0 comments on commit 48dee94

Please sign in to comment.