diff --git a/backend/routes/mikrotikConfigRoutes.js b/backend/routes/mikrotikConfigRoutes.js index 9d4bb29..6bbb5eb 100644 --- a/backend/routes/mikrotikConfigRoutes.js +++ b/backend/routes/mikrotikConfigRoutes.js @@ -238,8 +238,11 @@ const APPLY_TIMEOUT_MS = Number(process.env.MIKROTIK_APPLY_TIMEOUT_MS) || 60000; */ async function applyMikrotikConfig(req, res) { let conn = null; + const mikrotikRequests = []; + const body = req.body || {}; + console.log('[MikroTik apply] Request body:', { serverId: body.serverId, type: body.type, dryRun: body.dryRun }); try { - const { serverId, type = 'all', dryRun = true } = req.body || {}; + const { serverId, type = 'all', dryRun = true } = body; if (!serverId) { return sendError(res, 400, 'serverId is required', 'E_BAD_REQUEST'); } @@ -290,7 +293,15 @@ async function applyMikrotikConfig(req, res) { port: Number(port) || 8728, }); try { + console.log('[MikroTik apply] Connecting to', host + ':' + port, 'user:', user); await conn.connect(); + const originalWrite = conn.write.bind(conn); + conn.write = async function (path, args) { + const req = { path, args: args || [] }; + mikrotikRequests.push(req); + console.log('[MikroTik API]', path, req.args); + return originalWrite(path, args); + }; const allResults = []; for (const block of blocks) { const blockResults = await applyBlock(conn, block, !!dryRun); @@ -308,7 +319,7 @@ async function applyMikrotikConfig(req, res) { 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 }; + return { ok: true, dryRun: !!dryRun, summary, results: allResults, mikrotikRequests }; } catch (err) { if (conn) try { conn.close(); } catch (_) {} conn = null; @@ -326,12 +337,14 @@ async function applyMikrotikConfig(req, res) { if (conn) try { conn.close(); } catch (_) {} console.error('Error applying MikroTik config:', error); const msg = error.message || String(error); - return res.status(500).json({ + const errPayload = { ok: false, error: msg.includes('ECONNREFUSED') ? 'Соединение отклонено. Проверьте хост и порт.' : msg.includes('ETIMEDOUT') || msg.includes('Таймаут') ? msg : msg.includes('Authentication') || msg.includes('login') ? 'Неверный логин или пароль' : msg, - }); + }; + if (mikrotikRequests.length > 0) errPayload.mikrotikRequests = mikrotikRequests; + return res.status(500).json(errPayload); } } diff --git a/frontend/src/NetworkConfigManager.jsx b/frontend/src/NetworkConfigManager.jsx index 3973e53..ffebbce 100644 --- a/frontend/src/NetworkConfigManager.jsx +++ b/frontend/src/NetworkConfigManager.jsx @@ -2229,37 +2229,49 @@ function NetworkConfigManager() { setApplyLoading(true); if (dryRunOverride !== null) setApplyResult(null); + const requestBody = { serverId: applyServerId, type: 'all', dryRun: useDryRun }; + console.log('[MikroTik apply] Request:', { url: '/api/mikrotik/apply', method: 'POST', body: requestBody }); + try { const response = await fetch('/api/mikrotik/apply', { method: 'POST', headers: { 'Content-Type': 'application/json', }, - body: JSON.stringify({ - serverId: applyServerId, - type: 'all', - dryRun: useDryRun, - }), + body: JSON.stringify(requestBody), }); - const data = await response.json().catch(() => ({})); + console.log('[MikroTik apply] Response:', { status: response.status, ok: response.ok, url: response.url }); + + const data = await response.json().catch((parseErr) => { + console.warn('[MikroTik apply] JSON parse error:', parseErr); + return {}; + }); if (!response.ok || data.ok === false) { const errorMessage = data.error || data.message || `HTTP ${response.status}`; - setApplyResult({ ok: false, error: errorMessage }); + console.error('[MikroTik apply] Error:', { status: response.status, error: errorMessage, data }); + setApplyResult({ ok: false, error: errorMessage, mikrotikRequests: data.mikrotikRequests }); notify.error('Не удалось применить конфигурацию по API'); return; } + console.log('[MikroTik apply] Success:', data); + if (data.mikrotikRequests?.length) { + console.log('[MikroTik apply] Запросы к MikroTik:', data.mikrotikRequests); + } setApplyResult(data); if (data.ok && !useDryRun) { notify.success(`Применено: создано ${data.summary?.created || 0}, обновлено ${data.summary?.updated || 0}, пропущено ${data.summary?.skipped || 0}`); } } catch (error) { + console.error('[MikroTik apply] Fetch failed:', error); + const errMsg = error.message || 'Ошибка применения'; + const hint = errMsg.includes('fetch') || errMsg.includes('Failed') ? ' Проверьте логи backend — при ERR_EMPTY_RESPONSE сервер мог не ответить.' : ''; setApplyResult({ ok: false, - error: error.message || 'Ошибка применения', + error: errMsg + hint, }); notify.error('Не удалось применить конфигурацию по API'); } finally { @@ -5373,9 +5385,31 @@ function NetworkConfigManager() { ))} + {applyResult.mikrotikRequests?.length > 0 && ( +
+ Запросы к MikroTik API ({applyResult.mikrotikRequests.length}) +
+                              {applyResult.mikrotikRequests.map((req, i) => (
+                                
{req.path} {Array.isArray(req.args) ? req.args.join(' ') : ''}
+ ))} +
+
+ )} ) : ( -
{applyResult.error}
+ <> +
{applyResult.error}
+ {applyResult.mikrotikRequests?.length > 0 && ( +
+ Запросы к MikroTik API до ошибки ({applyResult.mikrotikRequests.length}) +
+                              {applyResult.mikrotikRequests.map((req, i) => (
+                                
{req.path} {Array.isArray(req.args) ? req.args.join(' ') : ''}
+ ))} +
+
+ )} + )} )}