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
Publish Fast Tabler Docker image / build-and-push-fast (push) Failing after 3m5s
This commit is contained in:
@@ -331,7 +331,18 @@ function CommunityAutocomplete({ label, value, onChange, communities = [], requi
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Компонент выбора Gateway в виде карточек (вместо выпадающего списка)
|
// Компонент выбора 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 [inputValue, setInputValue] = useState(value || '');
|
||||||
const [isOpen, setIsOpen] = useState(false);
|
const [isOpen, setIsOpen] = useState(false);
|
||||||
const [highlightedIndex, setHighlightedIndex] = useState(-1);
|
const [highlightedIndex, setHighlightedIndex] = useState(-1);
|
||||||
@@ -478,6 +489,10 @@ function GatewayAutocomplete({ label, value, onChange, gateways = [], required =
|
|||||||
{filteredGateways.map((gw, idx) => {
|
{filteredGateways.map((gw, idx) => {
|
||||||
const isSelected = gw.name === value;
|
const isSelected = gw.name === value;
|
||||||
const isHighlighted = highlightedIndex === idx;
|
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 (
|
return (
|
||||||
<div className="col" key={gw.name || idx}>
|
<div className="col" key={gw.name || idx}>
|
||||||
<button
|
<button
|
||||||
@@ -511,12 +526,15 @@ function GatewayAutocomplete({ label, value, onChange, gateways = [], required =
|
|||||||
{gw.serverDns}
|
{gw.serverDns}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
{(gw.country || gw.provider) && (
|
{/* Пинг шлюза (вместо строки с провайдером/страной), как на странице Easy Switch */}
|
||||||
<div className={`small text-truncate ${isSelected ? 'text-white-75' : 'text-muted'}`}>
|
<div
|
||||||
{gw.country && <span className="me-1">{countryToFlag(gw.country)}</span>}
|
className={`fw-bold mt-1 ${
|
||||||
{gw.provider}
|
isSelected ? 'text-white' : 'text-success'
|
||||||
</div>
|
}`}
|
||||||
)}
|
style={{ fontSize: '1.05rem' }}
|
||||||
|
>
|
||||||
|
{ping != null ? ping : '—'}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{gw.primary && (
|
{gw.primary && (
|
||||||
<span className={`badge ${isSelected ? 'bg-white text-primary' : 'bg-green-lt text-green'}`}>
|
<span className={`badge ${isSelected ? 'bg-white text-primary' : 'bg-green-lt text-green'}`}>
|
||||||
@@ -764,17 +782,33 @@ function FilterManager() {
|
|||||||
const [inventoryServers, setInventoryServers] = useState([]);
|
const [inventoryServers, setInventoryServers] = useState([]);
|
||||||
// Network config gateways (from /network-config endpoint)
|
// Network config gateways (from /network-config endpoint)
|
||||||
const [networkConfigGateways, setNetworkConfigGateways] = useState([]);
|
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 только для выбранного сервера
|
// Собираем gateways только для выбранного сервера
|
||||||
const selectedServerGateways = (() => {
|
const selectedServerGateways = (() => {
|
||||||
if (!selectedServer) return [];
|
if (!selectedServer) return [];
|
||||||
|
|
||||||
// Ищем соответствующий сервер в inventoryServers по имени
|
// Ищем соответствующий сервер в inventoryServers по имени
|
||||||
const matchedServer = inventoryServers.find(srv =>
|
const matchedServer = getInventoryMetaForServer(selectedServer.name);
|
||||||
String(srv.dns || '').trim() === selectedServer.name ||
|
|
||||||
String(srv.hostName || '').trim() === selectedServer.name ||
|
|
||||||
String(srv.ip || '').trim() === selectedServer.name
|
|
||||||
);
|
|
||||||
|
|
||||||
const serverDns = matchedServer?.dns || matchedServer?.hostName || selectedServer.name || '';
|
const serverDns = matchedServer?.dns || matchedServer?.hostName || selectedServer.name || '';
|
||||||
const serverId = matchedServer?.id || matchedServer?.ip || selectedServer.id || selectedServer.name || '';
|
const serverId = matchedServer?.id || matchedServer?.ip || selectedServer.id || selectedServer.name || '';
|
||||||
@@ -854,6 +888,55 @@ function FilterManager() {
|
|||||||
});
|
});
|
||||||
})();
|
})();
|
||||||
|
|
||||||
|
// Предзагружаем пинги для 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(() => {
|
useEffect(() => {
|
||||||
// Загружаем базовую AS из настроек
|
// Загружаем базовую AS из настроек
|
||||||
(async () => {
|
(async () => {
|
||||||
@@ -2278,6 +2361,8 @@ function FilterManager() {
|
|||||||
value={newFilter.gateway}
|
value={newFilter.gateway}
|
||||||
onChange={(val) => setNewFilter({ ...newFilter, gateway: val })}
|
onChange={(val) => setNewFilter({ ...newFilter, gateway: val })}
|
||||||
gateways={selectedServerGateways}
|
gateways={selectedServerGateways}
|
||||||
|
pingMap={pingMap}
|
||||||
|
routerId={getRouterIdForServer(selectedServer)}
|
||||||
required
|
required
|
||||||
/>
|
/>
|
||||||
|
|
||||||
@@ -2319,6 +2404,8 @@ function FilterManager() {
|
|||||||
value={editingFilter.gateway}
|
value={editingFilter.gateway}
|
||||||
onChange={(val) => setEditingFilter({ ...editingFilter, gateway: val })}
|
onChange={(val) => setEditingFilter({ ...editingFilter, gateway: val })}
|
||||||
gateways={selectedServerGateways}
|
gateways={selectedServerGateways}
|
||||||
|
pingMap={pingMap}
|
||||||
|
routerId={getRouterIdForServer(selectedServer)}
|
||||||
required
|
required
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user