diff options
| author | dujinkim <dujin.kim@dtsolution.co.kr> | 2025-08-14 11:54:47 +0000 |
|---|---|---|
| committer | dujinkim <dujin.kim@dtsolution.co.kr> | 2025-08-14 11:54:47 +0000 |
| commit | 969c25b56f6d29d7ffa4bc2ce04c5fb4e5846b34 (patch) | |
| tree | 551d335e850e6163792ded0e7a75fa41d96d612a /lib/vendor-document-list/dolce-upload-service.ts | |
| parent | dd20ba9785cdbd3d61f6b014d003d3bd9646ad13 (diff) | |
(대표님) 정규벤더등록, 벤더문서관리, 벤더데이터입력, 첨부파일관리
Diffstat (limited to 'lib/vendor-document-list/dolce-upload-service.ts')
| -rw-r--r-- | lib/vendor-document-list/dolce-upload-service.ts | 606 |
1 files changed, 318 insertions, 288 deletions
diff --git a/lib/vendor-document-list/dolce-upload-service.ts b/lib/vendor-document-list/dolce-upload-service.ts index 2d6a83c6..84ae4525 100644 --- a/lib/vendor-document-list/dolce-upload-service.ts +++ b/lib/vendor-document-list/dolce-upload-service.ts @@ -5,6 +5,8 @@ import { eq, and, desc, sql, inArray, min } from "drizzle-orm" import { v4 as uuidv4 } from "uuid" import path from "path" import * as crypto from "crypto" +import { getServerSession } from "next-auth/next" +import { authOptions } from "@/app/api/auth/[...nextauth]/route" export interface DOLCEUploadResult { success: boolean @@ -87,7 +89,7 @@ interface DOLCEFileMapping { function getFileReaderConfig(): FileReaderConfig { const isProduction = process.env.NODE_ENV === "production"; - + if (isProduction) { return { baseDir: process.env.NAS_PATH || "/evcp_nas", // NAS 기본 경로 @@ -118,13 +120,13 @@ class DOLCEUploadService { ): Promise<DOLCEUploadResult> { try { console.log(`Starting DOLCE upload for contract ${projectId}, revisions: ${revisionIds.join(', ')}`) - + // 1. 계약 정보 조회 (프로젝트 코드, 벤더 코드 등) const contractInfo = await this.getContractInfo(projectId) if (!contractInfo) { throw new Error(`Contract info not found for ID: ${projectId}`) } - + // 2. 업로드할 리비전 정보 조회 const revisionsToUpload = await this.getRevisionsForUpload(revisionIds) if (revisionsToUpload.length === 0) { @@ -134,7 +136,7 @@ class DOLCEUploadService { uploadedFiles: 0 } } - + let uploadedDocuments = 0 let uploadedFiles = 0 const errors: string[] = [] @@ -143,19 +145,19 @@ class DOLCEUploadService { fileResults: [], mappingResults: [] } - + // 3. 각 리비전별로 처리 for (const revision of revisionsToUpload) { try { console.log(`Processing revision ${revision.revision} for document ${revision.documentNo}`) - + // 3-1. UploadId 미리 생성 (파일이 있는 경우에만) let uploadId: string | undefined if (revision.attachments && revision.attachments.length > 0) { uploadId = uuidv4() // 문서 업로드 시 사용할 UploadId 미리 생성 console.log(`Generated UploadId for document upload: ${uploadId}`) } - + // 3-2. 문서 정보 업로드 (UploadId 포함) const dolceDoc = this.transformToDoLCEDocument( revision, @@ -163,43 +165,43 @@ class DOLCEUploadService { uploadId, // 미리 생성된 UploadId 사용 contractInfo.vendorCode, ) - + const docResult = await this.uploadDocument([dolceDoc], userId) if (!docResult.success) { errors.push(`Document upload failed for ${revision.documentNo}: ${docResult.error}`) continue // 문서 업로드 실패 시 다음 리비전으로 넘어감 } - + uploadedDocuments++ results.documentResults.push(docResult) console.log(`✅ Document uploaded successfully: ${revision.documentNo}`) - + // 3-3. 파일 업로드 (이미 생성된 UploadId 사용) if (uploadId && revision.attachments && revision.attachments.length > 0) { try { // 파일 업로드 시 이미 생성된 UploadId 사용 const fileUploadResults = await this.uploadFiles( - revision.attachments, - userId, + revision.attachments, + userId, uploadId // 이미 생성된 UploadId 전달 ) - + } catch (fileError) { errors.push(`File upload failed for ${revision.documentNo}: ${fileError instanceof Error ? fileError.message : 'Unknown error'}`) console.error(`❌ File upload failed for ${revision.documentNo}:`, fileError) } } - + // 3-5. 성공한 리비전의 상태 업데이트 await this.updateRevisionStatus(revision.id, 'SUBMITTED', uploadId) - + } catch (error) { const errorMessage = `Failed to process revision ${revision.revision}: ${error instanceof Error ? error.message : 'Unknown error'}` errors.push(errorMessage) console.error(errorMessage, error) } } - + return { success: errors.length === 0, uploadedDocuments, @@ -207,7 +209,7 @@ class DOLCEUploadService { errors: errors.length > 0 ? errors : undefined, results } - + } catch (error) { console.error('DOLCE upload failed:', error) throw error @@ -216,22 +218,34 @@ class DOLCEUploadService { /** * 계약 정보 조회 */ - private async getContractInfo(projectId: number) { + private async getContractInfo(projectId: number): Promise<{ + projectCode: string; + vendorCode: string; + } | null> { + + const session = await getServerSession(authOptions) + if (!session?.user?.companyId) { + throw new Error("인증이 필요합니다.") + } + + const [result] = await db .select({ projectCode: projects.code, - vendorCode: vendors.vendorCode, - contractNo: contracts.contractNo + vendorCode: vendors.vendorCode }) .from(contracts) .innerJoin(projects, eq(contracts.projectId, projects.id)) .innerJoin(vendors, eq(contracts.vendorId, vendors.id)) - .where(eq(contracts.projectId, projectId)) + .where(and(eq(contracts.projectId, projectId), eq(contracts.vendorId, Number(session.user.companyId)))) .limit(1) - return result + return result?.projectCode && result?.vendorCode + ? { projectCode: result.projectCode, vendorCode: result.vendorCode } + : null } + /** * 각 issueStageId별로 첫 번째 revision 정보를 조회 */ @@ -264,7 +278,7 @@ class DOLCEUploadService { .select({ // revision 테이블 정보 id: revisions.id, - registerId:revisions.registerId, + registerId: revisions.registerId, revision: revisions.revision, // revisionNo가 아니라 revision revisionStatus: revisions.revisionStatus, uploaderId: revisions.uploaderId, @@ -341,181 +355,181 @@ class DOLCEUploadService { return revisionsWithAttachments } -/** - * 파일 업로드 (PWPUploadService.ashx) - 수정된 버전 - * @param attachments 업로드할 첨부파일 목록 - * @param userId 사용자 ID - * @param uploadId 이미 생성된 UploadId (문서 업로드 시 생성됨) - */ -private async uploadFiles( - attachments: any[], - userId: string, - uploadId: string // 이미 생성된 UploadId를 매개변수로 받음 -): Promise<Array<{ uploadId: string, fileId: string, filePath: string }>> { - const uploadResults = [] - const resultDataArray: ResultData[] = [] + /** + * 파일 업로드 (PWPUploadService.ashx) - 수정된 버전 + * @param attachments 업로드할 첨부파일 목록 + * @param userId 사용자 ID + * @param uploadId 이미 생성된 UploadId (문서 업로드 시 생성됨) + */ + private async uploadFiles( + attachments: any[], + userId: string, + uploadId: string // 이미 생성된 UploadId를 매개변수로 받음 + ): Promise<Array<{ uploadId: string, fileId: string, filePath: string }>> { + const uploadResults = [] + const resultDataArray: ResultData[] = [] - for (let i = 0; i < attachments.length; i++) { - const attachment = attachments[i] - try { - // FileId만 새로 생성 (UploadId는 이미 생성된 것 사용) - const fileId = uuidv4() + for (let i = 0; i < attachments.length; i++) { + const attachment = attachments[i] + try { + // FileId만 새로 생성 (UploadId는 이미 생성된 것 사용) + const fileId = uuidv4() - console.log(`Uploading file with predefined UploadId: ${uploadId}, FileId: ${fileId}`) + console.log(`Uploading file with predefined UploadId: ${uploadId}, FileId: ${fileId}`) - // 파일 데이터 읽기 - const fileBuffer = await this.getFileBuffer(attachment.filePath) + // 파일 데이터 읽기 + const fileBuffer = await this.getFileBuffer(attachment.filePath) - const uploadUrl = `${this.UPLOAD_SERVICE_URL}?UploadId=${uploadId}&FileId=${fileId}` + const uploadUrl = `${this.UPLOAD_SERVICE_URL}?UploadId=${uploadId}&FileId=${fileId}` - const response = await fetch(uploadUrl, { - method: 'POST', - headers: { - 'Content-Type': 'application/octet-stream', - }, - body: fileBuffer - }) + const response = await fetch(uploadUrl, { + method: 'POST', + headers: { + 'Content-Type': 'application/octet-stream', + }, + body: fileBuffer + }) - if (!response.ok) { - const errorText = await response.text() - throw new Error(`File upload failed: HTTP ${response.status} - ${errorText}`) - } + if (!response.ok) { + const errorText = await response.text() + throw new Error(`File upload failed: HTTP ${response.status} - ${errorText}`) + } - const dolceFilePath = await response.text() // DOLCE에서 반환하는 파일 경로 - - // 업로드 성공 후 documentAttachments 테이블 업데이트 - await db - .update(documentAttachments) - .set({ - uploadId: uploadId, // 이미 생성된 UploadId 사용 - fileId: fileId, - uploadedBy: userId, - dolceFilePath: dolceFilePath, - uploadedAt: new Date(), - updatedAt: new Date() + const dolceFilePath = await response.text() // DOLCE에서 반환하는 파일 경로 + + // 업로드 성공 후 documentAttachments 테이블 업데이트 + await db + .update(documentAttachments) + .set({ + uploadId: uploadId, // 이미 생성된 UploadId 사용 + fileId: fileId, + uploadedBy: userId, + dolceFilePath: dolceFilePath, + uploadedAt: new Date(), + updatedAt: new Date() + }) + .where(eq(documentAttachments.id, attachment.id)) + + uploadResults.push({ + uploadId, + fileId, + filePath: dolceFilePath }) - .where(eq(documentAttachments.id, attachment.id)) - uploadResults.push({ - uploadId, - fileId, - filePath: dolceFilePath - }) - - // ResultData 객체 생성 (PWPUploadResultService 호출용) - const fileStats = await this.getFileStats(attachment.filePath) // 파일 통계 정보 조회 - - const resultData: ResultData = { - FileId: fileId, - UploadId: uploadId, - FileSeq: i + 1, // 1부터 시작하는 시퀀스 - FileName: attachment.fileName, - FileRelativePath: dolceFilePath, - FileSize: fileStats.size, - FileCreateDT: fileStats.birthtime.toISOString(), - FileWriteDT: fileStats.mtime.toISOString(), - OwnerUserId: userId - } + // ResultData 객체 생성 (PWPUploadResultService 호출용) + const fileStats = await this.getFileStats(attachment.filePath) // 파일 통계 정보 조회 + + const resultData: ResultData = { + FileId: fileId, + UploadId: uploadId, + FileSeq: i + 1, // 1부터 시작하는 시퀀스 + FileName: attachment.fileName, + FileRelativePath: dolceFilePath, + FileSize: fileStats.size, + FileCreateDT: fileStats.birthtime.toISOString(), + FileWriteDT: fileStats.mtime.toISOString(), + OwnerUserId: userId + } - resultDataArray.push(resultData) + resultDataArray.push(resultData) - console.log(`✅ File uploaded successfully: ${attachment.fileName} -> ${dolceFilePath}`) - console.log(`✅ DB updated for attachment ID: ${attachment.id}`) + console.log(`✅ File uploaded successfully: ${attachment.fileName} -> ${dolceFilePath}`) + console.log(`✅ DB updated for attachment ID: ${attachment.id}`) - // 🧪 DOLCE 업로드 확인 테스트 - try { - const testResult = await this.testDOLCEFileDownload(fileId, userId, attachment.fileName) - if (testResult.success) { - console.log(`✅ DOLCE 업로드 확인 성공: ${attachment.fileName}`) - } else { - console.warn(`⚠️ DOLCE 업로드 확인 실패: ${attachment.fileName} - ${testResult.error}`) + // 🧪 DOLCE 업로드 확인 테스트 + try { + const testResult = await this.testDOLCEFileDownload(fileId, userId, attachment.fileName) + if (testResult.success) { + console.log(`✅ DOLCE 업로드 확인 성공: ${attachment.fileName}`) + } else { + console.warn(`⚠️ DOLCE 업로드 확인 실패: ${attachment.fileName} - ${testResult.error}`) + } + } catch (testError) { + console.warn(`⚠️ DOLCE 업로드 확인 중 오류: ${attachment.fileName}`, testError) } - } catch (testError) { - console.warn(`⚠️ DOLCE 업로드 확인 중 오류: ${attachment.fileName}`, testError) - } - } catch (error) { - console.error(`❌ File upload failed for ${attachment.fileName}:`, error) - throw error + } catch (error) { + console.error(`❌ File upload failed for ${attachment.fileName}:`, error) + throw error + } } - } - // 모든 파일 업로드가 완료된 후 PWPUploadResultService 호출 - if (resultDataArray.length > 0) { - try { - await this.finalizeUploadResult(resultDataArray) - console.log(`✅ Upload result finalized for UploadId: ${uploadId}`) - } catch (error) { - console.error(`❌ Failed to finalize upload result for UploadId: ${uploadId}`, error) - // 파일 업로드는 성공했지만 결과 저장 실패 - 로그만 남기고 계속 진행 + // 모든 파일 업로드가 완료된 후 PWPUploadResultService 호출 + if (resultDataArray.length > 0) { + try { + await this.finalizeUploadResult(resultDataArray) + console.log(`✅ Upload result finalized for UploadId: ${uploadId}`) + } catch (error) { + console.error(`❌ Failed to finalize upload result for UploadId: ${uploadId}`, error) + // 파일 업로드는 성공했지만 결과 저장 실패 - 로그만 남기고 계속 진행 + } } + + return uploadResults } - return uploadResults -} + private async finalizeUploadResult(resultDataArray: ResultData[]): Promise<void> { + const url = `${this.BASE_URL}/PWPUploadResultService.ashx?` -private async finalizeUploadResult(resultDataArray: ResultData[]): Promise<void> { - const url = `${this.BASE_URL}/PWPUploadResultService.ashx?` - - try { - const jsonData = JSON.stringify(resultDataArray) - const dataBuffer = Buffer.from(jsonData, 'utf-8') + try { + const jsonData = JSON.stringify(resultDataArray) + const dataBuffer = Buffer.from(jsonData, 'utf-8') - console.log(`Calling PWPUploadResultService with ${resultDataArray.length} files`) - console.log('ResultData:', JSON.stringify(resultDataArray, null, 2)) + console.log(`Calling PWPUploadResultService with ${resultDataArray.length} files`) + console.log('ResultData:', JSON.stringify(resultDataArray, null, 2)) - const response = await fetch(url, { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - }, - body: dataBuffer - }) + const response = await fetch(url, { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + body: dataBuffer + }) - if (!response.ok) { - const errorText = await response.text() - throw new Error(`PWPUploadResultService failed: HTTP ${response.status} - ${errorText}`) - } + if (!response.ok) { + const errorText = await response.text() + throw new Error(`PWPUploadResultService failed: HTTP ${response.status} - ${errorText}`) + } - const result = await response.text() - - if (result !== 'Success') { - console.log(result,"돌체 업로드 실패") - throw new Error(`PWPUploadResultService returned unexpected result: ${result}`) - } + const result = await response.text() - console.log('✅ PWPUploadResultService call successful') + if (result !== 'Success') { + console.log(result, "돌체 업로드 실패") + throw new Error(`PWPUploadResultService returned unexpected result: ${result}`) + } - } catch (error) { - console.error('❌ PWPUploadResultService call failed:', error) - throw error - } -} + console.log('✅ PWPUploadResultService call successful') -// 파일 통계 정보 조회 헬퍼 메서드 (파일시스템에서 파일 정보를 가져옴) -private async getFileStats(filePath: string): Promise<{ size: number, birthtime: Date, mtime: Date }> { - try { - // Node.js 환경이라면 fs.stat 사용 - const fs = require('fs').promises - const stats = await fs.stat(filePath) - - return { - size: stats.size, - birthtime: stats.birthtime, - mtime: stats.mtime + } catch (error) { + console.error('❌ PWPUploadResultService call failed:', error) + throw error } - } catch (error) { - console.warn(`Could not get file stats for ${filePath}, using defaults`) - // 파일 정보를 가져올 수 없는 경우 기본값 사용 - const now = new Date() - return { - size: 0, - birthtime: now, - mtime: now + } + + // 파일 통계 정보 조회 헬퍼 메서드 (파일시스템에서 파일 정보를 가져옴) + private async getFileStats(filePath: string): Promise<{ size: number, birthtime: Date, mtime: Date }> { + try { + // Node.js 환경이라면 fs.stat 사용 + const fs = require('fs').promises + const stats = await fs.stat(filePath) + + return { + size: stats.size, + birthtime: stats.birthtime, + mtime: stats.mtime + } + } catch (error) { + console.warn(`Could not get file stats for ${filePath}, using defaults`) + // 파일 정보를 가져올 수 없는 경우 기본값 사용 + const now = new Date() + return { + size: 0, + birthtime: now, + mtime: now + } } } -} /** * 문서 정보 업로드 (DetailDwgReceiptMgmtEdit) @@ -604,125 +618,125 @@ private async getFileStats(filePath: string): Promise<{ size: number, birthtime: /** * 리비전 데이터를 DOLCE 문서 형태로 변환 (업데이트된 스키마 사용) */ -/** - * 리비전 데이터를 DOLCE 문서 형태로 변환 (업데이트된 스키마 사용) - */ -private transformToDoLCEDocument( - revision: any, - contractInfo: any, - uploadId?: string, - vendorCode?: string, -): DOLCEDocument { - // Mode 결정: registerId가 있으면 MOD, 없으면 ADD - let mode: "ADD" | "MOD" = "ADD" // 기본값은 ADD - - if (revision.registerId) { - mode = "MOD" - } else { - mode = "ADD" - } + /** + * 리비전 데이터를 DOLCE 문서 형태로 변환 (업데이트된 스키마 사용) + */ + private transformToDoLCEDocument( + revision: any, + contractInfo: any, + uploadId?: string, + vendorCode?: string, + ): DOLCEDocument { + // Mode 결정: registerId가 있으면 MOD, 없으면 ADD + let mode: "ADD" | "MOD" = "ADD" // 기본값은 ADD + + if (revision.registerId) { + mode = "MOD" + } else { + mode = "ADD" + } - // RegisterKind 결정: usage와 usageType에 따라 설정 - let registerKind = "APPR" // 기본값 - - if (revision.usage && revision.usage !== 'DEFAULT') { - switch (revision.usage) { - case "APPROVAL": - if (revision.usageType === "Full") { - registerKind = "APPR" - } else if (revision.usageType === "Partial") { - registerKind = "APPR-P" - } else { - registerKind = "APPR" // 기본값 - } - break - - case "WORKING": - if (revision.usageType === "Full") { - registerKind = "WORK" - } else if (revision.usageType === "Partial") { - registerKind = "WORK-P" - } else { - registerKind = "WORK" // 기본값 - } - break + // RegisterKind 결정: usage와 usageType에 따라 설정 + let registerKind = "APPR" // 기본값 + + if (revision.usage && revision.usage !== 'DEFAULT') { + switch (revision.usage) { + case "APPROVAL": + if (revision.usageType === "Full") { + registerKind = "APPR" + } else if (revision.usageType === "Partial") { + registerKind = "APPR-P" + } else { + registerKind = "APPR" // 기본값 + } + break + + case "WORKING": + if (revision.usageType === "Full") { + registerKind = "WORK" + } else if (revision.usageType === "Partial") { + registerKind = "WORK-P" + } else { + registerKind = "WORK" // 기본값 + } + break - case "The 1st": - registerKind = "FMEA-1" - break + case "The 1st": + registerKind = "FMEA-1" + break - case "The 2nd": - registerKind = "FMEA-2" - break + case "The 2nd": + registerKind = "FMEA-2" + break - case "Pre": - registerKind = "RECP" - break + case "Pre": + registerKind = "RECP" + break - case "Working": - registerKind = "RECW" - break + case "Working": + registerKind = "RECW" + break - case "Mark-Up": - registerKind = "CMTM" - break + case "Mark-Up": + registerKind = "CMTM" + break - default: - console.warn(`Unknown usage type: ${revision.usage}, using default APPR`) - registerKind = "APPR" // 기본값 - break + default: + console.warn(`Unknown usage type: ${revision.usage}, using default APPR`) + registerKind = "APPR" // 기본값 + break + } + } else { + console.warn(`No usage specified for revision ${revision.revision}, using default APPR`) } - } else { - console.warn(`No usage specified for revision ${revision.revision}, using default APPR`) - } - // Serial Number 계산 함수 - const getSerialNumber = (revisionValue: string): number => { - if (!revisionValue) { - return 1 - } + // Serial Number 계산 함수 + const getSerialNumber = (revisionValue: string): number => { + if (!revisionValue) { + return 1 + } - // 먼저 숫자인지 확인 - const numericValue = parseInt(revisionValue) - if (!isNaN(numericValue)) { - return numericValue - } + // 먼저 숫자인지 확인 + const numericValue = parseInt(revisionValue) + if (!isNaN(numericValue)) { + return numericValue + } - // 문자인 경우 (a=1, b=2, c=3, ...) - if (typeof revisionValue === 'string' && revisionValue.length === 1) { - const charCode = revisionValue.toLowerCase().charCodeAt(0) - if (charCode >= 97 && charCode <= 122) { // a-z - return charCode - 96 // a=1, b=2, c=3, ... + // 문자인 경우 (a=1, b=2, c=3, ...) + if (typeof revisionValue === 'string' && revisionValue.length === 1) { + const charCode = revisionValue.toLowerCase().charCodeAt(0) + if (charCode >= 97 && charCode <= 122) { // a-z + return charCode - 96 // a=1, b=2, c=3, ... + } } + + // 기본값 + return 1 } - // 기본값 - return 1 - } + console.log(`Transform to DOLCE: Mode=${mode}, RegisterKind=${registerKind}, Usage=${revision.usage}, UsageType=${revision.usageType}`) - console.log(`Transform to DOLCE: Mode=${mode}, RegisterKind=${registerKind}, Usage=${revision.usage}, UsageType=${revision.usageType}`) - - return { - Mode: mode, - Status: revision.revisionStatus || "Standby", - RegisterId: revision.registerId || 0, // registerId가 없으면 0 (ADD 모드) - ProjectNo: contractInfo.projectCode, - Discipline: revision.discipline || "DL", - DrawingKind: revision.drawingKind || "B3", - DrawingNo: revision.documentNo, - DrawingName: revision.documentName, - RegisterGroupId: revision.registerGroupId || 0, - RegisterSerialNo: getSerialNumber(revision.revision || "1"), - RegisterKind: registerKind, // usage/usageType에 따라 동적 설정 - DrawingRevNo: revision.revision || "-", - Category: revision.category || "TS", - Receiver: null, - Manager: revision.managerNo || "202206", // 담당자 번호 사용 - RegisterDesc: revision.comment || "System upload", - UploadId: uploadId, - RegCompanyCode: vendorCode || "A0005531" // 벤더 코드 + return { + Mode: mode, + Status: revision.revisionStatus || "Standby", + RegisterId: revision.registerId || 0, // registerId가 없으면 0 (ADD 모드) + ProjectNo: contractInfo.projectCode, + Discipline: revision.discipline || "DL", + DrawingKind: revision.drawingKind || "B3", + DrawingNo: revision.documentNo, + DrawingName: revision.documentName, + RegisterGroupId: revision.registerGroupId || 0, + RegisterSerialNo: getSerialNumber(revision.revision || "1"), + RegisterKind: registerKind, // usage/usageType에 따라 동적 설정 + DrawingRevNo: revision.revision || "-", + Category: revision.category || "TS", + Receiver: null, + Manager: revision.managerNo || "202206", // 담당자 번호 사용 + RegisterDesc: revision.comment || "System upload", + UploadId: uploadId, + RegCompanyCode: vendorCode || "A0005531" // 벤더 코드 + } } -} /** * 파일 매핑 데이터 변환 */ @@ -769,28 +783,28 @@ private transformToDoLCEDocument( private async getFileBuffer(filePath: string): Promise<ArrayBuffer> { try { console.log(`📂 파일 읽기 요청: ${filePath}`); - + if (filePath.startsWith('http')) { // ✅ URL인 경우 직접 다운로드 (기존과 동일) console.log(`🌐 HTTP URL에서 파일 다운로드: ${filePath}`); - + const response = await fetch(filePath); if (!response.ok) { throw new Error(`파일 다운로드 실패: ${response.status}`); } - + const arrayBuffer = await response.arrayBuffer(); console.log(`✅ HTTP 다운로드 완료: ${arrayBuffer.byteLength} bytes`); - + return arrayBuffer; } else { // ✅ 로컬/NAS 파일 경로 처리 (환경별 분기) const fs = await import('fs'); const path = await import('path'); const config = getFileReaderConfig(); - + let actualFilePath: string; - + // 경로 형태별 처리 if (filePath.startsWith('/documents/')) { // ✅ DB에 저장된 경로 형태: "/documents/[uuid].ext" @@ -798,32 +812,48 @@ private transformToDoLCEDocument( // 프로덕션: /evcp_nas/documents/[uuid].ext actualFilePath = path.join(config.baseDir, 'public', filePath.substring(1)); // 앞의 '/' 제거 console.log(`📁 documents 경로 처리: ${filePath} → ${actualFilePath}`); - } - + } + else if (filePath.startsWith('/api/files')) { + + actualFilePath = `${process.env.NEXT_PUBLIC_URL}${filePath}` + + + const response = await fetch(actualFilePath); + if (!response.ok) { + throw new Error(`파일 다운로드 실패: ${response.status}`); + } + + const arrayBuffer = await response.arrayBuffer(); + console.log(`✅ HTTP 다운로드 완료: ${arrayBuffer.byteLength} bytes`); + + return arrayBuffer; + + } + else { // ✅ 상대 경로는 현재 디렉토리 기준 actualFilePath = filePath; console.log(`📂 상대 경로 사용: ${actualFilePath}`); } - + console.log(`🔍 실제 파일 경로: ${actualFilePath}`); console.log(`🏠 환경: ${config.isProduction ? 'PRODUCTION (NAS)' : 'DEVELOPMENT (public)'}`); - + // 파일 존재 여부 확인 if (!fs.existsSync(actualFilePath)) { console.error(`❌ 파일 없음: ${actualFilePath}`); throw new Error(`파일을 찾을 수 없습니다: ${actualFilePath}`); } - + // 파일 읽기 const fileBuffer = fs.readFileSync(actualFilePath); console.log(`✅ 파일 읽기 성공: ${actualFilePath} (${fileBuffer.length} bytes)`); - + // ✅ Buffer를 ArrayBuffer로 정확히 변환 const arrayBuffer = new ArrayBuffer(fileBuffer.length); const uint8Array = new Uint8Array(arrayBuffer); uint8Array.set(fileBuffer); - + return arrayBuffer; } } catch (error) { @@ -881,19 +911,19 @@ private transformToDoLCEDocument( try { // DES 암호화 (C# DESCryptoServiceProvider 호환) const DES_KEY = Buffer.from("4fkkdijg", "ascii") - + // 암호화 문자열 생성: FileId↔UserId↔FileName const encryptString = `${fileId}↔${userId}↔${fileName}` - + // DES 암호화 (createCipheriv 사용) const cipher = crypto.createCipheriv('des-ecb', DES_KEY, '') cipher.setAutoPadding(true) let encrypted = cipher.update(encryptString, 'utf8', 'base64') encrypted += cipher.final('base64') const encryptedKey = encrypted.replace(/\+/g, '|||') - + const downloadUrl = `${process.env.DOLCE_DOWNLOAD_URL}?key=${encryptedKey}` || `http://60.100.99.217:1111/Download.aspx?key=${encryptedKey}` - + console.log(`🧪 DOLCE 파일 다운로드 테스트:`) console.log(` 파일명: ${fileName}`) console.log(` FileId: ${fileId}`) @@ -919,7 +949,7 @@ private transformToDoLCEDocument( const buffer = Buffer.from(await response.arrayBuffer()) console.log(`✅ DOLCE 파일 다운로드 테스트 성공: ${fileName} (${buffer.length} bytes)`) - + return { success: true, downloadUrl |
