import { BadRequestException, Body, Controller, Get, Post, UploadedFile, UseInterceptors, Logger } from "@nestjs/common"; import { AdminService } from "./admin.service"; import { FileInterceptor } from "@nestjs/platform-express"; import { basename, extname, join } from "path"; import { BaseResultDto } from "src/common/dto/base.result.dto"; import { diskStorage } from "multer"; import * as fs from 'fs/promises'; import { randomUUID } from "crypto"; import { tmpdir } from "os"; /** ※업로드 관련 기능 추후 공통화 처리 필요. **/ const ALLOWED_EXTENSIONS = ['.xlsx', '.txt', '.csv', '.xls']; // 파일 업로드 허용 확장자 const ALLOWED_MIME_TYPES = [ // 파일 업로드 허용 MIME 타입 'text/plain', // XLSX 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet', // XLS (구형 + CSV도 섞여 나옴) 'application/vnd.ms-excel', // CSV 계열 'text/csv', 'application/csv', 'text/plain', // 한컴 'application/haansoftxls', ]; @Controller('admin') export class AdminController { constructor(private readonly adminService: AdminService) {} private readonly logger = new Logger(AdminController.name); @Get('dashboard') getDashboard() { return null; } @Post('batchUpload') @UseInterceptors(FileInterceptor('file', { storage: diskStorage({ destination: async (req, file, callback) => { // 환경 변수가 없으면 시스템 임시 디렉터리 사용 const uploadDir = process.env.UPLOAD_DESTINATION ? join(process.env.UPLOAD_DESTINATION, 'tmp') : join(tmpdir(), 'genome2025-uploads'); try { // 디렉터리가 없으면 생성 await fs.mkdir(uploadDir, { recursive: true }); callback(null, uploadDir); } catch (error) { callback(error, null); } }, filename: (req, file, callback) => { const ext = extname(file.originalname).toLowerCase(); callback(null, `${randomUUID()}-${Date.now()}${ext}`); }, }), fileFilter: (req, file, callback) => { // 파일 업로드 필터링 const ext = extname(file.originalname).toLowerCase(); const mime = file.mimetype; if (!ALLOWED_EXTENSIONS.includes(ext)) { // 허용되지 않은 확장자 필터링 return callback( new BadRequestException(`허용되지 않은 확장자: ${ext}`), false, ); } if (!ALLOWED_MIME_TYPES.includes(mime)) { // 허용되지 않은 MIME 타입 필터링 return callback( new BadRequestException(`허용되지 않은 MIME 타입: ${mime}`), false, ); } callback(null, true); } })) async batchUpload(@UploadedFile() file: Express.Multer.File, @Body('div') div: string) { let divName = ''; try { if (!file?.path){ throw new BadRequestException('파일 업로드 실패') }; if (div === 'genome-result') { // 유전체 분석 결과(DGV) divName = '유전체 분석 결과(DGV)'; await this.adminService.batchInsertGenomeResult(file); }else if (div === 'snp-typing') { // 개체별 SNP 타이핑 결과(유전자 타이핑 결과) divName = '개체별 SNP 타이핑 결과(유전자 타이핑 결과)'; await this.adminService.batchInsertSnpTyping(file); }else if (div === 'mpt-result') { // MPT 분석결과(종합혈액화학검사결과서) divName = 'MPT 분석결과(종합혈액화학검사결과서)'; await this.adminService.batchInsertMptResult(file); } // else if (div === 'animal-info') { // 소 정보 입력은 어디서 처리? // divName = '소 정보 입력'; // return this.adminService.batchUploadAnimalInfo(file); // } return BaseResultDto.ok(`${divName} 파일 업로드 성공.\n데이터 입력 중...`, 'SUCCESS', 'OK'); } catch (error) { return BaseResultDto.fail(`${divName} 파일 업로드 실패.\n${error.message}`, 'FAIL'); } finally { await fs.unlink(file.path).catch(() => {}); // 파일 삭제 this.logger.log(`[batchUpload] ${divName} 파일 업로드 완료`); } } }