From 79597031ac501220236b1fc9db08fb6ba80f5d1f Mon Sep 17 00:00:00 2001 From: mugabodannyshafi Date: Wed, 8 Oct 2025 19:13:29 +0200 Subject: [PATCH] feat(nestjs-tools-file-storage): add S3-compatible service support for DigitalOcean Spaces MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit **Changes:** - Add endpoint configuration with virtual-hosted-style URLs support - Enhance region extraction to handle DigitalOcean Spaces endpoints - Set forcePathStyle to false for S3-compatible services - Bump package versions across monorepo (file-storage: 1.4.1 → 1.4.2) - Update typed-event-emitter dependency (0.2.0 → 0.2.1) This enables the file-storage package to work with S3-compatible services like DigitalOcean Spaces by properly configuring endpoint URLs and region extraction from non-AWS S3 endpoints. --- package-lock.json | 20 ++++++++-------- .../src/lib/file-storage-s3.class.ts | 23 +++++++++++++++++-- 2 files changed, 31 insertions(+), 12 deletions(-) diff --git a/package-lock.json b/package-lock.json index 7b7c96d..99992eb 100644 --- a/package-lock.json +++ b/package-lock.json @@ -16486,10 +16486,10 @@ }, "packages/amqp-transport": { "name": "@getlarge/nestjs-tools-amqp-transport", - "version": "0.6.0", + "version": "0.7.0", "license": "Apache-2.0", "dependencies": { - "@getlarge/typed-event-emitter": "0.2.0", + "@getlarge/typed-event-emitter": "0.2.1", "tslib": "^2.3.0", "uuid": "^9.0.1" }, @@ -16504,7 +16504,7 @@ }, "packages/async-local-storage": { "name": "@getlarge/nestjs-tools-async-local-storage", - "version": "0.3.0", + "version": "0.3.1", "license": "Apache-2.0", "dependencies": { "tslib": "^2.3.0" @@ -16517,16 +16517,16 @@ }, "packages/cluster": { "name": "@getlarge/nestjs-tools-cluster", - "version": "0.15.6", + "version": "0.15.7", "license": "Apache-2.0", "dependencies": { - "@getlarge/typed-event-emitter": "0.2.0", + "@getlarge/typed-event-emitter": "0.2.1", "tslib": "^2.3.0" } }, "packages/eslint-plugin": { "name": "@getlarge/eslint-plugin-nestjs-tools", - "version": "0.1.5", + "version": "0.1.6", "license": "Apache-2.0", "dependencies": { "@typescript-eslint/utils": "^8.13.0", @@ -16541,7 +16541,7 @@ }, "packages/fastify-upload": { "name": "@getlarge/nestjs-tools-fastify-upload", - "version": "0.1.5", + "version": "0.1.6", "license": "Apache-2.0", "dependencies": { "tslib": "^2.3.0" @@ -16556,7 +16556,7 @@ }, "packages/file-storage": { "name": "@getlarge/nestjs-tools-file-storage", - "version": "1.4.1", + "version": "1.4.2", "license": "Apache-2.0", "dependencies": { "tslib": "^2.3.0" @@ -16581,7 +16581,7 @@ }, "packages/lock": { "name": "@getlarge/nestjs-tools-lock", - "version": "1.1.0", + "version": "1.1.1", "license": "Apache-2.0", "dependencies": { "redlock": "^5.0.0-beta.2", @@ -16594,7 +16594,7 @@ }, "packages/typed-event-emitter": { "name": "@getlarge/typed-event-emitter", - "version": "0.2.0", + "version": "0.2.1", "license": "Apache-2.0", "dependencies": { "tslib": "^2.3.0" diff --git a/packages/file-storage/src/lib/file-storage-s3.class.ts b/packages/file-storage/src/lib/file-storage-s3.class.ts index ad1b78c..8667863 100644 --- a/packages/file-storage/src/lib/file-storage-s3.class.ts +++ b/packages/file-storage/src/lib/file-storage-s3.class.ts @@ -31,6 +31,7 @@ function config(setup: FileStorageS3Setup) { // eslint-disable-next-line @typescript-eslint/no-require-imports const loaderFn = (): { S3: typeof import('@aws-sdk/client-s3').S3 } => require('@aws-sdk/client-s3'); const { S3 } = loadPackage('@aws-sdk/client-s3', FileStorageS3.name, loaderFn); + const s3 = new S3({ /** * We cannot really make calls without credentials unless we use a workaround @@ -38,6 +39,13 @@ function config(setup: FileStorageS3Setup) { */ ...(credentials ? { credentials } : {}), region, + // Add endpoint configuration for DigitalOcean Spaces and other S3-compatible services + ...(endpoint + ? { + endpoint, + forcePathStyle: false, // Use virtual-hosted-style URLs (required for DigitalOcean Spaces) + } + : {}), ...(logger ? { logger } : {}), }); @@ -71,8 +79,19 @@ export class FileStorageS3 implements FileStorage { } static extractRegionFromEndpoint(endpoint: string): string | null { - const match = endpoint?.match(/(?<=\.)[^.]+(?=\.amazonaws\.com)/); - return match?.length ? match[0] : null; + // Handle AWS S3 endpoints + const awsMatch = endpoint?.match(/(?<=\.)[^.]+(?=\.amazonaws\.com)/); + if (awsMatch?.length) { + return awsMatch[0]; + } + + // Handle DigitalOcean Spaces endpoints (e.g., nyc3.digitaloceanspaces.com) + const doMatch = endpoint?.match(/^https?:\/\/([^.]+)\.digitaloceanspaces\.com/); + if (doMatch && doMatch?.length > 1) { + return doMatch[1]; + } + + return null; } transformFilePath(