npm package: @gune/storage-nestjs
Description: NestJS integration for @gune/storage
Author: BUDDGUN92
Latest version: 0.1.0

# @gune/storage-nestjs

NestJS integration module for @gune/storage with dependency injection support.

## Installation

```bash
npm install @gune/storage-core @gune/storage-nestjs
# Plus your storage strategy
npm install @gune/storage-s3
```

## Features

- 🔌 **Dynamic Module**: Configure via `forRoot()` or `forRootAsync()`
- 💉 **Dependency Injection**: Inject storage via `@InjectStorage()` decorator
- 🎯 **Multi-Storage**: Support multiple storage strategies simultaneously
- 🌍 **Global Module**: Optional global registration
- ⚙️ **Async Configuration**: Integrate with `ConfigService`

## Quick Start

### 1. Synchronous Configuration

```typescript
import { Module } from '@nestjs/common';
import { StorageModule } from '@gune/storage-nestjs';
import { S3StorageStrategy } from '@gune/storage-s3';

@Module({
  imports: [
    StorageModule.forRoot({
      strategy: new S3StorageStrategy({
        region: 'us-east-1',
        bucket: 'my-bucket',
        credentials: {
          accessKeyId: process.env.AWS_ACCESS_KEY_ID!,
          secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY!,
        },
      }),
      isGlobal: true, // Available in all modules
    }),
  ],
})
export class AppModule {}
```

### 2. Async Configuration

```typescript
import { Module } from '@nestjs/common';
import { ConfigModule, ConfigService } from '@nestjs/config';
import { StorageModule } from '@gune/storage-nestjs';
import { S3StorageStrategy } from '@gune/storage-s3';

@Module({
  imports: [
    ConfigModule.forRoot(),
    StorageModule.forRootAsync({
      imports: [ConfigModule],
      isGlobal: true,
      useFactory: (config: ConfigService) => ({
        strategy: new S3StorageStrategy({
          region: config.getOrThrow('AWS_REGION'),
          bucket: config.getOrThrow('S3_BUCKET'),
          credentials: {
            accessKeyId: config.getOrThrow('AWS_ACCESS_KEY_ID'),
            secretAccessKey: config.getOrThrow('AWS_SECRET_ACCESS_KEY'),
          },
        }),
      }),
      inject: [ConfigService],
    }),
  ],
})
export class AppModule {}
```

### 3. Using in Services

```typescript
import { Injectable } from '@nestjs/common';
import { InjectStorage } from '@gune/storage-nestjs';
import { StorageInstance } from '@gune/storage-core';
import { Readable } from 'stream';

@Injectable()
export class FileService {
  constructor(
    @InjectStorage() private readonly storage: StorageInstance,
  ) {}

  async uploadFile(file: Express.Multer.File) {
    const result = await this.storage.uploadFile({
      stream: Readable.from(file.buffer),
      filename: file.originalname,
      contentType: file.mimetype,
      size: file.size,
    });

    return result;
  }

  async deleteFile(key: string) {
    await this.storage.deleteFile(key);
  }
}
```

## Multi-Storage Configuration

Use multiple storage strategies in the same application:

```typescript
@Module({
  imports: [
    // Default storage (S3)
    StorageModule.forRoot({
      strategy: new S3StorageStrategy({ ... }),
      isGlobal: true,
    }),
    
    // Named storage for temporary files
    StorageModule.forRoot({
      name: 'temp',
      strategy: new LocalStorageStrategy({
        basePath: './temp-storage',
        baseUrl: '/temp',
      }),
    }),
    
    // Named storage for media processing
    StorageModule.forRoot({
      name: 'media',
      strategy: new CloudinaryStorageStrategy({ ... }),
    }),
  ],
})
export class AppModule {}
```

Inject named storage instances:

```typescript
@Injectable()
export class MediaService {
  constructor(
    @InjectStorage() private readonly storage: StorageInstance,          // Default
    @InjectStorage('temp') private readonly tempStorage: StorageInstance, // Named
    @InjectStorage('media') private readonly mediaStorage: StorageInstance,
  ) {}

  async processMedia(file: Buffer) {
    // Upload to temp storage first
    const temp = await this.tempStorage.uploadFile({ ... });
    
    // Process and upload to media storage
    const processed = await this.processImage(temp.url);
    const result = await this.mediaStorage.uploadFile(processed);
    
    // Clean up temp file
    await this.tempStorage.deleteFile(temp.key);
    
    return result;
  }
}
```

## Advanced Patterns

### Factory Class Configuration

```typescript
import { Injectable } from '@nestjs/common';
import { StorageModuleOptionsFactory, StorageModuleOptions } from '@gune/storage-nestjs';

@Injectable()
export class StorageConfigService implements StorageModuleOptionsFactory {
  constructor(private config: ConfigService) {}

  createStorageOptions(): StorageModuleOptions {
    const driver = this.config.get('STORAGE_DRIVER');
    
    if (driver === 's3') {
      return {
        strategy: new S3StorageStrategy({ ... }),
      };
    }
    
    return {
      strategy: new LocalStorageStrategy({ ... }),
    };
  }
}

@Module({
  imports: [
    StorageModule.forRootAsync({
      useClass: StorageConfigService,
      inject: [ConfigService],
    }),
  ],
})
export class AppModule {}
```

### Custom Logger Integration

```typescript
import { Logger } from '@nestjs/common';
import { StorageLogger } from '@gune/storage-core';

class NestStorageLogger implements StorageLogger {
  private logger = new Logger('StorageModule');

  debug(message: string, context?: Record<string, unknown>): void {
    this.logger.debug(message, context);
  }

  info(message: string, context?: Record<string, unknown>): void {
    this.logger.log(message, context);
  }

  warn(message: string, context?: Record<string, unknown>): void {
    this.logger.warn(message, context);
  }

  error(message: string, error?: Error, context?: Record<string, unknown>): void {
    this.logger.error(message, error?.stack, context);
  }
}

// Use in strategy configuration
StorageModule.forRoot({
  strategy: new S3StorageStrategy({
    ...config,
    logger: new NestStorageLogger(),
  }),
});
```

## API Reference

### `StorageModule`

#### Static Methods

##### `forRoot(options: StorageModuleOptions): DynamicModule`

Configure storage module synchronously.

**Options:**
- `strategy: StorageStrategy` - Storage strategy instance
- `name?: string` - Name for multi-storage (optional)
- `isGlobal?: boolean` - Register as global module (default: false)

##### `forRootAsync(options: StorageModuleAsyncOptions): DynamicModule`

Configure storage module asynchronously.

**Options:**
- `imports?: any[]` - Modules to import
- `useFactory?: (...args) => StorageModuleOptions` - Factory function
- `inject?: any[]` - Dependencies for factory
- `useClass?: Type<StorageModuleOptionsFactory>` - Factory class
- `useExisting?: Type<StorageModuleOptionsFactory>` - Existing factory
- `name?: string` - Name for multi-storage
- `isGlobal?: boolean` - Register as global module

### `@InjectStorage(name?: string)`

Decorator to inject storage instance.

```typescript
constructor(
  @InjectStorage() private storage: StorageInstance,
  @InjectStorage('temp') private tempStorage: StorageInstance,
) {}
```

## Migration from Existing Implementation

### Before (Your Current Code)

```typescript
// src/internal/storage/storage.module.ts
@Global()
@Module({
  providers: [
    {
      provide: IStorageStrategy,
      useFactory: (config: ConfigService) => {
        const driver = config.getOrThrow<string>(configKeys.storage.driver);
        switch (driver) {
          case 's3':
            return new S3Strategy(config);
          // ...
        }
      },
      inject: [ConfigService],
    },
  ],
  exports: [IStorageStrategy],
})
export class StorageModule {}

// Usage
constructor(private readonly storageStrategy: IStorageStrategy) {}
```

### After (Using @gune/storage-nestjs)

```typescript
// app.module.ts
@Module({
  imports: [
    StorageModule.forRootAsync({
      imports: [ConfigModule],
      isGlobal: true,
      useFactory: (config: ConfigService) => {
        const driver = config.getOrThrow(configKeys.storage.driver);
        
        if (driver === 's3') {
          return {
            strategy: new S3StorageStrategy({
              region: config.getOrThrow(configKeys.storage.region),
              bucket: config.getOrThrow(configKeys.storage.bucketName),
              credentials: {
                accessKeyId: config.getOrThrow(configKeys.storage.accessKey),
                secretAccessKey: config.getOrThrow(configKeys.storage.secretKey),
              },
            }),
          };
        }
        
        return {
          strategy: new LocalStorageStrategy({
            basePath: './storage',
            baseUrl: config.get(configKeys.appUrl) + '/storage',
          }),
        };
      },
      inject: [ConfigService],
    }),
  ],
})
export class AppModule {}

// Usage
constructor(@InjectStorage() private readonly storage: StorageInstance) {}
```

## License

MIT