From bb12229661247ef45a4f272cd5c3b259f6e38674 Mon Sep 17 00:00:00 2001 From: Andrew Ghostuhin Date: Fri, 13 Sep 2024 00:30:55 +0300 Subject: [PATCH] feat(s3-client): init package --- packages/nestjs-s3-client/package.json | 49 +++++++++++++ packages/nestjs-s3-client/src/index.ts | 4 ++ packages/nestjs-s3-client/src/module/index.ts | 5 ++ .../src/module/s3-client.config-factory.ts | 27 +++++++ .../src/module/s3-client.factory.ts | 15 ++++ .../src/module/s3-client.module.constants.ts | 1 + .../src/module/s3-client.module.interfaces.ts | 20 ++++++ .../src/module/s3-client.module.ts | 72 +++++++++++++++++++ 8 files changed, 193 insertions(+) create mode 100644 packages/nestjs-s3-client/package.json create mode 100644 packages/nestjs-s3-client/src/index.ts create mode 100644 packages/nestjs-s3-client/src/module/index.ts create mode 100644 packages/nestjs-s3-client/src/module/s3-client.config-factory.ts create mode 100644 packages/nestjs-s3-client/src/module/s3-client.factory.ts create mode 100644 packages/nestjs-s3-client/src/module/s3-client.module.constants.ts create mode 100644 packages/nestjs-s3-client/src/module/s3-client.module.interfaces.ts create mode 100644 packages/nestjs-s3-client/src/module/s3-client.module.ts diff --git a/packages/nestjs-s3-client/package.json b/packages/nestjs-s3-client/package.json new file mode 100644 index 00000000..9dcc6a42 --- /dev/null +++ b/packages/nestjs-s3-client/package.json @@ -0,0 +1,49 @@ +{ + "name": "@monstrs/nestjs-s3-client", + "version": "0.1.4", + "license": "MIT", + "type": "module", + "exports": { + "./package.json": "./package.json", + ".": "./src/index.ts" + }, + "main": "src/index.ts", + "files": [ + "dist" + ], + "scripts": { + "build": "yarn library build", + "prepack": "yarn run build", + "postpack": "rm -rf dist" + }, + "dependencies": { + "@aws-sdk/client-s3": "^3.363.0", + "@aws-sdk/credential-providers": "^3.363.0", + "@aws-sdk/s3-request-presigner": "^3.363.0", + "@aws-sdk/types": "^3.357.0" + }, + "devDependencies": { + "@nestjs/common": "^10.0.5", + "@nestjs/core": "^10.0.5", + "reflect-metadata": "^0.1.13", + "rxjs": "^7.8.1" + }, + "peerDependencies": { + "@nestjs/common": "^10", + "@nestjs/core": "^10", + "reflect-metadata": "^0.1", + "rxjs": "^7" + }, + "publishConfig": { + "exports": { + "./package.json": "./package.json", + ".": { + "import": "./dist/index.js", + "types": "./dist/index.d.ts", + "default": "./dist/index.js" + } + }, + "main": "dist/index.js", + "typings": "dist/index.d.ts" + } +} diff --git a/packages/nestjs-s3-client/src/index.ts b/packages/nestjs-s3-client/src/index.ts new file mode 100644 index 00000000..c97a6038 --- /dev/null +++ b/packages/nestjs-s3-client/src/index.ts @@ -0,0 +1,4 @@ +export * from '@aws-sdk/s3-request-presigner' +export * from '@aws-sdk/client-s3' + +export * from './module/index.js' diff --git a/packages/nestjs-s3-client/src/module/index.ts b/packages/nestjs-s3-client/src/module/index.ts new file mode 100644 index 00000000..efdda9cf --- /dev/null +++ b/packages/nestjs-s3-client/src/module/index.ts @@ -0,0 +1,5 @@ +export * from './s3-client.module.interfaces.js' +export * from './s3-client.module.constants.js' +export * from './s3-client.config-factory.js' +export * from './s3-client.factory.js' +export * from './s3-client.module.js' diff --git a/packages/nestjs-s3-client/src/module/s3-client.config-factory.ts b/packages/nestjs-s3-client/src/module/s3-client.config-factory.ts new file mode 100644 index 00000000..7680053c --- /dev/null +++ b/packages/nestjs-s3-client/src/module/s3-client.config-factory.ts @@ -0,0 +1,27 @@ +import type { S3ClientConfig } from '@aws-sdk/client-s3' + +// eslint-disable-next-line @typescript-eslint/consistent-type-imports +import type { S3ClientModuleOptions } from './s3-client.module.interfaces.js' + +import { Inject } from '@nestjs/common' +import { Injectable } from '@nestjs/common' +import { fromEnv } from '@aws-sdk/credential-providers' + +import { S3_CLIENT_MODULE_OPTIONS } from './s3-client.module.constants.js' + +@Injectable() +export class S3ClientConfigFactory { + constructor( + @Inject(S3_CLIENT_MODULE_OPTIONS) + private readonly options: S3ClientModuleOptions + ) {} + + createS3ClientOptions(options: S3ClientModuleOptions = {}): S3ClientConfig { + return { + endpoint: options.endpoint || this.options.endpoint || process.env.S3_ENDPOINT, + region: options.region || this.options.region || process.env.S3_REGION, + credentials: options.credentials || this.options.credentials || fromEnv(), + forcePathStyle: true, + } + } +} diff --git a/packages/nestjs-s3-client/src/module/s3-client.factory.ts b/packages/nestjs-s3-client/src/module/s3-client.factory.ts new file mode 100644 index 00000000..1e5489c1 --- /dev/null +++ b/packages/nestjs-s3-client/src/module/s3-client.factory.ts @@ -0,0 +1,15 @@ +import type { S3ClientModuleOptions } from './s3-client.module.interfaces.js' + +import { S3Client } from '@aws-sdk/client-s3' +import { Injectable } from '@nestjs/common' + +import { S3ClientConfigFactory } from './s3-client.config-factory.js' + +@Injectable() +export class S3ClientFactory { + constructor(private readonly configFactory: S3ClientConfigFactory) {} + + create(options: S3ClientModuleOptions = {}): S3Client { + return new S3Client(this.configFactory.createS3ClientOptions(options)) + } +} diff --git a/packages/nestjs-s3-client/src/module/s3-client.module.constants.ts b/packages/nestjs-s3-client/src/module/s3-client.module.constants.ts new file mode 100644 index 00000000..0b99c168 --- /dev/null +++ b/packages/nestjs-s3-client/src/module/s3-client.module.constants.ts @@ -0,0 +1 @@ +export const S3_CLIENT_MODULE_OPTIONS = Symbol('s3-client-module-options') diff --git a/packages/nestjs-s3-client/src/module/s3-client.module.interfaces.ts b/packages/nestjs-s3-client/src/module/s3-client.module.interfaces.ts new file mode 100644 index 00000000..cad118b2 --- /dev/null +++ b/packages/nestjs-s3-client/src/module/s3-client.module.interfaces.ts @@ -0,0 +1,20 @@ +import type { AwsCredentialIdentity } from '@aws-sdk/types' +import type { ModuleMetadata } from '@nestjs/common/interfaces' +import type { Type } from '@nestjs/common/interfaces' + +export interface S3ClientModuleOptions { + endpoint?: string + region?: string + credentials?: AwsCredentialIdentity +} + +export interface S3ClientOptionsFactory { + createS3ClientOptions: () => Promise | S3ClientModuleOptions +} + +export interface S3ClientModuleAsyncOptions extends Pick { + useExisting?: Type + useClass?: Type + useFactory?: (...args: Array) => Promise | S3ClientModuleOptions + inject?: Array +} diff --git a/packages/nestjs-s3-client/src/module/s3-client.module.ts b/packages/nestjs-s3-client/src/module/s3-client.module.ts new file mode 100644 index 00000000..427cf170 --- /dev/null +++ b/packages/nestjs-s3-client/src/module/s3-client.module.ts @@ -0,0 +1,72 @@ +import type { DynamicModule } from '@nestjs/common' +import type { Provider } from '@nestjs/common' + +import type { S3ClientModuleOptions } from './s3-client.module.interfaces.js' +import type { S3ClientModuleAsyncOptions } from './s3-client.module.interfaces.js' +import type { S3ClientOptionsFactory } from './s3-client.module.interfaces.js' + +import { Module } from '@nestjs/common' + +import { S3ClientConfigFactory } from './s3-client.config-factory.js' +import { S3ClientFactory } from './s3-client.factory.js' +import { S3_CLIENT_MODULE_OPTIONS } from './s3-client.module.constants.js' + +@Module({}) +export class S3ClientModule { + static register(options: S3ClientModuleOptions = {}): DynamicModule { + return { + module: S3ClientModule, + providers: [ + S3ClientConfigFactory, + S3ClientFactory, + { + provide: S3_CLIENT_MODULE_OPTIONS, + useValue: options, + }, + ], + exports: [S3ClientConfigFactory, S3ClientFactory], + } + } + + static registerAsync(options: S3ClientModuleAsyncOptions): DynamicModule { + return { + module: S3ClientModule, + imports: options.imports || [], + providers: [...this.createAsyncProviders(options), S3ClientConfigFactory, S3ClientFactory], + exports: [S3ClientConfigFactory, S3ClientFactory], + } + } + + private static createAsyncProviders(options: S3ClientModuleAsyncOptions): Array { + if (options.useExisting || options.useFactory) { + return [this.createAsyncOptionsProvider(options)] + } + + return [ + this.createAsyncOptionsProvider(options), + { + provide: options.useClass!, + useClass: options.useClass!, + }, + ] + } + + private static createAsyncOptionsProvider(options: S3ClientModuleAsyncOptions): Provider { + if (options.useFactory) { + return { + provide: S3_CLIENT_MODULE_OPTIONS, + useFactory: options.useFactory, + inject: options.inject || [], + } + } + + return { + provide: S3_CLIENT_MODULE_OPTIONS, + useFactory: ( + optionsFactory: S3ClientOptionsFactory + ): Promise | S3ClientModuleOptions => + optionsFactory.createS3ClientOptions(), + inject: [options.useExisting! || options.useClass!], + } + } +}