/* ═══════════════════════════════════════════════════════════ * page-logs.js — 系統日誌 * 從原 logs.html L645-L865 抽出,邏輯不動 * 僅將 active class 名稱統一為 is-active(隨 CSS 對齊) * ═══════════════════════════════════════════════════════════ */ let autoRefreshEnabled = true; let autoScrollEnabled = true; let currentFilter = 'all'; let searchKeyword = ''; let refreshInterval = null; let rawLogs = ''; document.addEventListener('DOMContentLoaded', function () { if (typeof showLoading === 'function') showLoading('正在載入系統日誌...', '請稍候'); fetchLogs(); startAutoRefresh(); }); function updateLastUpdateTime() { const now = new Date(); document.getElementById('last-update').textContent = `最後更新: ${now.toLocaleTimeString('zh-TW')}`; } function fetchLogs() { fetch('/api/logs') .then(r => r.json()) .then(data => { rawLogs = data.logs || ''; updateStats(rawLogs); displayLogs(); updateLastUpdateTime(); document.getElementById('connection-status').textContent = '連接正常'; if (typeof hideLoading === 'function') hideLoading(); }) .catch(e => { console.error('Error fetching logs:', e); document.getElementById('connection-status').textContent = '連接失敗'; if (typeof showToast === 'function') showToast('載入日誌失敗', 'error'); if (typeof hideLoading === 'function') hideLoading(); }); } function displayLogs() { const container = document.getElementById('log-container'); if (!rawLogs || !rawLogs.trim()) { container.innerHTML = '

暫無日誌資料

'; return; } let lines = rawLogs.split('\n'); if (currentFilter !== 'all') { lines = lines.filter(line => { const u = line.toUpperCase(); if (currentFilter === 'error') return u.includes('ERROR'); if (currentFilter === 'warning') return u.includes('WARNING'); if (currentFilter === 'info') return u.includes('INFO'); return true; }); } if (searchKeyword) { lines = lines.filter(line => line.toLowerCase().includes(searchKeyword.toLowerCase())); } const html = lines.map(formatLogLine).join('\n'); container.innerHTML = html || '

沒有符合的日誌

'; if (autoScrollEnabled) container.scrollTop = container.scrollHeight; } function formatLogLine(line) { if (!line.trim()) return ''; let cls = ''; let f = line; const u = line.toUpperCase(); if (u.includes('ERROR')) { cls = 'error'; f = line.replace(/ERROR/gi, 'ERROR'); } else if (u.includes('WARNING')) { cls = 'warning'; f = line.replace(/WARNING/gi, 'WARNING'); } else if (u.includes('INFO')) { cls = 'info'; f = line.replace(/INFO/gi, 'INFO'); } f = f.replace(/(\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2})/g, '$1'); if (searchKeyword) { const re = new RegExp(`(${searchKeyword.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')})`, 'gi'); f = f.replace(re, '$1'); } return `
${f}
`; } function updateStats(logs) { const lines = logs.split('\n').filter(l => l.trim()); document.getElementById('total-lines').textContent = lines.length; document.getElementById('error-count').textContent = lines.filter(l => l.toUpperCase().includes('ERROR')).length; document.getElementById('warning-count').textContent = lines.filter(l => l.toUpperCase().includes('WARNING')).length; document.getElementById('info-count').textContent = lines.filter(l => l.toUpperCase().includes('INFO')).length; } function refreshLogs() { const btn = event.target.closest('.btn-control'); btn.classList.add('spinning'); btn.disabled = true; fetchLogs(); setTimeout(() => { btn.classList.remove('spinning'); btn.disabled = false; if (typeof showToast === 'function') showToast('日誌已刷新', 'success'); }, 600); } function toggleAutoRefresh() { autoRefreshEnabled = !autoRefreshEnabled; const btn = document.getElementById('pause-btn'); if (autoRefreshEnabled) { btn.className = 'btn-control btn-control--pause'; btn.innerHTML = '暫停自動刷新'; startAutoRefresh(); if (typeof showToast === 'function') showToast('已啟用自動刷新', 'success'); } else { btn.className = 'btn-control btn-control--resume'; btn.innerHTML = '繼續自動刷新'; stopAutoRefresh(); if (typeof showToast === 'function') showToast('已暫停自動刷新', 'info'); } } function startAutoRefresh() { if (refreshInterval) clearInterval(refreshInterval); refreshInterval = setInterval(fetchLogs, 5000); } function stopAutoRefresh() { if (refreshInterval) { clearInterval(refreshInterval); refreshInterval = null; } } function clearLogs() { if (!confirm('確定要清除日誌顯示嗎?(不會刪除實際日誌檔案)')) return; rawLogs = ''; updateStats(''); displayLogs(); if (typeof showToast === 'function') showToast('已清除日誌顯示', 'success'); } function downloadLogs() { if (!rawLogs) { if (typeof showToast === 'function') showToast('沒有可下載的日誌', 'error'); return; } const blob = new Blob([rawLogs], { type: 'text/plain' }); const url = URL.createObjectURL(blob); const a = document.createElement('a'); a.href = url; const ts = new Date().toISOString().replace(/[:.]/g, '-').slice(0, -5); a.download = `momo_system_logs_${ts}.txt`; document.body.appendChild(a); a.click(); document.body.removeChild(a); URL.revokeObjectURL(url); if (typeof showToast === 'function') showToast('日誌已下載', 'success'); } function filterByLevel(level) { currentFilter = level; document.querySelectorAll('.btn-filter').forEach(b => { b.classList.toggle('is-active', b.dataset.level === level); }); displayLogs(); } function searchLogs() { const input = document.getElementById('search-input'); const box = document.getElementById('search-box'); searchKeyword = input.value.trim(); box.classList.toggle('has-text', !!searchKeyword); displayLogs(); } function clearSearch() { const input = document.getElementById('search-input'); input.value = ''; searchKeyword = ''; document.getElementById('search-box').classList.remove('has-text'); displayLogs(); input.focus(); } function changeFontSize(size) { const c = document.getElementById('log-container'); c.classList.remove('font-small', 'font-medium', 'font-large'); c.classList.add(`font-${size}`); document.querySelectorAll('.btn-font-size').forEach(b => { b.classList.toggle('is-active', b.dataset.size === size); }); } function toggleAutoScroll() { autoScrollEnabled = document.getElementById('auto-scroll-toggle').checked; if (autoScrollEnabled) { const c = document.getElementById('log-container'); c.scrollTop = c.scrollHeight; } }