refactor(MikrotikConfig): implement timeout handling and improve error management in applyMikrotikConfig function
Publish Fast Tabler Docker image / build-and-push-fast (push) Successful in 1m51s
Publish Fast Tabler Docker image / build-and-push-fast (push) Successful in 1m51s
This commit is contained in:
@@ -228,6 +228,8 @@ async function testMikrotikConnection(req, res) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const APPLY_TIMEOUT_MS = Number(process.env.MIKROTIK_APPLY_TIMEOUT_MS) || 60000;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* POST /api/mikrotik/apply
|
* POST /api/mikrotik/apply
|
||||||
* Body: { serverId: string, type?: 'interfaces'|'recursive'|'all', dryRun?: boolean }
|
* Body: { serverId: string, type?: 'interfaces'|'recursive'|'all', dryRun?: boolean }
|
||||||
@@ -235,6 +237,7 @@ async function testMikrotikConnection(req, res) {
|
|||||||
* dryRun=true — только показать план, не выполнять.
|
* dryRun=true — только показать план, не выполнять.
|
||||||
*/
|
*/
|
||||||
async function applyMikrotikConfig(req, res) {
|
async function applyMikrotikConfig(req, res) {
|
||||||
|
let conn = null;
|
||||||
try {
|
try {
|
||||||
const { serverId, type = 'all', dryRun = true } = req.body || {};
|
const { serverId, type = 'all', dryRun = true } = req.body || {};
|
||||||
if (!serverId) {
|
if (!serverId) {
|
||||||
@@ -279,47 +282,55 @@ async function applyMikrotikConfig(req, res) {
|
|||||||
includeRecursive,
|
includeRecursive,
|
||||||
});
|
});
|
||||||
|
|
||||||
const conn = new RouterOSAPI({
|
const runApply = async () => {
|
||||||
host: String(host),
|
conn = new RouterOSAPI({
|
||||||
user: String(user),
|
host: String(host),
|
||||||
password: String(password),
|
user: String(user),
|
||||||
port: Number(port) || 8728,
|
password: String(password),
|
||||||
});
|
port: Number(port) || 8728,
|
||||||
|
|
||||||
await conn.connect();
|
|
||||||
|
|
||||||
const allResults = [];
|
|
||||||
for (const block of blocks) {
|
|
||||||
const blockResults = await applyBlock(conn, block, !!dryRun);
|
|
||||||
allResults.push({
|
|
||||||
blockType: block.type,
|
|
||||||
serverName: block.serverName,
|
|
||||||
results: blockResults,
|
|
||||||
});
|
});
|
||||||
}
|
try {
|
||||||
|
await conn.connect();
|
||||||
conn.close();
|
const allResults = [];
|
||||||
|
for (const block of blocks) {
|
||||||
const summary = {
|
const blockResults = await applyBlock(conn, block, !!dryRun);
|
||||||
created: allResults.flatMap(b => b.results).filter(r => r.status === 'created' || r.status === 'would_create').length,
|
allResults.push({
|
||||||
updated: allResults.flatMap(b => b.results).filter(r => r.status === 'updated' || r.status === 'would_update').length,
|
blockType: block.type,
|
||||||
skipped: allResults.flatMap(b => b.results).filter(r => r.status === 'skip').length,
|
serverName: block.serverName,
|
||||||
errors: allResults.flatMap(b => b.results).filter(r => r.status === 'error'),
|
results: blockResults,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
conn.close();
|
||||||
|
conn = null;
|
||||||
|
const summary = {
|
||||||
|
created: allResults.flatMap(b => b.results).filter(r => r.status === 'created' || r.status === 'would_create').length,
|
||||||
|
updated: allResults.flatMap(b => b.results).filter(r => r.status === 'updated' || r.status === 'would_update').length,
|
||||||
|
skipped: allResults.flatMap(b => b.results).filter(r => r.status === 'skip').length,
|
||||||
|
errors: allResults.flatMap(b => b.results).filter(r => r.status === 'error'),
|
||||||
|
};
|
||||||
|
return { ok: true, dryRun: !!dryRun, summary, results: allResults };
|
||||||
|
} catch (err) {
|
||||||
|
if (conn) try { conn.close(); } catch (_) {}
|
||||||
|
conn = null;
|
||||||
|
throw err;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
res.json({
|
const timeoutPromise = new Promise((_, reject) => {
|
||||||
ok: true,
|
setTimeout(() => reject(new Error('Таймаут: MikroTik недоступен или операция заняла слишком много времени. Проверьте хост, порт и доступность роутера.')), APPLY_TIMEOUT_MS);
|
||||||
dryRun: !!dryRun,
|
|
||||||
summary,
|
|
||||||
results: allResults,
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const result = await Promise.race([runApply(), timeoutPromise]);
|
||||||
|
return res.json(result);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
if (conn) try { conn.close(); } catch (_) {}
|
||||||
console.error('Error applying MikroTik config:', error);
|
console.error('Error applying MikroTik config:', error);
|
||||||
const msg = error.message || String(error);
|
const msg = error.message || String(error);
|
||||||
return res.status(500).json({
|
return res.status(500).json({
|
||||||
ok: false,
|
ok: false,
|
||||||
error: msg.includes('ECONNREFUSED') ? 'Соединение отклонено' :
|
error: msg.includes('ECONNREFUSED') ? 'Соединение отклонено. Проверьте хост и порт.' :
|
||||||
msg.includes('Authentication') || msg.includes('login') ? 'Неверный логин или пароль' : msg,
|
msg.includes('ETIMEDOUT') || msg.includes('Таймаут') ? msg :
|
||||||
|
msg.includes('Authentication') || msg.includes('login') ? 'Неверный логин или пароль' : msg,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user