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