фикс ссылок

This commit is contained in:
dv 2026-04-09 11:23:27 +03:00
parent 140d43f8fe
commit 87bbd2844c
2 changed files with 48 additions and 14 deletions

View file

@ -574,6 +574,9 @@ export function DeviceDetailPage() {
{device.rack_name && <Tag color="blue">{device.rack_name}</Tag>}
{device.vendor && <Tag color="default">{device.vendor}</Tag>}
{device.model && <Typography.Text type="secondary" style={{ fontSize: 13 }}>{device.model}</Typography.Text>}
{device.capabilities.map((c) => (
<Tag key={c} color="geekblue" style={{ fontSize: 11 }}>{c}</Tag>
))}
</Space>
</Col>
<Col>

View file

@ -60,6 +60,30 @@ import dayjs from 'dayjs'
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 =>
type ? `${name} ${type}` : name
const DEVICE_COL_SPAN = 8
@ -337,15 +361,15 @@ export function InventoryObjectDetailPage() {
)
}
const d = row as DeviceRow
return (
<Space size={4}>
<code
style={{ cursor: 'pointer', color: '#1677ff' }}
onClick={() => navigate(`/inventory/objects/${objId}/devices/${d.id}`)}
>
{d.ip}
</code>
</Space>
const webUrl = getDeviceWebUrl(d)
return webUrl ? (
<Tooltip title={`Открыть ${webUrl}`}>
<a href={webUrl} target="_blank" rel="noopener noreferrer">
<code>{d.ip}</code>
</a>
</Tooltip>
) : (
<code style={{ color: 'inherit' }}>{d.ip}</code>
)
},
},
@ -354,8 +378,16 @@ export function InventoryObjectDetailPage() {
dataIndex: 'hostname',
key: 'hostname',
onCell: (row: TableRow) => row._type === 'group' ? { colSpan: 0 } : {},
render: (_: unknown, row: TableRow) =>
row._type === 'group' ? null : ((row as DeviceRow).hostname ?? '—'),
render: (_: unknown, row: TableRow) => {
if (row._type === 'group') return null
const d = row as DeviceRow
if (!d.hostname) return <Typography.Text type="secondary"></Typography.Text>
return (
<a onClick={() => navigate(`/inventory/objects/${objId}/devices/${d.id}`)}>
{d.hostname}
</a>
)
},
},
{
title: 'Стойка / локация',
@ -365,9 +397,8 @@ export function InventoryObjectDetailPage() {
if (row._type === 'group') return null
const d = row as DeviceRow
if (d.rack_name) return <Tag color="blue">{rackLabel(d.rack_name, d.rack_type)}</Tag>
const loc = (d as any).location
if (loc === 'server_room') return <Tag color="purple">Серверная</Tag>
if (loc === 'street') return <Tag color="orange">Улица</Tag>
if (d.location === 'server_room') return <Tag color="purple">Серверная</Tag>
if (d.location === 'street') return <Tag color="orange">Улица</Tag>
return <Typography.Text type="secondary"></Typography.Text>
},
},