feat(MikrotikConfig, NetworkConfigManager): implement streaming logs for MikroTik configuration application and enhance error handling in response processing
Publish Fast Tabler Docker image / build-and-push-fast (push) Successful in 3m2s

This commit is contained in:
2026-02-03 20:40:32 +07:00
parent b374faa593
commit ce107db7ea
2 changed files with 135 additions and 23 deletions
+44 -10
View File
@@ -2237,16 +2237,53 @@ function NetworkConfigManager() {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-Stream-Logs': '1',
},
body: JSON.stringify(requestBody),
});
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 {};
});
const contentType = response.headers.get('Content-Type') || '';
const isStream = contentType.includes('ndjson') || contentType.includes('x-ndjson');
let data = {};
if (isStream && response.body) {
const reader = response.body.getReader();
const decoder = new TextDecoder();
let buffer = '';
let finalData = null;
while (true) {
const { done, value } = await reader.read();
if (done) break;
buffer += decoder.decode(value, { stream: true });
const lines = buffer.split('\n');
buffer = lines.pop() || '';
for (const line of lines) {
if (!line.trim()) continue;
try {
const obj = JSON.parse(line);
if (obj.t === 'log') {
console.log('[MikroTik apply]', obj.msg, obj);
} else if (obj.t === 'result') {
finalData = obj;
} else if (obj.t === 'error') {
finalData = { ok: false, error: obj.error, mikrotikRequests: obj.mikrotikRequests };
}
} catch (_) {}
}
}
if (buffer.trim()) {
try {
const obj = JSON.parse(buffer);
if (obj.t === 'result') finalData = obj;
else if (obj.t === 'error') finalData = { ok: false, error: obj.error, mikrotikRequests: obj.mikrotikRequests };
} catch (_) {}
}
data = finalData || {};
} else {
data = await response.json().catch(() => ({}));
}
if (!response.ok || data.ok === false) {
const errorMessage = data.error || data.message || `HTTP ${response.status}`;
@@ -2257,21 +2294,18 @@ function NetworkConfigManager() {
}
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 url = `${window.location.origin}/api/mikrotik/apply`;
console.error('[MikroTik apply] Fetch failed:', error, '\nURL:', url);
const errMsg = error.message || 'Ошибка применения';
const hint = errMsg.includes('fetch') || errMsg.includes('Failed') ? ' Проверьте логи backend — при ERR_EMPTY_RESPONSE сервер мог не ответить.' : '';
setApplyResult({
ok: false,
error: errMsg + hint,
error: errMsg + ` Сеть: ${url}`,
});
notify.error('Не удалось применить конфигурацию по API');
} finally {