From 140d43f8fed96bdbe8fa8afb3ca5dfa10e4c6af6 Mon Sep 17 00:00:00 2001 From: dv Date: Thu, 9 Apr 2026 11:05:56 +0300 Subject: [PATCH] =?UTF-8?q?=D0=BD=D1=83=20=D0=B8=20=D0=BD=D1=83?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CLAUDE.md | 79 ++++++++++++++++---- frontend/src/pages/InventoryObjectDetail.tsx | 1 + 2 files changed, 64 insertions(+), 16 deletions(-) diff --git a/CLAUDE.md b/CLAUDE.md index bdcf1e0..40815c4 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -36,7 +36,7 @@ utp_service/ ├── pages/ # Page-level components ├── api/ # React Query hooks + API calls ├── hooks/ # useSSE, useMonitorSSE - ├── components/ # AppLayout, StatusBadge + ├── components/ # AppLayout, StatusBadge, DeviceEditModal └── store/auth.ts # Zustand auth store ``` @@ -106,10 +106,10 @@ Content-addressed: `SnapshotContent(sha256 PK, content)`, `ConfigSnapshot(device | global_devices.py | /devices | GET all devices across objects (filters: category, city, client, object_id, status) | | racks.py | /objects/{id}/racks | CRUD | | zones.py | /objects/{id}/zones | CRUD | -| tasks.py | /tasks | POST create + GET list/detail + GET /{id}/events (SSE) | +| tasks.py | /tasks | POST create + GET list/detail + GET /{id}/events (SSE); GET list accepts `?device_id=X` (JOIN task_results) | | events.py | /events | GET SSE stream (task_id param) | -| alerts.py | /alerts | GET list + POST /{id}/acknowledge + POST /{id}/resolve | -| snapshots.py | /snapshots | GET list + GET /{id}/diff | +| alerts.py | /alerts | GET list (filters: status, object_id, device_id) + POST /{id}/acknowledge + POST /{id}/resolve | +| snapshots.py | /objects/{id}/snapshots | GET list (filter: device_id) + GET /diff?a=&b= + GET /{id} | | tags.py | /tags | CRUD | | admin.py | /admin | User mgmt + plugin control + health | @@ -157,24 +157,71 @@ SSE fix: `onerror` must `throw err` to prevent fetchEventSource auto-retry loop ## Frontend Routes ``` -/ → redirect to /inventory/objects -/inventory/objects → ObjectsPage (mode=inventory) — city-grouped list -/inventory/objects/:id → InventoryObjectDetailPage — device CRUD, SSH discovery -/inventory/objects/:id/snapshots → SnapshotsPage -/work/objects → ObjectsPage (mode=work) — city-grouped list -/work/objects/:id → WorkObjectDetailPage — task execution, device status -/work/devices → WorkDevicesPage — global device list with filters +/ → redirect to /inventory/objects +/inventory/objects → ObjectsPage (mode=inventory) — city-grouped list +/inventory/objects/:id → InventoryObjectDetailPage — device CRUD, SSH discovery +/inventory/objects/:id/devices/:deviceId → DeviceDetailPage — device card (4 tabs) +/inventory/objects/:id/snapshots → SnapshotsPage +/work/objects → ObjectsPage (mode=work) — city-grouped list +/work/objects/:id → WorkObjectDetailPage — task execution, device status +/work/devices → WorkDevicesPage — global device list with filters ``` Layout nav: ИНВЕНТАРЬ (DatabaseOutlined) | РАБОТА (ControlOutlined, AppstoreOutlined) +### DeviceDetailPage (`pages/DeviceDetail.tsx`) + +URL: `/inventory/objects/:id/devices/:deviceId` + +Шапка: IP + live-статус (SSE via `useMonitorSSE`), hostname, теги категории/стойки/вендора, кнопки Редактировать / Удалить. + +Вкладки: +- **Основное** — все поля `DeviceItem` через `Descriptions`, `device_meta`/`connection_params` в JSON-блоках, capabilities тегами +- **Алерты** — список алертов устройства (`useAlerts({ device_id })`), фильтр по статусу, кнопки acknowledge/resolve +- **Снапшоты** — список (`useSnapshots(objId, { device_id })`), выбор двух → цветной diff-viewer через `useSnapshotDiff` +- **История задач** — задачи через `useDeviceTasks(deviceId)`, раскрытие строки показывает stdout/stderr из `TaskResult` + +### DeviceEditModal (`components/DeviceEditModal.tsx`) + +Самодостаточный компонент формы редактирования/добавления устройства. Используется в: +- `InventoryObjectDetailPage` (кнопки Edit и «Добавить устройство») +- `DeviceDetailPage` (кнопка «Редактировать» в шапке) + +Props: `open`, `device: DeviceItem | null` (null = режим добавления), `objId`, `onClose`, `onSaved?` + +Внутри: `useCreateDevice` / `useUpdateDevice`, `useRacks` для списка стоек. + ## Frontend API Hooks (`src/api/`) -- `objects.ts`: useObjects, useObject, useCreateObject, useUpdateObject, useDeleteObject, useObjectsMeta (cities/clients autocomplete), useDiscoverObject, useSyncFromDB, useSyncFromSheets -- `devices.ts`: useDevices, useDevice, useCreateDevice, useUpdateDevice, useDeleteDevice, useImportDevicesPreview, useImportDevices, useAllDevices (global) -- `tasks.ts`: useTasks, useTaskDetail, useCreateTask, useTaskSSE -- `alerts.ts`: useAlerts, useAcknowledgeAlert, useResolveAlert -- `snapshots.ts`: useSnapshots, useSnapshotDetail, useSnapshotDiff, useCaptureSnapshot +- `objects.ts`: useObjects, useObject, useCreateObject, useUpdateObject, useDeleteObject, useObjectsMeta, + useDiscoverObject, useSyncObject, useSyncSheets, usePreviewCSV, useImportCSV, + **useDevices(objId)** — список устройств объекта, + **useDevice(objId, deviceId)** — одно устройство (queryKey: `['device', objId, deviceId]`), + useCreateDevice, useUpdateDevice (инвалидирует `['devices', objId]` + `['device', objId, id]`), useDeleteDevice, + useDeleteAllDevices, useAllDevices (global, фильтры: category/city/client/object_id/status) +- `tasks.ts`: useTasks, useTask, useActions, useCreateTask, + **useDeviceTasks(deviceId)** — задачи по устройству (queryKey: `['tasks', 'device', deviceId]`) +- `alerts.ts`: useAlerts (params: status, object_id, **device_id**, limit, skip), useAcknowledgeAlert, useResolveAlert +- `snapshots.ts`: useSnapshots(objId, params?: {**device_id**?, label?, limit?, skip?}), useSnapshotDetail, useSnapshotDiff(objId, a, b) +- `racks.ts`: useRacks, useCreateRack, useUpdateRack, useDeleteRack +- `zones.ts`: useZones, useCreateZone, useDeleteZone + +### DeviceItem interface (полный, `api/objects.ts`) + +```typescript +interface DeviceItem { + id, object_id, ip, hostname, mac + vendor, model, firmware_version, external_id + category, role, source + rack_id, rack_name, rack_type, location + ssh_user_override, ssh_port_override + device_meta: Record // JSONB + connection_params: Record // JSONB + capabilities: string[] + status, last_seen, last_ping_ms, monitor_enabled + is_active, notes, created_at, updated_at +} +``` ## Plugins (builtin) diff --git a/frontend/src/pages/InventoryObjectDetail.tsx b/frontend/src/pages/InventoryObjectDetail.tsx index 9390737..3406462 100644 --- a/frontend/src/pages/InventoryObjectDetail.tsx +++ b/frontend/src/pages/InventoryObjectDetail.tsx @@ -16,6 +16,7 @@ import { Button, Col, Descriptions, + Divider, Form, Input, Modal,