Files
MC_Report/public/js/pages/logs.js
canglan 8c5ce78fd0 chore: add AGPLv3 copyright header to all source files
- 52 JS files (backend + public/js): header with
  Copyright (C) 2026 Sea Network Technology Studio
  Author: CangLan <admin@sea-studio.top>
  + AGPLv3 notice
- idempotent (skips if header present), all syntax-checked
2026-08-19 20:27:05 +08:00

106 lines
5.0 KiB
JavaScript

/*
* MC Report System
* Copyright (C) 2026 Sea Network Technology Studio
* Author: CangLan <admin@sea-studio.top>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published
* by the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
const LogsPage = {
tab: 'system',
async render() { return '<div class="loading"><i class="fas fa-spinner"></i></div>'; },
async mount() {
document.getElementById('page-title').textContent = '系统日志';
document.getElementById('page-actions').innerHTML = `<button class="btn btn-o btn-sm" onclick="LogsPage.refresh()"><i class="fas fa-rotate"></i> 刷新</button>`;
const ct = document.getElementById('page-content');
ct.innerHTML = `
<div class="tab-nav" id="logs-tabs">
<button data-tab="system" class="active"><i class="fas fa-server"></i> 系统日志</button>
<button data-tab="email"><i class="fas fa-envelope"></i> 邮件日志</button>
</div>
<div class="filter-bar">
<select id="lg-level" onchange="LogsPage.applyFilter()">
<option value="">全部级别</option><option value="info">信息</option><option value="warn">警告</option><option value="error">错误</option>
</select>
<input id="lg-source" placeholder="来源关键字" onkeyup="if(event.key==='Enter')LogsPage.applyFilter()">
<button class="btn btn-o btn-sm" onclick="LogsPage.applyFilter()"><i class="fas fa-filter"></i></button>
</div>
<div id="logs-list"></div>
`;
document.querySelectorAll('#logs-tabs button').forEach(b => {
b.onclick = () => {
document.querySelectorAll('#logs-tabs button').forEach(x => x.classList.remove('active'));
b.classList.add('active');
this.tab = b.dataset.tab;
this.load();
};
});
await this.load();
},
refresh() { this.load(); },
async applyFilter() {
this.load();
},
async load() {
const list = document.getElementById('logs-list');
U.loading(list);
const level = document.getElementById('lg-level')?.value || '';
const src = document.getElementById('lg-source')?.value.trim() || '';
const isEmail = this.tab === 'email';
try {
const params = new URLSearchParams();
if (level) params.set('level', level);
if (src) params.set(isEmail ? 'email' : 'source', src);
const qs = params.toString();
const rows = await API.get('/logs/' + (isEmail ? 'emails' : 'system') + (qs ? '?' + qs : ''));
this.renderList(list, rows, isEmail);
} catch (e) { list.innerHTML = `<div class="alert alert-e">${e.message}</div>`; }
},
renderList(ct, rows, isEmail) {
if (isEmail) {
ct.innerHTML = rows.length === 0
? '<div class="empty"><i class="fas fa-envelope-open"></i><p>暂无邮件日志</p></div>'
: `<div class="card table-wrap"><table><thead><tr><th>#</th><th>收件人</th><th>模板</th><th>主题</th><th>状态</th><th>错误</th><th>时间</th></tr></thead><tbody>${rows.map(r => `
<tr>
<td>${r.id}</td>
<td>${U.esc(r.to_email)}</td>
<td><span class="badge bg-admin">${U.esc(r.template_code||'-')}</span></td>
<td class="ts">${U.esc(r.subject||'')}</td>
<td>${r.status==='sent'?'<span class="badge bg-resolved">成功</span>':'<span class="badge bg-rejected">失败</span>'}</td>
<td class="ts" style="max-width:260px;word-break:break-all">${U.esc(r.error||'-')}</td>
<td class="ts">${U.date(r.created_at)}</td>
</tr>`).join('')}</tbody></table></div>`;
} else {
const lv = { info:'<span class="badge bg-resolved">信息</span>', warn:'<span class="badge bg-medium">警告</span>', error:'<span class="badge bg-rejected">错误</span>' };
ct.innerHTML = rows.length === 0
? '<div class="empty"><i class="fas fa-clipboard"></i><p>暂无系统日志</p></div>'
: `<div class="card table-wrap"><table><thead><tr><th>#</th><th>级别</th><th>来源</th><th>消息</th><th>详情</th><th>时间</th></tr></thead><tbody>${rows.map(r => `
<tr>
<td>${r.id}</td>
<td>${lv[r.level]||r.level}</td>
<td><span class="badge bg-admin">${U.esc(r.source)}</span></td>
<td>${U.esc(r.message)}</td>
<td class="ts" style="max-width:280px;word-break:break-all">${U.esc(r.details||'-')}</td>
<td class="ts">${U.date(r.created_at)}</td>
</tr>`).join('')}</tbody></table></div>`;
}
}
};