From b3119a81bd584fc086e30fbdc71c15b77bdf57f1 Mon Sep 17 00:00:00 2001 From: dv Date: Thu, 9 Apr 2026 15:07:15 +0300 Subject: [PATCH] =?UTF-8?q?=D0=BF=D0=B8=D0=BD=D0=B3=20=D1=84=D0=B8=D0=BA?= =?UTF-8?q?=D1=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- backend/app/services/task.py | 26 +++++++++++++++++++++++++ frontend/src/pages/DeviceDetail.tsx | 8 +++++--- frontend/src/pages/WorkObjectDetail.tsx | 8 ++++++-- frontend/src/utils/time.ts | 17 ++++++++++++++++ 4 files changed, 54 insertions(+), 5 deletions(-) create mode 100644 frontend/src/utils/time.ts diff --git a/backend/app/services/task.py b/backend/app/services/task.py index c7847f9..e55cbe9 100644 --- a/backend/app/services/task.py +++ b/backend/app/services/task.py @@ -164,6 +164,32 @@ async def _run_on_device( tr.data = action_data or None tr.duration_ms = duration_ms + # For ping action: update device status, last_seen, last_ping_ms + if action.name == "ping": + new_status = "online" if action_result_success else "offline" + async with AsyncSessionLocal() as db: + async with db.begin(): + device_row = await db.get(Device, device.id) + if device_row: + device_row.status = new_status + if action_result_success: + device_row.last_seen = finished_at + ping_ms = (action_data or {}).get("ping_ms") + if ping_ms is not None: + device_row.last_ping_ms = ping_ms + await sse_manager.publish(SSEEvent( + type="device_status", + task_id="__monitor__", + data={ + "device_id": device.id, + "object_id": device.object_id, + "ip": device.ip, + "status": new_status, + "ping_ms": (action_data or {}).get("ping_ms") if action_result_success else None, + "ts": finished_at.isoformat(), + }, + )) + await sse_manager.publish(SSEEvent( type="task_result", task_id=task_id, diff --git a/frontend/src/pages/DeviceDetail.tsx b/frontend/src/pages/DeviceDetail.tsx index 39c835b..7159e53 100644 --- a/frontend/src/pages/DeviceDetail.tsx +++ b/frontend/src/pages/DeviceDetail.tsx @@ -31,6 +31,7 @@ import { useNavigate, useParams } from 'react-router-dom' import { useAlerts, useAcknowledgeAlert, useResolveAlert, type AlertItem } from '../api/alerts' import { useDevice, useDeleteDevice, useObject } from '../api/objects' import { getDeviceWebUrl } from '../utils/deviceUrl' +import { elapsedAgo } from '../utils/time' import { useSnapshots, useSnapshotDiff } from '../api/snapshots' import { useDeviceTasks, useTask, type Task } from '../api/tasks' import { DeviceEditModal } from '../components/DeviceEditModal' @@ -463,10 +464,11 @@ export function DeviceDetailPage() { {device.last_seen ? ( - - + + - {effectivePing !== null ? `${effectivePing}мс` : '—'} + {dayjs(device.last_seen).format('DD.MM.YYYY HH:mm:ss')} + {effectivePing !== null && {effectivePing}мс} ) : '—'} diff --git a/frontend/src/pages/WorkObjectDetail.tsx b/frontend/src/pages/WorkObjectDetail.tsx index b979a04..cc3cc33 100644 --- a/frontend/src/pages/WorkObjectDetail.tsx +++ b/frontend/src/pages/WorkObjectDetail.tsx @@ -31,6 +31,7 @@ import { useDevices, useObject, type DeviceItem } from '../api/objects' import { useRacks } from '../api/racks' import { useActions, useCreateTask, useTask } from '../api/tasks' import { CategoryTag, DeviceStatusBadge } from '../components/StatusBadge' +import { elapsedAgo } from '../utils/time' import dayjs from 'dayjs' function PingButton({ @@ -256,8 +257,11 @@ export function WorkObjectDetailPage() { if (row._type === 'group') return null const d = row as DeviceRow return d.last_seen ? ( - - {d.last_ping_ms}ms + + + {dayjs(d.last_seen).format('DD.MM.YYYY HH:mm:ss')} + {d.last_ping_ms != null ? {d.last_ping_ms}мс : null} + ) : '—' }, diff --git a/frontend/src/utils/time.ts b/frontend/src/utils/time.ts new file mode 100644 index 0000000..a4a6218 --- /dev/null +++ b/frontend/src/utils/time.ts @@ -0,0 +1,17 @@ +/** + * Возвращает строку «сколько времени прошло» с момента date до сейчас. + * < 60 сек → «N сек назад» + * < 60 мин → «N мин назад» + * < 24 ч → «N ч назад» + * иначе → «N д назад» + */ +export function elapsedAgo(date: string | Date): string { + const diffMs = Date.now() - new Date(date).getTime() + const secs = Math.floor(diffMs / 1000) + if (secs < 60) return `${secs} сек назад` + const mins = Math.floor(secs / 60) + if (mins < 60) return `${mins} мин назад` + const hours = Math.floor(mins / 60) + if (hours < 24) return `${hours} ч назад` + return `${Math.floor(hours / 24)} д назад` +}