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>
214 lines
8.9 KiB
TypeScript
214 lines
8.9 KiB
TypeScript
import { apiClient } from "./client";
|
|
|
|
const BASE = "/fleet";
|
|
|
|
export interface FleetDevice {
|
|
id?: number;
|
|
device_id: string;
|
|
company_code?: string;
|
|
device_name?: string;
|
|
device_type?: string;
|
|
ip_address?: string;
|
|
mac_address?: string;
|
|
hardware_fingerprint?: string;
|
|
last_seen_at?: string;
|
|
is_online?: boolean;
|
|
equipment_id?: number | null;
|
|
equipment_name?: string;
|
|
equipment_code?: string;
|
|
agent_version?: string;
|
|
os_info?: Record<string, any>;
|
|
hardware_info?: Record<string, any>;
|
|
device_group?: string;
|
|
tags?: any[];
|
|
}
|
|
|
|
export interface FleetCommand {
|
|
id?: number;
|
|
device_id: string;
|
|
command_type: string;
|
|
payload?: Record<string, any>;
|
|
status?: string;
|
|
result?: Record<string, any>;
|
|
error_message?: string;
|
|
issued_by?: string;
|
|
issued_at?: string;
|
|
sent_at?: string;
|
|
responded_at?: string;
|
|
}
|
|
|
|
export interface FleetAlert {
|
|
id: number;
|
|
rule_id: number;
|
|
rule_name?: string;
|
|
device_id: string;
|
|
severity: string;
|
|
title: string;
|
|
message: string;
|
|
metric: string;
|
|
value: number;
|
|
threshold: number;
|
|
status: "open" | "acknowledged" | "resolved";
|
|
created_at: string;
|
|
}
|
|
|
|
export const fleetApi = {
|
|
// 디바이스
|
|
getDevices: (filter?: { is_online?: boolean; search?: string }) =>
|
|
apiClient.get(`${BASE}/devices`, { params: filter }).then((r) => r.data),
|
|
|
|
getDevice: (deviceId: string) =>
|
|
apiClient.get(`${BASE}/devices/${deviceId}`).then((r) => r.data),
|
|
|
|
registerDevice: (data: Partial<FleetDevice>) =>
|
|
apiClient.post(`${BASE}/devices/register`, data).then((r) => r.data),
|
|
|
|
updateDevice: (deviceId: string, data: Partial<FleetDevice>) =>
|
|
apiClient.patch(`${BASE}/devices/${deviceId}`, data).then((r) => r.data),
|
|
|
|
deleteDevice: (deviceId: string) =>
|
|
apiClient.delete(`${BASE}/devices/${deviceId}`).then((r) => r.data),
|
|
|
|
getMetrics: (deviceId: string, limit = 100) =>
|
|
apiClient.get(`${BASE}/devices/${deviceId}/metrics`, { params: { limit } }).then((r) => r.data),
|
|
|
|
// 커맨드
|
|
getCommands: (filter?: { device_id?: string; status?: string; limit?: number }) =>
|
|
apiClient.get(`${BASE}/commands`, { params: filter }).then((r) => r.data),
|
|
|
|
getCommandTypes: () =>
|
|
apiClient.get(`${BASE}/commands/types`).then((r) => r.data),
|
|
|
|
issueCommand: (data: { device_id: string; command_type: string; payload?: any; timeout_sec?: number }) =>
|
|
apiClient.post(`${BASE}/commands`, data).then((r) => r.data),
|
|
|
|
// 알림
|
|
getAlerts: (status: string = "open") =>
|
|
apiClient.get(`${BASE}/alerts`, { params: { status } }).then((r) => r.data),
|
|
|
|
ackAlert: (id: number) =>
|
|
apiClient.post(`${BASE}/alerts/${id}/ack`).then((r) => r.data),
|
|
|
|
resolveAlert: (id: number) =>
|
|
apiClient.post(`${BASE}/alerts/${id}/resolve`).then((r) => r.data),
|
|
|
|
getAlertRules: () =>
|
|
apiClient.get(`${BASE}/alert-rules`).then((r) => r.data),
|
|
|
|
// 배포
|
|
getDeployments: () =>
|
|
apiClient.get(`${BASE}/deployments`).then((r) => r.data),
|
|
|
|
getReleases: () =>
|
|
apiClient.get(`${BASE}/releases`).then((r) => r.data),
|
|
|
|
// 통계
|
|
getStats: () =>
|
|
apiClient.get(`${BASE}/stats`).then((r) => r.data),
|
|
|
|
// 실시간 데이터
|
|
getLatestValues: (deviceId: string) =>
|
|
apiClient.get(`${BASE}/devices/${deviceId}/latest-values`).then((r) => r.data),
|
|
|
|
getLatestValuesByEquipment: (equipmentId: number) =>
|
|
apiClient.get(`${BASE}/equipment/${equipmentId}/latest-values`).then((r) => r.data),
|
|
|
|
getTagTimeseries: (deviceId: string, tagName: string, limit = 500) =>
|
|
apiClient
|
|
.get(`${BASE}/devices/${deviceId}/tags/${encodeURIComponent(tagName)}/timeseries`, {
|
|
params: { limit },
|
|
})
|
|
.then((r) => r.data),
|
|
|
|
getDataStats: (deviceId?: string) =>
|
|
apiClient.get(`${BASE}/data/stats`, { params: { device_id: deviceId } }).then((r) => r.data),
|
|
|
|
// ===== Python Hook 스크립트 =====
|
|
getHookTypes: () =>
|
|
apiClient.get(`${BASE}/scripts/hook-types`).then((r) => r.data),
|
|
|
|
listScripts: (filter?: any) =>
|
|
apiClient.get(`${BASE}/scripts`, { params: filter }).then((r) => r.data),
|
|
|
|
getScript: (id: number) =>
|
|
apiClient.get(`${BASE}/scripts/${id}`).then((r) => r.data),
|
|
|
|
createScript: (data: any) =>
|
|
apiClient.post(`${BASE}/scripts`, data).then((r) => r.data),
|
|
|
|
updateScript: (id: number, data: any) =>
|
|
apiClient.put(`${BASE}/scripts/${id}`, data).then((r) => r.data),
|
|
|
|
deleteScript: (id: number) =>
|
|
apiClient.delete(`${BASE}/scripts/${id}`).then((r) => r.data),
|
|
|
|
dryRunScript: (code: string, hook_type: string, test_input: any, timeout_ms?: number) =>
|
|
apiClient.post(`${BASE}/scripts/dry-run`, { code, hook_type, test_input, timeout_ms }).then((r) => r.data),
|
|
|
|
getScriptVersions: (id: number) =>
|
|
apiClient.get(`${BASE}/scripts/${id}/versions`).then((r) => r.data),
|
|
|
|
getScriptVersion: (id: number, version: number) =>
|
|
apiClient.get(`${BASE}/scripts/${id}/versions/${version}`).then((r) => r.data),
|
|
|
|
rollbackScript: (id: number, version: number) =>
|
|
apiClient.post(`${BASE}/scripts/${id}/rollback/${version}`).then((r) => r.data),
|
|
|
|
// ===== 릴리즈 =====
|
|
getReleases: (filter?: any) => apiClient.get(`${BASE}/releases`, { params: filter }).then(r => r.data),
|
|
getRelease: (id: number) => apiClient.get(`${BASE}/releases/${id}`).then(r => r.data),
|
|
createRelease: (data: any) => apiClient.post(`${BASE}/releases`, data).then(r => r.data),
|
|
updateRelease: (id: number, data: any) => apiClient.put(`${BASE}/releases/${id}`, data).then(r => r.data),
|
|
deleteRelease: (id: number) => apiClient.delete(`${BASE}/releases/${id}`).then(r => r.data),
|
|
transitionRelease: (id: number, status: string) =>
|
|
apiClient.post(`${BASE}/releases/${id}/transition`, { status }).then(r => r.data),
|
|
|
|
// ===== 배포 =====
|
|
createDeployment: (data: any) => apiClient.post(`${BASE}/deployments`, data).then(r => r.data),
|
|
getDeploymentDetail: (id: number) => apiClient.get(`${BASE}/deployments/${id}`).then(r => r.data),
|
|
getDeploymentStatus: (id: number) => apiClient.get(`${BASE}/deployments/${id}/status`).then(r => r.data),
|
|
startDeployment: (id: number) => apiClient.post(`${BASE}/deployments/${id}/start`).then(r => r.data),
|
|
cancelDeployment: (id: number) => apiClient.post(`${BASE}/deployments/${id}/cancel`).then(r => r.data),
|
|
rollbackDeployment: (id: number) => apiClient.post(`${BASE}/deployments/${id}/rollback`).then(r => r.data),
|
|
|
|
// ===== Harbor =====
|
|
getHarborProjects: () => apiClient.get(`${BASE}/harbor/projects`).then(r => r.data),
|
|
getHarborRepos: (project: string) => apiClient.get(`${BASE}/harbor/projects/${project}/repos`).then(r => r.data),
|
|
getHarborTags: (project: string, repo: string) =>
|
|
apiClient.get(`${BASE}/harbor/projects/${project}/repos/${repo}/tags`).then(r => r.data),
|
|
pingHarbor: () => apiClient.get(`${BASE}/harbor/ping`).then(r => r.data),
|
|
|
|
// ===== 태그 템플릿 =====
|
|
getTagTemplates: (filter?: any) => apiClient.get(`${BASE}/tag-templates`, { params: filter }).then(r => r.data),
|
|
getTagTemplate: (id: number) => apiClient.get(`${BASE}/tag-templates/${id}`).then(r => r.data),
|
|
createTagTemplate: (data: any) => apiClient.post(`${BASE}/tag-templates`, data).then(r => r.data),
|
|
updateTagTemplate: (id: number, data: any) => apiClient.put(`${BASE}/tag-templates/${id}`, data).then(r => r.data),
|
|
deleteTagTemplate: (id: number) => apiClient.delete(`${BASE}/tag-templates/${id}`).then(r => r.data),
|
|
applyTagTemplate: (templateId: number, connectionId: number, overwrite = false) =>
|
|
apiClient.post(`${BASE}/tag-templates/${templateId}/apply/${connectionId}`, { overwrite }).then(r => r.data),
|
|
|
|
// ===== 알림 규칙 =====
|
|
createAlertRule: (data: any) => apiClient.post(`${BASE}/alert-rules`, data).then(r => r.data),
|
|
updateAlertRule: (id: number, data: any) => apiClient.put(`${BASE}/alert-rules/${id}`, data).then(r => r.data),
|
|
deleteAlertRule: (id: number) => apiClient.delete(`${BASE}/alert-rules/${id}`).then(r => r.data),
|
|
toggleAlertRule: (id: number) => apiClient.post(`${BASE}/alert-rules/${id}/toggle`).then(r => r.data),
|
|
|
|
// ===== V1 매핑 =====
|
|
getV1Mappings: (filter?: any) => apiClient.get(`${BASE}/v1-mappings`, { params: filter }).then(r => r.data),
|
|
createV1Mapping: (data: any) => apiClient.post(`${BASE}/v1-mappings`, data).then(r => r.data),
|
|
updateV1Mapping: (id: number, data: any) => apiClient.put(`${BASE}/v1-mappings/${id}`, data).then(r => r.data),
|
|
deleteV1Mapping: (id: number) => apiClient.delete(`${BASE}/v1-mappings/${id}`).then(r => r.data),
|
|
|
|
// ===== PLC 상태 =====
|
|
getPlcStatus: (filter?: any) => apiClient.get(`${BASE}/plc-status`, { params: filter }).then(r => r.data),
|
|
getPlcSummary: () => apiClient.get(`${BASE}/plc-status/summary`).then(r => r.data),
|
|
|
|
// ===== Audit =====
|
|
getAuditLogs: (filter?: any) => apiClient.get(`${BASE}/audit-logs`, { params: filter }).then(r => r.data),
|
|
getAuditStats: () => apiClient.get(`${BASE}/audit-logs/stats`).then(r => r.data),
|
|
|
|
// ===== Provisioning =====
|
|
getPreRegistered: () => apiClient.get(`${BASE}/provision/pre-registered`).then(r => r.data),
|
|
preRegister: (data: any) => apiClient.post(`${BASE}/provision/pre-register`, data).then(r => r.data),
|
|
};
|