feat: upload module (#486)

* feat: wip upload module

* feat: local storage and serve local images

* feat: protect against injections

* feat: server local and s3 files

* fix: use storage location when serving local files

* feat: cross field env validation
This commit is contained in:
Jérémy M
2023-07-04 16:02:44 +02:00
committed by GitHub
parent 820ef184d3
commit 5e1fc1ad11
52 changed files with 2632 additions and 64 deletions

View File

@ -0,0 +1 @@
export * from './local-storage.interface';

View File

@ -0,0 +1,3 @@
export interface LocalStorageModuleOptions {
storagePath: string;
}

View File

@ -0,0 +1,9 @@
import { ConfigurableModuleBuilder } from '@nestjs/common';
import { LocalStorageModuleOptions } from './interfaces';
export const { ConfigurableModuleClass, MODULE_OPTIONS_TOKEN } =
new ConfigurableModuleBuilder<LocalStorageModuleOptions>({
moduleName: 'LocalStorage',
})
.setClassMethodName('forRoot')
.build();

View File

@ -0,0 +1,10 @@
import { Global, Module } from '@nestjs/common';
import { LocalStorageService } from './local-storage.service';
import { ConfigurableModuleClass } from './local-storage.module-definition';
@Global()
@Module({
providers: [LocalStorageService],
exports: [LocalStorageService],
})
export class LocalStorageModule extends ConfigurableModuleClass {}

View File

@ -0,0 +1,25 @@
import { Test, TestingModule } from '@nestjs/testing';
import { LocalStorageService } from './local-storage.service';
import { MODULE_OPTIONS_TOKEN } from './local-storage.module-definition';
describe('LocalStorageService', () => {
let service: LocalStorageService;
beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
providers: [
LocalStorageService,
{
provide: MODULE_OPTIONS_TOKEN,
useValue: {},
},
],
}).compile();
service = module.get<LocalStorageService>(LocalStorageService);
});
it('should be defined', () => {
expect(service).toBeDefined();
});
});

View File

@ -0,0 +1,35 @@
import { Injectable, Inject } from '@nestjs/common';
import * as fs from 'fs/promises';
import { existsSync } from 'fs';
import * as path from 'path';
import { MODULE_OPTIONS_TOKEN } from './local-storage.module-definition';
import { LocalStorageModuleOptions } from './interfaces';
@Injectable()
export class LocalStorageService {
constructor(
@Inject(MODULE_OPTIONS_TOKEN)
private readonly options: LocalStorageModuleOptions,
) {}
async createFolder(path: string) {
if (existsSync(path)) {
return;
}
return fs.mkdir(path, { recursive: true });
}
async uploadFile(params: {
file: Buffer | Uint8Array | string;
name: string;
folder: string;
}) {
const filePath = `${this.options.storagePath}/${params.folder}/${params.name}`;
const folderPath = path.dirname(filePath);
await this.createFolder(folderPath);
return fs.writeFile(filePath, params.file);
}
}