'use server' import fs from 'fs/promises' import path from 'path' import { NextResponse } from 'next/server' /** * 첨부 파일 다운로드를 위한 서버 액션 * * @param filePath 파일의 상대 경로 * @returns 파일 내용(Base64 인코딩) 및 메타데이터를 포함한 객체 */ export async function downloadFileAction(filePath: string) { try { // 보안: 파일 경로가 uploads 디렉토리 내에 있는지 확인 if (!filePath.startsWith('/uploads/') && !filePath.startsWith('uploads/')) { return { ok: false, error: 'Invalid file path. Only files in the uploads directory can be downloaded.' }; } // 실제 서버 파일 시스템에서의 전체 경로 계산 // 참고: process.cwd()는 현재 실행 중인 프로세스의 작업 디렉토리를 반환합니다. // 환경에 따라 public 폴더나 다른 위치를 기준으로 할 수도 있습니다. const normalizedPath = filePath.startsWith('/') ? filePath.slice(1) : filePath; const fullPath = path.join(process.cwd(), 'public', normalizedPath); // 파일 존재 여부 확인 try { await fs.access(fullPath); } catch { return { ok: false, error: 'File not found' }; } // 파일 읽기 const fileBuffer = await fs.readFile(fullPath); // 파일 통계 정보 가져오기 const stats = await fs.stat(fullPath); // MIME 타입 추측 const extension = path.extname(fullPath).toLowerCase(); let mimeType = 'application/octet-stream'; // 기본값 // 일반적인 파일 타입에 대한 MIME 타입 매핑 const mimeTypes = { '.pdf': 'application/pdf', '.doc': 'application/msword', '.docx': 'application/vnd.openxmlformats-officedocument.wordprocessingml.document', '.xls': 'application/vnd.ms-excel', '.xlsx': 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet', '.png': 'image/png', '.jpg': 'image/jpeg', '.jpeg': 'image/jpeg', '.gif': 'image/gif', '.txt': 'text/plain', }; if (extension in mimeTypes) { mimeType = mimeTypes[extension]; } // Base64로 인코딩하여 반환 return { ok: true, data: { content: fileBuffer.toString('base64'), fileName: path.basename(fullPath), size: stats.size, mimeType, }, }; } catch (error) { console.error('Download error:', error); return { ok: false, error: error instanceof Error ? error.message : 'An unknown error occurred' }; } }