// app/api/evaluation/attachments/route.ts import { NextRequest, NextResponse } from "next/server"; import db from "@/db/db"; import { reviewerEvaluationAttachments, reviewerEvaluationDetails, reviewerEvaluations } from "@/db/schema"; import { eq, and } from "drizzle-orm"; import { getServerSession } from "next-auth/next" import { authOptions } from "@/app/api/auth/[...nextauth]/route" import { saveFile } from "@/lib/file-stroage"; // 파일 업로드 export async function POST(request: NextRequest) { try { // 인증 확인 const session = await getServerSession(authOptions); if (!session?.user?.id) { return NextResponse.json( { success: false, error: "인증이 필요합니다." }, { status: 401 } ); } const formData = await request.formData(); const file = formData.get("file") as File; const questionId = formData.get("questionId") as string; const evaluationId = formData.get("evaluationId") as string; const description = formData.get("description") as string | null; if (!file || !questionId || !evaluationId) { return NextResponse.json( { success: false, error: "필수 파라미터가 누락되었습니다." }, { status: 400 } ); } // 파일 크기 제한 (10MB) const maxSize = 10 * 1024 * 1024; if (file.size > maxSize) { return NextResponse.json( { success: false, error: "파일 크기는 10MB를 초과할 수 없습니다." }, { status: 400 } ); } // 파일 타입 제한 const allowedTypes = [ 'application/pdf', 'application/msword', 'application/vnd.openxmlformats-officedocument.wordprocessingml.document', 'application/vnd.hancom.hwp', 'application/vnd.ms-excel', 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet', 'image/jpeg', 'image/png', 'image/gif' ]; if (!allowedTypes.includes(file.type)) { return NextResponse.json( { success: false, error: "지원하지 않는 파일 형식입니다." }, { status: 400 } ); } const result = await db.transaction(async (tx) => { // 1. 해당 평가 항목에 대한 reviewerEvaluationDetailId 찾기 const evaluationDetail = await tx .select({ id: reviewerEvaluationDetails.id, reviewerEvaluationId: reviewerEvaluationDetails.reviewerEvaluationId, }) .from(reviewerEvaluationDetails) .innerJoin( reviewerEvaluations, eq(reviewerEvaluationDetails.reviewerEvaluationId, reviewerEvaluations.id) ) .where( and( eq(reviewerEvaluations.id, parseInt(evaluationId)), // questionId는 실제로는 criteriaId를 의미 // 여기서는 regEvalCriteriaDetailsId를 통해 연결해야 함 ) ) .limit(1); if (evaluationDetail.length === 0) { throw new Error("평가 세부사항을 찾을 수 없습니다."); } // 2. 파일 저장 const fileResult = await saveFile({ file, directory: "evaluation-attachments", originalName: file.name, }); if (!fileResult.success) { throw new Error(fileResult.error || "파일 저장에 실패했습니다."); } // 3. DB에 첨부파일 정보 저장 const [attachment] = await tx .insert(reviewerEvaluationAttachments) .values({ reviewerEvaluationDetailId: evaluationDetail[0].id, originalFileName: file.name, storedFileName: fileResult.fileName!, filePath: fileResult.filePath!, publicPath: fileResult.publicPath!, fileSize: file.size, mimeType: file.type, fileExtension: file.name.split('.').pop()?.toLowerCase() || '', description: description || null, uploadedBy: parseInt(session.user.id), }) .returning({ id: reviewerEvaluationAttachments.id, originalFileName: reviewerEvaluationAttachments.originalFileName, publicPath: reviewerEvaluationAttachments.publicPath, fileSize: reviewerEvaluationAttachments.fileSize, description: reviewerEvaluationAttachments.description, createdAt: reviewerEvaluationAttachments.createdAt, }); return attachment; }); return NextResponse.json({ success: true, attachment: result, }); } catch (error) { console.error("파일 업로드 실패:", error); return NextResponse.json( { success: false, error: error instanceof Error ? error.message : "파일 업로드 중 오류가 발생했습니다.", }, { status: 500 } ); } } // 특정 질문의 첨부파일 목록 조회 export async function GET(request: NextRequest) { try { const session = await getServerSession(authOptions); if (!session?.user?.id) { return NextResponse.json( { success: false, error: "인증이 필요합니다." }, { status: 401 } ); } const { searchParams } = new URL(request.url); const questionId = searchParams.get("questionId"); const evaluationId = searchParams.get("evaluationId"); if (!questionId || !evaluationId) { return NextResponse.json( { success: false, error: "필수 파라미터가 누락되었습니다." }, { status: 400 } ); } const attachments = await db .select({ id: reviewerEvaluationAttachments.id, originalFileName: reviewerEvaluationAttachments.originalFileName, publicPath: reviewerEvaluationAttachments.publicPath, fileSize: reviewerEvaluationAttachments.fileSize, description: reviewerEvaluationAttachments.description, createdAt: reviewerEvaluationAttachments.createdAt, }) .from(reviewerEvaluationAttachments) .innerJoin( reviewerEvaluationDetails, eq(reviewerEvaluationAttachments.reviewerEvaluationDetailId, reviewerEvaluationDetails.id) ) .innerJoin( reviewerEvaluations, eq(reviewerEvaluationDetails.reviewerEvaluationId, reviewerEvaluations.id) ) .where(eq(reviewerEvaluations.id, parseInt(evaluationId))); return NextResponse.json({ success: true, attachments, }); } catch (error) { console.error("첨부파일 조회 실패:", error); return NextResponse.json( { success: false, error: "첨부파일 조회 중 오류가 발생했습니다.", }, { status: 500 } ); } }