feat(MikrotikConfigRoutes): enhance gateway and interface matching logic to support additional server IDs and improve route extraction
Publish Fast Tabler Docker image / build-and-push-fast (push) Successful in 2m24s
Publish Fast Tabler Docker image / build-and-push-fast (push) Successful in 2m24s
This commit is contained in:
@@ -455,12 +455,11 @@ async function runPingViaRouter(serverId, gatewayIp, target, count = 5, interfac
|
||||
const config = await loadNetworkConfig();
|
||||
const tunnelIfaces = Array.isArray(config.tunnelInterfaces) ? config.tunnelInterfaces : [];
|
||||
|
||||
// 1) Пытаемся сопоставить gatewayIp с локальным/удалённым IP туннельного интерфейса
|
||||
// 1) Пытаемся сопоставить gatewayIp с локальным/удалённым IP туннельного интерфейса (сервер может быть serverId или serverId2)
|
||||
const iface = tunnelIfaces.find((i) => {
|
||||
const sameServer =
|
||||
i.serverId === serverId ||
|
||||
i.serverId === server.ip ||
|
||||
i.serverId === server.dns;
|
||||
i.serverId === serverId || i.serverId === server.ip || i.serverId === server.dns ||
|
||||
i.serverId2 === serverId || i.serverId2 === server.ip || i.serverId2 === server.dns;
|
||||
const sameIp = i.remoteIp === gatewayIp || i.localIp === gatewayIp;
|
||||
return sameServer && sameIp;
|
||||
});
|
||||
@@ -474,9 +473,8 @@ async function runPingViaRouter(serverId, gatewayIp, target, count = 5, interfac
|
||||
const gw = config.gateways.find((g) => {
|
||||
if (!g || !g.ip) return false;
|
||||
const sameServer =
|
||||
g.serverId === serverId ||
|
||||
g.serverId === server.ip ||
|
||||
g.serverId === server.dns;
|
||||
g.serverId === serverId || g.serverId === server.ip || g.serverId === server.dns ||
|
||||
(g.serverId2 && (g.serverId2 === serverId || g.serverId2 === server.ip || g.serverId2 === server.dns));
|
||||
return sameServer && g.ip === gatewayIp;
|
||||
});
|
||||
|
||||
@@ -504,33 +502,40 @@ async function runPingViaRouter(serverId, gatewayIp, target, count = 5, interfac
|
||||
}
|
||||
|
||||
// 3) Если даже из /network-config не смогли получить interface —
|
||||
// пробуем вытащить его напрямую из маршрутов MikroTik по полю gateway.
|
||||
// пробуем вытащить его из маршрутов MikroTik (gateway в формате "IP%interface" или отдельное поле interface).
|
||||
if (!ifaceName) {
|
||||
try {
|
||||
const routeRes = await client.command('ip/route/print', {
|
||||
'.proplist': ['gateway'],
|
||||
'.query': [`gateway~${gatewayIp}`],
|
||||
'.proplist': ['gateway', 'interface'],
|
||||
});
|
||||
const data = routeRes?.data;
|
||||
const routes = Array.isArray(data) ? data : (data ? [data] : []);
|
||||
const match = routes.find((r) => typeof r.gateway === 'string' && r.gateway.includes(gatewayIp));
|
||||
if (match && typeof match.gateway === 'string') {
|
||||
const gwStr = match.gateway;
|
||||
const percentIdx = gwStr.indexOf('%');
|
||||
if (percentIdx >= 0 && percentIdx < gwStr.length - 1) {
|
||||
ifaceName = gwStr.slice(percentIdx + 1);
|
||||
const routeList = Array.isArray(routes) ? routes : (routes && typeof routes === 'object' && routes.ret ? routes.ret : []);
|
||||
const match = routeList.find((r) => {
|
||||
const gw = r.gateway ?? r.Gateway;
|
||||
return typeof gw === 'string' && (gw.includes(gatewayIp) || gw.startsWith(gatewayIp));
|
||||
});
|
||||
if (match) {
|
||||
const gwStr = String(match.gateway ?? match.Gateway ?? '');
|
||||
const ifaceFromRoute = match.interface ?? match.Interface;
|
||||
if (typeof ifaceFromRoute === 'string' && ifaceFromRoute.trim()) {
|
||||
ifaceName = ifaceFromRoute.trim();
|
||||
} else {
|
||||
const percentIdx = gwStr.indexOf('%');
|
||||
if (percentIdx >= 0 && percentIdx < gwStr.length - 1) {
|
||||
ifaceName = gwStr.slice(percentIdx + 1).trim();
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (_) {
|
||||
// Если чтение маршрутов не удалось — просто продолжаем без interface,
|
||||
// чтобы не ломать сам ping.
|
||||
// Если чтение маршрутов не удалось — просто продолжаем без interface.
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 4) Последний фоллбек (только для jumphost или home с указанным шлюзом): ether1/ether2.
|
||||
// Для home без шлюза интерфейс не задаём — пинг идёт по маршруту по умолчанию.
|
||||
if (!ifaceName && !isHomeNoGateway) {
|
||||
// 4) При указанном gatewayIp не подставляем ether1/ether2 — иначе пинг пойдёт не в туннель. Оставляем interface пустым, RouterOS выберет маршрут.
|
||||
// Фоллбек ether1/ether2 только если шлюз не указан (редкий кейс).
|
||||
if (!ifaceName && !isHomeNoGateway && !gatewayIp) {
|
||||
try {
|
||||
const ifRes = await client.command('interface/print', {
|
||||
'.proplist': ['name'],
|
||||
@@ -727,12 +732,11 @@ async function tracerouteViaGateway(req, res) {
|
||||
const config = await loadNetworkConfig();
|
||||
const tunnelIfaces = Array.isArray(config.tunnelInterfaces) ? config.tunnelInterfaces : [];
|
||||
|
||||
// 1) Сопоставляем gatewayIp с локальным/удалённым IP туннельного интерфейса
|
||||
// 1) Сопоставляем gatewayIp с локальным/удалённым IP туннельного интерфейса (сервер может быть serverId или serverId2)
|
||||
const iface = tunnelIfaces.find((i) => {
|
||||
const sameServer =
|
||||
i.serverId === serverId ||
|
||||
i.serverId === server.ip ||
|
||||
i.serverId === server.dns;
|
||||
i.serverId === serverId || i.serverId === server.ip || i.serverId === server.dns ||
|
||||
i.serverId2 === serverId || i.serverId2 === server.ip || i.serverId2 === server.dns;
|
||||
const sameIp = i.remoteIp === gatewayIp || i.localIp === gatewayIp;
|
||||
return sameServer && sameIp;
|
||||
});
|
||||
@@ -746,9 +750,8 @@ async function tracerouteViaGateway(req, res) {
|
||||
const gw = config.gateways.find((g) => {
|
||||
if (!g || !g.ip) return false;
|
||||
const sameServer =
|
||||
g.serverId === serverId ||
|
||||
g.serverId === server.ip ||
|
||||
g.serverId === server.dns;
|
||||
g.serverId === serverId || g.serverId === server.ip || g.serverId === server.dns ||
|
||||
(g.serverId2 && (g.serverId2 === serverId || g.serverId2 === server.ip || g.serverId2 === server.dns));
|
||||
return sameServer && g.ip === gatewayIp;
|
||||
});
|
||||
|
||||
@@ -776,34 +779,39 @@ async function tracerouteViaGateway(req, res) {
|
||||
}
|
||||
|
||||
// 3) Если даже из /network-config не смогли получить interface —
|
||||
// пробуем вытащить его напрямую из маршрутов MikroTik по полю gateway.
|
||||
// пробуем вытащить его из маршрутов MikroTik (gateway в формате "IP%interface" или отдельное поле interface).
|
||||
if (!ifaceName) {
|
||||
try {
|
||||
const routeRes = await client.command('ip/route/print', {
|
||||
'.proplist': ['gateway'],
|
||||
'.query': [`gateway~${gatewayIp}`],
|
||||
'.proplist': ['gateway', 'interface'],
|
||||
});
|
||||
const data = routeRes?.data;
|
||||
const routes = Array.isArray(data) ? data : (data ? [data] : []);
|
||||
const match = routes.find((r) => typeof r.gateway === 'string' && r.gateway.includes(gatewayIp));
|
||||
if (match && typeof match.gateway === 'string') {
|
||||
const gwStr = match.gateway;
|
||||
const percentIdx = gwStr.indexOf('%');
|
||||
if (percentIdx >= 0 && percentIdx < gwStr.length - 1) {
|
||||
ifaceName = gwStr.slice(percentIdx + 1);
|
||||
const routeList = Array.isArray(routes) ? routes : (routes && typeof routes === 'object' && routes.ret ? routes.ret : []);
|
||||
const match = routeList.find((r) => {
|
||||
const gw = r.gateway ?? r.Gateway;
|
||||
return typeof gw === 'string' && (gw.includes(gatewayIp) || gw.startsWith(gatewayIp));
|
||||
});
|
||||
if (match) {
|
||||
const gwStr = String(match.gateway ?? match.Gateway ?? '');
|
||||
const ifaceFromRoute = match.interface ?? match.Interface;
|
||||
if (typeof ifaceFromRoute === 'string' && ifaceFromRoute.trim()) {
|
||||
ifaceName = ifaceFromRoute.trim();
|
||||
} else {
|
||||
const percentIdx = gwStr.indexOf('%');
|
||||
if (percentIdx >= 0 && percentIdx < gwStr.length - 1) {
|
||||
ifaceName = gwStr.slice(percentIdx + 1).trim();
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (_) {
|
||||
// Если чтение маршрутов не удалось — просто продолжаем без interface,
|
||||
// чтобы не ломать сам traceroute.
|
||||
// Если чтение маршрутов не удалось — просто продолжаем без interface.
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 4) Последний фоллбек: если ifaceName до сих пор не определён,
|
||||
// пробуем взять первый физический порт ether1/ether2 (если есть),
|
||||
// иначе любой интерфейс с префиксом "ether".
|
||||
if (!ifaceName) {
|
||||
// 4) При указанном gatewayIp не подставляем ether1/ether2. Фоллбек только если шлюз не указан.
|
||||
if (!ifaceName && !gatewayIp) {
|
||||
try {
|
||||
const ifRes = await client.command('interface/print', {
|
||||
'.proplist': ['name'],
|
||||
|
||||
Reference in New Issue
Block a user