245 lines
7.4 KiB
TypeScript
245 lines
7.4 KiB
TypeScript
import {
|
|
Controller,
|
|
Post,
|
|
Delete,
|
|
Param,
|
|
UseInterceptors,
|
|
UploadedFile,
|
|
Body,
|
|
HttpCode,
|
|
HttpStatus,
|
|
UseGuards,
|
|
ParseFilePipe,
|
|
MaxFileSizeValidator,
|
|
FileTypeValidator,
|
|
} from '@nestjs/common';
|
|
import { FileInterceptor } from '@nestjs/platform-express';
|
|
import {
|
|
ApiTags,
|
|
ApiOperation,
|
|
ApiResponse,
|
|
ApiConsumes,
|
|
ApiBody,
|
|
ApiBearerAuth,
|
|
ApiParam,
|
|
} from '@nestjs/swagger';
|
|
import { StorageService } from './storage.service';
|
|
import { UploadFileDto } from './dto/upload-file.dto';
|
|
import { JwtAuthGuard } from '../auth/guards/jwt-auth.guard';
|
|
import { RolesGuard } from '../auth/guards/roles.guard';
|
|
import { Roles } from '../auth/decorators/roles.decorator';
|
|
import type { FileUpload } from './interfaces/storage-provider.interface';
|
|
|
|
@ApiTags('storage')
|
|
@Controller('storage')
|
|
export class StorageController {
|
|
constructor(private readonly storageService: StorageService) {}
|
|
|
|
/**
|
|
* 管理员上传文件(需要鉴权)
|
|
* 用于上传券商Logo等基础数据
|
|
*/
|
|
@Post('upload')
|
|
@HttpCode(HttpStatus.OK)
|
|
@UseGuards(JwtAuthGuard, RolesGuard)
|
|
@Roles('admin', 'super_admin')
|
|
@ApiBearerAuth()
|
|
@UseInterceptors(FileInterceptor('file'))
|
|
@ApiConsumes('multipart/form-data')
|
|
@ApiOperation({
|
|
summary: '管理员上传文件',
|
|
description: '上传单个文件,需要管理员权限,用于上传券商Logo等基础数据',
|
|
})
|
|
@ApiBody({
|
|
schema: {
|
|
type: 'object',
|
|
properties: {
|
|
file: {
|
|
type: 'string',
|
|
format: 'binary',
|
|
description: '要上传的文件',
|
|
},
|
|
folder: {
|
|
type: 'string',
|
|
enum: ['broker', 'user', 'temp'],
|
|
description: '存储文件夹',
|
|
},
|
|
filename: {
|
|
type: 'string',
|
|
description: '自定义文件名',
|
|
},
|
|
},
|
|
},
|
|
})
|
|
@ApiResponse({
|
|
status: 200,
|
|
description: '上传成功',
|
|
schema: {
|
|
type: 'object',
|
|
properties: {
|
|
path: {
|
|
type: 'string',
|
|
example: 'broker/1234567890-abcdef-broker-logo.jpg',
|
|
},
|
|
url: {
|
|
type: 'string',
|
|
example:
|
|
'http://localhost:3200/uploads/broker/1234567890-abcdef-broker-logo.jpg',
|
|
},
|
|
filename: {
|
|
type: 'string',
|
|
example: '1234567890-abcdef-broker-logo.jpg',
|
|
},
|
|
size: {
|
|
type: 'number',
|
|
example: 102400,
|
|
},
|
|
mimetype: {
|
|
type: 'string',
|
|
example: 'image/jpeg',
|
|
},
|
|
},
|
|
},
|
|
})
|
|
@ApiResponse({ status: 400, description: '文件格式或大小不符合要求' })
|
|
@ApiResponse({ status: 401, description: '未授权' })
|
|
@ApiResponse({ status: 403, description: '权限不足' })
|
|
async uploadAdmin(
|
|
@UploadedFile(
|
|
new ParseFilePipe({
|
|
validators: [
|
|
new MaxFileSizeValidator({ maxSize: 5 * 1024 * 1024 }), // 5MB
|
|
new FileTypeValidator({
|
|
fileType: /^image\/(jpeg|jpg|png|gif|webp)$/,
|
|
}),
|
|
],
|
|
}),
|
|
)
|
|
file: FileUpload,
|
|
@Body() uploadDto: UploadFileDto,
|
|
) {
|
|
const options = {
|
|
folder: uploadDto.folder || 'temp',
|
|
maxSize: 5 * 1024 * 1024, // 5MB
|
|
allowedMimeTypes: [
|
|
'image/jpeg',
|
|
'image/jpg',
|
|
'image/png',
|
|
'image/gif',
|
|
'image/webp',
|
|
],
|
|
filename: uploadDto.filename,
|
|
};
|
|
|
|
return await this.storageService.upload(file, options);
|
|
}
|
|
|
|
/**
|
|
* 用户上传头像(不需要鉴权)
|
|
* 用于用户注册或更新头像
|
|
*/
|
|
@Post('upload/avatar')
|
|
@HttpCode(HttpStatus.OK)
|
|
@UseInterceptors(FileInterceptor('file'))
|
|
@ApiConsumes('multipart/form-data')
|
|
@ApiOperation({
|
|
summary: '上传用户头像',
|
|
description: '上传用户头像,不需要鉴权,用于用户注册或更新头像',
|
|
})
|
|
@ApiBody({
|
|
schema: {
|
|
type: 'object',
|
|
properties: {
|
|
file: {
|
|
type: 'string',
|
|
format: 'binary',
|
|
description: '要上传的头像文件',
|
|
},
|
|
},
|
|
},
|
|
})
|
|
@ApiResponse({
|
|
status: 200,
|
|
description: '上传成功',
|
|
schema: {
|
|
type: 'object',
|
|
properties: {
|
|
path: {
|
|
type: 'string',
|
|
example: 'user/1234567890-abcdef-avatar.jpg',
|
|
},
|
|
url: {
|
|
type: 'string',
|
|
example:
|
|
'http://localhost:3200/uploads/user/1234567890-abcdef-avatar.jpg',
|
|
},
|
|
filename: {
|
|
type: 'string',
|
|
example: '1234567890-abcdef-avatar.jpg',
|
|
},
|
|
size: {
|
|
type: 'number',
|
|
example: 102400,
|
|
},
|
|
mimetype: {
|
|
type: 'string',
|
|
example: 'image/jpeg',
|
|
},
|
|
},
|
|
},
|
|
})
|
|
@ApiResponse({ status: 400, description: '文件格式或大小不符合要求' })
|
|
async uploadAvatar(
|
|
@UploadedFile(
|
|
new ParseFilePipe({
|
|
validators: [
|
|
new MaxFileSizeValidator({ maxSize: 2 * 1024 * 1024 }), // 2MB
|
|
new FileTypeValidator({
|
|
fileType: /^image\/(jpeg|jpg|png|gif|webp)$/,
|
|
}),
|
|
],
|
|
}),
|
|
)
|
|
file: FileUpload,
|
|
) {
|
|
const options = {
|
|
folder: 'user', // 用户头像固定存储在 user 文件夹
|
|
maxSize: 2 * 1024 * 1024, // 2MB(头像文件限制更小)
|
|
allowedMimeTypes: [
|
|
'image/jpeg',
|
|
'image/jpg',
|
|
'image/png',
|
|
'image/gif',
|
|
'image/webp',
|
|
],
|
|
};
|
|
|
|
return await this.storageService.upload(file, options);
|
|
}
|
|
|
|
/**
|
|
* 删除文件(需要管理员权限)
|
|
*/
|
|
@Delete(':path(*)')
|
|
@HttpCode(HttpStatus.NO_CONTENT)
|
|
@UseGuards(JwtAuthGuard, RolesGuard)
|
|
@Roles('admin', 'super_admin')
|
|
@ApiBearerAuth()
|
|
@ApiOperation({
|
|
summary: '删除文件',
|
|
description: '根据文件路径删除文件,需要管理员权限',
|
|
})
|
|
@ApiParam({
|
|
name: 'path',
|
|
description: '文件路径(相对路径)',
|
|
example: 'broker/1234567890-abcdef-broker-logo.jpg',
|
|
})
|
|
@ApiResponse({ status: 204, description: '删除成功' })
|
|
@ApiResponse({ status: 404, description: '文件不存在' })
|
|
@ApiResponse({ status: 401, description: '未授权' })
|
|
@ApiResponse({ status: 403, description: '权限不足' })
|
|
async delete(@Param('path') filePath: string) {
|
|
await this.storageService.delete(filePath);
|
|
}
|
|
}
|