From e816379280a33058c51fcdc990e832a039bfd265 Mon Sep 17 00:00:00 2001 From: Joy Bawa Date: Mon, 29 Jun 2026 21:50:00 +0000 Subject: [PATCH] Implement env-driven signed URL strategy providers This change introduces an env-driven strategy pattern for signed URL generation in the documents module. A factory now selects the active provider based on the SIGNED_URL_PROVIDER environment variable, with explicit S3, GCS, and Azure provider shells that can be wired to real implementations by integrators. The default path remains a not-configured provider that throws a clear error when no backend is selected, making the behavior explicit and extensible for future storage integrations. Closes the issue requesting signed URL generation to be implemented through a strategy pattern driven by environment variables, with S3/GCS/Azure placeholders and a default not-configured failure mode. --- src/documents/documents.module.ts | 10 +++++++- .../signed-url/azure-signed-url-provider.ts | 23 +++++++++++++++++++ .../signed-url/gcs-signed-url-provider.ts | 23 +++++++++++++++++++ .../signed-url/s3-signed-url-provider.ts | 17 ++++---------- 4 files changed, 59 insertions(+), 14 deletions(-) create mode 100644 src/documents/signed-url/azure-signed-url-provider.ts create mode 100644 src/documents/signed-url/gcs-signed-url-provider.ts diff --git a/src/documents/documents.module.ts b/src/documents/documents.module.ts index abe70fbd..a616cb9d 100644 --- a/src/documents/documents.module.ts +++ b/src/documents/documents.module.ts @@ -8,6 +8,8 @@ import { AuthModule } from '../auth/auth.module'; import { SignedUrlService } from './signed-url/signed-url.service'; import { NotConfiguredSignedUrlProvider } from './signed-url/not-configured.signed-url-provider'; import { S3SignedUrlProvider } from './signed-url/s3-signed-url-provider'; +import { GcsSignedUrlProvider } from './signed-url/gcs-signed-url-provider'; +import { AzureSignedUrlProvider } from './signed-url/azure-signed-url-provider'; import { DocumentsDownloadController } from './documents-download.controller'; import { SignedUrlProvider } from './signed-url/signed-url-provider.interface'; @@ -18,7 +20,9 @@ export const SIGNED_URL_PROVIDER_TOKEN = 'SIGNED_URL_PROVIDER_TOKEN'; * SIGNED_URL_PROVIDER environment variable. * * Supported values: - * - 's3' -> S3SignedUrlProvider (requires AWS credentials) + * - 's3' -> S3SignedUrlProvider (no-op shell for integrators) + * - 'gcs' -> GcsSignedUrlProvider (no-op shell for integrators) + * - 'azure' -> AzureSignedUrlProvider (no-op shell for integrators) * - (unset/other) -> NotConfiguredSignedUrlProvider (throws a configured error) */ function signedUrlProviderFactory(): new (...args: any[]) => SignedUrlProvider { @@ -26,6 +30,10 @@ function signedUrlProviderFactory(): new (...args: any[]) => SignedUrlProvider { switch (provider) { case 's3': return S3SignedUrlProvider; + case 'gcs': + return GcsSignedUrlProvider; + case 'azure': + return AzureSignedUrlProvider; default: return NotConfiguredSignedUrlProvider; } diff --git a/src/documents/signed-url/azure-signed-url-provider.ts b/src/documents/signed-url/azure-signed-url-provider.ts new file mode 100644 index 00000000..eb560b57 --- /dev/null +++ b/src/documents/signed-url/azure-signed-url-provider.ts @@ -0,0 +1,23 @@ +// @ts-nocheck + +import { Injectable } from '@nestjs/common'; +import { + SignedUrlProvider, + SignedUrlRequest, + SignedUrlResponse, +} from './signed-url-provider.interface'; + +/** + * Placeholder strategy for Azure Blob Storage signed URLs. + * This shell intentionally throws until an integrator wires in a real implementation. + */ +@Injectable() +export class AzureSignedUrlProvider implements SignedUrlProvider { + isConfigured(): boolean { + return false; + } + + async getSignedUrl(_req: SignedUrlRequest): Promise { + throw new Error('Azure signed URL provider is not configured.'); + } +} diff --git a/src/documents/signed-url/gcs-signed-url-provider.ts b/src/documents/signed-url/gcs-signed-url-provider.ts new file mode 100644 index 00000000..4eaf767a --- /dev/null +++ b/src/documents/signed-url/gcs-signed-url-provider.ts @@ -0,0 +1,23 @@ +// @ts-nocheck + +import { Injectable } from '@nestjs/common'; +import { + SignedUrlProvider, + SignedUrlRequest, + SignedUrlResponse, +} from './signed-url-provider.interface'; + +/** + * Placeholder strategy for Google Cloud Storage signed URLs. + * This shell intentionally throws until an integrator wires in a real implementation. + */ +@Injectable() +export class GcsSignedUrlProvider implements SignedUrlProvider { + isConfigured(): boolean { + return false; + } + + async getSignedUrl(_req: SignedUrlRequest): Promise { + throw new Error('GCS signed URL provider is not configured.'); + } +} diff --git a/src/documents/signed-url/s3-signed-url-provider.ts b/src/documents/signed-url/s3-signed-url-provider.ts index 7d4b27a0..41059cf3 100644 --- a/src/documents/signed-url/s3-signed-url-provider.ts +++ b/src/documents/signed-url/s3-signed-url-provider.ts @@ -8,25 +8,16 @@ import { } from './signed-url-provider.interface'; /** - * Placeholder for AWS S3 signed URLs. - * Intentionally throws if required env vars are missing. + * Placeholder strategy for AWS S3 signed URLs. + * This shell intentionally throws until an integrator wires in a real implementation. */ @Injectable() export class S3SignedUrlProvider implements SignedUrlProvider { isConfigured(): boolean { - return Boolean( - process.env.AWS_S3_BUCKET && - process.env.AWS_ACCESS_KEY_ID && - process.env.AWS_SECRET_ACCESS_KEY, - ); + return false; } async getSignedUrl(_req: SignedUrlRequest): Promise { - if (!this.isConfigured()) { - throw new Error('S3SignedUrlProvider not configured'); - } - - // Placeholder: implement via AWS SDK v3 once you wire credentials. - throw new Error('S3 signed URL not implemented yet'); + throw new Error('S3 signed URL provider is not configured.'); } }