diff options
Diffstat (limited to 'app/api')
| -rw-r--r-- | app/api/vendor-evaluation/delete-attachment/[id]/route.ts | 87 | ||||
| -rw-r--r-- | app/api/vendor-evaluation/upload-attachment/route.ts | 106 |
2 files changed, 193 insertions, 0 deletions
diff --git a/app/api/vendor-evaluation/delete-attachment/[id]/route.ts b/app/api/vendor-evaluation/delete-attachment/[id]/route.ts new file mode 100644 index 00000000..f899883c --- /dev/null +++ b/app/api/vendor-evaluation/delete-attachment/[id]/route.ts @@ -0,0 +1,87 @@ +// app/api/delete-attachment/[id]/route.ts +import { NextRequest, NextResponse } from 'next/server' +import { unlink } from 'fs/promises' +import { join } from 'path' +import db from '@/db/db' +import { vendorEvaluationAttachments, generalEvaluationResponses } from '@/db/schema' +import { eq, and } from 'drizzle-orm' + +export async function DELETE( + request: NextRequest, + { params }: { params: { id: string } } +) { + try { + const attachmentId = parseInt(params.id) + + if (isNaN(attachmentId)) { + return NextResponse.json({ error: '유효하지 않은 첨부파일 ID입니다.' }, { status: 400 }) + } + + // 1. 파일 정보 조회 + const [attachment] = await db + .select() + .from(vendorEvaluationAttachments) + .where( + and( + eq(vendorEvaluationAttachments.id, attachmentId), + eq(vendorEvaluationAttachments.isActive, true) + ) + ) + + if (!attachment) { + return NextResponse.json({ error: '첨부파일을 찾을 수 없습니다.' }, { status: 404 }) + } + + // 2. 실제 파일 삭제 + try { + const fullPath = join(process.cwd(), 'public', attachment.filePath) + await unlink(fullPath) + } catch (fileError) { + console.warn('파일 삭제 실패 (파일이 이미 없을 수 있음):', fileError) + // 파일 삭제 실패해도 DB 레코드는 삭제 진행 + } + + // 3. DB에서 소프트 삭제 + await db + .update(vendorEvaluationAttachments) + .set({ + isActive: false, + updatedAt: new Date() + }) + .where(eq(vendorEvaluationAttachments.id, attachmentId)) + + // 4. 해당 응답의 첨부파일 상태 업데이트 + if (attachment.generalEvaluationResponseId) { + // 남은 활성 첨부파일 개수 확인 + const remainingAttachments = await db + .select() + .from(vendorEvaluationAttachments) + .where( + and( + eq(vendorEvaluationAttachments.generalEvaluationResponseId, attachment.generalEvaluationResponseId), + eq(vendorEvaluationAttachments.isActive, true) + ) + ) + + const hasAttachments = remainingAttachments.length > 0 + + // generalEvaluationResponses 테이블 업데이트 + await db + .update(generalEvaluationResponses) + .set({ + hasAttachments, + updatedAt: new Date() + }) + .where(eq(generalEvaluationResponses.id, attachment.generalEvaluationResponseId)) + } + + return NextResponse.json({ + success: true, + message: '파일이 성공적으로 삭제되었습니다.' + }) + + } catch (error) { + console.error('파일 삭제 오류:', error) + return NextResponse.json({ error: '파일 삭제에 실패했습니다.' }, { status: 500 }) + } +}
\ No newline at end of file diff --git a/app/api/vendor-evaluation/upload-attachment/route.ts b/app/api/vendor-evaluation/upload-attachment/route.ts new file mode 100644 index 00000000..3fe8164c --- /dev/null +++ b/app/api/vendor-evaluation/upload-attachment/route.ts @@ -0,0 +1,106 @@ +// app/api/upload-attachment/route.ts +import { NextRequest, NextResponse } from 'next/server' +import { writeFile, mkdir } from 'fs/promises' +import { join } from 'path' +import { randomUUID } from 'crypto' +import db from '@/db/db' +import { vendorEvaluationAttachments } from '@/db/schema' + +export async function POST(request: NextRequest) { + try { + const formData = await request.formData() + const files = formData.getAll('files') as File[] + const submissionId = formData.get('submissionId') as string + const responseId = formData.get('responseId') as string + const uploadedBy = formData.get('uploadedBy') as string + + if (!files || files.length === 0) { + return NextResponse.json({ error: '파일이 선택되지 않았습니다.' }, { status: 400 }) + } + + if (!submissionId || !responseId || !uploadedBy) { + return NextResponse.json({ error: '필수 매개변수가 누락되었습니다.' }, { status: 400 }) + } + + const uploadedFiles = [] + + // uploads 디렉토리 생성 (없는 경우) + const uploadsDir = join(process.cwd(), 'public', 'uploads', 'attachments') + try { + await mkdir(uploadsDir, { recursive: true }) + } catch (error) { + // 디렉토리가 이미 존재하는 경우 무시 + } + + for (const file of files) { + // 파일 검증 + if (file.size > 10 * 1024 * 1024) { // 10MB 제한 + return NextResponse.json({ error: `파일 ${file.name}이 너무 큽니다. (최대 10MB)` }, { status: 400 }) + } + + // 파일 확장자 검증 (선택적) + const allowedTypes = [ + 'image/jpeg', 'image/png', 'image/gif', 'image/webp', + 'application/pdf', + 'application/msword', 'application/vnd.openxmlformats-officedocument.wordprocessingml.document', + 'application/vnd.ms-excel', 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet', + 'text/plain', 'text/csv' + ] + + if (!allowedTypes.includes(file.type)) { + return NextResponse.json({ error: `지원되지 않는 파일 형식입니다: ${file.name}` }, { status: 400 }) + } + + // 고유한 파일명 생성 + const fileExtension = file.name.split('.').pop() || '' + const storedFileName = `${randomUUID()}.${fileExtension}` + const filePath = join('uploads', 'attachments', storedFileName) + const fullPath = join(process.cwd(), 'public', filePath) + + try { + // 파일 저장 + const bytes = await file.arrayBuffer() + const buffer = Buffer.from(bytes) + await writeFile(fullPath, buffer) + + // 데이터베이스에 파일 정보 저장 + const [savedAttachment] = await db + .insert(vendorEvaluationAttachments) + .values({ + submissionId: parseInt(submissionId), + generalEvaluationResponseId: parseInt(responseId), + originalFileName: file.name, + storedFileName: storedFileName, + filePath: filePath, + fileSize: file.size, + mimeType: file.type, + uploadedBy: uploadedBy, + isActive: true, + }) + .returning() + + uploadedFiles.push({ + id: savedAttachment.id, + fileId: savedAttachment.fileId, + originalFileName: savedAttachment.originalFileName, + fileSize: savedAttachment.fileSize, + mimeType: savedAttachment.mimeType, + }) + + } catch (fileError) { + console.error(`파일 저장 실패: ${file.name}`, fileError) + return NextResponse.json({ error: `파일 저장에 실패했습니다: ${file.name}` }, { status: 500 }) + } + } + + return NextResponse.json({ + success: true, + files: uploadedFiles, + message: `${uploadedFiles.length}개 파일이 성공적으로 업로드되었습니다.` + }) + + } catch (error) { + console.error('파일 업로드 오류:', error) + return NextResponse.json({ error: '파일 업로드에 실패했습니다.' }, { status: 500 }) + } +}
\ No newline at end of file |
