feat(MikrotikTools): add traceroute functionality via MikroTik API and integrate new tools route in frontend
Publish Fast Tabler Docker image / build-and-push-fast (push) Failing after 2m36s
Publish Fast Tabler Docker image / build-and-push-fast (push) Failing after 2m36s
This commit is contained in:
@@ -541,6 +541,111 @@ async function pingViaInterface(req, res) {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* POST /api/mikrotik/traceroute
|
||||
* Трассировка до адреса через указанный MikroTik (jumphost) и, при желании, конкретный gateway.
|
||||
*
|
||||
* Body:
|
||||
* - serverId: ID/имя сервера из servers.json (обязательно)
|
||||
* - target: адрес назначения (обязательно)
|
||||
* - gatewayIp?: IP шлюза, через который выполнять трассировку
|
||||
* - maxHops?: максимальное количество хопов (по умолчанию 30)
|
||||
*/
|
||||
async function tracerouteViaGateway(req, res) {
|
||||
try {
|
||||
let {
|
||||
serverId,
|
||||
target,
|
||||
gatewayIp,
|
||||
maxHops = 30,
|
||||
} = req.body || {};
|
||||
|
||||
if (!serverId) {
|
||||
return sendError(res, 400, 'serverId is required', 'E_BAD_REQUEST');
|
||||
}
|
||||
|
||||
if (target == null || String(target).trim() === '') {
|
||||
return sendError(res, 400, 'target is required', 'E_BAD_REQUEST');
|
||||
}
|
||||
|
||||
target = String(target).trim();
|
||||
|
||||
const servers = await readServersFromS3();
|
||||
const server = servers.find((s) => (s.id || s.dns || s.ip) === serverId);
|
||||
if (!server || server.type !== 'jumphost') {
|
||||
return sendError(res, 400, 'Jumphost server not found', 'E_NOT_FOUND');
|
||||
}
|
||||
|
||||
const creds = getMikrotikCredentials(server);
|
||||
if (!creds) {
|
||||
return sendError(res, 400, 'MikroTik credentials not configured for this server', 'E_CREDENTIALS');
|
||||
}
|
||||
|
||||
const client = createRosClient(creds);
|
||||
|
||||
const body = {
|
||||
address: target,
|
||||
};
|
||||
|
||||
if (gatewayIp) {
|
||||
body.gateway = gatewayIp;
|
||||
}
|
||||
|
||||
const hopsNum = Number(maxHops);
|
||||
if (Number.isFinite(hopsNum) && hopsNum > 0) {
|
||||
body['max-hops'] = hopsNum;
|
||||
}
|
||||
|
||||
try {
|
||||
const cliParts = [
|
||||
'/tool/traceroute',
|
||||
`address=${body.address}`,
|
||||
];
|
||||
if (body.gateway) cliParts.push(`gateway=${body.gateway}`);
|
||||
if (body['max-hops']) cliParts.push(`max-hops=${body['max-hops']}`);
|
||||
|
||||
console.log('[mikrotik][tracerouteViaGateway]', {
|
||||
serverId,
|
||||
gatewayIp,
|
||||
body,
|
||||
cli: cliParts.join(' '),
|
||||
});
|
||||
} catch (_) {}
|
||||
|
||||
const trRes = await client.command('tool/traceroute', body);
|
||||
const raw = trRes?.data;
|
||||
const rows = Array.isArray(raw) ? raw : (raw ? [raw] : []);
|
||||
|
||||
const parseMs = (val) => {
|
||||
if (val == null) return null;
|
||||
const s = String(val).trim();
|
||||
const m = s.match(/([\d.]+)/);
|
||||
return m ? Number(m[1]) : null;
|
||||
};
|
||||
|
||||
const hops = rows.map((r, index) => ({
|
||||
hop: r.hop != null ? Number(r.hop) : index + 1,
|
||||
host: r.host || r.address || '',
|
||||
avgMs: parseMs(r['avg-rtt'] || r.time || r.avg),
|
||||
bestMs: parseMs(r['best-rtt'] || r['min-rtt']),
|
||||
worstMs: parseMs(r['worst-rtt'] || r['max-rtt']),
|
||||
loss:
|
||||
r['packet-loss'] != null
|
||||
? Number(String(r['packet-loss']).replace('%', ''))
|
||||
: null,
|
||||
status: r.status || '',
|
||||
raw: r,
|
||||
}));
|
||||
|
||||
return res.json({ ok: true, hops });
|
||||
} catch (error) {
|
||||
const msg = error.response?.data?.message || error.message || 'Traceroute failed';
|
||||
const status = error.response?.status;
|
||||
console.error('tracerouteViaGateway:', error);
|
||||
return sendError(res, status && status >= 400 ? status : 502, msg, 'E_TRACEROUTE');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* POST /api/mikrotik/run-script
|
||||
* Body: { serverId, script?: string } — по умолчанию script=update_bgp_filter
|
||||
@@ -587,5 +692,6 @@ module.exports = {
|
||||
testMikrotikConnection,
|
||||
applyMikrotikConfig,
|
||||
runScript,
|
||||
tracerouteViaGateway,
|
||||
pingViaInterface,
|
||||
};
|
||||
|
||||
@@ -456,6 +456,8 @@ app.post('/api/mikrotik/test-connection', mikrotikConfigRoutes.testMikrotikConne
|
||||
app.get('/api/mikrotik/ping', (req, res) => res.json({ ok: true, ts: Date.now() }));
|
||||
// Реальный ping через RouterOS по интерфейсу/шлюзу
|
||||
app.post('/api/mikrotik/ping', writeLimiter, mikrotikConfigRoutes.pingViaInterface);
|
||||
// Traceroute через RouterOS с выбором сервера и шлюза
|
||||
app.post('/api/mikrotik/traceroute', writeLimiter, mikrotikConfigRoutes.tracerouteViaGateway);
|
||||
app.post('/api/mikrotik/apply', mikrotikConfigRoutes.applyMikrotikConfig);
|
||||
app.post('/api/mikrotik/run-script', mikrotikConfigRoutes.runScript);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user