Preserve device inventory across unavailable observations
Build and Deploy Gateway / build-and-push (push) Successful in 19s
Build and Deploy Gateway / deploy (push) Successful in 13s

This commit is contained in:
2026-08-10 10:49:02 +03:00
parent f233660dc3
commit c89e56942a
6 changed files with 26 additions and 19 deletions
@@ -1015,6 +1015,7 @@ export function createDeviceInventoryService({
const lastSeenAt = observation.active || !previous
? observationTime
: previous.lastSeenAt;
const replaceAddress = observation.active || !previous;
byMac.set(mac, {
id: previous?.id || deviceId(mac),
alias: previous?.alias || '',
@@ -1023,8 +1024,8 @@ export function createDeviceInventoryService({
hostname: previous?.hostname || null,
manufacturer: previous?.manufacturer || vendor(mac),
mac,
ip: String(observation.ip || previous?.ip || ''),
interface: String(observation.interface || previous?.interface || ''),
ip: replaceAddress ? observation.ip : previous.ip,
interface: replaceAddress ? observation.interface : previous.interface,
firstSeenAt: previous?.firstSeenAt || observationTime,
lastSeenAt,
source: 'neighbor',
@@ -1034,8 +1035,9 @@ export function createDeviceInventoryService({
});
}
const cutoff = new Date(observedAt).getTime() - RETENTION_MS;
const sourceUnavailable = typeof result.error === 'string';
const devices = [...byMac.values()].filter((device) => (
device.pinned || device.deprioritized || device.alias || new Date(device.lastSeenAt).getTime() >= cutoff
sourceUnavailable || new Date(device.lastSeenAt).getTime() >= cutoff
));
let traffic = state.traffic;
if (trafficResult) {
+3 -3
View File
@@ -1,7 +1,7 @@
export const HARBOR_VERSIONS = Object.freeze({
macClient: '0.22.0',
gatewayClient: '0.23.0',
gatewayBackend: '0.23.0',
macClient: '0.22.1',
gatewayClient: '0.23.1',
gatewayBackend: '0.23.1',
});
export interface ParsedVersion {
+3 -3
View File
@@ -391,7 +391,7 @@ export function DevicesPanel({ feature }: { feature: DevicesFeature }) {
: '';
const groupLabel = group === 'pinned'
? 'Закреплённые'
: group === 'deprioritized' ? 'Убраны вниз' : 'Остальные';
: group === 'deprioritized' ? 'Фоновые' : 'Остальные';
const groupStart = group !== previousGroup;
const hasName = Boolean(device.alias || device.hostname);
const title = device.alias || device.hostname || device.ip || 'Неизвестное устройство';
@@ -559,7 +559,7 @@ export function DevicesPanel({ feature }: { feature: DevicesFeature }) {
className="client-device-deprioritize"
type="button"
aria-pressed={deprioritized}
aria-label={deprioritized ? `Вернуть ${title} в основной список` : `Убрать ${title} вниз`}
aria-label={deprioritized ? `Вернуть ${title} в основной список` : `Убрать ${title} в фон`}
disabled={saving}
onClick={() => toggleDeprioritized(device)}
>
@@ -569,7 +569,7 @@ export function DevicesPanel({ feature }: { feature: DevicesFeature }) {
: <path d="M12 4v12M7 11l5 5 5-5M5 20h14" />}
</svg>
</button>
<Tooltip>{deprioritized ? 'Вернуть в список' : 'Убрать вниз'}</Tooltip>
<Tooltip>{deprioritized ? 'Вернуть в основной список' : 'Убрать в фон'}</Tooltip>
</span>
{!compact && <TrafficChart
samples={device.trafficHistory || []}