From 5fd6038cd58f2dc99f06c58da6a6152acc1579e3 Mon Sep 17 00:00:00 2001 From: Murderlon Date: Tue, 11 Feb 2025 15:28:16 +0100 Subject: [PATCH 1/6] Bump required Node.js version from 16 to 20 --- package-lock.json | 23 +++++++++++------------ packages/azure-store/package.json | 2 +- packages/file-store/package.json | 2 +- packages/gcs-store/package.json | 2 +- packages/s3-store/package.json | 2 +- packages/server/package.json | 2 +- 6 files changed, 16 insertions(+), 17 deletions(-) diff --git a/package-lock.json b/package-lock.json index 4c5a489c..8ccb1f55 100644 --- a/package-lock.json +++ b/package-lock.json @@ -7,7 +7,6 @@ "": { "workspaces": [ "packages/*", - "demo", "test" ], "devDependencies": { @@ -6109,7 +6108,7 @@ "should": "^13.2.3" }, "engines": { - "node": ">=16" + "node": ">=20" } }, "packages/eslint-config-custom": { @@ -6145,7 +6144,7 @@ "should": "^13.2.3" }, "engines": { - "node": ">=16" + "node": ">=20" }, "optionalDependencies": { "@redis/client": "^1.6.0" @@ -6153,7 +6152,7 @@ }, "packages/gcs-store": { "name": "@tus/gcs-store", - "version": "1.4.1", + "version": "1.4.2", "license": "MIT", "dependencies": { "@tus/utils": "^0.5.0", @@ -6169,7 +6168,7 @@ "should": "^13.2.3" }, "engines": { - "node": ">=16" + "node": ">=20" }, "peerDependencies": { "@google-cloud/storage": "*" @@ -6177,7 +6176,7 @@ }, "packages/s3-store": { "name": "@tus/s3-store", - "version": "1.7.0", + "version": "1.9.0", "license": "MIT", "dependencies": { "@aws-sdk/client-s3": "^3.717.0", @@ -6195,12 +6194,12 @@ "should": "^13.2.3" }, "engines": { - "node": ">=16" + "node": ">=20" } }, "packages/server": { "name": "@tus/server", - "version": "1.10.1", + "version": "1.10.2", "license": "MIT", "dependencies": { "@tus/utils": "^0.5.1", @@ -6222,7 +6221,7 @@ "ts-node": "^10.9.2" }, "engines": { - "node": ">=16" + "node": ">=20" }, "optionalDependencies": { "@redis/client": "^1.6.0", @@ -6249,9 +6248,9 @@ "test": { "dependencies": { "@tus/file-store": "^1.5.1", - "@tus/gcs-store": "^1.4.1", - "@tus/s3-store": "^1.7.0", - "@tus/server": "^1.10.1" + "@tus/gcs-store": "^1.4.2", + "@tus/s3-store": "^1.9.0", + "@tus/server": "^1.10.2" }, "devDependencies": { "@types/mocha": "^10.0.6", diff --git a/packages/azure-store/package.json b/packages/azure-store/package.json index b43211e1..f0caafcd 100644 --- a/packages/azure-store/package.json +++ b/packages/azure-store/package.json @@ -31,6 +31,6 @@ "should": "^13.2.3" }, "engines": { - "node": ">=16" + "node": ">=20" } } diff --git a/packages/file-store/package.json b/packages/file-store/package.json index c5448bb9..3689d554 100644 --- a/packages/file-store/package.json +++ b/packages/file-store/package.json @@ -28,6 +28,6 @@ "@redis/client": "^1.6.0" }, "engines": { - "node": ">=16" + "node": ">=20" } } diff --git a/packages/gcs-store/package.json b/packages/gcs-store/package.json index 3c268873..ae3aa429 100644 --- a/packages/gcs-store/package.json +++ b/packages/gcs-store/package.json @@ -35,6 +35,6 @@ "@google-cloud/storage": "*" }, "engines": { - "node": ">=16" + "node": ">=20" } } diff --git a/packages/s3-store/package.json b/packages/s3-store/package.json index e913379a..9c2a9728 100644 --- a/packages/s3-store/package.json +++ b/packages/s3-store/package.json @@ -34,6 +34,6 @@ "should": "^13.2.3" }, "engines": { - "node": ">=16" + "node": ">=20" } } diff --git a/packages/server/package.json b/packages/server/package.json index 2a5a14a0..773792d2 100644 --- a/packages/server/package.json +++ b/packages/server/package.json @@ -42,6 +42,6 @@ "ioredis": "^5.4.1" }, "engines": { - "node": ">=16" + "node": ">=20" } } From 4523e10899929e70076cdecab8e0277036218dd5 Mon Sep 17 00:00:00 2001 From: Murderlon Date: Wed, 12 Feb 2025 10:50:13 +0100 Subject: [PATCH 2/6] Update readme's, CI, and tsconfig --- .github/workflows/ci.yml | 4 +- README.md | 21 ++- packages/azure-store/README.md | 31 ++--- packages/file-store/README.md | 35 +++-- packages/gcs-store/README.md | 21 ++- packages/s3-store/README.md | 40 +++--- packages/server/README.md | 237 ++++++++++++++++----------------- packages/utils/package.json | 2 +- tsconfig.base.json | 2 +- 9 files changed, 187 insertions(+), 206 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index d4d5178f..2791a180 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -16,7 +16,7 @@ concurrency: ${{ github.workflow }}--${{ github.ref }} jobs: main: - name: Node.js LTS + name: Node.js 20 runs-on: ubuntu-latest steps: @@ -33,7 +33,7 @@ jobs: - name: Install Node.js uses: actions/setup-node@v3 with: - node-version: lts/* + node-version: 20 - name: Install dependencies run: npm ci --no-fund --no-audit diff --git a/README.md b/README.md index d181d493..715dad30 100644 --- a/README.md +++ b/README.md @@ -52,17 +52,17 @@ A standalone server which stores files on disk. > Try it yourself in [StackBlitz](https://stackblitz.com/edit/stackblitz-starters-zg6mgnuf?file=index.js) ```js -const {Server} = require('@tus/server') -const {FileStore} = require('@tus/file-store') +const { Server } = require("@tus/server"); +const { FileStore } = require("@tus/file-store"); -const host = '127.0.0.1' -const port = 1080 +const host = "127.0.0.1"; +const port = 1080; const server = new Server({ - path: '/files', - datastore: new FileStore({directory: './files'}), -}) + path: "/files", + datastore: new FileStore({ directory: "./files" }), +}); -server.listen({host, port}) +server.listen({ host, port }); ``` A tus server integrated into your existing Node.js server. `@tus/server` has no @@ -124,7 +124,7 @@ All packages are fully typed with TypeScript. ## Compatibility -All packages require Node.js 16.0+. +All packages require Node.js 20+. ## Contribute @@ -144,8 +144,7 @@ See [`@tus/azure-store`]: https://github.com/tus/tus-node-server/tree/main/packages/azure-store [extensions]: https://tus.io/protocols/resumable-upload.html#protocol-extensions [creation]: https://tus.io/protocols/resumable-upload.html#creation -[creation with upload]: - https://tus.io/protocols/resumable-upload.html#creation-with-upload +[creation with upload]: https://tus.io/protocols/resumable-upload.html#creation-with-upload [expiration]: https://tus.io/protocols/resumable-upload.html#expiration [checksum]: https://tus.io/protocols/resumable-upload.html#checksum [termination]: https://tus.io/protocols/resumable-upload.html#termination diff --git a/packages/azure-store/README.md b/packages/azure-store/README.md index 853b88de..076fd34a 100644 --- a/packages/azure-store/README.md +++ b/packages/azure-store/README.md @@ -16,7 +16,7 @@ Azure Store based on the Append Blob Client [Azure Blob AppendBlobClient](https: ## Install -In Node.js (16.0+), install with npm: +In Node.js (20+), install with npm: ```bash npm install @tus/azure-store @@ -25,17 +25,17 @@ npm install @tus/azure-store ## Use ```js -const {Server} = require('@tus/server') -const {AzureStore} = require('@tus/azure-store') +const { Server } = require("@tus/server"); +const { AzureStore } = require("@tus/azure-store"); const server = new Server({ - path: '/files', + path: "/files", datastore: new AzureStore({ - account: process.env.AZURE_ACCOUNT_ID, - accountKey: process.env.AZURE_ACCOUNT_KEY, - containerName: process.env.AZURE_CONTAINER_NAME, + account: process.env.AZURE_ACCOUNT_ID, + accountKey: process.env.AZURE_ACCOUNT_KEY, + containerName: process.env.AZURE_CONTAINER_NAME, }), -}) +}); // ... ``` @@ -84,7 +84,7 @@ This package is fully typed with TypeScript. ## Compatibility -This package requires Node.js 16.0+. +This package requires Node.js 20+. ## Contribute @@ -98,17 +98,12 @@ See [extensions]: https://tus.io/protocols/resumable-upload.html#protocol-extensions [creation]: https://tus.io/protocols/resumable-upload.html#creation -[creation with upload]: - https://tus.io/protocols/resumable-upload.html#creation-with-upload +[creation with upload]: https://tus.io/protocols/resumable-upload.html#creation-with-upload [expiration]: https://tus.io/protocols/resumable-upload.html#expiration [checksum]: https://tus.io/protocols/resumable-upload.html#checksum [termination]: https://tus.io/protocols/resumable-upload.html#termination [concatenation]: https://tus.io/protocols/resumable-upload.html#concatenation -[`cleanUpExpiredUploads`]: - https://github.com/tus/tus-node-server/tree/main/packages/server#cleanupexpireduploads +[`cleanUpExpiredUploads`]: https://github.com/tus/tus-node-server/tree/main/packages/server#cleanupexpireduploads [kvstores]: https://github.com/tus/tus-node-server/tree/main/packages/server#kvstores -[`KvStore`]: - https://github.com/tus/tus-node-server/blob/main/packages/utils/src/kvstores/Types.ts - -[`MemoryKvStore`]: - https://github.com/tus/tus-node-server/blob/main/packages/utils/src/kvstores/MemoryKvStore.ts +[`KvStore`]: https://github.com/tus/tus-node-server/blob/main/packages/utils/src/kvstores/Types.ts +[`MemoryKvStore`]: https://github.com/tus/tus-node-server/blob/main/packages/utils/src/kvstores/MemoryKvStore.ts diff --git a/packages/file-store/README.md b/packages/file-store/README.md index 21a3a596..a1485449 100644 --- a/packages/file-store/README.md +++ b/packages/file-store/README.md @@ -20,7 +20,7 @@ ## Install -In Node.js (16.0+), install with npm: +In Node.js (20+), install with npm: ```bash npm install @tus/file-store @@ -29,13 +29,13 @@ npm install @tus/file-store ## Use ```js -const {Server} = require('@tus/server') -const {FileStore} = require('@tus/file-store') +const { Server } = require("@tus/server"); +const { FileStore } = require("@tus/file-store"); const server = new Server({ - path: '/files', - datastore: new FileStore({directory: './some/path'}), -}) + path: "/files", + datastore: new FileStore({ directory: "./some/path" }), +}); // ... ``` @@ -87,25 +87,25 @@ For demonstration purposes we will create a memory config store, but that's not idea. It's written in TypeScript. ```ts -import type {Upload} from '@tus/server' +import type { Upload } from "@tus/server"; export class MemoryConfigstore { - data: Map = new Map() + data: Map = new Map(); get(key: string): Upload | undefined { - return this.data.get(key) + return this.data.get(key); } set(key: string, value: Upload) { - this.data.set(key, value) + this.data.set(key, value); } delete(key: string) { - return this.data.delete(key) + return this.data.delete(key); } get list(): Record { - return Object.fromEntries(this.data.entries()) + return Object.fromEntries(this.data.entries()); } } ``` @@ -124,7 +124,7 @@ This package is fully typed with TypeScript. ## Compatibility -This package requires Node.js 16.0+. +This package requires Node.js 20+. ## Contribute @@ -138,14 +138,11 @@ See [extensions]: https://tus.io/protocols/resumable-upload.html#protocol-extensions [creation]: https://tus.io/protocols/resumable-upload.html#creation -[creation with upload]: - https://tus.io/protocols/resumable-upload.html#creation-with-upload +[creation with upload]: https://tus.io/protocols/resumable-upload.html#creation-with-upload [expiration]: https://tus.io/protocols/resumable-upload.html#expiration [checksum]: https://tus.io/protocols/resumable-upload.html#checksum [termination]: https://tus.io/protocols/resumable-upload.html#termination [concatenation]: https://tus.io/protocols/resumable-upload.html#concatenation -[`cleanUpExpiredUploads`]: - https://github.com/tus/tus-node-server/tree/main/packages/server#cleanupexpireduploads +[`cleanUpExpiredUploads`]: https://github.com/tus/tus-node-server/tree/main/packages/server#cleanupexpireduploads [kvstores]: https://github.com/tus/tus-node-server/tree/main/packages/server#kvstores -[`KvStore`]: - https://github.com/tus/tus-node-server/blob/main/packages/utils/src/kvstores/Types.ts +[`KvStore`]: https://github.com/tus/tus-node-server/blob/main/packages/utils/src/kvstores/Types.ts diff --git a/packages/gcs-store/README.md b/packages/gcs-store/README.md index 55ace76f..5af750b9 100644 --- a/packages/gcs-store/README.md +++ b/packages/gcs-store/README.md @@ -17,7 +17,7 @@ ## Install -In Node.js (16.0+), install with npm: +In Node.js (20+), install with npm: ```bash npm install @tus/gcs-store @@ -26,19 +26,19 @@ npm install @tus/gcs-store ## Use ```js -const {Server} = require('@tus/server') -const {GCSStore} = require('@tus/gcs-store') +const { Server } = require("@tus/server"); +const { GCSStore } = require("@tus/gcs-store"); -const {Storage} = require('@google-cloud/storage') +const { Storage } = require("@google-cloud/storage"); -const storage = new Storage({keyFilename: 'key.json'}) +const storage = new Storage({ keyFilename: "key.json" }); const server = new Server({ - path: '/files', + path: "/files", datastore: new GCSStore({ - bucket: storage.bucket('tus-node-server-ci'), + bucket: storage.bucket("tus-node-server-ci"), }), -}) +}); // ... ``` @@ -74,7 +74,7 @@ This package is fully typed with TypeScript. ## Compatibility -This package requires Node.js 16.0+. +This package requires Node.js 20+. ## Contribute @@ -88,8 +88,7 @@ See [extensions]: https://tus.io/protocols/resumable-upload.html#protocol-extensions [creation]: https://tus.io/protocols/resumable-upload.html#creation -[creation with upload]: - https://tus.io/protocols/resumable-upload.html#creation-with-upload +[creation with upload]: https://tus.io/protocols/resumable-upload.html#creation-with-upload [expiration]: https://tus.io/protocols/resumable-upload.html#expiration [checksum]: https://tus.io/protocols/resumable-upload.html#checksum [termination]: https://tus.io/protocols/resumable-upload.html#termination diff --git a/packages/s3-store/README.md b/packages/s3-store/README.md index d2f30883..db465c4b 100644 --- a/packages/s3-store/README.md +++ b/packages/s3-store/README.md @@ -22,7 +22,7 @@ ## Install -In Node.js (16.0+), install with npm: +In Node.js (20+), install with npm: ```bash npm install @tus/s3-store @@ -31,8 +31,8 @@ npm install @tus/s3-store ## Use ```js -const {Server} = require('@tus/server') -const {S3Store} = require('@tus/s3-store') +const { Server } = require("@tus/server"); +const { S3Store } = require("@tus/s3-store"); const s3Store = new S3Store({ partSize: 8 * 1024 * 1024, // Each uploaded part will have ~8MiB, @@ -44,8 +44,8 @@ const s3Store = new S3Store({ secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY, }, }, -}) -const server = new Server({path: '/files', datastore: s3Store}) +}); +const server = new Server({ path: "/files", datastore: s3Store }); // ... ``` @@ -199,9 +199,9 @@ docs for the supported values of [credentials](https://docs.aws.amazon.com/AWSJavaScriptSDK/latest/AWS/Credentials.html#constructor-property) ```js -const aws = require('aws-sdk') -const {Server} = require('@tus/server') -const {S3Store} = require('@tus/s3-store') +const aws = require("aws-sdk"); +const { Server } = require("@tus/server"); +const { S3Store } = require("@tus/s3-store"); const s3Store = new S3Store({ partSize: 8 * 1024 * 1024, @@ -209,12 +209,12 @@ const s3Store = new S3Store({ bucket: process.env.AWS_BUCKET, region: process.env.AWS_REGION, credentials: new aws.ECSCredentials({ - httpOptions: {timeout: 5000}, + httpOptions: { timeout: 5000 }, maxRetries: 10, }), }, -}) -const server = new Server({path: '/files', datastore: s3Store}) +}); +const server = new Server({ path: "/files", datastore: s3Store }); // ... ``` @@ -231,7 +231,7 @@ const s3Store = new S3Store({ partSize: 8 * 1024 * 1024, minPartSize: 8 * 1024 * 1024, // ... -}) +}); ``` ### Example: use with Scaleway Object Storage @@ -242,7 +242,7 @@ const s3Store = new S3Store({ const s3Store = new S3Store({ maxMultipartParts: 1000, // ... -}) +}); ``` ## Types @@ -251,7 +251,7 @@ This package is fully typed with TypeScript. ## Compatibility -This package requires Node.js 16.0+. +This package requires Node.js 20+. ## Contribute @@ -265,16 +265,12 @@ See [extensions]: https://tus.io/protocols/resumable-upload.html#protocol-extensions [creation]: https://tus.io/protocols/resumable-upload.html#creation -[creation with upload]: - https://tus.io/protocols/resumable-upload.html#creation-with-upload +[creation with upload]: https://tus.io/protocols/resumable-upload.html#creation-with-upload [expiration]: https://tus.io/protocols/resumable-upload.html#expiration [checksum]: https://tus.io/protocols/resumable-upload.html#checksum [termination]: https://tus.io/protocols/resumable-upload.html#termination [concatenation]: https://tus.io/protocols/resumable-upload.html#concatenation -[cleanExpiredUploads]: - https://github.com/tus/tus-node-server/tree/main/packages/server#servercleanupexpireduploads -[lifecyle]: - https://docs.aws.amazon.com/AmazonS3/latest/userguide/object-lifecycle-mgmt.html +[cleanExpiredUploads]: https://github.com/tus/tus-node-server/tree/main/packages/server#servercleanupexpireduploads +[lifecyle]: https://docs.aws.amazon.com/AmazonS3/latest/userguide/object-lifecycle-mgmt.html [kvstores]: https://github.com/tus/tus-node-server/tree/main/packages/server#kvstores -[`KvStore`]: - https://github.com/tus/tus-node-server/blob/main/packages/utils/src/kvstores/Types.ts +[`KvStore`]: https://github.com/tus/tus-node-server/blob/main/packages/utils/src/kvstores/Types.ts diff --git a/packages/server/README.md b/packages/server/README.md index a49d202a..334e2b29 100644 --- a/packages/server/README.md +++ b/packages/server/README.md @@ -26,7 +26,7 @@ ## Install -In Node.js (16.0+), install with npm: +In Node.js (20+), install with npm: ```bash npm install @tus/server @@ -37,16 +37,16 @@ npm install @tus/server A standalone server which stores files on disk. ```js -const {Server} = require('@tus/server') -const {FileStore} = require('@tus/file-store') -const host = '127.0.0.1' -const port = 1080 +const { Server } = require("@tus/server"); +const { FileStore } = require("@tus/file-store"); +const host = "127.0.0.1"; +const port = 1080; const server = new Server({ - path: '/files', - datastore: new FileStore({directory: './files'}), -}) -server.listen({host, port}) + path: "/files", + datastore: new FileStore({ directory: "./files" }), +}); +server.listen({ host, port }); ``` ## API @@ -311,54 +311,54 @@ can also be used as a cache in other stores, such as `@tus/s3-store`. #### `MemoryKvStore` ```ts -import {MemoryKvStore} from '@tus/server' -import S3Store, {type MetadataValue} from '@tus/s3-store' +import { MemoryKvStore } from "@tus/server"; +import S3Store, { type MetadataValue } from "@tus/s3-store"; new S3Store({ // ... cache: new MemoryKvStore(), -}) +}); ``` #### `FileKvStore` ```ts -import {FileKvStore} from '@tus/server' -import S3Store, {type MetadataValue} from '@tus/s3-store' +import { FileKvStore } from "@tus/server"; +import S3Store, { type MetadataValue } from "@tus/s3-store"; -const path = './uploads' +const path = "./uploads"; new S3Store({ // ... cache: new FileKvStore(path), -}) +}); ``` #### `RedisKvStore` ```ts -import {RedisKvStore} from '@tus/server' -import S3Store, {type MetadataValue} from '@tus/s3-store' -import {createClient} from '@redis/client' +import { RedisKvStore } from "@tus/server"; +import S3Store, { type MetadataValue } from "@tus/s3-store"; +import { createClient } from "@redis/client"; -const client = await createClient().connect() -const prefix = 'foo' // prefix for the key (foo${id}) +const client = await createClient().connect(); +const prefix = "foo"; // prefix for the key (foo${id}) new S3Store({ // ... cache: new RedisKvStore(client, prefix), -}) +}); ``` #### `IoRedisKvStore` ```ts -import { IoRedisKvStore } from '@tus/server'; -import S3Store, { type MetadataValue } from '@tus/s3-store'; -import Redis from 'ioredis'; +import { IoRedisKvStore } from "@tus/server"; +import S3Store, { type MetadataValue } from "@tus/s3-store"; +import Redis from "ioredis"; const client = new Redis(); -const prefix = 'foo'; // prefix for the key (foo${id}) +const prefix = "foo"; // prefix for the key (foo${id}) new S3Store({ // ... @@ -371,66 +371,66 @@ new S3Store({ ### Example: integrate tus into Express ```js -const {Server} = require('@tus/server') -const {FileStore} = require('@tus/file-store') -const express = require('express') - -const host = '127.0.0.1' -const port = 1080 -const app = express() -const uploadApp = express() +const { Server } = require("@tus/server"); +const { FileStore } = require("@tus/file-store"); +const express = require("express"); + +const host = "127.0.0.1"; +const port = 1080; +const app = express(); +const uploadApp = express(); const server = new Server({ - path: '/uploads', - datastore: new FileStore({directory: '/files'}), -}) + path: "/uploads", + datastore: new FileStore({ directory: "/files" }), +}); -uploadApp.all('*', server.handle.bind(server)) -app.use('/uploads', uploadApp) -app.listen(port, host) +uploadApp.all("*", server.handle.bind(server)); +app.use("/uploads", uploadApp); +app.listen(port, host); ``` ### Example: integrate tus into Koa ```js -const http = require('node:http') -const url = require('node:url') -const Koa = require('koa') -const {Server} = require('@tus/server') -const {FileStore} = require('@tus/file-store') - -const app = new Koa() -const appCallback = app.callback() -const port = 1080 +const http = require("node:http"); +const url = require("node:url"); +const Koa = require("koa"); +const { Server } = require("@tus/server"); +const { FileStore } = require("@tus/file-store"); + +const app = new Koa(); +const appCallback = app.callback(); +const port = 1080; const tusServer = new Server({ - path: '/files', - datastore: new FileStore({directory: '/files'}), -}) + path: "/files", + datastore: new FileStore({ directory: "/files" }), +}); const server = http.createServer((req, res) => { - const urlPath = url.parse(req.url).pathname + const urlPath = url.parse(req.url).pathname; // handle any requests with the `/files/*` pattern if (/^\/files\/.+/.test(urlPath.toLowerCase())) { - return tusServer.handle(req, res) + return tusServer.handle(req, res); } - appCallback(req, res) -}) + appCallback(req, res); +}); -server.listen(port) +server.listen(port); ``` ### Example: integrate tus into Fastify ```js -const fastify = require('fastify')({logger: true}) -const {Server} = require('@tus/server') -const {FileStore} = require('@tus/file-store') +const fastify = require("fastify")({ logger: true }); +const { Server } = require("@tus/server"); +const { FileStore } = require("@tus/file-store"); const tusServer = new Server({ - path: '/files', - datastore: new FileStore({directory: './files'}), -}) + path: "/files", + datastore: new FileStore({ directory: "./files" }), +}); /** * add new content-type to fastify forewards request @@ -438,9 +438,9 @@ const tusServer = new Server({ * @see https://www.fastify.io/docs/latest/Reference/ContentTypeParser/ */ fastify.addContentTypeParser( - 'application/offset+octet-stream', + "application/offset+octet-stream", (request, payload, done) => done(null) -) +); /** * let tus handle preparation and filehandling requests @@ -448,18 +448,18 @@ fastify.addContentTypeParser( * @see https://www.fastify.io/docs/latest/Reference/Request/ * @see https://www.fastify.io/docs/latest/Reference/Reply/#raw */ -fastify.all('/files', (req, res) => { - tusServer.handle(req.raw, res.raw) -}) -fastify.all('/files/*', (req, res) => { - tusServer.handle(req.raw, res.raw) -}) +fastify.all("/files", (req, res) => { + tusServer.handle(req.raw, res.raw); +}); +fastify.all("/files/*", (req, res) => { + tusServer.handle(req.raw, res.raw); +}); fastify.listen(3000, (err) => { if (err) { - fastify.log.error(err) - process.exit(1) + fastify.log.error(err); + process.exit(1); } -}) +}); ``` ### Example: integrate tus into Next.js @@ -470,9 +470,9 @@ Attach the tus server handler to a Next.js route handler in an `/pages/api/upload/[[...file]].ts` ```ts -import type {NextApiRequest, NextApiResponse} from 'next' -import {Server, Upload} from '@tus/server' -import {FileStore} from '@tus/file-store' +import type { NextApiRequest, NextApiResponse } from "next"; +import { Server, Upload } from "@tus/server"; +import { FileStore } from "@tus/file-store"; /** * !Important. This will tell Next.js NOT Parse the body as tus requires @@ -482,40 +482,40 @@ export const config = { api: { bodyParser: false, }, -} +}; const tusServer = new Server({ // `path` needs to match the route declared by the next file router // ie /api/upload - path: '/api/upload', - datastore: new FileStore({directory: './files'}), -}) + path: "/api/upload", + datastore: new FileStore({ directory: "./files" }), +}); export default function handler(req: NextApiRequest, res: NextApiResponse) { - return tusServer.handle(req, res) + return tusServer.handle(req, res); } ``` ### Example: validate metadata when an upload is created ```js -const {Server} = require('@tus/server') +const { Server } = require("@tus/server"); // ... const server = new Server({ // .. async onUploadCreate(req, res, upload) { - const {ok, expected, received} = validateMetadata(upload) // your logic + const { ok, expected, received } = validateMetadata(upload); // your logic if (!ok) { - const body = `Expected "${expected}" in "Upload-Metadata" but received "${received}"` - throw {status_code: 500, body} // if undefined, falls back to 500 with "Internal server error". + const body = `Expected "${expected}" in "Upload-Metadata" but received "${received}"`; + throw { status_code: 500, body }; // if undefined, falls back to 500 with "Internal server error". } // You can optionally return metadata to override the upload metadata, // such as `{ storagePath: "/upload/123abc..." }` - const extraMeta = getExtraMetadata(req) // your logic - return {res, metadata: {...upload.metadata, ...extraMeta}} + const extraMeta = getExtraMetadata(req); // your logic + return { res, metadata: { ...upload.metadata, ...extraMeta } }; }, -}) +}); ``` ### Example: access control @@ -524,30 +524,30 @@ Access control is opinionated and can be done in different ways. This example is psuedo-code for what it could look like with JSON Web Tokens. ```js -const {Server} = require('@tus/server') +const { Server } = require("@tus/server"); // ... const server = new Server({ // .. async onIncomingRequest(req, res) { - const token = req.headers.authorization + const token = req.headers.authorization; if (!token) { - throw {status_code: 401, body: 'Unauthorized'} + throw { status_code: 401, body: "Unauthorized" }; } try { - const decodedToken = await jwt.verify(token, 'your_secret_key') - req.user = decodedToken + const decodedToken = await jwt.verify(token, "your_secret_key"); + req.user = decodedToken; } catch (error) { - throw {status_code: 401, body: 'Invalid token'} + throw { status_code: 401, body: "Invalid token" }; } - if (req.user.role !== 'admin') { - throw {status_code: 403, body: 'Access denied'} + if (req.user.role !== "admin") { + throw { status_code: 403, body: "Access denied" }; } }, -}) +}); ``` ### Example: store files in custom nested directories @@ -560,26 +560,26 @@ Adding a slash means you create a new directory, for which you need to implement functions as we need encode the id with base64 into the URL. ```js -const path = '/files' +const path = "/files"; const server = new Server({ path, - datastore: new FileStore({directory: './test/output'}), + datastore: new FileStore({ directory: "./test/output" }), namingFunction(req) { - const id = crypto.randomBytes(16).toString('hex') - const folder = getFolderForUser(req) // your custom logic - return `users/${folder}/${id}` + const id = crypto.randomBytes(16).toString("hex"); + const folder = getFolderForUser(req); // your custom logic + return `users/${folder}/${id}`; }, - generateUrl(req, {proto, host, path, id}) { - id = Buffer.from(id, 'utf-8').toString('base64url') - return `${proto}://${host}${path}/${id}` + generateUrl(req, { proto, host, path, id }) { + id = Buffer.from(id, "utf-8").toString("base64url"); + return `${proto}://${host}${path}/${id}`; }, 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') + return Buffer.from(lastPath, "base64url").toString("utf-8"); }, -}) +}); ``` ### Example: use with Nginx @@ -592,13 +592,13 @@ Firstly, you must set `respectForwardedHeaders` indicating that a reverse proxy and that it should respect the `X-Forwarded-*`/`Forwarded` headers: ```js -const {Server} = require('@tus/server') +const { Server } = require("@tus/server"); // ... const server = new Server({ // .. respectForwardedHeaders: true, -}) +}); ``` Secondly, some of the reverse proxy's settings should be adjusted. The exact steps depend @@ -631,7 +631,7 @@ This package is fully typed with TypeScript. ## Compatibility -This package requires Node.js 16.0+. +This package requires Node.js 20+. ## Contribute @@ -646,15 +646,10 @@ See [`@tus/file-store`]: https://github.com/tus/tus-node-server/tree/main/packages/file-store [`@tus/s3-store`]: https://github.com/tus/tus-node-server/tree/main/packages/s3-store [`@tus/gcs-store`]: https://github.com/tus/tus-node-server/tree/main/packages/gcs-store -[`constants`]: - https://github.com/tus/tus-node-server/blob/main/packages/utils/src/constants.ts +[`constants`]: https://github.com/tus/tus-node-server/blob/main/packages/utils/src/constants.ts [`types`]: https://github.com/tus/tus-node-server/blob/main/packages/server/src/types.ts -[`models`]: - https://github.com/tus/tus-node-server/blob/main/packages/utils/src/models/index.ts -[`kvstores`]: - https://github.com/tus/tus-node-server/blob/main/packages/utils/src/kvstores/index.ts +[`models`]: https://github.com/tus/tus-node-server/blob/main/packages/utils/src/models/index.ts +[`kvstores`]: https://github.com/tus/tus-node-server/blob/main/packages/utils/src/kvstores/index.ts [expiration]: https://tus.io/protocols/resumable-upload.html#expiration -[`Locker`]: - https://github.com/tus/tus-node-server/blob/main/packages/utils/src/models/Locker.ts -[`MemoryLocker`]: - https://github.com/tus/tus-node-server/blob/main/packages/server/src/lockers/MemoryLocker.ts +[`Locker`]: https://github.com/tus/tus-node-server/blob/main/packages/utils/src/models/Locker.ts +[`MemoryLocker`]: https://github.com/tus/tus-node-server/blob/main/packages/server/src/lockers/MemoryLocker.ts diff --git a/packages/utils/package.json b/packages/utils/package.json index 7d097f87..0e28bfad 100644 --- a/packages/utils/package.json +++ b/packages/utils/package.json @@ -28,6 +28,6 @@ "ts-node": "^10.9.2" }, "engines": { - "node": ">=16" + "node": ">=20" } } diff --git a/tsconfig.base.json b/tsconfig.base.json index fa960cdc..3c233fd3 100644 --- a/tsconfig.base.json +++ b/tsconfig.base.json @@ -3,7 +3,7 @@ "compilerOptions": { "composite": true, "lib": ["es2020"], - "module": "node16", + "module": "NodeNext", "target": "es2020", "strict": true, "declaration": true, From c61bb66b402ec10fb9c4a3820f217414a9c61281 Mon Sep 17 00:00:00 2001 From: Murderlon Date: Wed, 12 Feb 2025 10:57:09 +0100 Subject: [PATCH 3/6] Undo formatting --- README.md | 19 +-- packages/azure-store/README.md | 27 ++-- packages/file-store/README.md | 31 +++-- packages/gcs-store/README.md | 17 +-- packages/s3-store/README.md | 36 ++--- packages/server/README.md | 233 +++++++++++++++++---------------- 6 files changed, 191 insertions(+), 172 deletions(-) diff --git a/README.md b/README.md index 715dad30..d1a37d4d 100644 --- a/README.md +++ b/README.md @@ -52,17 +52,17 @@ A standalone server which stores files on disk. > Try it yourself in [StackBlitz](https://stackblitz.com/edit/stackblitz-starters-zg6mgnuf?file=index.js) ```js -const { Server } = require("@tus/server"); -const { FileStore } = require("@tus/file-store"); +const {Server} = require('@tus/server') +const {FileStore} = require('@tus/file-store') -const host = "127.0.0.1"; -const port = 1080; +const host = '127.0.0.1' +const port = 1080 const server = new Server({ - path: "/files", - datastore: new FileStore({ directory: "./files" }), -}); + path: '/files', + datastore: new FileStore({directory: './files'}), +}) -server.listen({ host, port }); +server.listen({host, port}) ``` A tus server integrated into your existing Node.js server. `@tus/server` has no @@ -144,7 +144,8 @@ See [`@tus/azure-store`]: https://github.com/tus/tus-node-server/tree/main/packages/azure-store [extensions]: https://tus.io/protocols/resumable-upload.html#protocol-extensions [creation]: https://tus.io/protocols/resumable-upload.html#creation -[creation with upload]: https://tus.io/protocols/resumable-upload.html#creation-with-upload +[creation with upload]: + https://tus.io/protocols/resumable-upload.html#creation-with-upload [expiration]: https://tus.io/protocols/resumable-upload.html#expiration [checksum]: https://tus.io/protocols/resumable-upload.html#checksum [termination]: https://tus.io/protocols/resumable-upload.html#termination diff --git a/packages/azure-store/README.md b/packages/azure-store/README.md index 076fd34a..dcc3f457 100644 --- a/packages/azure-store/README.md +++ b/packages/azure-store/README.md @@ -25,17 +25,17 @@ npm install @tus/azure-store ## Use ```js -const { Server } = require("@tus/server"); -const { AzureStore } = require("@tus/azure-store"); +const {Server} = require('@tus/server') +const {AzureStore} = require('@tus/azure-store') const server = new Server({ - path: "/files", + path: '/files', datastore: new AzureStore({ - account: process.env.AZURE_ACCOUNT_ID, - accountKey: process.env.AZURE_ACCOUNT_KEY, - containerName: process.env.AZURE_CONTAINER_NAME, + account: process.env.AZURE_ACCOUNT_ID, + accountKey: process.env.AZURE_ACCOUNT_KEY, + containerName: process.env.AZURE_CONTAINER_NAME, }), -}); +}) // ... ``` @@ -98,12 +98,17 @@ See [extensions]: https://tus.io/protocols/resumable-upload.html#protocol-extensions [creation]: https://tus.io/protocols/resumable-upload.html#creation -[creation with upload]: https://tus.io/protocols/resumable-upload.html#creation-with-upload +[creation with upload]: + https://tus.io/protocols/resumable-upload.html#creation-with-upload [expiration]: https://tus.io/protocols/resumable-upload.html#expiration [checksum]: https://tus.io/protocols/resumable-upload.html#checksum [termination]: https://tus.io/protocols/resumable-upload.html#termination [concatenation]: https://tus.io/protocols/resumable-upload.html#concatenation -[`cleanUpExpiredUploads`]: https://github.com/tus/tus-node-server/tree/main/packages/server#cleanupexpireduploads +[`cleanUpExpiredUploads`]: + https://github.com/tus/tus-node-server/tree/main/packages/server#cleanupexpireduploads [kvstores]: https://github.com/tus/tus-node-server/tree/main/packages/server#kvstores -[`KvStore`]: https://github.com/tus/tus-node-server/blob/main/packages/utils/src/kvstores/Types.ts -[`MemoryKvStore`]: https://github.com/tus/tus-node-server/blob/main/packages/utils/src/kvstores/MemoryKvStore.ts +[`KvStore`]: + https://github.com/tus/tus-node-server/blob/main/packages/utils/src/kvstores/Types.ts + +[`MemoryKvStore`]: + https://github.com/tus/tus-node-server/blob/main/packages/utils/src/kvstores/MemoryKvStore.ts diff --git a/packages/file-store/README.md b/packages/file-store/README.md index a1485449..f0f4eb42 100644 --- a/packages/file-store/README.md +++ b/packages/file-store/README.md @@ -29,13 +29,13 @@ npm install @tus/file-store ## Use ```js -const { Server } = require("@tus/server"); -const { FileStore } = require("@tus/file-store"); +const {Server} = require('@tus/server') +const {FileStore} = require('@tus/file-store') const server = new Server({ - path: "/files", - datastore: new FileStore({ directory: "./some/path" }), -}); + path: '/files', + datastore: new FileStore({directory: './some/path'}), +}) // ... ``` @@ -87,25 +87,25 @@ For demonstration purposes we will create a memory config store, but that's not idea. It's written in TypeScript. ```ts -import type { Upload } from "@tus/server"; +import type {Upload} from '@tus/server' export class MemoryConfigstore { - data: Map = new Map(); + data: Map = new Map() get(key: string): Upload | undefined { - return this.data.get(key); + return this.data.get(key) } set(key: string, value: Upload) { - this.data.set(key, value); + this.data.set(key, value) } delete(key: string) { - return this.data.delete(key); + return this.data.delete(key) } get list(): Record { - return Object.fromEntries(this.data.entries()); + return Object.fromEntries(this.data.entries()) } } ``` @@ -138,11 +138,14 @@ See [extensions]: https://tus.io/protocols/resumable-upload.html#protocol-extensions [creation]: https://tus.io/protocols/resumable-upload.html#creation -[creation with upload]: https://tus.io/protocols/resumable-upload.html#creation-with-upload +[creation with upload]: + https://tus.io/protocols/resumable-upload.html#creation-with-upload [expiration]: https://tus.io/protocols/resumable-upload.html#expiration [checksum]: https://tus.io/protocols/resumable-upload.html#checksum [termination]: https://tus.io/protocols/resumable-upload.html#termination [concatenation]: https://tus.io/protocols/resumable-upload.html#concatenation -[`cleanUpExpiredUploads`]: https://github.com/tus/tus-node-server/tree/main/packages/server#cleanupexpireduploads +[`cleanUpExpiredUploads`]: + https://github.com/tus/tus-node-server/tree/main/packages/server#cleanupexpireduploads [kvstores]: https://github.com/tus/tus-node-server/tree/main/packages/server#kvstores -[`KvStore`]: https://github.com/tus/tus-node-server/blob/main/packages/utils/src/kvstores/Types.ts +[`KvStore`]: + https://github.com/tus/tus-node-server/blob/main/packages/utils/src/kvstores/Types.ts diff --git a/packages/gcs-store/README.md b/packages/gcs-store/README.md index 5af750b9..f1138028 100644 --- a/packages/gcs-store/README.md +++ b/packages/gcs-store/README.md @@ -26,19 +26,19 @@ npm install @tus/gcs-store ## Use ```js -const { Server } = require("@tus/server"); -const { GCSStore } = require("@tus/gcs-store"); +const {Server} = require('@tus/server') +const {GCSStore} = require('@tus/gcs-store') -const { Storage } = require("@google-cloud/storage"); +const {Storage} = require('@google-cloud/storage') -const storage = new Storage({ keyFilename: "key.json" }); +const storage = new Storage({keyFilename: 'key.json'}) const server = new Server({ - path: "/files", + path: '/files', datastore: new GCSStore({ - bucket: storage.bucket("tus-node-server-ci"), + bucket: storage.bucket('tus-node-server-ci'), }), -}); +}) // ... ``` @@ -88,7 +88,8 @@ See [extensions]: https://tus.io/protocols/resumable-upload.html#protocol-extensions [creation]: https://tus.io/protocols/resumable-upload.html#creation -[creation with upload]: https://tus.io/protocols/resumable-upload.html#creation-with-upload +[creation with upload]: + https://tus.io/protocols/resumable-upload.html#creation-with-upload [expiration]: https://tus.io/protocols/resumable-upload.html#expiration [checksum]: https://tus.io/protocols/resumable-upload.html#checksum [termination]: https://tus.io/protocols/resumable-upload.html#termination diff --git a/packages/s3-store/README.md b/packages/s3-store/README.md index db465c4b..62f6fa69 100644 --- a/packages/s3-store/README.md +++ b/packages/s3-store/README.md @@ -31,8 +31,8 @@ npm install @tus/s3-store ## Use ```js -const { Server } = require("@tus/server"); -const { S3Store } = require("@tus/s3-store"); +const {Server} = require('@tus/server') +const {S3Store} = require('@tus/s3-store') const s3Store = new S3Store({ partSize: 8 * 1024 * 1024, // Each uploaded part will have ~8MiB, @@ -44,8 +44,8 @@ const s3Store = new S3Store({ secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY, }, }, -}); -const server = new Server({ path: "/files", datastore: s3Store }); +}) +const server = new Server({path: '/files', datastore: s3Store}) // ... ``` @@ -199,9 +199,9 @@ docs for the supported values of [credentials](https://docs.aws.amazon.com/AWSJavaScriptSDK/latest/AWS/Credentials.html#constructor-property) ```js -const aws = require("aws-sdk"); -const { Server } = require("@tus/server"); -const { S3Store } = require("@tus/s3-store"); +const aws = require('aws-sdk') +const {Server} = require('@tus/server') +const {S3Store} = require('@tus/s3-store') const s3Store = new S3Store({ partSize: 8 * 1024 * 1024, @@ -209,12 +209,12 @@ const s3Store = new S3Store({ bucket: process.env.AWS_BUCKET, region: process.env.AWS_REGION, credentials: new aws.ECSCredentials({ - httpOptions: { timeout: 5000 }, + httpOptions: {timeout: 5000}, maxRetries: 10, }), }, -}); -const server = new Server({ path: "/files", datastore: s3Store }); +}) +const server = new Server({path: '/files', datastore: s3Store}) // ... ``` @@ -231,7 +231,7 @@ const s3Store = new S3Store({ partSize: 8 * 1024 * 1024, minPartSize: 8 * 1024 * 1024, // ... -}); +}) ``` ### Example: use with Scaleway Object Storage @@ -242,7 +242,7 @@ const s3Store = new S3Store({ const s3Store = new S3Store({ maxMultipartParts: 1000, // ... -}); +}) ``` ## Types @@ -265,12 +265,16 @@ See [extensions]: https://tus.io/protocols/resumable-upload.html#protocol-extensions [creation]: https://tus.io/protocols/resumable-upload.html#creation -[creation with upload]: https://tus.io/protocols/resumable-upload.html#creation-with-upload +[creation with upload]: + https://tus.io/protocols/resumable-upload.html#creation-with-upload [expiration]: https://tus.io/protocols/resumable-upload.html#expiration [checksum]: https://tus.io/protocols/resumable-upload.html#checksum [termination]: https://tus.io/protocols/resumable-upload.html#termination [concatenation]: https://tus.io/protocols/resumable-upload.html#concatenation -[cleanExpiredUploads]: https://github.com/tus/tus-node-server/tree/main/packages/server#servercleanupexpireduploads -[lifecyle]: https://docs.aws.amazon.com/AmazonS3/latest/userguide/object-lifecycle-mgmt.html +[cleanExpiredUploads]: + https://github.com/tus/tus-node-server/tree/main/packages/server#servercleanupexpireduploads +[lifecyle]: + https://docs.aws.amazon.com/AmazonS3/latest/userguide/object-lifecycle-mgmt.html [kvstores]: https://github.com/tus/tus-node-server/tree/main/packages/server#kvstores -[`KvStore`]: https://github.com/tus/tus-node-server/blob/main/packages/utils/src/kvstores/Types.ts +[`KvStore`]: + https://github.com/tus/tus-node-server/blob/main/packages/utils/src/kvstores/Types.ts diff --git a/packages/server/README.md b/packages/server/README.md index 334e2b29..aceae5e8 100644 --- a/packages/server/README.md +++ b/packages/server/README.md @@ -37,16 +37,16 @@ npm install @tus/server A standalone server which stores files on disk. ```js -const { Server } = require("@tus/server"); -const { FileStore } = require("@tus/file-store"); -const host = "127.0.0.1"; -const port = 1080; +const {Server} = require('@tus/server') +const {FileStore} = require('@tus/file-store') +const host = '127.0.0.1' +const port = 1080 const server = new Server({ - path: "/files", - datastore: new FileStore({ directory: "./files" }), -}); -server.listen({ host, port }); + path: '/files', + datastore: new FileStore({directory: './files'}), +}) +server.listen({host, port}) ``` ## API @@ -311,54 +311,54 @@ can also be used as a cache in other stores, such as `@tus/s3-store`. #### `MemoryKvStore` ```ts -import { MemoryKvStore } from "@tus/server"; -import S3Store, { type MetadataValue } from "@tus/s3-store"; +import {MemoryKvStore} from '@tus/server' +import S3Store, {type MetadataValue} from '@tus/s3-store' new S3Store({ // ... cache: new MemoryKvStore(), -}); +}) ``` #### `FileKvStore` ```ts -import { FileKvStore } from "@tus/server"; -import S3Store, { type MetadataValue } from "@tus/s3-store"; +import {FileKvStore} from '@tus/server' +import S3Store, {type MetadataValue} from '@tus/s3-store' -const path = "./uploads"; +const path = './uploads' new S3Store({ // ... cache: new FileKvStore(path), -}); +}) ``` #### `RedisKvStore` ```ts -import { RedisKvStore } from "@tus/server"; -import S3Store, { type MetadataValue } from "@tus/s3-store"; -import { createClient } from "@redis/client"; +import {RedisKvStore} from '@tus/server' +import S3Store, {type MetadataValue} from '@tus/s3-store' +import {createClient} from '@redis/client' -const client = await createClient().connect(); -const prefix = "foo"; // prefix for the key (foo${id}) +const client = await createClient().connect() +const prefix = 'foo' // prefix for the key (foo${id}) new S3Store({ // ... cache: new RedisKvStore(client, prefix), -}); +}) ``` #### `IoRedisKvStore` ```ts -import { IoRedisKvStore } from "@tus/server"; -import S3Store, { type MetadataValue } from "@tus/s3-store"; -import Redis from "ioredis"; +import { IoRedisKvStore } from '@tus/server'; +import S3Store, { type MetadataValue } from '@tus/s3-store'; +import Redis from 'ioredis'; const client = new Redis(); -const prefix = "foo"; // prefix for the key (foo${id}) +const prefix = 'foo'; // prefix for the key (foo${id}) new S3Store({ // ... @@ -371,66 +371,66 @@ new S3Store({ ### Example: integrate tus into Express ```js -const { Server } = require("@tus/server"); -const { FileStore } = require("@tus/file-store"); -const express = require("express"); - -const host = "127.0.0.1"; -const port = 1080; -const app = express(); -const uploadApp = express(); +const {Server} = require('@tus/server') +const {FileStore} = require('@tus/file-store') +const express = require('express') + +const host = '127.0.0.1' +const port = 1080 +const app = express() +const uploadApp = express() const server = new Server({ - path: "/uploads", - datastore: new FileStore({ directory: "/files" }), -}); + path: '/uploads', + datastore: new FileStore({directory: '/files'}), +}) -uploadApp.all("*", server.handle.bind(server)); -app.use("/uploads", uploadApp); -app.listen(port, host); +uploadApp.all('*', server.handle.bind(server)) +app.use('/uploads', uploadApp) +app.listen(port, host) ``` ### Example: integrate tus into Koa ```js -const http = require("node:http"); -const url = require("node:url"); -const Koa = require("koa"); -const { Server } = require("@tus/server"); -const { FileStore } = require("@tus/file-store"); - -const app = new Koa(); -const appCallback = app.callback(); -const port = 1080; +const http = require('node:http') +const url = require('node:url') +const Koa = require('koa') +const {Server} = require('@tus/server') +const {FileStore} = require('@tus/file-store') + +const app = new Koa() +const appCallback = app.callback() +const port = 1080 const tusServer = new Server({ - path: "/files", - datastore: new FileStore({ directory: "/files" }), -}); + path: '/files', + datastore: new FileStore({directory: '/files'}), +}) const server = http.createServer((req, res) => { - const urlPath = url.parse(req.url).pathname; + const urlPath = url.parse(req.url).pathname // handle any requests with the `/files/*` pattern if (/^\/files\/.+/.test(urlPath.toLowerCase())) { - return tusServer.handle(req, res); + return tusServer.handle(req, res) } - appCallback(req, res); -}); + appCallback(req, res) +}) -server.listen(port); +server.listen(port) ``` ### Example: integrate tus into Fastify ```js -const fastify = require("fastify")({ logger: true }); -const { Server } = require("@tus/server"); -const { FileStore } = require("@tus/file-store"); +const fastify = require('fastify')({logger: true}) +const {Server} = require('@tus/server') +const {FileStore} = require('@tus/file-store') const tusServer = new Server({ - path: "/files", - datastore: new FileStore({ directory: "./files" }), -}); + path: '/files', + datastore: new FileStore({directory: './files'}), +}) /** * add new content-type to fastify forewards request @@ -438,9 +438,9 @@ const tusServer = new Server({ * @see https://www.fastify.io/docs/latest/Reference/ContentTypeParser/ */ fastify.addContentTypeParser( - "application/offset+octet-stream", + 'application/offset+octet-stream', (request, payload, done) => done(null) -); +) /** * let tus handle preparation and filehandling requests @@ -448,18 +448,18 @@ fastify.addContentTypeParser( * @see https://www.fastify.io/docs/latest/Reference/Request/ * @see https://www.fastify.io/docs/latest/Reference/Reply/#raw */ -fastify.all("/files", (req, res) => { - tusServer.handle(req.raw, res.raw); -}); -fastify.all("/files/*", (req, res) => { - tusServer.handle(req.raw, res.raw); -}); +fastify.all('/files', (req, res) => { + tusServer.handle(req.raw, res.raw) +}) +fastify.all('/files/*', (req, res) => { + tusServer.handle(req.raw, res.raw) +}) fastify.listen(3000, (err) => { if (err) { - fastify.log.error(err); - process.exit(1); + fastify.log.error(err) + process.exit(1) } -}); +}) ``` ### Example: integrate tus into Next.js @@ -470,9 +470,9 @@ Attach the tus server handler to a Next.js route handler in an `/pages/api/upload/[[...file]].ts` ```ts -import type { NextApiRequest, NextApiResponse } from "next"; -import { Server, Upload } from "@tus/server"; -import { FileStore } from "@tus/file-store"; +import type {NextApiRequest, NextApiResponse} from 'next' +import {Server, Upload} from '@tus/server' +import {FileStore} from '@tus/file-store' /** * !Important. This will tell Next.js NOT Parse the body as tus requires @@ -482,40 +482,40 @@ export const config = { api: { bodyParser: false, }, -}; +} const tusServer = new Server({ // `path` needs to match the route declared by the next file router // ie /api/upload - path: "/api/upload", - datastore: new FileStore({ directory: "./files" }), -}); + path: '/api/upload', + datastore: new FileStore({directory: './files'}), +}) export default function handler(req: NextApiRequest, res: NextApiResponse) { - return tusServer.handle(req, res); + return tusServer.handle(req, res) } ``` ### Example: validate metadata when an upload is created ```js -const { Server } = require("@tus/server"); +const {Server} = require('@tus/server') // ... const server = new Server({ // .. async onUploadCreate(req, res, upload) { - const { ok, expected, received } = validateMetadata(upload); // your logic + const {ok, expected, received} = validateMetadata(upload) // your logic if (!ok) { - const body = `Expected "${expected}" in "Upload-Metadata" but received "${received}"`; - throw { status_code: 500, body }; // if undefined, falls back to 500 with "Internal server error". + const body = `Expected "${expected}" in "Upload-Metadata" but received "${received}"` + throw {status_code: 500, body} // if undefined, falls back to 500 with "Internal server error". } // You can optionally return metadata to override the upload metadata, // such as `{ storagePath: "/upload/123abc..." }` - const extraMeta = getExtraMetadata(req); // your logic - return { res, metadata: { ...upload.metadata, ...extraMeta } }; + const extraMeta = getExtraMetadata(req) // your logic + return {res, metadata: {...upload.metadata, ...extraMeta}} }, -}); +}) ``` ### Example: access control @@ -524,30 +524,30 @@ Access control is opinionated and can be done in different ways. This example is psuedo-code for what it could look like with JSON Web Tokens. ```js -const { Server } = require("@tus/server"); +const {Server} = require('@tus/server') // ... const server = new Server({ // .. async onIncomingRequest(req, res) { - const token = req.headers.authorization; + const token = req.headers.authorization if (!token) { - throw { status_code: 401, body: "Unauthorized" }; + throw {status_code: 401, body: 'Unauthorized'} } try { - const decodedToken = await jwt.verify(token, "your_secret_key"); - req.user = decodedToken; + const decodedToken = await jwt.verify(token, 'your_secret_key') + req.user = decodedToken } catch (error) { - throw { status_code: 401, body: "Invalid token" }; + throw {status_code: 401, body: 'Invalid token'} } - if (req.user.role !== "admin") { - throw { status_code: 403, body: "Access denied" }; + if (req.user.role !== 'admin') { + throw {status_code: 403, body: 'Access denied'} } }, -}); +}) ``` ### Example: store files in custom nested directories @@ -560,26 +560,26 @@ Adding a slash means you create a new directory, for which you need to implement functions as we need encode the id with base64 into the URL. ```js -const path = "/files"; +const path = '/files' const server = new Server({ path, - datastore: new FileStore({ directory: "./test/output" }), + datastore: new FileStore({directory: './test/output'}), namingFunction(req) { - const id = crypto.randomBytes(16).toString("hex"); - const folder = getFolderForUser(req); // your custom logic - return `users/${folder}/${id}`; + const id = crypto.randomBytes(16).toString('hex') + const folder = getFolderForUser(req) // your custom logic + return `users/${folder}/${id}` }, - generateUrl(req, { proto, host, path, id }) { - id = Buffer.from(id, "utf-8").toString("base64url"); - return `${proto}://${host}${path}/${id}`; + generateUrl(req, {proto, host, path, id}) { + id = Buffer.from(id, 'utf-8').toString('base64url') + return `${proto}://${host}${path}/${id}` }, 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"); + return Buffer.from(lastPath, 'base64url').toString('utf-8') }, -}); +}) ``` ### Example: use with Nginx @@ -592,13 +592,13 @@ Firstly, you must set `respectForwardedHeaders` indicating that a reverse proxy and that it should respect the `X-Forwarded-*`/`Forwarded` headers: ```js -const { Server } = require("@tus/server"); +const {Server} = require('@tus/server') // ... const server = new Server({ // .. respectForwardedHeaders: true, -}); +}) ``` Secondly, some of the reverse proxy's settings should be adjusted. The exact steps depend @@ -646,10 +646,15 @@ See [`@tus/file-store`]: https://github.com/tus/tus-node-server/tree/main/packages/file-store [`@tus/s3-store`]: https://github.com/tus/tus-node-server/tree/main/packages/s3-store [`@tus/gcs-store`]: https://github.com/tus/tus-node-server/tree/main/packages/gcs-store -[`constants`]: https://github.com/tus/tus-node-server/blob/main/packages/utils/src/constants.ts +[`constants`]: + https://github.com/tus/tus-node-server/blob/main/packages/utils/src/constants.ts [`types`]: https://github.com/tus/tus-node-server/blob/main/packages/server/src/types.ts -[`models`]: https://github.com/tus/tus-node-server/blob/main/packages/utils/src/models/index.ts -[`kvstores`]: https://github.com/tus/tus-node-server/blob/main/packages/utils/src/kvstores/index.ts +[`models`]: + https://github.com/tus/tus-node-server/blob/main/packages/utils/src/models/index.ts +[`kvstores`]: + https://github.com/tus/tus-node-server/blob/main/packages/utils/src/kvstores/index.ts [expiration]: https://tus.io/protocols/resumable-upload.html#expiration -[`Locker`]: https://github.com/tus/tus-node-server/blob/main/packages/utils/src/models/Locker.ts -[`MemoryLocker`]: https://github.com/tus/tus-node-server/blob/main/packages/server/src/lockers/MemoryLocker.ts +[`Locker`]: + https://github.com/tus/tus-node-server/blob/main/packages/utils/src/models/Locker.ts +[`MemoryLocker`]: + https://github.com/tus/tus-node-server/blob/main/packages/server/src/lockers/MemoryLocker.ts From 5e1f348afd2bb39e721bec9659b9a00ce66711a3 Mon Sep 17 00:00:00 2001 From: Murderlon Date: Wed, 12 Feb 2025 11:02:39 +0100 Subject: [PATCH 4/6] Set azure-store version to 1.0.0 so it will be bumped to 2.0.0 too --- package-lock.json | 18 ++---------------- packages/azure-store/package.json | 2 +- 2 files changed, 3 insertions(+), 17 deletions(-) diff --git a/package-lock.json b/package-lock.json index 8ccb1f55..b1efa175 100644 --- a/package-lock.json +++ b/package-lock.json @@ -16,20 +16,6 @@ "typescript": "^5.6.2" } }, - "demo": { - "extraneous": true, - "dependencies": { - "@tus/azure-store": "^0.1.2", - "@tus/file-store": "^1.5.1", - "@tus/gcs-store": "^1.4.1", - "@tus/s3-store": "^1.7.0", - "@tus/server": "^1.10.1", - "tus-js-client": "^2.3.2" - }, - "devDependencies": { - "cross-env": "^7.0.3" - } - }, "node_modules/@aws-crypto/crc32": { "version": "5.2.0", "resolved": "https://registry.npmjs.org/@aws-crypto/crc32/-/crc32-5.2.0.tgz", @@ -6093,7 +6079,7 @@ }, "packages/azure-store": { "name": "@tus/azure-store", - "version": "0.1.2", + "version": "1.0.0", "license": "MIT", "dependencies": { "@azure/storage-blob": "^12.24.0", @@ -6242,7 +6228,7 @@ "ts-node": "^10.9.2" }, "engines": { - "node": ">=16" + "node": ">=20" } }, "test": { diff --git a/packages/azure-store/package.json b/packages/azure-store/package.json index f0caafcd..1ef19978 100644 --- a/packages/azure-store/package.json +++ b/packages/azure-store/package.json @@ -1,7 +1,7 @@ { "$schema": "https://json.schemastore.org/package.json", "name": "@tus/azure-store", - "version": "0.1.2", + "version": "1.0.0", "description": "Azure blob storage for @tus/server", "main": "dist/index.js", "homepage": "https://github.com/tus/tus-node-server#readme", From 063ccf68accda0ca35690a03c713789d9afce83d Mon Sep 17 00:00:00 2001 From: Murderlon Date: Wed, 12 Feb 2025 11:02:47 +0100 Subject: [PATCH 5/6] Add changeset --- .changeset/perfect-rats-type.md | 10 ++++++++++ 1 file changed, 10 insertions(+) create mode 100644 .changeset/perfect-rats-type.md diff --git a/.changeset/perfect-rats-type.md b/.changeset/perfect-rats-type.md new file mode 100644 index 00000000..6d9759f9 --- /dev/null +++ b/.changeset/perfect-rats-type.md @@ -0,0 +1,10 @@ +--- +"@tus/azure-store": major +"@tus/file-store": major +"@tus/gcs-store": major +"@tus/s3-store": major +"@tus/server": major +"@tus/utils": minor +--- + +Change required Node.js version from 16 to 20 From 061de23b222262407780e80c11d8edf5afd462c9 Mon Sep 17 00:00:00 2001 From: Murderlon Date: Thu, 13 Mar 2025 16:39:48 +0100 Subject: [PATCH 6/6] Require >=20.19.0 --- .changeset/perfect-rats-type.md | 2 +- .github/workflows/ci.yml | 2 +- README.md | 2 +- packages/azure-store/README.md | 4 ++-- packages/azure-store/package.json | 2 +- packages/file-store/README.md | 4 ++-- packages/file-store/package.json | 2 +- packages/gcs-store/README.md | 4 ++-- packages/gcs-store/package.json | 2 +- packages/s3-store/README.md | 4 ++-- packages/s3-store/package.json | 2 +- packages/server/README.md | 4 ++-- packages/server/package.json | 2 +- packages/utils/package.json | 2 +- 14 files changed, 19 insertions(+), 19 deletions(-) diff --git a/.changeset/perfect-rats-type.md b/.changeset/perfect-rats-type.md index 6d9759f9..3cd23fd4 100644 --- a/.changeset/perfect-rats-type.md +++ b/.changeset/perfect-rats-type.md @@ -7,4 +7,4 @@ "@tus/utils": minor --- -Change required Node.js version from 16 to 20 +Change required Node.js version from 16 to 20.19.0 diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 2791a180..68c5c66d 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -33,7 +33,7 @@ jobs: - name: Install Node.js uses: actions/setup-node@v3 with: - node-version: 20 + node-version: 20.19 - name: Install dependencies run: npm ci --no-fund --no-audit diff --git a/README.md b/README.md index d1a37d4d..9d628d5c 100644 --- a/README.md +++ b/README.md @@ -124,7 +124,7 @@ All packages are fully typed with TypeScript. ## Compatibility -All packages require Node.js 20+. +All packages require Node.js >=20.19.0. ## Contribute diff --git a/packages/azure-store/README.md b/packages/azure-store/README.md index dcc3f457..cfc3502e 100644 --- a/packages/azure-store/README.md +++ b/packages/azure-store/README.md @@ -16,7 +16,7 @@ Azure Store based on the Append Blob Client [Azure Blob AppendBlobClient](https: ## Install -In Node.js (20+), install with npm: +In Node.js >=20.19.0, install with npm: ```bash npm install @tus/azure-store @@ -84,7 +84,7 @@ This package is fully typed with TypeScript. ## Compatibility -This package requires Node.js 20+. +This package requires Node.js >=20.19.0. ## Contribute diff --git a/packages/azure-store/package.json b/packages/azure-store/package.json index 62155dcb..554a5674 100644 --- a/packages/azure-store/package.json +++ b/packages/azure-store/package.json @@ -31,6 +31,6 @@ "should": "^13.2.3" }, "engines": { - "node": ">=20" + "node": ">=20.19.0" } } diff --git a/packages/file-store/README.md b/packages/file-store/README.md index f0f4eb42..a6bd8fa6 100644 --- a/packages/file-store/README.md +++ b/packages/file-store/README.md @@ -20,7 +20,7 @@ ## Install -In Node.js (20+), install with npm: +In Node.js >=20.19.0, install with npm: ```bash npm install @tus/file-store @@ -124,7 +124,7 @@ This package is fully typed with TypeScript. ## Compatibility -This package requires Node.js 20+. +This package requires Node.js >=20.19.0. ## Contribute diff --git a/packages/file-store/package.json b/packages/file-store/package.json index 26e27ed4..ef948036 100644 --- a/packages/file-store/package.json +++ b/packages/file-store/package.json @@ -28,6 +28,6 @@ "@redis/client": "^1.6.0" }, "engines": { - "node": ">=20" + "node": ">=20.19.0" } } diff --git a/packages/gcs-store/README.md b/packages/gcs-store/README.md index f1138028..a31799f2 100644 --- a/packages/gcs-store/README.md +++ b/packages/gcs-store/README.md @@ -17,7 +17,7 @@ ## Install -In Node.js (20+), install with npm: +In Node.js >=20.19.0, install with npm: ```bash npm install @tus/gcs-store @@ -74,7 +74,7 @@ This package is fully typed with TypeScript. ## Compatibility -This package requires Node.js 20+. +This package requires Node.js >=20.19.0. ## Contribute diff --git a/packages/gcs-store/package.json b/packages/gcs-store/package.json index 97b0a566..3e2f124a 100644 --- a/packages/gcs-store/package.json +++ b/packages/gcs-store/package.json @@ -35,6 +35,6 @@ "@google-cloud/storage": "*" }, "engines": { - "node": ">=20" + "node": ">=20.19.0" } } diff --git a/packages/s3-store/README.md b/packages/s3-store/README.md index 62f6fa69..2005e9b4 100644 --- a/packages/s3-store/README.md +++ b/packages/s3-store/README.md @@ -22,7 +22,7 @@ ## Install -In Node.js (20+), install with npm: +In Node.js >=20.19.0, install with npm: ```bash npm install @tus/s3-store @@ -251,7 +251,7 @@ This package is fully typed with TypeScript. ## Compatibility -This package requires Node.js 20+. +This package requires Node.js >=20.19.0. ## Contribute diff --git a/packages/s3-store/package.json b/packages/s3-store/package.json index 4bbbe805..4b791047 100644 --- a/packages/s3-store/package.json +++ b/packages/s3-store/package.json @@ -35,6 +35,6 @@ "should": "^13.2.3" }, "engines": { - "node": ">=20" + "node": ">=20.19.0" } } diff --git a/packages/server/README.md b/packages/server/README.md index aceae5e8..2c87df48 100644 --- a/packages/server/README.md +++ b/packages/server/README.md @@ -26,7 +26,7 @@ ## Install -In Node.js (20+), install with npm: +In Node.js >=20.19.0, install with npm: ```bash npm install @tus/server @@ -631,7 +631,7 @@ This package is fully typed with TypeScript. ## Compatibility -This package requires Node.js 20+. +This package requires Node.js >=20.19.0. ## Contribute diff --git a/packages/server/package.json b/packages/server/package.json index 2b23aea1..5c03ecc5 100644 --- a/packages/server/package.json +++ b/packages/server/package.json @@ -42,6 +42,6 @@ "ioredis": "^5.4.1" }, "engines": { - "node": ">=20" + "node": ">=20.19.0" } } diff --git a/packages/utils/package.json b/packages/utils/package.json index 7bfc62ba..6e914ff7 100644 --- a/packages/utils/package.json +++ b/packages/utils/package.json @@ -28,6 +28,6 @@ "ts-node": "^10.9.2" }, "engines": { - "node": ">=20" + "node": ">=20.19.0" } }