Default new devices to Direct routing
Build and Deploy Gateway / build-and-push (push) Successful in 24s
Build and Deploy Gateway / deploy (push) Successful in 13s

This commit is contained in:
2026-08-31 01:07:59 +03:00
parent f977874da6
commit 7e15cc199f
11 changed files with 276 additions and 22 deletions
+9 -1
View File
@@ -14,6 +14,7 @@ import {
byteString,
formatByteString,
formatLastSeen,
isNewDevice,
positiveByteDelta,
stabilizeDevicesByTraffic,
} from '../../utils/format.js';
@@ -444,6 +445,7 @@ export function DevicesPanel({ feature }: { feature: DevicesFeature }) {
const groupStart = group !== previousGroup;
const hasName = Boolean(device.alias || device.hostname);
const title = device.alias || device.hostname || device.ip || 'Неизвестное устройство';
const newDevice = isNewDevice(device.firstSeenAt);
const editing = editingId === device.id;
const saving = savingId === device.id;
const seen = formatLastSeen(device.lastSeenAt);
@@ -493,7 +495,9 @@ export function DevicesPanel({ feature }: { feature: DevicesFeature }) {
: device.confidence === 'ambiguous' && device.desiredPolicy !== 'direct'
? 'Маршрут недоступен, пока Gateway видит несколько сетевых адресов одного устройства'
: displayPolicy === 'direct'
? 'Полностью обходит sing-box. Нажмите, чтобы вернуть обработку Gateway'
? newDevice
? 'Новое устройство идёт напрямую. Нажмите, чтобы разрешить VPN через правила Gateway'
: 'Полностью обходит sing-box. Нажмите, чтобы вернуть обработку Gateway'
: 'Проходит через sing-box и правила Gateway. Нажмите, чтобы пустить полностью напрямую';
return <article
key={device.id}
@@ -553,6 +557,10 @@ export function DevicesPanel({ feature }: { feature: DevicesFeature }) {
onClick={() => startEditing(device)}
>{title}</button>}
{!hasName && !editing && <span className="client-device-fallback-name">{title}</span>}
{!editing && newDevice && <span className="client-device-new-badge">
<span aria-hidden="true">NEW</span>
<span className="client-device-new-a11y">Новое устройство</span>
</span>}
{!editing && <span className="client-device-identity-details" role="group" aria-label={`Технические данные устройства ${title}`}>
{device.ip && <button
className={`client-device-identity-copy${feedback?.field === 'IP' ? feedback.failed ? ' is-copy-error' : ' is-copied' : ''}`}
@@ -32,6 +32,7 @@ export interface Device extends Record<string, unknown> {
hostname: string | null;
mac: string;
ip: string | null;
firstSeenAt: string;
lastSeenAt: string | null;
status: DeviceStatus;
pinned: boolean;
@@ -151,6 +152,7 @@ function validDevice(value: unknown): value is Device {
&& typeof value.mac === 'string'
&& /^[0-9a-f]{2}(?::[0-9a-f]{2}){5}$/.test(value.mac)
&& nullableString(value.ip)
&& timestamp(value.firstSeenAt)
&& nullableTimestamp(value.lastSeenAt)
&& (value.status === 'online' || value.status === 'recent' || value.status === 'offline')
&& typeof value.pinned === 'boolean'