- | {d.device_name || 'Устройство'} |
- {d.device_id} |
- {platformLabel(d)} |
-
- {d.online ? 'Онлайн' : 'Офлайн'}
+
+
+ {d.device_name || 'Устройство'}
+
+
+
+
- |
- {bytes(d.rx)} |
- {bytes(d.tx)} |
- {d.last_seen ? timeAgo(d.last_seen) + ' назад' : 'никогда'}
{d.rekey_requested && · ждёт смены ключа}
- {syncLabel(d) && {syncLabel(d)}} |
+ {syncLabel(d) && {syncLabel(d)}}
+
+ {statusChip(d)} |
+ |
+ {ipCell(d)} |
+ {platformLabel(d)} |
+ {bytes(d.rx)} |
+ {bytes(d.tx)} |
|
{deviceActions(d)} |
diff --git a/awg-ui/src/tabs/masterkeys/masterkeys.css b/awg-ui/src/tabs/masterkeys/masterkeys.css
index 5ad1914..0f6790c 100644
--- a/awg-ui/src/tabs/masterkeys/masterkeys.css
+++ b/awg-ui/src/tabs/masterkeys/masterkeys.css
@@ -26,13 +26,6 @@ LICENSE file in the root directory of this source tree.
.mk-tr-master td { background: var(--surface-container); font-weight: 500; padding-top: 6px; padding-bottom: 6px; }
.mk-tr-master:hover td { background: var(--surface-container); }
.mk-name { display: inline-flex; align-items: center; gap: 8px; color: var(--primary); }
-/* UUID ключа в строке: выглядит как ID устройства, по клику копирует */
-.mk-uuid-btn {
- background: none; border: 0; padding: 0; margin: 0;
- cursor: pointer; font: inherit; font-weight: 400; color: inherit; line-height: inherit;
-}
-.mk-uuid-btn:hover .mk-device-id { color: var(--primary); }
-.mk-uuid-btn:focus-visible { outline: 2px solid var(--primary); outline-offset: 2px; border-radius: 4px; }
.mk-master-uuid {
display: block;
font-family: var(--mono); font-size: 12px; font-weight: 400;
@@ -46,6 +39,9 @@ LICENSE file in the root directory of this source tree.
.mk-tr-tools:hover { background: transparent; }
.mk-tr-device td, .mk-tr-free td { padding-top: 10px; padding-bottom: 10px; }
.mk-flag { color: var(--neutral-text); font-size: 12px; }
+/* чипы приоритета и торрентов в одной ячейке — как во вкладке «Пользователи» */
+.mk-table .user-chips { display: inline-flex; flex-wrap: nowrap; gap: 6px; }
+.mk-ip { font-family: var(--mono); }
/* иерархия: линия слева от имени и «ножка» к каждой строке; у последней линия обрывается */
.mk-tr-device td:first-child, .mk-tr-free td:first-child {
background: linear-gradient(var(--outline), var(--outline)) 30px 0 / 2px 100% no-repeat;
@@ -210,6 +206,13 @@ LICENSE file in the root directory of this source tree.
}
.mk-device-name { font-size: 14px; font-weight: 500; color: var(--on-surface); }
.mk-device-sub { font-size: 12px; color: var(--neutral-text); }
+.mk-device-ip {
+ display: flex; flex-wrap: wrap; align-items: center; gap: 8px;
+ width: 100%;
+ color: var(--neutral-text);
+}
+.mk-device-ip .mk-ip { font-size: 13px; }
+.mk-device-ip .tip-wrap { width: auto; }
.mk-device-traffic { display: flex; gap: 12px; font-family: var(--mono); font-size: 12px; color: var(--neutral-text); }
.mk-metric { display: inline-flex; gap: 4px; }
.mk-metric i, .mk-metric b { font-style: normal; font-weight: 400; }
diff --git a/internal/ui/sub_admin.go b/internal/ui/sub_admin.go
index 39fd17e..b51cb66 100644
--- a/internal/ui/sub_admin.go
+++ b/internal/ui/sub_admin.go
@@ -437,10 +437,14 @@ func (s *Sub) masterDevices(m *masterRow) ([]any, error) {
st = &devStat{}
}
okN := 0
+ ips := []any{} // IP устройства на каждом сервере ключа, где он уже выдан
for _, l := range links {
if l.PeerPub.Valid && l.PeerPub.String == d.PubKey && l.IP.Valid && l.IP.String != "" {
okN++
}
+ if l.IP.Valid && l.IP.String != "" {
+ ips = append(ips, jsjson.Object{{Key: "server_id", Val: l.ServerID}, {Key: "ip", Val: l.IP.String}})
+ }
}
prio := "normal"
if st.background {
@@ -452,7 +456,7 @@ func (s *Sub) masterDevices(m *masterRow) ([]any, error) {
{Key: "rekey_requested", Val: d.RekeyRequested == 1}, {Key: "online", Val: st.online}, {Key: "lastHandshake", Val: st.lastHS},
{Key: "rx", Val: st.rx}, {Key: "tx", Val: st.tx}, {Key: "priority", Val: prio},
{Key: "torrent", Val: st.torrent},
- {Key: "servers_total", Val: len(links)}, {Key: "servers_ok", Val: okN},
+ {Key: "servers_total", Val: len(links)}, {Key: "servers_ok", Val: okN}, {Key: "ips", Val: ips},
})
}
return out, nil