diff --git a/backend/src/routes/workstations.ts b/backend/src/routes/workstations.ts index 7f16aa9..c1ca6e4 100644 --- a/backend/src/routes/workstations.ts +++ b/backend/src/routes/workstations.ts @@ -343,6 +343,23 @@ const workstationRoutes: FastifyPluginAsync = async (fastify) => { }, ) + // ── POST /api/hotels/:slug/workstations/:id/ping ──────────────────────────── + fastify.post( + '/api/hotels/:slug/workstations/:id/ping', + { onRequest: [fastify.authenticate] }, + async (req, reply) => { + const { id } = req.params + const { host } = req.body + if (!host?.trim()) return reply.code(400).send({ error: 'host required' }) + try { + const result = await sendCommandAndWait(id, { type: 'ping_host', host: host.trim() }, 20000) as { ok: boolean; output?: string; error?: string } + return result + } catch (err) { + return reply.code(502).send({ ok: false, error: err instanceof Error ? err.message : 'Ошибка' }) + } + }, + ) + // ── POST /api/hotels/:slug/workstations/update-all ────────────────────────── fastify.post( '/api/hotels/:slug/workstations/update-all', diff --git a/src/lib/api.ts b/src/lib/api.ts index 0854ea7..2b61d34 100644 --- a/src/lib/api.ts +++ b/src/lib/api.ts @@ -361,6 +361,8 @@ export const api = { req<{ ports: { port: string; description?: string }[] }>('GET', `/api/hotels/${slug}/workstations/${wsId}/ports`), listPrinters: (slug: string, wsId: string) => req<{ printers: { name: string; isDefault: boolean }[] }>('GET', `/api/hotels/${slug}/workstations/${wsId}/printers`), + ping: (slug: string, wsId: string, host: string) => + req<{ ok: boolean; output?: string; error?: string }>('POST', `/api/hotels/${slug}/workstations/${wsId}/ping`, { host }), }, // ── Agents ──────────────────────────────────────────────────────────────── diff --git a/src/pages/EquipmentPage.tsx b/src/pages/EquipmentPage.tsx index 41f6d27..d8989c9 100644 --- a/src/pages/EquipmentPage.tsx +++ b/src/pages/EquipmentPage.tsx @@ -599,6 +599,9 @@ function WorkstationCard({ const [updateMsg, setUpdateMsg] = useState(null) const [testResults, setTestResults] = useState>({}) const [testingDeviceId, setTestingDeviceId] = useState(null) + const [pingHost, setPingHost] = useState('') + const [pinging, setPinging] = useState(false) + const [pingResult, setPingResult] = useState<{ ok: boolean; output?: string; error?: string } | null>(null) const hasUpdate = !!( ws.isOnline && @@ -676,6 +679,20 @@ function WorkstationCard({ } } + const pingNow = async () => { + if (!pingHost.trim() || pinging) return + setPinging(true) + setPingResult(null) + try { + const r = await api.workstations.ping(slug, ws.id, pingHost.trim()) + setPingResult(r) + } catch (err) { + setPingResult({ ok: false, error: err instanceof Error ? err.message : 'Ошибка' }) + } finally { + setPinging(false) + } + } + const devices = ws.devices ?? [] return ( @@ -905,6 +922,44 @@ function WorkstationCard({ Добавить устройство )} + + {/* Ping tool — only when agent is online */} + {ws.isOnline && ( +
+

Пинг через агента

+
+ setPingHost(e.target.value)} + onKeyDown={e => e.key === 'Enter' && pingNow()} + placeholder="192.168.1.1 или hostname" + className="input text-sm font-mono flex-1 py-1.5" + /> + +
+ {pingResult && ( +
+ {pingResult.ok && pingResult.output ? ( +
{pingResult.output.trim()}
+ ) : ( +

{pingResult.error ?? 'Ошибка'}

+ )} +
+ )} +
+ )} )}