diff options
Diffstat (limited to 'app/api/revision-upload')
| -rw-r--r-- | app/api/revision-upload/route.ts | 213 |
1 files changed, 213 insertions, 0 deletions
diff --git a/app/api/revision-upload/route.ts b/app/api/revision-upload/route.ts new file mode 100644 index 00000000..2138d674 --- /dev/null +++ b/app/api/revision-upload/route.ts @@ -0,0 +1,213 @@ +// app/api/revision-upload/route.ts +import { NextRequest, NextResponse } from 'next/server' +import { writeFile } from 'fs/promises' +import { join } from 'path' +import { v4 as uuidv4 } from 'uuid' +import path from 'path' +import db from '@/db/db' +import { documents, issueStages, revisions, documentAttachments } from '@/db/schema/vendorDocu' +import { and, eq } from 'drizzle-orm' + +export async function POST(request: NextRequest) { + try { + const formData = await request.formData() + + // FormData에서 데이터 추출 + const stage = formData.get("stage") as string | null + const revision = formData.get("revision") as string | null + const docIdStr = formData.get("documentId") as string + const docId = parseInt(docIdStr, 10) + const uploaderName = formData.get("uploaderName") as string | null + const comment = formData.get("comment") as string | null + const mode = formData.get("mode") as string || "new" // 'new' | 'append' + + // 파일들 추출 + const attachmentFiles = formData.getAll("attachments") as File[] + + // 유효성 검증 + if (!docId || Number.isNaN(docId)) { + return NextResponse.json( + { error: "Invalid or missing documentId" }, + { status: 400 } + ) + } + + if (!stage || !revision) { + return NextResponse.json( + { error: "Missing stage or revision" }, + { status: 400 } + ) + } + + if (attachmentFiles.length === 0) { + return NextResponse.json( + { error: "No files provided" }, + { status: 400 } + ) + } + + // 파일 크기 검증 (각 파일 최대 3GB) + const maxFileSize = 3 * 1024 * 1024 * 1024 // 3GB + for (const file of attachmentFiles) { + if (file.size > maxFileSize) { + return NextResponse.json( + { error: `파일 ${file.name}이 너무 큽니다. 최대 3GB까지 허용됩니다.` }, + { status: 400 } + ) + } + } + + // 트랜잭션 실행 + const result = await db.transaction(async (tx) => { + // (1) issueStageId 찾기 또는 생성 + let issueStageId: number + const stageRecord = await tx + .select() + .from(issueStages) + .where(and(eq(issueStages.stageName, stage), eq(issueStages.documentId, docId))) + .limit(1) + + if (!stageRecord.length) { + // Stage가 없으면 새로 생성 + const [newStage] = await tx + .insert(issueStages) + .values({ + documentId: docId, + stageName: stage, + updatedAt: new Date(), + }) + .returning() + + issueStageId = newStage.id + } else { + issueStageId = stageRecord[0].id + } + + // (2) Revision 찾기 또는 생성 + let revisionId: number + const revisionRecord = await tx + .select() + .from(revisions) + .where(and(eq(revisions.issueStageId, issueStageId), eq(revisions.revision, revision))) + .limit(1) + + const currentDate = new Date().toISOString().split('T')[0] // YYYY-MM-DD 형식 + + if (!revisionRecord.length || mode === 'new') { + // 새 리비전 생성 + const [newRevision] = await tx + .insert(revisions) + .values({ + issueStageId, + revision, + uploaderType: "vendor", // 항상 vendor로 고정 + uploaderName: uploaderName || undefined, + revisionStatus: "SUBMITTED", // 기본 상태 + submittedDate: currentDate, // 제출일 설정 + comment: comment || undefined, + updatedAt: new Date(), + }) + .returning() + + revisionId = newRevision.id + console.log("✅ 새 리비전 생성:", { revisionId, revision }) + } else { + // 기존 리비전에 파일 추가 (append 모드) + revisionId = revisionRecord[0].id + + // 기존 리비전 정보 업데이트 (코멘트나 업로더명 변경 가능) + await tx + .update(revisions) + .set({ + uploaderName: uploaderName || revisionRecord[0].uploaderName, + comment: comment || revisionRecord[0].comment, + updatedAt: new Date(), + }) + .where(eq(revisions.id, revisionId)) + + console.log("✅ 기존 리비전에 파일 추가:", { revisionId, revision }) + } + + // (3) 파일들 저장 및 DB 기록 + const uploadedFiles = [] + const baseDir = join(process.cwd(), "public", "documents") + + for (const file of attachmentFiles) { + if (file.size > 0) { + const originalName = file.name + const ext = path.extname(originalName) + const uniqueName = uuidv4() + ext + const savePath = join(baseDir, uniqueName) + + // 파일 저장 + const arrayBuffer = await file.arrayBuffer() + const buffer = Buffer.from(arrayBuffer) + await writeFile(savePath, buffer) + + // DB에 첨부파일 정보 저장 + const [attachmentRecord] = await tx + .insert(documentAttachments) + .values({ + revisionId, + fileName: originalName, + filePath: "/documents/" + uniqueName, + fileSize: file.size, + fileType: ext.replace('.', '').toLowerCase() || undefined, + updatedAt: new Date(), + }) + .returning() + + uploadedFiles.push({ + id: attachmentRecord.id, + fileName: originalName, + fileSize: file.size, + filePath: attachmentRecord.filePath, + }) + + console.log("✅ 파일 저장 완료:", originalName) + } + } + + // (4) Documents 테이블의 updatedAt 갱신 + await tx + .update(documents) + .set({ updatedAt: new Date() }) + .where(eq(documents.id, docId)) + + return { + revisionId, + stage, + revision, + uploaderName, + comment, + uploadedFiles, + mode, + } + }) + + console.log("✅ 리비전 업로드 완료:", { + documentId: docId, + stage, + revision, + filesCount: result.uploadedFiles.length, + mode + }) + + return NextResponse.json({ + success: true, + message: `${result.uploadedFiles.length}개 파일이 성공적으로 업로드되었습니다.`, + data: result, + }) + + } catch (error) { + console.error('❌ 리비전 업로드 오류:', error) + + return NextResponse.json( + { + error: 'Failed to upload revision', + details: error instanceof Error ? error.message : String(error), + }, + { status: 500 } + ) + } +}
\ No newline at end of file |
