npm package: @neoxia-js/nestjs-storage
Description: nestjs module allawing communication with different kind of local and cloud storage
Latest version: 1.0.0

## Nestjs storage

This package allow the management of your projects uploaded files using differents strategies

### Installation

yarn add @neoxia-js/nestjs-storage

### Example of usage

Create an entity which extends the package storage.entity.ts

```javascript
import { Entity, OneToMany } from 'typeorm';

import {
  Storage as StorageBase,
  StorageInterface,
} from '@neoxia-js/nestjs-storage';

import { User } from '../user/user.entity';

@Entity()
export class Storage extends StorageBase implements StorageInterface {}
```

this entity is the one where all your files informations will be stored, you have to put a relation between the original entity that needs the file and the storage entity

```javascript
// user.entity.ts
import { Entity, Column, PrimaryGeneratedColumn, ManyToOne } from 'typeorm';
import { Storage } from '../storage/storage.entity';

@Entity()
export class User {
  // ...
  @OneToOne(() => Storage, { eager: true, cascade: true })
  @JoinColumn()
  thumbnail: Storage;
}
```

import the storage module in `app.module.ts`, you can use the exported multer config from StorageService to initialize the MulterModule

```javascript
import { Module } from '@nestjs/common';
import { TypeOrmModule } from '@nestjs/typeorm';
import { ConfigModule, ConfigService } from '@nestjs/config';
import { MulterModule } from '@nestjs/platform-express';
import {
  StorageModule,
  StorageModuleConfig,
  StorageService,
} from '@neoxia-js/nestjs-storage';
import { MulterOptions } from '@nestjs/platform-express/multer/interfaces/multer-options.interface';

@Module({
  imports: [
    ConfigModule.forRoot({
      isGlobal: true,
      load: [databaseConfig, storageConfig],
      envFilePath: !ENV ? '.env' : `.env.${ENV}`,
    }),
    TypeOrmModule.forRootAsync({
      imports: [ConfigModule],
      useFactory: async (configService: ConfigService) =>
        configService.get('database'),
      inject: [ConfigService],
    }),
    StorageModule.registerAsync({
      imports: [ConfigModule],
      useFactory: (configService: ConfigService): StorageModuleConfig =>
        configService.get('storage'),
      inject: [ConfigService],
    }),
    MulterModule.registerAsync({
      imports: [StorageModule],
      useFactory: (storageService: StorageService): MulterOptions =>
        storageService.getMulterConfig(),
      inject: [StorageService],
    }),

```

do not forget to import the `ServeStaticModule` if the used strategy is `local` (and if you need it)

```javascript
// app.moduke.ts

@Module({
  imports: [
    // ...
    ServeStaticModule.forRoot({
      rootPath: join(process.cwd(), '.', 'public'),
    }),
})
```

here is un example of `StorageModuleConfig`

```javascript
// src/config/storage.config.ts

import { registerAs } from '@nestjs/config';
import { StorageModuleConfig, STORAGE_KINDS } from '@neoxia-js/nestjs-storage';

export default registerAs('storage', (): StorageModuleConfig => ({
  defaultFileSystem: process.env.FILE_SYSTEM || STORAGE_KINDS.LOCAL,
  maxFileSize: parseInt(process.env.MAX_FILE_SIZE),
  fileSystems: {
    someLocalFileSystem: {
      strategy: STORAGE_KINDS.LOCAL,
      baseUrl: process.env.BASE_URL || 'http://localhost',
      publicDir: process.env.PUBLIC_DIR || 'public',
      destinationDir: process.env.STORAGE_DIR || 'storage',
    },
    someAzurePrivateFileSystem: {
      strategy: STORAGE_KINDS.AZURE,
      connectionString: process.env.AZURE_STORAGE_CONNECTION_STRING,
      container: process.env.AZURE_STORAGE_CONTAINER,
      sasTime: parseInt(process.env.AZURE_SAS_TIME),
      isPublic: false,
    },
    azurePublicFileSystem: {
      strategy: STORAGE_KINDS.AZURE,
      connectionString: process.env.AZURE_STORAGE_CONNECTION_STRING,
      container: process.env.AZURE_STORAGE_PUBLIC_CONTAINER,
      isPublic: true,
    },
  },
}));
```

now the multer configuration used by the FileInterceptor will be the one retrived by the `storageService.getMulterConfig()` and every eagered storage entity will have at least the properties

```
fieldname
originalname
mimetype
path
url
```

the function `getMulterConfig` accepts the fileSystem name as argument, default is the `defaultFileSystem`

where url is the autogenerated (not in database) url of the media

this url is also generated using the `shaed access signature` (only for azure strategy for now), allowing the url to be public for a short period of time

### Config description

```javascript

{
  defaultFileSystem: "someLocalFileSystem", // fileSystem kind, it must be one of the "fileSystems" key property
  maxFileSize: 8000000, // max file size in bytes, default 8000000
  fileSystems: {
    someLocalFileSystem: {
      strategy: "local", // only "local" or "azure" for now
      baseUrl: 'http://localhost', // url of the server
      publicDir: 'public', // server public directory, default 'public'
      destinationDir: 'storage', // server image directory inside publiic directory, default 'storage'
    },
    someAzureFileSystem: {
      strategy: "azure", // only "local" or "azure" for now
      connectionString: "", // azure bucket connection string
      container: "", // azure bucket container name
      sasTime: 100, // azue public sas url time validity (in minutes) default 100
      isPublic: false, // the container (and so the image) is publicly accessible or not
      // even if the property is set to true, but the container already exists and it is private,
      // the image won't be publicly accessible
    },
  }
}
```

### TO DO

- tests
- improve documentation
- custom compute filename function for all strategies
- ...