ээээ
This commit is contained in:
parent
87bbd2844c
commit
0d0cec1f9f
3 changed files with 56 additions and 24 deletions
|
|
@ -4,6 +4,7 @@ import {
|
||||||
DeleteOutlined,
|
DeleteOutlined,
|
||||||
EditOutlined,
|
EditOutlined,
|
||||||
EyeOutlined,
|
EyeOutlined,
|
||||||
|
LinkOutlined,
|
||||||
} from '@ant-design/icons'
|
} from '@ant-design/icons'
|
||||||
import {
|
import {
|
||||||
Badge,
|
Badge,
|
||||||
|
|
@ -29,6 +30,7 @@ import { useState } from 'react'
|
||||||
import { useNavigate, useParams } from 'react-router-dom'
|
import { useNavigate, useParams } from 'react-router-dom'
|
||||||
import { useAlerts, useAcknowledgeAlert, useResolveAlert, type AlertItem } from '../api/alerts'
|
import { useAlerts, useAcknowledgeAlert, useResolveAlert, type AlertItem } from '../api/alerts'
|
||||||
import { useDevice, useDeleteDevice, useObject } from '../api/objects'
|
import { useDevice, useDeleteDevice, useObject } from '../api/objects'
|
||||||
|
import { getDeviceWebUrl } from '../utils/deviceUrl'
|
||||||
import { useSnapshots, useSnapshotDiff } from '../api/snapshots'
|
import { useSnapshots, useSnapshotDiff } from '../api/snapshots'
|
||||||
import { useDeviceTasks, useTask, type Task } from '../api/tasks'
|
import { useDeviceTasks, useTask, type Task } from '../api/tasks'
|
||||||
import { DeviceEditModal } from '../components/DeviceEditModal'
|
import { DeviceEditModal } from '../components/DeviceEditModal'
|
||||||
|
|
@ -397,6 +399,7 @@ export function DeviceDetailPage() {
|
||||||
|
|
||||||
const effectiveStatus = liveStatus?.status ?? device?.status ?? 'unknown'
|
const effectiveStatus = liveStatus?.status ?? device?.status ?? 'unknown'
|
||||||
const effectivePing = liveStatus !== null ? liveStatus.ping_ms : (device?.last_ping_ms ?? null)
|
const effectivePing = liveStatus !== null ? liveStatus.ping_ms : (device?.last_ping_ms ?? null)
|
||||||
|
const webUrl = device ? getDeviceWebUrl(device) : null
|
||||||
|
|
||||||
const handleDelete = async () => {
|
const handleDelete = async () => {
|
||||||
try {
|
try {
|
||||||
|
|
@ -581,6 +584,18 @@ export function DeviceDetailPage() {
|
||||||
</Col>
|
</Col>
|
||||||
<Col>
|
<Col>
|
||||||
<Space>
|
<Space>
|
||||||
|
{webUrl && (
|
||||||
|
<Tooltip title={webUrl}>
|
||||||
|
<Button
|
||||||
|
icon={<LinkOutlined />}
|
||||||
|
href={webUrl}
|
||||||
|
target="_blank"
|
||||||
|
rel="noopener noreferrer"
|
||||||
|
>
|
||||||
|
Веб-интерфейс
|
||||||
|
</Button>
|
||||||
|
</Tooltip>
|
||||||
|
)}
|
||||||
<Button icon={<EditOutlined />} onClick={() => setEditOpen(true)}>
|
<Button icon={<EditOutlined />} onClick={() => setEditOpen(true)}>
|
||||||
Редактировать
|
Редактировать
|
||||||
</Button>
|
</Button>
|
||||||
|
|
|
||||||
|
|
@ -56,34 +56,11 @@ import { CategoryTag, DeviceStatusBadge } from '../components/StatusBadge'
|
||||||
import { DeviceEditModal } from '../components/DeviceEditModal'
|
import { DeviceEditModal } from '../components/DeviceEditModal'
|
||||||
import { DiscoveryDrawer } from '../components/DiscoveryDrawer'
|
import { DiscoveryDrawer } from '../components/DiscoveryDrawer'
|
||||||
import { stripEmpty } from '../utils/form'
|
import { stripEmpty } from '../utils/form'
|
||||||
|
import { getDeviceWebUrl } from '../utils/deviceUrl'
|
||||||
import dayjs from 'dayjs'
|
import dayjs from 'dayjs'
|
||||||
|
|
||||||
const SERVER_CATEGORIES = new Set(['main_server', 'vm', 'router'])
|
const SERVER_CATEGORIES = new Set(['main_server', 'vm', 'router'])
|
||||||
|
|
||||||
/** Возвращает URL веб-интерфейса устройства или null, если ссылка не применима. */
|
|
||||||
function getDeviceWebUrl(device: DeviceRow): string | null {
|
|
||||||
const hostname = (device.hostname ?? '').toLowerCase()
|
|
||||||
|
|
||||||
// Proxmox — определяем по hostname
|
|
||||||
if (hostname.includes('proxmox') || hostname.includes('pve')) {
|
|
||||||
return `http://${device.ip}:8006`
|
|
||||||
}
|
|
||||||
|
|
||||||
// RTSP-прокси (go2rtc/mediamtx) — определяем по capabilities
|
|
||||||
if (device.capabilities?.includes('rtsp')) {
|
|
||||||
return `http://${device.ip}:1984`
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (device.category) {
|
|
||||||
case 'main_server': return `http://${device.ip}:80`
|
|
||||||
case 'router': return `http://${device.ip}:80` // MikroTik WebFig
|
|
||||||
case 'embedded': return `http://${device.ip}:5000`
|
|
||||||
case 'camera': return `http://${device.ip}:80`
|
|
||||||
// vm без proxmox/pve в hostname — asterisk, db и прочие, ссылка не нужна
|
|
||||||
default: return null
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const rackLabel = (name: string, type: string | null | undefined): string =>
|
const rackLabel = (name: string, type: string | null | undefined): string =>
|
||||||
type ? `${name} ${type}` : name
|
type ? `${name} ${type}` : name
|
||||||
const DEVICE_COL_SPAN = 8
|
const DEVICE_COL_SPAN = 8
|
||||||
|
|
|
||||||
40
frontend/src/utils/deviceUrl.ts
Normal file
40
frontend/src/utils/deviceUrl.ts
Normal file
|
|
@ -0,0 +1,40 @@
|
||||||
|
import type { DeviceItem } from '../api/objects'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Возвращает URL веб-интерфейса устройства или null, если ссылка не применима.
|
||||||
|
*
|
||||||
|
* Приоритет проверок:
|
||||||
|
* 1. Proxmox — hostname содержит «proxmox» или «pve» → :8006
|
||||||
|
* 2. RTSP-прокси (go2rtc/mediamtx) — capabilities содержит «rtsp»
|
||||||
|
* ИЛИ device_meta.cls === «rtsp_server»
|
||||||
|
* ИЛИ device_meta.plugin содержит «rtsp» → :1984
|
||||||
|
* 3. Категория: main_server/:80, router (WebFig)/:80, embedded/:5000, camera/:80
|
||||||
|
* 4. vm без proxmox/pve, io_board, bank_terminal, cash_register, other → null
|
||||||
|
*/
|
||||||
|
export function getDeviceWebUrl(device: DeviceItem): string | null {
|
||||||
|
const hostname = (device.hostname ?? '').toLowerCase()
|
||||||
|
const metaCls = String(device.device_meta?.cls ?? '').toLowerCase()
|
||||||
|
const metaPlugin = String(device.device_meta?.plugin ?? '').toLowerCase()
|
||||||
|
|
||||||
|
// Proxmox
|
||||||
|
if (hostname.includes('proxmox') || hostname.includes('pve')) {
|
||||||
|
return `http://${device.ip}:8006`
|
||||||
|
}
|
||||||
|
|
||||||
|
// RTSP-прокси (go2rtc / mediamtx)
|
||||||
|
if (
|
||||||
|
device.capabilities?.includes('rtsp') ||
|
||||||
|
metaCls === 'rtsp_server' ||
|
||||||
|
metaPlugin.includes('rtsp')
|
||||||
|
) {
|
||||||
|
return `http://${device.ip}:1984`
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (device.category) {
|
||||||
|
case 'main_server': return `http://${device.ip}:80`
|
||||||
|
case 'router': return `http://${device.ip}:80` // MikroTik WebFig
|
||||||
|
case 'embedded': return `http://${device.ip}:5000`
|
||||||
|
case 'camera': return `http://${device.ip}:80`
|
||||||
|
default: return null
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Add table
Reference in a new issue