Files
genome2025/backend/src/admin/admin.controller.ts
2026-01-06 17:23:53 +09:00

117 lines
4.1 KiB
TypeScript

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} 파일 업로드 완료`);
}
}
}