/** * 플로우 관리 API 클라이언트 */ import { FlowDefinition, CreateFlowDefinitionRequest, UpdateFlowDefinitionRequest, FlowStep, CreateFlowStepRequest, UpdateFlowStepRequest, FlowStepConnection, CreateFlowConnectionRequest, FlowStepDataCount, FlowStepDataList, MoveDataRequest, MoveBatchDataRequest, FlowAuditLog, ApiResponse, } from "@/types/flow"; // API URL 동적 설정 const getApiBaseUrl = (): string => { // 1. 환경변수가 있으면 우선 사용 if (process.env.NEXT_PUBLIC_API_URL) { return process.env.NEXT_PUBLIC_API_URL; } // 2. 클라이언트 사이드에서 동적 설정 if (typeof window !== "undefined") { const currentHost = window.location.hostname; // 프로덕션 환경: v1.vexplor.com → api.vexplor.com if (currentHost === "v1.vexplor.com") { return "https://api.vexplor.com/api"; } // 로컬 개발환경 if (currentHost === "localhost" || currentHost === "127.0.0.1") { return "http://localhost:8080/api"; } } // 3. 기본값 return "/api"; }; const API_BASE = getApiBaseUrl(); // 토큰 가져오기 function getAuthToken(): string | null { if (typeof window === "undefined") return null; return localStorage.getItem("authToken") || sessionStorage.getItem("authToken"); } // 인증 헤더 생성 헬퍼 function getAuthHeaders(): HeadersInit { const token = getAuthToken(); const headers: HeadersInit = { "Content-Type": "application/json", }; if (token) { headers["Authorization"] = `Bearer ${token}`; } return headers; } // ============================================ // 플로우 정의 API // ============================================ /** * 플로우 정의 목록 조회 */ export async function getFlowDefinitions(params?: { tableName?: string; isActive?: boolean; }): Promise> { try { const queryParams = new URLSearchParams(); if (params?.tableName) queryParams.append("tableName", params.tableName); if (params?.isActive !== undefined) queryParams.append("isActive", String(params.isActive)); const url = `${API_BASE}/flow/definitions${queryParams.toString() ? `?${queryParams.toString()}` : ""}`; const response = await fetch(url, { headers: getAuthHeaders(), credentials: "include", }); return await response.json(); } catch (error: any) { return { success: false, error: error.message, }; } } /** * 플로우 정의 상세 조회 */ export async function getFlowDefinition(id: number): Promise> { try { const response = await fetch(`${API_BASE}/flow/definitions/${id}`, { headers: getAuthHeaders(), credentials: "include", }); return await response.json(); } catch (error: any) { return { success: false, error: error.message, }; } } /** * 플로우 정의 상세 조회 (별칭) */ export const getFlowById = getFlowDefinition; /** * 플로우 정의 생성 */ export async function createFlowDefinition(data: CreateFlowDefinitionRequest): Promise> { try { const response = await fetch(`${API_BASE}/flow/definitions`, { method: "POST", headers: getAuthHeaders(), credentials: "include", body: JSON.stringify(data), }); return await response.json(); } catch (error: any) { return { success: false, error: error.message, }; } } /** * 플로우 정의 수정 */ export async function updateFlowDefinition( id: number, data: UpdateFlowDefinitionRequest, ): Promise> { try { const response = await fetch(`${API_BASE}/flow/definitions/${id}`, { method: "PUT", headers: getAuthHeaders(), credentials: "include", body: JSON.stringify(data), }); return await response.json(); } catch (error: any) { return { success: false, error: error.message, }; } } /** * 플로우 정의 삭제 */ export async function deleteFlowDefinition(id: number): Promise> { try { const response = await fetch(`${API_BASE}/flow/definitions/${id}`, { method: "DELETE", headers: getAuthHeaders(), credentials: "include", }); return await response.json(); } catch (error: any) { return { success: false, error: error.message, }; } } // ============================================ // 플로우 단계 API // ============================================ /** * 플로우 단계 목록 조회 */ export async function getFlowSteps(flowId: number): Promise> { try { const response = await fetch(`${API_BASE}/flow/definitions/${flowId}/steps`, { headers: getAuthHeaders(), credentials: "include", }); return await response.json(); } catch (error: any) { return { success: false, error: error.message, }; } } /** * 플로우 단계 생성 */ export async function createFlowStep(flowId: number, data: CreateFlowStepRequest): Promise> { try { const response = await fetch(`${API_BASE}/flow/definitions/${flowId}/steps`, { method: "POST", headers: getAuthHeaders(), credentials: "include", body: JSON.stringify(data), }); return await response.json(); } catch (error: any) { return { success: false, error: error.message, }; } } /** * 플로우 단계 수정 */ export async function updateFlowStep(stepId: number, data: UpdateFlowStepRequest): Promise> { try { const response = await fetch(`${API_BASE}/flow/steps/${stepId}`, { method: "PUT", headers: getAuthHeaders(), credentials: "include", body: JSON.stringify(data), }); return await response.json(); } catch (error: any) { return { success: false, error: error.message, }; } } /** * 플로우 단계 삭제 */ export async function deleteFlowStep(stepId: number): Promise> { try { const response = await fetch(`${API_BASE}/flow/steps/${stepId}`, { method: "DELETE", headers: getAuthHeaders(), credentials: "include", }); return await response.json(); } catch (error: any) { return { success: false, error: error.message, }; } } // ============================================ // 플로우 연결 API // ============================================ /** * 플로우 연결 목록 조회 */ export async function getFlowConnections(flowId: number): Promise> { try { const response = await fetch(`${API_BASE}/flow/connections/${flowId}`, { headers: getAuthHeaders(), credentials: "include", }); return await response.json(); } catch (error: any) { return { success: false, error: error.message, }; } } /** * 플로우 연결 생성 */ export async function createFlowConnection( data: CreateFlowConnectionRequest, ): Promise> { try { const response = await fetch(`${API_BASE}/flow/connections`, { method: "POST", headers: getAuthHeaders(), credentials: "include", body: JSON.stringify(data), }); return await response.json(); } catch (error: any) { return { success: false, error: error.message, }; } } /** * 플로우 연결 삭제 */ export async function deleteFlowConnection(connectionId: number): Promise> { try { const response = await fetch(`${API_BASE}/flow/connections/${connectionId}`, { method: "DELETE", headers: getAuthHeaders(), credentials: "include", }); return await response.json(); } catch (error: any) { return { success: false, error: error.message, }; } } // ============================================ // 플로우 실행 API // ============================================ /** * 특정 단계의 데이터 카운트 조회 */ export async function getStepDataCount(flowId: number, stepId: number): Promise> { try { const response = await fetch(`${API_BASE}/flow/${flowId}/step/${stepId}/count`, { headers: getAuthHeaders(), credentials: "include", }); return await response.json(); } catch (error: any) { return { success: false, error: error.message, }; } } /** * 특정 단계의 데이터 리스트 조회 (페이징) */ export async function getStepDataList( flowId: number, stepId: number, page: number = 1, pageSize: number = 20, ): Promise> { try { const response = await fetch(`${API_BASE}/flow/${flowId}/step/${stepId}/list?page=${page}&pageSize=${pageSize}`, { headers: getAuthHeaders(), credentials: "include", }); return await response.json(); } catch (error: any) { return { success: false, error: error.message, }; } } /** * 플로우 스텝의 컬럼 라벨 조회 */ export async function getStepColumnLabels( flowId: number, stepId: number, ): Promise>> { try { const response = await fetch(`${API_BASE}/flow/${flowId}/step/${stepId}/column-labels`, { headers: getAuthHeaders(), credentials: "include", }); return await response.json(); } catch (error: any) { return { success: false, error: error.message, }; } } /** * 모든 단계의 데이터 카운트 조회 */ export async function getAllStepCounts(flowId: number): Promise> { try { const response = await fetch(`${API_BASE}/flow/${flowId}/steps/counts`, { headers: getAuthHeaders(), credentials: "include", }); return await response.json(); } catch (error: any) { return { success: false, error: error.message, }; } } /** * 데이터 이동 */ export async function moveData(data: MoveDataRequest): Promise> { try { const response = await fetch(`${API_BASE}/flow/move`, { method: "POST", headers: getAuthHeaders(), credentials: "include", body: JSON.stringify(data), }); return await response.json(); } catch (error: any) { return { success: false, error: error.message, }; } } /** * 데이터를 다음 스텝으로 이동 (편의 함수) */ export async function moveDataToNextStep( flowId: number, currentStepId: number, dataId: number, ): Promise> { return moveData({ flowId, currentStepId, dataId, }); } /** * 배치 데이터 이동 */ export async function moveBatchData( data: MoveBatchDataRequest, ): Promise> { try { const response = await fetch(`${API_BASE}/flow/move-batch`, { method: "POST", headers: getAuthHeaders(), credentials: "include", body: JSON.stringify(data), }); return await response.json(); } catch (error: any) { return { success: false, error: error.message, }; } } // ============================================ // 오딧 로그 API // ============================================ /** * 특정 레코드의 오딧 로그 조회 */ export async function getAuditLogs(flowId: number, recordId: string): Promise> { try { const response = await fetch(`${API_BASE}/flow/audit/${flowId}/${recordId}`, { headers: getAuthHeaders(), credentials: "include", }); return await response.json(); } catch (error: any) { return { success: false, error: error.message, }; } } /** * 플로우 전체 오딧 로그 조회 */ export async function getFlowAuditLogs(flowId: number, limit: number = 100): Promise> { try { const response = await fetch(`${API_BASE}/flow/audit/${flowId}?limit=${limit}`, { headers: getAuthHeaders(), credentials: "include", }); return await response.json(); } catch (error: any) { return { success: false, error: error.message, }; } }