summaryrefslogtreecommitdiff
path: root/app/api
diff options
context:
space:
mode:
Diffstat (limited to 'app/api')
-rw-r--r--app/api/vendor-evaluation/delete-attachment/[id]/route.ts87
-rw-r--r--app/api/vendor-evaluation/upload-attachment/route.ts106
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