/** * 기본 데이터 소스 연결 시드 * * 부팅 시 IDC 엣지 관련 연결 정보를 external_db_connections 테이블에 등록. * 이미 같은 이름의 연결이 있으면 스킵. * * 등록 대상 (2026-04-21 기준): * - IDC Central TimescaleDB (edge_telemetry) — 수집 데이터 시계열 * - IDC Digital-Twin PostgreSQL — 메타데이터 * - IDC Fleet PostgreSQL — fleet 관리 메타 * - IDC Vex Space PostgreSQL — Vex Space 전용 */ import { query, queryOne } from "./db"; import { PasswordEncryption } from "../utils/passwordEncryption"; import logger from "../utils/logger"; interface DefaultDataSource { connection_name: string; description: string; db_type: "postgresql" | "mysql" | "mariadb" | "mssql" | "oracle"; host: string; port: number; database_name: string; username: string; password: string; company_code: string; is_active: "Y" | "N"; connection_options?: Record; } const DEFAULT_SOURCES: DefaultDataSource[] = [ { connection_name: "IDC_TimescaleDB_edge_telemetry", description: "IDC 중앙 TimescaleDB — 엣지 수집 데이터 시계열 (edge_telemetry DB). digital-twin-timescale NodePort :30543", db_type: "postgresql", host: "211.115.91.170", port: 30543, database_name: "edge_telemetry", username: "telemetry_user", password: "qlalfqjsgh11", company_code: "*", is_active: "Y", connection_options: { note: "TimescaleDB extension enabled" }, }, { connection_name: "IDC_DigitalTwin_Postgres", description: "IDC 중앙 Digital-Twin 웹 메타데이터 PostgreSQL (NodePort :30533). digital-twin-web-postgres", db_type: "postgresql", host: "211.115.91.170", port: 30533, database_name: "digital_twin_web_database", username: "digital_twin_web_user_dev", password: "", // 비어 있으면 스킵 company_code: "*", is_active: "N", // 비밀번호 모르므로 비활성으로 등록 }, { connection_name: "IDC_VexSpace_Postgres", description: "IDC VexSpace 전용 PostgreSQL (NodePort :31141). vexspace-postgres", db_type: "postgresql", host: "211.115.91.170", port: 31141, database_name: "vexspace", username: "vexspace_user", password: "", // 비어 있으면 스킵 company_code: "*", is_active: "N", }, { connection_name: "IDC_Fleet_Postgres", description: "IDC Fleet 관리 PostgreSQL (NodePort :31985). fleet-postgres", db_type: "postgresql", host: "211.115.91.170", port: 31985, database_name: "fleet", username: "fleet_user", password: "", // 비밀번호 모르므로 비활성 company_code: "*", is_active: "N", }, ]; /** * 기본 데이터 소스 연결을 시드. 이미 존재하면 스킵. * 비밀번호가 비어있는 항목도 등록하지만 is_active='N'으로 두어 사용자가 나중에 채울 수 있게. */ export async function seedDefaultDataSources(): Promise { try { // external_db_connections 테이블이 없으면 스킵 const tableCheck = await queryOne<{ exists: boolean }>( `SELECT EXISTS ( SELECT 1 FROM information_schema.tables WHERE table_name = 'external_db_connections' ) AS exists` ); if (!tableCheck?.exists) { logger.info("[DataSourceSeed] external_db_connections 없음 — 스킵"); return; } let inserted = 0; let skipped = 0; for (const src of DEFAULT_SOURCES) { const existing = await queryOne( `SELECT id FROM external_db_connections WHERE connection_name = $1 AND company_code = $2 LIMIT 1`, [src.connection_name, src.company_code] ); if (existing) { skipped++; continue; } const encryptedPassword = src.password ? PasswordEncryption.encrypt(src.password) : ""; await query( `INSERT INTO external_db_connections ( connection_name, description, db_type, host, port, database_name, username, password, connection_timeout, query_timeout, max_connections, ssl_enabled, connection_options, company_code, is_active, created_by, updated_by, created_date, updated_date ) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16, $17, NOW(), NOW())`, [ src.connection_name, src.description, src.db_type, src.host, src.port, src.database_name, src.username, encryptedPassword, 30, 60, 10, "N", JSON.stringify(src.connection_options || {}), src.company_code, src.is_active, "system", "system", ] ); inserted++; logger.info( `[DataSourceSeed] 등록: ${src.connection_name} (${src.host}:${src.port}, is_active=${src.is_active})` ); } logger.info( `[DataSourceSeed] 완료: 신규 ${inserted}개, 기존 ${skipped}개 스킵` ); } catch (err) { logger.error(`[DataSourceSeed] 실패: ${(err as Error).message}`); } }