feat: complete cmpp platform phases 0-5

This commit is contained in:
hectorzhao
2026-07-01 13:22:04 +08:00
parent 824a8b334f
commit ee926fea04
86 changed files with 10490 additions and 14 deletions
+25
View File
@@ -0,0 +1,25 @@
import { Body, Controller, Get, Post } from '@nestjs/common';
import { ApiTags } from '@nestjs/swagger';
import { TenantId } from '../common/tenant-id.decorator';
import { CreateFileObjectDto, CreatePresignedUploadDto, FilesService } from './files.service';
@ApiTags('files')
@Controller('admin/files')
export class FilesController {
constructor(private readonly files: FilesService) {}
@Get()
list(@TenantId() tenantId?: string) {
return this.files.list(tenantId);
}
@Post()
create(@Body() body: CreateFileObjectDto) {
return this.files.create(body);
}
@Post('presigned-upload')
createPresignedUpload(@Body() body: CreatePresignedUploadDto) {
return this.files.createPresignedUpload(body);
}
}
+11
View File
@@ -0,0 +1,11 @@
import { Module } from '@nestjs/common';
import { FilesController } from './files.controller';
import { FilesService } from './files.service';
import { ObjectStorageService } from './object-storage.service';
@Module({
controllers: [FilesController],
providers: [FilesService, ObjectStorageService],
exports: [FilesService, ObjectStorageService],
})
export class FilesModule {}
+60
View File
@@ -0,0 +1,60 @@
import { Injectable } from '@nestjs/common';
import { Prisma } from '@prisma/client';
import { PrismaService } from '../prisma/prisma.service';
import { ObjectStorageService } from './object-storage.service';
export interface CreateFileObjectDto {
tenantId?: string;
bucket: string;
objectKey: string;
fileName: string;
contentType: string;
sizeBytes: number | string;
checksum?: string;
purpose: string;
}
export interface CreatePresignedUploadDto {
objectKey: string;
expiresInSeconds?: number;
}
@Injectable()
export class FilesService {
constructor(
private readonly prisma: PrismaService,
private readonly objectStorage: ObjectStorageService,
) {}
list(tenantId?: string) {
return this.prisma.fileObject.findMany({
where: tenantId ? { tenantId } : undefined,
orderBy: { createdAt: 'desc' },
take: 100,
});
}
create(data: CreateFileObjectDto) {
const createData: Prisma.FileObjectUncheckedCreateInput = {
tenantId: data.tenantId,
bucket: data.bucket,
objectKey: data.objectKey,
fileName: data.fileName,
contentType: data.contentType,
sizeBytes: BigInt(data.sizeBytes),
checksum: data.checksum,
purpose: data.purpose,
};
return this.prisma.fileObject.create({ data: createData });
}
async createPresignedUpload(data: CreatePresignedUploadDto) {
const uploadUrl = await this.objectStorage.presignedPutObject(data.objectKey, data.expiresInSeconds ?? 3600);
return {
bucket: this.objectStorage.getBucket(),
objectKey: data.objectKey,
uploadUrl,
expiresInSeconds: data.expiresInSeconds ?? 3600,
};
}
}
+30
View File
@@ -0,0 +1,30 @@
import { Injectable } from '@nestjs/common';
import { ConfigService } from '@nestjs/config';
import { Client } from 'minio';
@Injectable()
export class ObjectStorageService {
private readonly client: Client;
private readonly bucket: string;
constructor(config: ConfigService) {
const endpoint = config.get<string>('MINIO_ENDPOINT') ?? 'localhost:9000';
const [endPoint, portText] = endpoint.split(':');
this.bucket = config.get<string>('MINIO_BUCKET') ?? 'cmpp-platform';
this.client = new Client({
endPoint,
port: Number(portText ?? 9000),
useSSL: config.get<string>('MINIO_USE_SSL') === 'true',
accessKey: config.get<string>('MINIO_ACCESS_KEY') ?? 'cmpp_minio',
secretKey: config.get<string>('MINIO_SECRET_KEY') ?? 'cmpp_minio_password',
});
}
presignedPutObject(objectKey: string, expirySeconds = 3600) {
return this.client.presignedPutObject(this.bucket, objectKey, expirySeconds);
}
getBucket() {
return this.bucket;
}
}