summaryrefslogtreecommitdiff
path: root/app/api/vendor-evaluation/upload-attachment/route.ts
blob: 3fe8164ca8b514c91b7a5282cf52a403a33e9949 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
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 })
  }
}