4c1dc4082e
Build and Push Images / build-and-push (push) Has been cancelled
이전 세션들에서 작업된 아래 범위를 모두 포함: Fleet 서브시스템 (src/fleet/) - fleetDeviceService / fleetCommandService / fleetDeploymentService / fleetReleaseService - fleetMetricsService, fleetScriptService, fleetEdgeConfigService - Edge 디바이스 관리, 커맨드 발행, 배포/릴리스, 스크립트 동기화 Collector 확장 - centralMqttForwarder / centralForwarderConfigService - equipmentStateService, pythonHookRunner, scriptCache - Modbus/OPC-UA/S7/XGT 프로토콜 클라이언트 - targetDbIntrospection (저장 DB 조회) Routes / API - automationDashboardRoutes, centralForwarderRoutes, equipmentStateRoutes DB - importEdgeConfig (Python cached config → Pipeline DB) - seedDataSources (external_db_connections 초기 시드) 엣지 배포 리소스 - docker/edge/Dockerfile.backend.prod, Dockerfile.frontend.prod - docker/edge/docker-compose.edge.yml 프론트엔드 - admin/automaticMng (centralForwarder, dashboard, equipmentState) - admin/fleet (commands, devices, deployments, releases, scripts, alerts) - admin/pipeline-device 개선 (저장 DB 드롭다운, 태그 매핑 등) - ExternalDbConnectionModal, ScriptsManagerDialog 등 신규 컴포넌트 - lib/api: automationDashboard, centralForwarder, equipmentState, fleet docs/ - EDGE_SERVER_STRUCTURE, FLEET_COMPLETE, FLEET_EDGE_INTEGRATION, FLEET_HOOK_INTEGRATION Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
163 lines
5.0 KiB
TypeScript
163 lines
5.0 KiB
TypeScript
/**
|
|
* 기본 데이터 소스 연결 시드
|
|
*
|
|
* 부팅 시 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<string, unknown>;
|
|
}
|
|
|
|
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<void> {
|
|
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}`);
|
|
}
|
|
}
|