feat: Fleet/Collector/엣지 배포 관련 누적 작업 일괄 커밋
Build and Push Images / build-and-push (push) Has been cancelled
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>
This commit is contained in:
@@ -0,0 +1,162 @@
|
||||
/**
|
||||
* 기본 데이터 소스 연결 시드
|
||||
*
|
||||
* 부팅 시 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}`);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user