"use server"; import db from "@/db/db"; import { integrationLogTable } from "@/db/schema/integration-log"; import { integrations } from "@/db/schema/integration"; import { eq } from "drizzle-orm"; import { GetIntegrationLogsSchema } from "./validations"; import { filterColumns } from "@/lib/filter-columns"; import { asc, desc, ilike, and, or, count } from "drizzle-orm"; import { NewIntegrationLog } from "@/db/schema/integration-log"; /* ----------------------------------------------------- 1) 통합 로그 목록 조회 ----------------------------------------------------- */ export async function getIntegrationLogs(input: GetIntegrationLogsSchema) { try { const offset = (input.page - 1) * input.perPage; // 1. where 절 let advancedWhere; try { advancedWhere = filterColumns({ table: integrationLogTable, filters: input.filters, joinOperator: input.joinOperator, }); } catch (whereErr) { console.error("Error building advanced where:", whereErr); advancedWhere = undefined; } let globalWhere; if (input.search) { try { const s = `%${input.search}%`; globalWhere = or( ilike(integrationLogTable.status, s), ilike(integrationLogTable.errorMessage, s), ilike(integrationLogTable.requestUrl, s), ilike(integrationLogTable.requestMethod, s) ); } catch (searchErr) { console.error("Error building search where:", searchErr); globalWhere = undefined; } } // 2. where 결합 let finalWhere; const whereArr = [advancedWhere, globalWhere].filter(Boolean); if (whereArr.length === 2) { finalWhere = and(...whereArr); } else if (whereArr.length === 1) { finalWhere = whereArr[0]; } else { finalWhere = undefined; } // 3. order by let orderBy = [desc(integrationLogTable.executionTime)]; try { if (input.sort.length > 0) { const sortItems = input.sort .map((item) => { if (!item || !item.id || typeof item.id !== "string") return null; // 기본 정렬 컬럼들만 허용 switch (item.id) { case "id": return item.desc ? desc(integrationLogTable.id) : asc(integrationLogTable.id); case "integrationId": return item.desc ? desc(integrationLogTable.integrationId) : asc(integrationLogTable.integrationId); case "executionTime": return item.desc ? desc(integrationLogTable.executionTime) : asc(integrationLogTable.executionTime); case "status": return item.desc ? desc(integrationLogTable.status) : asc(integrationLogTable.status); case "responseTime": return item.desc ? desc(integrationLogTable.responseTime) : asc(integrationLogTable.responseTime); case "errorMessage": return item.desc ? desc(integrationLogTable.errorMessage) : asc(integrationLogTable.errorMessage); case "httpStatusCode": return item.desc ? desc(integrationLogTable.httpStatusCode) : asc(integrationLogTable.httpStatusCode); case "retryCount": return item.desc ? desc(integrationLogTable.retryCount) : asc(integrationLogTable.retryCount); case "requestMethod": return item.desc ? desc(integrationLogTable.requestMethod) : asc(integrationLogTable.requestMethod); case "requestUrl": return item.desc ? desc(integrationLogTable.requestUrl) : asc(integrationLogTable.requestUrl); case "ipAddress": return item.desc ? desc(integrationLogTable.ipAddress) : asc(integrationLogTable.ipAddress); case "userAgent": return item.desc ? desc(integrationLogTable.userAgent) : asc(integrationLogTable.userAgent); case "sessionId": return item.desc ? desc(integrationLogTable.sessionId) : asc(integrationLogTable.sessionId); case "correlationId": return item.desc ? desc(integrationLogTable.correlationId) : asc(integrationLogTable.correlationId); case "createdAt": return item.desc ? desc(integrationLogTable.createdAt) : asc(integrationLogTable.createdAt); default: return null; } }) .filter((v): v is Exclude => v !== null); if (sortItems.length > 0) { orderBy = sortItems; } } } catch (orderErr) { console.error("Error building order by:", orderErr); } // 4. 쿼리 실행 let data = []; let total = 0; try { const queryBuilder = db.select({ id: integrationLogTable.id, integrationId: integrationLogTable.integrationId, executionTime: integrationLogTable.executionTime, status: integrationLogTable.status, responseTime: integrationLogTable.responseTime, errorMessage: integrationLogTable.errorMessage, httpStatusCode: integrationLogTable.httpStatusCode, retryCount: integrationLogTable.retryCount, requestMethod: integrationLogTable.requestMethod, requestUrl: integrationLogTable.requestUrl, ipAddress: integrationLogTable.ipAddress, userAgent: integrationLogTable.userAgent, sessionId: integrationLogTable.sessionId, correlationId: integrationLogTable.correlationId, createdAt: integrationLogTable.createdAt, // 통합 정보 code: integrations.code, name: integrations.name, type: integrations.type, sourceSystem: integrations.sourceSystem, targetSystem: integrations.targetSystem, integrationStatus: integrations.status, }) .from(integrationLogTable) .leftJoin(integrations, eq(integrationLogTable.integrationId, integrations.id)); if (finalWhere !== undefined) { queryBuilder.where(finalWhere); } if (orderBy && orderBy.length > 0) { queryBuilder.orderBy(...orderBy); } if (typeof offset === "number" && !isNaN(offset)) { queryBuilder.offset(offset); } if (typeof input.perPage === "number" && !isNaN(input.perPage)) { queryBuilder.limit(input.perPage); } data = await queryBuilder; const countBuilder = db .select({ count: count() }) .from(integrationLogTable); if (finalWhere !== undefined) { countBuilder.where(finalWhere); } const countResult = await countBuilder; total = countResult[0]?.count || 0; } catch (queryErr) { console.error("Query execution failed:", queryErr); throw queryErr; } const pageCount = Math.ceil(total / input.perPage); return { data, pageCount }; } catch (err) { console.error("Error in getIntegrationLogs:", err); if (err instanceof Error) { console.error("Error message:", err.message); console.error("Error stack:", err.stack); } return { data: [], pageCount: 0 }; } } /* ----------------------------------------------------- 2) 통합 로그 상세 조회 ----------------------------------------------------- */ export async function getIntegrationLogById(id: number) { try { const [result] = await db.select({ id: integrationLogTable.id, integrationId: integrationLogTable.integrationId, executionTime: integrationLogTable.executionTime, status: integrationLogTable.status, responseTime: integrationLogTable.responseTime, errorMessage: integrationLogTable.errorMessage, httpStatusCode: integrationLogTable.httpStatusCode, retryCount: integrationLogTable.retryCount, requestMethod: integrationLogTable.requestMethod, requestUrl: integrationLogTable.requestUrl, ipAddress: integrationLogTable.ipAddress, userAgent: integrationLogTable.userAgent, sessionId: integrationLogTable.sessionId, correlationId: integrationLogTable.correlationId, createdAt: integrationLogTable.createdAt, // 통합 정보 code: integrations.code, name: integrations.name, type: integrations.type, sourceSystem: integrations.sourceSystem, targetSystem: integrations.targetSystem, integrationStatus: integrations.status, }) .from(integrationLogTable) .leftJoin(integrations, eq(integrationLogTable.integrationId, integrations.id)) .where(eq(integrationLogTable.id, id)); return result; } catch (err) { console.error("Error getting integration log by id:", err); return null; } } /* ----------------------------------------------------- 3) 통합별 로그 조회 ----------------------------------------------------- */ export async function getIntegrationLogsByIntegrationId(integrationId: number, limit: number = 50) { try { const data = await db.select({ id: integrationLogTable.id, integrationId: integrationLogTable.integrationId, executionTime: integrationLogTable.executionTime, status: integrationLogTable.status, responseTime: integrationLogTable.responseTime, errorMessage: integrationLogTable.errorMessage, httpStatusCode: integrationLogTable.httpStatusCode, retryCount: integrationLogTable.retryCount, requestMethod: integrationLogTable.requestMethod, requestUrl: integrationLogTable.requestUrl, ipAddress: integrationLogTable.ipAddress, userAgent: integrationLogTable.userAgent, sessionId: integrationLogTable.sessionId, correlationId: integrationLogTable.correlationId, createdAt: integrationLogTable.createdAt, }) .from(integrationLogTable) .where(eq(integrationLogTable.integrationId, integrationId)) .orderBy(desc(integrationLogTable.executionTime)) .limit(limit); return data; } catch (err) { console.error("Error getting integration logs by integration id:", err); return []; } } /* ----------------------------------------------------- 로그 저장 함수 추가 ----------------------------------------------------- */ export async function createIntegrationLog(logData: NewIntegrationLog) { try { const [created] = await db.insert(integrationLogTable).values(logData).returning(); return { data: created }; } catch (err) { console.error("Error creating integration log:", err); return { error: "로그 저장 중 오류가 발생했습니다." }; } } /* ----------------------------------------------------- Server Action: 외부에서 호출 가능한 로그 저장 함수 ----------------------------------------------------- */ export async function logIntegrationExecution(logData: { integrationId: number; status: 'success' | 'failed' | 'timeout' | 'pending'; responseTime?: number; errorMessage?: string; httpStatusCode?: number; requestMethod?: string; requestUrl?: string; ipAddress?: string; userAgent?: string; sessionId?: string; correlationId?: string; retryCount?: number; }) { try { const logEntry: NewIntegrationLog = { integrationId: logData.integrationId, executionTime: new Date(), status: logData.status, responseTime: logData.responseTime || 0, errorMessage: logData.errorMessage || null, httpStatusCode: logData.httpStatusCode || null, retryCount: logData.retryCount || 0, requestMethod: logData.requestMethod || null, requestUrl: logData.requestUrl || null, ipAddress: logData.ipAddress || null, userAgent: logData.userAgent || null, sessionId: logData.sessionId || null, correlationId: logData.correlationId || null, }; const result = await createIntegrationLog(logEntry); return result; } catch (error) { console.error("Error in logIntegrationExecution:", error); return { error: "로그 저장에 실패했습니다." }; } }