mirror of
https://git.meshkee.com/Meshkee/backend.git
synced 2026-08-12 06:40:58 +04:30
Initial commit: Meshkee CMS API
NestJS backend with Prisma, Docker Compose for Postgres/Redis, and deploy docs for the production VM.
This commit is contained in:
@@ -0,0 +1,85 @@
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import { ConfigService } from '@nestjs/config';
|
||||
import {
|
||||
DeleteObjectCommand,
|
||||
GetObjectCommand,
|
||||
PutObjectCommand,
|
||||
S3Client,
|
||||
} from '@aws-sdk/client-s3';
|
||||
import { StoredObject, UploadObjectInput } from './storage.types';
|
||||
|
||||
@Injectable()
|
||||
export class S3StorageDriver {
|
||||
private readonly client: S3Client;
|
||||
private readonly bucket: string;
|
||||
private readonly publicUrlBase: string;
|
||||
private readonly storageDisk = 's3';
|
||||
|
||||
constructor(private readonly config: ConfigService) {
|
||||
const endpoint = this.config.getOrThrow<string>('S3_ENDPOINT');
|
||||
const region = this.config.get<string>('S3_REGION', 'us-east-1');
|
||||
const forcePathStyle =
|
||||
this.config.get<string>('S3_FORCE_PATH_STYLE', 'true') === 'true';
|
||||
|
||||
this.bucket = this.config.getOrThrow<string>('S3_BUCKET');
|
||||
this.publicUrlBase = this.config
|
||||
.getOrThrow<string>('S3_PUBLIC_URL')
|
||||
.replace(/\/$/, '');
|
||||
|
||||
this.client = new S3Client({
|
||||
endpoint,
|
||||
region,
|
||||
forcePathStyle,
|
||||
credentials: {
|
||||
accessKeyId: this.config.getOrThrow<string>('S3_ACCESS_KEY_ID'),
|
||||
secretAccessKey: this.config.getOrThrow<string>('S3_SECRET_ACCESS_KEY'),
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
async upload(input: UploadObjectInput): Promise<StoredObject> {
|
||||
const key = input.key.replace(/^\/+/, '');
|
||||
|
||||
await this.client.send(
|
||||
new PutObjectCommand({
|
||||
Bucket: this.bucket,
|
||||
Key: key,
|
||||
Body: input.body,
|
||||
ContentType: input.contentType,
|
||||
}),
|
||||
);
|
||||
|
||||
return {
|
||||
storageDisk: this.storageDisk,
|
||||
storagePath: key,
|
||||
publicUrl: `${this.publicUrlBase}/${key}`,
|
||||
};
|
||||
}
|
||||
|
||||
async getBuffer(storagePath: string): Promise<Buffer> {
|
||||
const key = storagePath.replace(/^\/+/, '');
|
||||
const result = await this.client.send(
|
||||
new GetObjectCommand({
|
||||
Bucket: this.bucket,
|
||||
Key: key,
|
||||
}),
|
||||
);
|
||||
|
||||
if (!result.Body) {
|
||||
throw new Error(`Empty object body for ${key}`);
|
||||
}
|
||||
|
||||
return Buffer.from(await result.Body.transformToByteArray());
|
||||
}
|
||||
|
||||
async delete(storagePath: string): Promise<void> {
|
||||
const key = storagePath.replace(/^\/+/, '');
|
||||
|
||||
await this.client.send(
|
||||
new DeleteObjectCommand({
|
||||
Bucket: this.bucket,
|
||||
Key: key,
|
||||
}),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
import { Global, Module } from '@nestjs/common';
|
||||
import { S3StorageDriver } from './s3-storage.driver';
|
||||
import { StorageService } from './storage.service';
|
||||
|
||||
@Global()
|
||||
@Module({
|
||||
providers: [S3StorageDriver, StorageService],
|
||||
exports: [StorageService],
|
||||
})
|
||||
export class StorageModule {}
|
||||
@@ -0,0 +1,28 @@
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import { S3StorageDriver } from './s3-storage.driver';
|
||||
import { StoredObject, UploadObjectInput } from './storage.types';
|
||||
|
||||
@Injectable()
|
||||
export class StorageService {
|
||||
constructor(private readonly s3: S3StorageDriver) {}
|
||||
|
||||
upload(input: UploadObjectInput): Promise<StoredObject> {
|
||||
return this.s3.upload(input);
|
||||
}
|
||||
|
||||
getBuffer(storagePath: string, storageDisk: string): Promise<Buffer> {
|
||||
if (storageDisk !== 's3') {
|
||||
return Promise.reject(new Error(`Unsupported storage disk: ${storageDisk}`));
|
||||
}
|
||||
|
||||
return this.s3.getBuffer(storagePath);
|
||||
}
|
||||
|
||||
delete(storagePath: string, storageDisk: string): Promise<void> {
|
||||
if (storageDisk !== 's3') {
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
return this.s3.delete(storagePath);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
export interface StoredObject {
|
||||
storageDisk: string;
|
||||
storagePath: string;
|
||||
publicUrl: string;
|
||||
}
|
||||
|
||||
export interface UploadObjectInput {
|
||||
key: string;
|
||||
body: Buffer;
|
||||
contentType: string;
|
||||
}
|
||||
Reference in New Issue
Block a user