/* ═══════════════════════════════════════════════════════════
* 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;
}
}