feat(FilterManager): enhance GatewayAutocomplete with ping functionality and prefetching for selected server
Publish Fast Tabler Docker image / build-and-push-fast (push) Failing after 3m5s

This commit is contained in:
2026-02-12 11:46:50 +07:00
parent 6b8e060d54
commit 793b8acaea
+99 -12
View File
@@ -331,7 +331,18 @@ function CommunityAutocomplete({ label, value, onChange, communities = [], requi
}
// Компонент выбора Gateway в виде карточек (вместо выпадающего списка)
function GatewayAutocomplete({ label, value, onChange, gateways = [], required = false, placeholder = 'Введите или выберите gateway...' }) {
function GatewayAutocomplete({
label,
value,
onChange,
gateways = [],
required = false,
placeholder = 'Введите или выберите gateway...',
// Карта пингов, как на Easy Switch: ключ `${routerId}:${gatewayIp}` → avg RTT
pingMap = {},
// Идентификатор роутера (id/dns/ip из inventory), как на Easy Switch
routerId = null,
}) {
const [inputValue, setInputValue] = useState(value || '');
const [isOpen, setIsOpen] = useState(false);
const [highlightedIndex, setHighlightedIndex] = useState(-1);
@@ -478,6 +489,10 @@ function GatewayAutocomplete({ label, value, onChange, gateways = [], required =
{filteredGateways.map((gw, idx) => {
const isSelected = gw.name === value;
const isHighlighted = highlightedIndex === idx;
const pingKey = routerId && gw.ip ? `${routerId}:${gw.ip}` : null;
const ping = pingKey && Object.prototype.hasOwnProperty.call(pingMap, pingKey)
? pingMap[pingKey]
: null;
return (
<div className="col" key={gw.name || idx}>
<button
@@ -511,12 +526,15 @@ function GatewayAutocomplete({ label, value, onChange, gateways = [], required =
{gw.serverDns}
</div>
)}
{(gw.country || gw.provider) && (
<div className={`small text-truncate ${isSelected ? 'text-white-75' : 'text-muted'}`}>
{gw.country && <span className="me-1">{countryToFlag(gw.country)}</span>}
{gw.provider}
</div>
)}
{/* Пинг шлюза (вместо строки с провайдером/страной), как на странице Easy Switch */}
<div
className={`fw-bold mt-1 ${
isSelected ? 'text-white' : 'text-success'
}`}
style={{ fontSize: '1.05rem' }}
>
{ping != null ? ping : '—'}
</div>
</div>
{gw.primary && (
<span className={`badge ${isSelected ? 'bg-white text-primary' : 'bg-green-lt text-green'}`}>
@@ -764,17 +782,33 @@ function FilterManager() {
const [inventoryServers, setInventoryServers] = useState([]);
// Network config gateways (from /network-config endpoint)
const [networkConfigGateways, setNetworkConfigGateways] = useState([]);
// Пинги по шлюзам: ключ `${routerId}:${gatewayIp}` → avg RTT в мс (как на Easy Switch)
const [pingMap, setPingMap] = useState({});
// Получить metadata сервера из inventory по имени (dns/hostName/ip)
const getInventoryMetaForServer = (serverName) => {
if (!serverName) return null;
return inventoryServers.find((srv) =>
String(srv.dns || '').trim() === serverName ||
String(srv.hostName || '').trim() === serverName ||
String(srv.ip || '').trim() === serverName
);
};
// Получить routerId для MikroTik (id/dns/ip), как на странице Easy Switch
const getRouterIdForServer = (server) => {
if (!server) return null;
const meta = getInventoryMetaForServer(server.name);
if (!meta) return null;
return meta.id || meta.dns || meta.ip || null;
};
// Собираем gateways только для выбранного сервера
const selectedServerGateways = (() => {
if (!selectedServer) return [];
// Ищем соответствующий сервер в inventoryServers по имени
const matchedServer = inventoryServers.find(srv =>
String(srv.dns || '').trim() === selectedServer.name ||
String(srv.hostName || '').trim() === selectedServer.name ||
String(srv.ip || '').trim() === selectedServer.name
);
const matchedServer = getInventoryMetaForServer(selectedServer.name);
const serverDns = matchedServer?.dns || matchedServer?.hostName || selectedServer.name || '';
const serverId = matchedServer?.id || matchedServer?.ip || selectedServer.id || selectedServer.name || '';
@@ -853,6 +887,55 @@ function FilterManager() {
return a.name.localeCompare(b.name);
});
})();
// Предзагружаем пинги для gateways выбранного сервера (как на Easy Switch, но только для одного сервера)
const prefetchPingsForSelectedServer = () => {
try {
if (!selectedServer) return;
const routerId = getRouterIdForServer(selectedServer);
if (!routerId) return;
if (!selectedServerGateways || selectedServerGateways.length === 0) return;
const tasks = [];
const seen = new Set();
selectedServerGateways.forEach((gw) => {
const ip = gw.ip || '';
if (!ip) return;
const key = `${routerId}:${ip}`;
if (seen.has(key) || Object.prototype.hasOwnProperty.call(pingMap, key)) return;
seen.add(key);
tasks.push({ routerId, ip, key });
});
if (tasks.length === 0) return;
tasks.forEach(({ routerId: rId, ip, key }) => {
api
.post('/mikrotik/ping', {
serverId: rId,
gatewayIp: ip,
count: 5,
})
.then(({ data }) => {
const value =
typeof data?.avgMs === 'number' ? Math.round(data.avgMs) : null;
setPingMap((prev) => ({ ...prev, [key]: value }));
})
.catch((e) => {
console.warn('Failed to fetch ping for', rId, ip, e?.message || e);
setPingMap((prev) => ({ ...prev, [key]: null }));
});
});
} catch (e) {
console.warn('prefetchPingsForSelectedServer error:', e?.message || e);
}
};
// Автоматически обновляем пинги при смене выбранного сервера или списка его gateways
useEffect(() => {
prefetchPingsForSelectedServer();
}, [selectedServer, selectedServerGateways, inventoryServers]);
useEffect(() => {
// Загружаем базовую AS из настроек
@@ -2278,6 +2361,8 @@ function FilterManager() {
value={newFilter.gateway}
onChange={(val) => setNewFilter({ ...newFilter, gateway: val })}
gateways={selectedServerGateways}
pingMap={pingMap}
routerId={getRouterIdForServer(selectedServer)}
required
/>
@@ -2319,6 +2404,8 @@ function FilterManager() {
value={editingFilter.gateway}
onChange={(val) => setEditingFilter({ ...editingFilter, gateway: val })}
gateways={selectedServerGateways}
pingMap={pingMap}
routerId={getRouterIdForServer(selectedServer)}
required
/>