// 외부 DB 연결 API 클라이언트 // 작성일: 2024-12-19 import { apiClient } from "./client"; export interface ExternalDbConnection { id?: number; connection_name: string; description?: string; db_type: "mysql" | "postgresql" | "oracle" | "mssql" | "sqlite" | "mariadb"; host: string; port: number; database_name: string; username: string; password: string; connection_timeout?: number; query_timeout?: number; max_connections?: number; ssl_enabled?: string; ssl_cert_path?: string; connection_options?: Record; company_code: string; is_active: string; created_date?: Date; created_by?: string; updated_date?: Date; updated_by?: string; } export type AuthType = "none" | "api-key" | "bearer" | "basic" | "oauth2"; export interface ExternalApiConnection { id?: number; connection_name: string; description?: string; base_url: string; endpoint_path?: string; default_headers: Record; // 기본 HTTP 메서드/바디 (외부 REST API 커넥션과 동일한 필드) default_method?: string; default_body?: string; auth_type: AuthType; auth_config?: { keyLocation?: "header" | "query"; keyName?: string; keyValue?: string; token?: string; username?: string; password?: string; clientId?: string; clientSecret?: string; tokenUrl?: string; accessToken?: string; }; timeout?: number; company_code: string; is_active: string; created_date?: Date; created_by?: string; updated_date?: Date; updated_by?: string; } export interface ExternalDbConnectionFilter { db_type?: string; is_active?: string; company_code?: string; search?: string; } export interface ApiResponse { success: boolean; data?: T; message?: string; error?: { code?: string; details?: string; }; } // 연결 테스트 관련 타입 export interface ConnectionTestRequest { db_type: string; host: string; port: number; database_name: string; username: string; password: string; connection_timeout?: number; ssl_enabled?: string; } export interface ConnectionTestResult { success: boolean; message: string; details?: { response_time?: number; server_version?: string; database_size?: string; }; error?: { code?: string; details?: string; }; } export class ExternalDbConnectionAPI { private static readonly BASE_PATH = "/external-db-connections"; /** * 외부 DB 연결 목록 조회 */ static async getConnections(filter: ExternalDbConnectionFilter = {}): Promise { try { const params = new URLSearchParams(); if (filter.db_type) params.append("db_type", filter.db_type); if (filter.is_active) params.append("is_active", filter.is_active); if (filter.company_code) params.append("company_code", filter.company_code); if (filter.search) params.append("search", filter.search); const response = await apiClient.get>( `${this.BASE_PATH}?${params.toString()}`, ); if (!response.data.success) { throw new Error(response.data.message || "연결 목록 조회에 실패했습니다."); } return response.data.data || []; } catch (error) { console.error("외부 DB 연결 목록 조회 오류:", error); throw error; } } /** * 특정 외부 DB 연결 조회 */ static async getConnectionById(id: number): Promise { try { const response = await apiClient.get>(`${this.BASE_PATH}/${id}`); if (!response.data.success) { throw new Error(response.data.message || "연결 정보 조회에 실패했습니다."); } if (!response.data.data) { throw new Error("연결 정보를 찾을 수 없습니다."); } return response.data.data; } catch (error) { console.error("외부 DB 연결 조회 오류:", error); throw error; } } /** * 새 외부 DB 연결 생성 */ static async createConnection(data: ExternalDbConnection): Promise { try { const response = await apiClient.post>(this.BASE_PATH, data); if (!response.data.success) { throw new Error(response.data.message || "연결 생성에 실패했습니다."); } if (!response.data.data) { throw new Error("생성된 연결 정보를 받을 수 없습니다."); } return response.data.data; } catch (error) { console.error("외부 DB 연결 생성 오류:", error); throw error; } } /** * 외부 DB 연결 수정 */ static async updateConnection(id: number, data: ExternalDbConnection): Promise { try { const response = await apiClient.put>(`${this.BASE_PATH}/${id}`, data); if (!response.data.success) { throw new Error(response.data.message || "연결 수정에 실패했습니다."); } if (!response.data.data) { throw new Error("수정된 연결 정보를 받을 수 없습니다."); } return response.data.data; } catch (error) { console.error("외부 DB 연결 수정 오류:", error); throw error; } } /** * 외부 DB 연결 삭제 */ static async deleteConnection(id: number): Promise { try { const response = await apiClient.delete>(`${this.BASE_PATH}/${id}`); if (!response.data.success) { throw new Error(response.data.message || "연결 삭제에 실패했습니다."); } } catch (error) { console.error("외부 DB 연결 삭제 오류:", error); throw error; } } /** * 지원되는 DB 타입 목록 조회 */ static async getSupportedTypes(): Promise> { try { const response = await apiClient.get }>>( `${this.BASE_PATH}/types/supported`, ); if (!response.data.success) { throw new Error(response.data.message || "지원 DB 타입 조회에 실패했습니다."); } return response.data.data?.types || []; } catch (error) { console.error("지원 DB 타입 조회 오류:", error); throw error; } } /** * 데이터베이스 연결 테스트 (ID 기반) */ static async testConnection(connectionId: number, password?: string): Promise { try { const response = await apiClient.post>( `${this.BASE_PATH}/${connectionId}/test`, password ? { password } : undefined, ); if (!response.data.success) { return { success: false, message: response.data.message || "연결 테스트에 실패했습니다.", error: response.data.error, }; } return ( response.data.data || { success: true, message: response.data.message || "연결 테스트가 완료되었습니다.", } ); } catch (error) { console.error("연결 테스트 오류:", error); return { success: false, message: "연결 테스트 중 오류가 발생했습니다.", error: { code: "NETWORK_ERROR", details: error instanceof Error ? error.message : "알 수 없는 오류", }, }; } } /** * SQL 쿼리 실행 */ /** * 데이터베이스 테이블 목록 조회 */ static async getTables(connectionId: number): Promise> { try { const response = await apiClient.get>(`${this.BASE_PATH}/${connectionId}/tables`); return response.data; } catch (error) { console.error("테이블 목록 조회 오류:", error); throw error; } } static async getTableColumns(connectionId: number, tableName: string): Promise> { try { console.log("컬럼 정보 API 요청:", `${this.BASE_PATH}/${connectionId}/tables/${tableName}/columns`); // 컬럼 메타데이터 조회는 외부 DB 성능/네트워크 영향으로 오래 걸릴 수 있으므로 // 기본 30초보다 넉넉한 타임아웃을 사용 const response = await apiClient.get>( `${this.BASE_PATH}/${connectionId}/tables/${tableName}/columns`, { timeout: 120000, // 120초 }, ); console.log("컬럼 정보 API 응답:", response.data); return response.data; } catch (error) { console.error("테이블 컬럼 조회 오류:", error); throw error; } } static async executeQuery(connectionId: number, query: string): Promise> { try { console.log("API 요청:", `${this.BASE_PATH}/${connectionId}/execute`, { query }); const response = await apiClient.post>(`${this.BASE_PATH}/${connectionId}/execute`, { query }); console.log("API 응답:", response.data); return response.data; } catch (error) { console.error("SQL 쿼리 실행 오류:", error); throw error; } } /** * 활성 제어 커넥션 목록 조회 (플로우 관리용) */ static async getActiveControlConnections(): Promise> { try { const response = await apiClient.get>(`${this.BASE_PATH}/control/active`); return response.data; } catch (error) { console.error("활성 제어 커넥션 조회 오류:", error); return { success: false, message: error instanceof Error ? error.message : "활성 제어 커넥션 조회에 실패했습니다.", data: [], }; } } /** * REST API 연결 목록 조회 (외부 커넥션에서) */ static async getApiConnections(filter: { is_active?: string } = {}): Promise { try { const params = new URLSearchParams(); if (filter.is_active) params.append("is_active", filter.is_active); const response = await apiClient.get>( `/external-rest-api-connections?${params.toString()}`, ); if (!response.data.success) { throw new Error(response.data.message || "API 연결 목록 조회에 실패했습니다."); } return response.data.data || []; } catch (error) { console.error("API 연결 목록 조회 오류:", error); return []; } } /** * 특정 REST API 연결 조회 */ static async getApiConnectionById(id: number): Promise { try { const response = await apiClient.get>(`/external-rest-api-connections/${id}`); if (!response.data.success || !response.data.data) { return null; } return response.data.data; } catch (error) { console.error("API 연결 조회 오류:", error); return null; } } }