import type { NextApiRequest, NextApiResponse } from "next"; import type { File as FormidableFile } from "formidable"; import formidable from "formidable"; import fs from "fs/promises"; import { createReport } from "@/lib/pdftron/serverSDK/createReport"; export const config = { api: { bodyParser: false, // ✅ 이게 false면 안 됨! }, }; // 서버 사이드용 DRM 복호화 함수 (API 라우트 내부에 정의) async function decryptBufferWithDRM(buffer: Buffer, originalFileName: string): Promise { try { // Buffer를 Blob으로 변환하여 FormData에 추가 const blob = new Blob([buffer]); const file = new File([blob], originalFileName); const formData = new FormData(); formData.append('file', file); // 로컬 6543 포트에 drm-proxy 서버가 실행되고 있어야 함 const backendUrl = "http://localhost:6543/api/drm-proxy/decrypt"; console.log(`[DRM] 서버에서 파일 복호화 시도: ${originalFileName} (크기: ${buffer.length} bytes)`); const response = await fetch(backendUrl, { method: "POST", body: formData, }); if (!response.ok) { const errorText = await response.text().catch(() => '응답 텍스트를 가져올 수 없음'); throw new Error(`DRM 서버 응답 오류 [${response.status}]: ${errorText}`); } // 응답을 ArrayBuffer로 받아서 Buffer로 변환 const arrayBuffer = await response.arrayBuffer(); const decryptedBuffer = Buffer.from(arrayBuffer); console.log(`[DRM] 서버에서 파일 복호화 성공: ${originalFileName} (결과 크기: ${decryptedBuffer.length} bytes)`); return decryptedBuffer; } catch (error) { // 오류 발생시 로깅하며, 폴백으로 복호화되지 않은 원본 버퍼를 리턴 const errorMessage = error instanceof Error ? `${error.name}: ${error.message}` : String(error); console.error(`[DRM] 서버 복호화 오류: ${errorMessage}`, { fileName: originalFileName, fileSize: buffer.length, remark: ` [정상 동작 안내] DTS 개발 서버나 로컬 환경에서는 에러가 발생하는 것이 정상적인 동작입니다. 이 경우 원본 파일 버퍼가 그대로 반환됩니다. [발생 가능한 에러 케이스] 1. DRM 백엔드 서버가 없는 경우 - CONNECTION_REJECTED 발생 2. DRM 중앙 서버와 통신 불가한 경우 - PARENT CERT 속성 추가 불가로 인한 백엔드측 500 에러 `, error }); return buffer; // 원본 버퍼 반환 (폴백) } } export default async function handler( req: NextApiRequest, res: NextApiResponse ) { if (req.method !== "POST") { return res.status(405).end(); } try { const form = formidable({ multiples: false }); form.parse(req, async (err, fields, files) => { if (err) { console.error(err); return res.status(500).json({ error: "Error parsing form" }); } try { const fileName = fields?.customFileName?.[0] ?? ""; const reportDatas = JSON.parse(fields?.reportDatas?.[0] ?? "[]") as { [key: string]: any; }[]; const reportTempPath = fields?.reportTempPath?.[0] ?? ""; const reportCoverPage: FormidableFile | undefined = files?.file?.[0]; if ( !reportCoverPage || fileName.length === 0 || reportDatas.length === 0 || reportTempPath.length === 0 ) { return res.status(400).json({ error: "Invalid Report Data" }); } // 원본 파일 읽기 const originalBuffer = await fs.readFile(reportCoverPage.filepath); // DRM 복호화 처리 console.log(`[DRM] 파일 복호화 시작: ${reportCoverPage.originalFilename || 'unknown'}`); const decryptedBuffer = await decryptBufferWithDRM( originalBuffer, reportCoverPage.originalFilename || 'document.docx' ); // 복호화된 버퍼로 리포트 생성 const { result, buffer: pdfBuffer, error, } = await createReport(decryptedBuffer, reportTempPath, reportDatas); if (result && pdfBuffer) { res.setHeader("Content-Type", "application/pdf"); res.setHeader( "Content-Disposition", `attachment; filename="${fileName}"` ); return res.send(Buffer.from(pdfBuffer)); } return res.status(200).json({ success: false, message: "Report 생성에 실패하였습니다.", error, }); } catch (e) { console.log(e); return res.status(400).json({ error: "Invalid additionalData" }); } }); } catch (err) { return res.status(401).end(); } }