feat: refactor storage module (#521)

* feat: refactor storage module

* fix: folder need to be kebab case

* fix: comment wrong auth
This commit is contained in:
Jérémy M
2023-07-05 16:34:39 +02:00
committed by GitHub
parent 6e1ffdcc72
commit 2961fed932
74 changed files with 330 additions and 355 deletions

View File

@ -0,0 +1,11 @@
import { Readable } from 'stream';
export interface StorageDriver {
read(params: { folderPath: string; filename: string }): Promise<Readable>;
write(params: {
file: Buffer | Uint8Array | string;
name: string;
folder: string;
mimeType: string | undefined;
}): Promise<void>;
}

View File

@ -0,0 +1,57 @@
import * as fs from 'fs/promises';
import { createReadStream, existsSync } from 'fs';
import { join, dirname } from 'path';
import { StorageDriver } from './interfaces/storage-driver.interface';
import { Readable } from 'stream';
import { kebabCase } from 'src/utils/kebab-case';
export interface LocalDriverOptions {
storagePath: string;
}
export class LocalDriver implements StorageDriver {
private options: LocalDriverOptions;
constructor(options: LocalDriverOptions) {
this.options = options;
}
async createFolder(path: string) {
if (existsSync(path)) {
return;
}
return fs.mkdir(path, { recursive: true });
}
async write(params: {
file: Buffer | Uint8Array | string;
name: string;
folder: string;
mimeType: string | undefined;
}): Promise<void> {
const filePath = join(
`${this.options.storagePath}/`,
kebabCase(params.folder),
params.name,
);
const folderPath = dirname(filePath);
await this.createFolder(folderPath);
await fs.writeFile(filePath, params.file);
}
async read(params: {
folderPath: string;
filename: string;
}): Promise<Readable> {
const filePath = join(
`${this.options.storagePath}/`,
params.folderPath,
params.filename,
);
return createReadStream(filePath);
}
}

View File

@ -0,0 +1,99 @@
import {
CreateBucketCommandInput,
GetObjectCommand,
HeadBucketCommandInput,
NotFound,
PutObjectCommand,
S3,
S3ClientConfig,
} from '@aws-sdk/client-s3';
import { StorageDriver } from './interfaces/storage-driver.interface';
import { Readable } from 'stream';
import { kebabCase } from 'src/utils/kebab-case';
export interface S3DriverOptions extends S3ClientConfig {
bucketName: string;
region: string;
}
export class S3Driver implements StorageDriver {
private s3Client: S3;
private bucketName: string;
constructor(options: S3DriverOptions) {
const { bucketName, region, ...s3Options } = options;
if (!bucketName || !region) {
return;
}
this.s3Client = new S3({ ...s3Options, region });
this.bucketName = bucketName;
}
public get client(): S3 {
return this.s3Client;
}
async write(params: {
file: Buffer | Uint8Array | string;
name: string;
folder: string;
mimeType: string | undefined;
}): Promise<void> {
const command = new PutObjectCommand({
Key: `${kebabCase(params.folder)}/${params.name}`,
Body: params.file,
ContentType: params.mimeType,
Bucket: this.bucketName,
});
await this.createBucket({ Bucket: this.bucketName });
await this.s3Client.send(command);
}
async read(params: {
folderPath: string;
filename: string;
}): Promise<Readable> {
const command = new GetObjectCommand({
Key: `${params.folderPath}/${params.filename}`,
Bucket: this.bucketName,
});
const file = await this.s3Client.send(command);
if (!file || !file.Body || !(file.Body instanceof Readable)) {
throw new Error('Unable to get file stream');
}
return Readable.from(file.Body);
}
async checkBucketExists(args: HeadBucketCommandInput) {
try {
await this.s3Client.headBucket(args);
return true;
} catch (error) {
console.log(error);
if (error instanceof NotFound) {
return false;
}
throw error;
}
}
async createBucket(args: CreateBucketCommandInput) {
const exist = await this.checkBucketExists({
Bucket: args.Bucket,
});
if (exist) {
return;
}
return this.s3Client.createBucket(args);
}
}