summaryrefslogtreecommitdiff
path: root/lib/vendor-document-list
diff options
context:
space:
mode:
Diffstat (limited to 'lib/vendor-document-list')
-rw-r--r--lib/vendor-document-list/dolce-upload-service.ts87
-rw-r--r--lib/vendor-document-list/import-service.ts71
-rw-r--r--lib/vendor-document-list/ship/send-to-shi-button.tsx6
-rw-r--r--lib/vendor-document-list/sync-service.ts14
4 files changed, 162 insertions, 16 deletions
diff --git a/lib/vendor-document-list/dolce-upload-service.ts b/lib/vendor-document-list/dolce-upload-service.ts
index d0db9f2f..2d6a83c6 100644
--- a/lib/vendor-document-list/dolce-upload-service.ts
+++ b/lib/vendor-document-list/dolce-upload-service.ts
@@ -4,6 +4,7 @@ import { documents, revisions, documentAttachments, contracts, projects, vendors
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"
export interface DOLCEUploadResult {
success: boolean
@@ -94,7 +95,7 @@ function getFileReaderConfig(): FileReaderConfig {
};
} else {
return {
- baseDir: path.join(process.cwd(), "public"), // 개발환경 public 폴더
+ baseDir: process.cwd(), // 개발환경 현재 디렉토리
isProduction: false,
};
}
@@ -215,7 +216,7 @@ class DOLCEUploadService {
/**
* 계약 정보 조회
*/
- private async getContractInfo(revisionIds: number) {
+ private async getContractInfo(projectId: number) {
const [result] = await db
.select({
projectCode: projects.code,
@@ -225,7 +226,7 @@ class DOLCEUploadService {
.from(contracts)
.innerJoin(projects, eq(contracts.projectId, projects.id))
.innerJoin(vendors, eq(contracts.vendorId, vendors.id))
- .where(eq(contracts.projectId, revisionIds))
+ .where(eq(contracts.projectId, projectId))
.limit(1)
return result
@@ -421,6 +422,18 @@ private async uploadFiles(
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}`)
+ }
+ } catch (testError) {
+ console.warn(`⚠️ DOLCE 업로드 확인 중 오류: ${attachment.fileName}`, testError)
+ }
+
} catch (error) {
console.error(`❌ File upload failed for ${attachment.fileName}:`, error)
throw error
@@ -443,7 +456,7 @@ private async uploadFiles(
private async finalizeUploadResult(resultDataArray: ResultData[]): Promise<void> {
- const url = `${this.UPLOAD_SERVICE_URL}/PWPUploadResultService.ashx`
+ const url = `${this.BASE_URL}/PWPUploadResultService.ashx?`
try {
const jsonData = JSON.stringify(resultDataArray)
@@ -783,7 +796,7 @@ private transformToDoLCEDocument(
// ✅ DB에 저장된 경로 형태: "/documents/[uuid].ext"
// 개발: public/documents/[uuid].ext
// 프로덕션: /evcp_nas/documents/[uuid].ext
- actualFilePath = path.join(config.baseDir, filePath.substring(1)); // 앞의 '/' 제거
+ actualFilePath = path.join(config.baseDir, 'public', filePath.substring(1)); // 앞의 '/' 제거
console.log(`📁 documents 경로 처리: ${filePath} → ${actualFilePath}`);
}
@@ -856,6 +869,70 @@ private transformToDoLCEDocument(
const enabled = process.env.DOLCE_UPLOAD_ENABLED
return enabled === 'true' || enabled === '1'
}
+
+ /**
+ * DOLCE 업로드 확인 테스트 (업로드 후 파일이 DOLCE에 존재하는지 확인)
+ */
+ private async testDOLCEFileDownload(
+ fileId: string,
+ userId: string,
+ fileName: string
+ ): Promise<{ success: boolean; downloadUrl?: string; error?: string }> {
+ 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}`)
+ console.log(` UserId: ${userId}`)
+ console.log(` 암호화 키: ${encryptedKey}`)
+ console.log(` 다운로드 URL: ${downloadUrl}`)
+
+ const response = await fetch(downloadUrl, {
+ method: 'GET',
+ headers: {
+ 'User-Agent': 'DOLCE-Integration-Service'
+ }
+ })
+
+ if (!response.ok) {
+ console.error(`❌ DOLCE 파일 다운로드 테스트 실패: HTTP ${response.status}`)
+ return {
+ success: false,
+ downloadUrl,
+ error: `HTTP ${response.status}`
+ }
+ }
+
+ const buffer = Buffer.from(await response.arrayBuffer())
+ console.log(`✅ DOLCE 파일 다운로드 테스트 성공: ${fileName} (${buffer.length} bytes)`)
+
+ return {
+ success: true,
+ downloadUrl
+ }
+
+ } catch (error) {
+ console.error(`❌ DOLCE 파일 다운로드 테스트 실패: ${fileName}`, error)
+ return {
+ success: false,
+ error: error instanceof Error ? error.message : 'Unknown error'
+ }
+ }
+ }
}
export const dolceUploadService = new DOLCEUploadService()
diff --git a/lib/vendor-document-list/import-service.ts b/lib/vendor-document-list/import-service.ts
index 9e1016ea..f2c62d0b 100644
--- a/lib/vendor-document-list/import-service.ts
+++ b/lib/vendor-document-list/import-service.ts
@@ -471,7 +471,7 @@ class ImportService {
*/
private encryptDES(text: string): string {
try {
- const cipher = crypto.createCipher('des-ecb', this.DES_KEY)
+ const cipher = crypto.createCipheriv('des-ecb', this.DES_KEY, '')
cipher.setAutoPadding(true)
let encrypted = cipher.update(text, 'utf8', 'base64')
encrypted += cipher.final('base64')
@@ -498,7 +498,13 @@ class ImportService {
const downloadUrl = `${process.env.DOLCE_DOWNLOAD_URL}?key=${encryptedKey}` ||`http://60.100.99.217:1111/Download.aspx?key=${encryptedKey}`
- console.log(`Downloading file: ${fileName} with key: ${encryptedKey.substring(0, 20)}...`)
+ console.log(`🔗 DOLCE 다운로드 링크 생성:`)
+ console.log(` 파일명: ${fileName}`)
+ console.log(` FileId: ${fileId}`)
+ console.log(` UserId: ${userId}`)
+ console.log(` 암호화 키: ${encryptedKey}`)
+ console.log(` 다운로드 URL: ${downloadUrl}`)
+ console.log(`📥 DOLCE에서 파일 다운로드 시작: ${fileName}`)
const response = await fetch(downloadUrl, {
method: 'GET',
@@ -508,16 +514,18 @@ class ImportService {
})
if (!response.ok) {
+ console.error(`❌ DOLCE 다운로드 실패: HTTP ${response.status}`)
+ console.error(` URL: ${downloadUrl}`)
throw new Error(`File download failed: HTTP ${response.status}`)
}
const buffer = Buffer.from(await response.arrayBuffer())
- console.log(`Downloaded ${buffer.length} bytes for ${fileName}`)
+ console.log(`✅ DOLCE에서 파일 다운로드 완료: ${fileName} (${buffer.length} bytes)`)
return buffer
} catch (error) {
- console.error(`Failed to download file ${fileName}:`, error)
+ console.error(`❌ DOLCE 파일 다운로드 실패: ${fileName}`, error)
throw error
}
}
@@ -1524,6 +1532,61 @@ async getImportStatus(
const enabled = process.env[`IMPORT_${upperSystem}_ENABLED`]
return enabled === 'true' || enabled === '1'
}
+
+ /**
+ * DOLCE 업로드 확인 테스트 (업로드 후 파일이 DOLCE에 존재하는지 확인)
+ */
+ async testDOLCEFileDownload(
+ fileId: string,
+ userId: string,
+ fileName: string
+ ): Promise<{ success: boolean; downloadUrl?: string; error?: string }> {
+ try {
+ // 암호화 문자열 생성: FileId↔UserId↔FileName
+ const encryptString = `${fileId}↔${userId}↔${fileName}`
+ const encryptedKey = this.encryptDES(encryptString)
+
+ 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}`)
+ console.log(` UserId: ${userId}`)
+ console.log(` 암호화 키: ${encryptedKey}`)
+ console.log(` 다운로드 URL: ${downloadUrl}`)
+
+ const response = await fetch(downloadUrl, {
+ method: 'GET',
+ headers: {
+ 'User-Agent': 'DOLCE-Integration-Service'
+ }
+ })
+
+ if (!response.ok) {
+ console.error(`❌ DOLCE 파일 다운로드 테스트 실패: HTTP ${response.status}`)
+ return {
+ success: false,
+ downloadUrl,
+ error: `HTTP ${response.status}`
+ }
+ }
+
+ const buffer = Buffer.from(await response.arrayBuffer())
+ console.log(`✅ DOLCE 파일 다운로드 테스트 성공: ${fileName} (${buffer.length} bytes)`)
+
+ return {
+ success: true,
+ downloadUrl
+ }
+
+ } catch (error) {
+ console.error(`❌ DOLCE 파일 다운로드 테스트 실패: ${fileName}`, error)
+ return {
+ success: false,
+ error: error instanceof Error ? error.message : 'Unknown error'
+ }
+ }
+ }
}
export const importService = new ImportService() \ No newline at end of file
diff --git a/lib/vendor-document-list/ship/send-to-shi-button.tsx b/lib/vendor-document-list/ship/send-to-shi-button.tsx
index c67c7b2c..7236dfde 100644
--- a/lib/vendor-document-list/ship/send-to-shi-button.tsx
+++ b/lib/vendor-document-list/ship/send-to-shi-button.tsx
@@ -44,10 +44,10 @@ export function SendToSHIButton({
const targetSystem = projectType === 'ship' ? "DOLCE" : "SWP"
- // 문서에서 유효한 계약 ID 목록 추출
+ // 문서에서 유효한 계약 ID 목록 추출 (projectId 사용)
const documentsContractIds = React.useMemo(() => {
const validIds = documents
- .map(doc => doc.projectId)
+ .map(doc => (doc as any).projectId)
.filter((id): id is number => typeof id === 'number' && id > 0)
const uniqueIds = [...new Set(validIds)]
@@ -185,7 +185,7 @@ export function SendToSHIButton({
setSyncProgress(0)
setCurrentSyncingContract(null)
- const errorMessage = syncUtils.formatError(error as any)
+ const errorMessage = syncUtils.formatError(error as Error)
toast.error('동기화 실패', {
description: errorMessage
})
diff --git a/lib/vendor-document-list/sync-service.ts b/lib/vendor-document-list/sync-service.ts
index e058803b..0544ce06 100644
--- a/lib/vendor-document-list/sync-service.ts
+++ b/lib/vendor-document-list/sync-service.ts
@@ -309,10 +309,16 @@ class SyncService {
throw new Error('DOLCE upload is not enabled')
}
- // 변경사항에서 리비전 ID들 추출
- const revisionIds = changes
- .filter(change => change.entityType === 'revision')
- .map(change => change.entityId)
+ // 변경사항에서 리비전 ID들 추출 (revision 엔티티 + attachment 엔티티의 revisionId)
+ const revisionIds = [...new Set([
+ ...changes
+ .filter(change => change.entityType === 'revision')
+ .map(change => change.entityId),
+ ...changes
+ .filter(change => change.entityType === 'attachment')
+ .map(change => change.newValues?.revisionId)
+ .filter((id): id is number => typeof id === 'number' && id > 0)
+ ])]
if (revisionIds.length === 0) {
return {