Files
Train/apps/api/src/photos/photos.controller.ts
T
2026-06-16 00:55:20 +08:00

102 lines
2.6 KiB
TypeScript

import {
BadRequestException,
Body,
Controller,
Delete,
Get,
Param,
ParseIntPipe,
Post,
UploadedFile,
UseGuards,
UseInterceptors,
} from '@nestjs/common';
import { FileInterceptor } from '@nestjs/platform-express';
import { diskStorage } from 'multer';
import { extname } from 'path';
import { randomBytes } from 'crypto';
import { PhotosService } from './photos.service';
import { uploadDir } from './uploads';
import {
CurrentUser,
JwtAuthGuard,
Roles,
RolesGuard,
type JwtPayload,
} from '../auth/roles';
const ALLOWED = /\.(jpe?g|png|webp|gif)$/i;
const storage = diskStorage({
destination: (_req, _file, cb) => cb(null, uploadDir()),
filename: (_req, file, cb) =>
cb(null, `${Date.now()}-${randomBytes(6).toString('hex')}${extname(file.originalname).toLowerCase()}`),
});
@Controller('api')
export class PhotosController {
constructor(private readonly photos: PhotosService) {}
/** 公共图库:某车型的全部照片(所有人可看)。*/
@Get('models/:id/photos')
list(@Param('id', ParseIntPipe) id: number) {
return this.photos.listForModel(id);
}
/** 管理员候选审图:全站待确认照片。*/
@Get('photos/candidates')
@UseGuards(JwtAuthGuard, RolesGuard)
@Roles('admin')
candidates() {
return this.photos.listCandidates();
}
/** 管理员上传到共享图库。*/
@Post('models/:id/photos')
@UseGuards(JwtAuthGuard, RolesGuard)
@Roles('admin')
@UseInterceptors(
FileInterceptor('file', {
storage,
limits: { fileSize: 8 * 1024 * 1024 },
fileFilter: (_req, file, cb) =>
ALLOWED.test(file.originalname)
? cb(null, true)
: cb(new BadRequestException('仅支持 jpg/png/webp/gif'), false),
}),
)
upload(
@Param('id', ParseIntPipe) id: number,
@UploadedFile() file: Express.Multer.File,
@Body('caption') caption: string,
@CurrentUser() user: JwtPayload,
) {
if (!file) throw new BadRequestException('未收到文件');
return this.photos.add(id, user.sub, file.filename, {
caption: caption ?? '',
status: 'confirmed',
});
}
@Post('photos/:pid/confirm')
@UseGuards(JwtAuthGuard, RolesGuard)
@Roles('admin')
confirm(@Param('pid', ParseIntPipe) pid: number) {
return this.photos.confirm(pid);
}
@Post('photos/:pid/feature')
@UseGuards(JwtAuthGuard, RolesGuard)
@Roles('admin')
feature(@Param('pid', ParseIntPipe) pid: number) {
return this.photos.setFeatured(pid);
}
@Delete('photos/:pid')
@UseGuards(JwtAuthGuard, RolesGuard)
@Roles('admin')
remove(@Param('pid', ParseIntPipe) pid: number) {
return this.photos.remove(pid);
}
}