diff --git a/templates/index.html b/templates/index.html index f65512c..fade638 100644 --- a/templates/index.html +++ b/templates/index.html @@ -627,6 +627,39 @@ const els = { })(); function setWsState(s){ els.wsstate.textContent = 'ws: ' + s; } + +// Функция для обновления всех логов при изменении фильтров +function refreshAllLogs() { + Object.keys(state.open).forEach(id => { + const obj = state.open[id]; + if (!obj || !obj.logEl) return; + + // Получаем все строки логов + const lines = obj.logEl.innerHTML.split('\n'); + const filteredLines = []; + + lines.forEach(line => { + if (line.trim() === '') return; + + // Проверяем уровень логирования + const cls = classify(line); + if (!allowedByLevel(cls)) return; + + // Проверяем фильтр + if (!applyFilter(line)) return; + + filteredLines.push(line); + }); + + // Обновляем отображение + obj.logEl.innerHTML = filteredLines.join('\n'); + + // Обновляем современный интерфейс + if (state.current && state.current.id === id && els.logContent) { + els.logContent.innerHTML = obj.logEl.innerHTML; + } + }); +} function escapeHtml(s){ return s.replace(/[&<>"']/g, m => ({'&':'&','<':'<','>':'>','"':'"',"'":'''}[m])); } function classify(line){ @@ -889,10 +922,29 @@ function closeWs(id){ async function sendSnapshot(id){ const o = state.open[id]; if (!o){ alert('not open'); return; } - const text = o.logEl.textContent; + + // Получаем текст логов из современного интерфейса или из legacy + let text = ''; + if (state.current && state.current.id === id && els.logContent) { + text = els.logContent.textContent; + } else if (o.logEl) { + text = o.logEl.textContent; + } + + if (!text || text.trim() === '') { + alert('No logs to save'); + return; + } + + console.log('Saving snapshot with content length:', text.length); + const payload = {container_id: id, service: o.serviceName || id, content: text}; const res = await fetch('/api/snapshot', {method:'POST', headers:{'Content-Type':'application/json'}, body: JSON.stringify(payload)}); - if (!res.ok){ alert('snapshot failed'); return; } + if (!res.ok){ + console.error('Snapshot failed:', res.status, res.statusText); + alert('snapshot failed'); + return; + } const js = await res.json(); const a = document.createElement('a'); a.href = js.url; a.download = js.file; a.click(); @@ -1133,8 +1185,24 @@ if (els.groupBtn && els.groupBtn.onclick !== null) { } // Controls -els.clearBtn.onclick = ()=> Object.values(state.open).forEach(o=> o.logEl.textContent=''); -els.refreshBtn.onclick = fetchServices; +els.clearBtn.onclick = ()=> { + Object.values(state.open).forEach(o => { + if (o.logEl) o.logEl.textContent = ''; + }); + // Очищаем современный интерфейс + if (els.logContent) { + els.logContent.textContent = ''; + } + // Сбрасываем счетчики + document.querySelectorAll('.cdbg, .cinfo, .cwarn, .cerr').forEach(el => { + el.textContent = '0'; + }); +}; + +els.refreshBtn.onclick = async () => { + console.log('Refreshing services...'); + await fetchServices(); +}; els.projectSelect.onchange = fetchServices; // Mobile menu toggle @@ -1157,16 +1225,83 @@ els.tail.onchange = ()=> { state.open[id].logEl.textContent=''; closeWs(id); openWs(svc, panel); }); + + // Обновляем современный интерфейс + if (state.current && els.logContent) { + els.logContent.textContent = 'Reconnecting...'; + } }; els.wrapToggle.onchange = ()=> { document.querySelectorAll('.log').forEach(el=> el.style.whiteSpace = els.wrapToggle.checked ? 'pre-wrap' : 'pre'); - els.logContent.style.whiteSpace = els.wrapToggle.checked ? 'pre-wrap' : 'pre'; + if (els.logContent) { + els.logContent.style.whiteSpace = els.wrapToggle.checked ? 'pre-wrap' : 'pre'; + } +}; + +// Добавляем обработчики для autoscroll и pause +els.autoscroll.onchange = ()=> { + // Обновляем настройку автопрокрутки для всех открытых логов + Object.keys(state.open).forEach(id => { + const obj = state.open[id]; + if (obj && obj.wrapEl) { + if (els.autoscroll.checked) { + obj.wrapEl.scrollTop = obj.wrapEl.scrollHeight; + } + } + }); + + // Обновляем современный интерфейс + if (state.current && els.logContent) { + const logContent = document.querySelector('.log-content'); + if (logContent && els.autoscroll.checked) { + logContent.scrollTop = logContent.scrollHeight; + } + } +}; + +els.pause.onchange = ()=> { + // При снятии паузы показываем накопленные логи + if (!els.pause.checked) { + Object.keys(state.open).forEach(id => { + const obj = state.open[id]; + if (obj && obj.pausedBuffer && obj.pausedBuffer.length > 0) { + obj.pausedBuffer.forEach(html => { + obj.logEl.insertAdjacentHTML('beforeend', html); + }); + obj.pausedBuffer = []; + + // Обновляем современный интерфейс + if (state.current && state.current.id === id && els.logContent) { + els.logContent.innerHTML = obj.logEl.innerHTML; + const logContent = document.querySelector('.log-content'); + if (logContent && els.autoscroll.checked) { + logContent.scrollTop = logContent.scrollHeight; + } + } + } + }); + } +}; +els.filter.oninput = ()=> { + state.filter = els.filter.value.trim(); + refreshAllLogs(); +}; +els.lvlDebug.onchange = ()=> { + state.levels.debug = els.lvlDebug.checked; + refreshAllLogs(); +}; +els.lvlInfo.onchange = ()=> { + state.levels.info = els.lvlInfo.checked; + refreshAllLogs(); +}; +els.lvlWarn.onchange = ()=> { + state.levels.warn = els.lvlWarn.checked; + refreshAllLogs(); +}; +els.lvlErr.onchange = ()=> { + state.levels.err = els.lvlErr.checked; + refreshAllLogs(); }; -els.filter.oninput = ()=> { state.filter = els.filter.value.trim(); }; -els.lvlDebug.onchange = ()=> state.levels.debug = els.lvlDebug.checked; -els.lvlInfo.onchange = ()=> state.levels.info = els.lvlInfo.checked; -els.lvlWarn.onchange = ()=> state.levels.warn = els.lvlWarn.checked; -els.lvlErr.onchange = ()=> state.levels.err = els.lvlErr.checked; // Hotkeys: [ ] — tabs, M — multi window.addEventListener('keydown', (e)=>{