feat: rich log viewers for all remaining log types
- Syslog-style table (postfix, dovecot, sogo, netfilter, acme): Time | Priority badge (colour-coded debug→info→notice→warn→error→crit) | Process | Message — columns hidden when unused by that log type - API access log: Time | Method badge (GET=green, POST=blue, PUT=orange, DELETE=red) | Endpoint | Remote IP | Data - Autodiscover: Time | User | Service badge (ActiveSync, CalDAV, CardDAV, IMAP, SMTP) | User Agent - Watchdog: Time | Service | Health badge (Healthy/Degraded/Critical based on lvl) | Processes (now/total) | Change (+/-/±0 coloured) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -952,6 +952,168 @@
|
||||
$('#wc-log-wrap').html(html);
|
||||
};
|
||||
|
||||
// ── Syslog-style renderer (postfix, dovecot, sogo, netfilter, acme) ──
|
||||
|
||||
const priorityBadge = (priority) => {
|
||||
const p = (priority || 'info').toLowerCase();
|
||||
const styles = {
|
||||
debug: 'background:#f0f0f0;color:#888',
|
||||
info: 'background:#e9ecef;color:#495057',
|
||||
notice: 'background:#cce5ff;color:#004085',
|
||||
warn: 'background:#fff3cd;color:#856404',
|
||||
warning: 'background:#fff3cd;color:#856404',
|
||||
err: 'background:#f8d7da;color:#721c24',
|
||||
error: 'background:#f8d7da;color:#721c24',
|
||||
crit: 'background:#721c24;color:#fff',
|
||||
critical: 'background:#721c24;color:#fff',
|
||||
alert: 'background:#721c24;color:#fff',
|
||||
emerg: 'background:#1a1a1a;color:#fff',
|
||||
};
|
||||
const style = styles[p] || 'background:#e9ecef;color:#495057';
|
||||
const label = p.charAt(0).toUpperCase() + p.slice(1);
|
||||
return `<span class="woocow-badge" style="${style}">${label}</span>`;
|
||||
};
|
||||
|
||||
const renderSyslogTable = (entries, label) => {
|
||||
const hasProgram = entries.some(e => e.program);
|
||||
const hasPriority = entries.some(e => e.priority);
|
||||
let html = `<div class="woocow-log-toolbar">
|
||||
<strong>${esc(label)}</strong>
|
||||
<span style="color:#666;font-size:12px">${entries.length} entries</span>
|
||||
</div>
|
||||
<table class="wp-list-table widefat fixed striped woocow-table">
|
||||
<thead><tr>
|
||||
<th style="width:140px">Time</th>
|
||||
${hasPriority ? '<th style="width:80px">Priority</th>' : ''}
|
||||
${hasProgram ? '<th style="width:150px">Process</th>' : ''}
|
||||
<th>Message</th>
|
||||
</tr></thead><tbody>`;
|
||||
entries.forEach(e => {
|
||||
const dt = e.time ? new Date(parseInt(e.time) * 1000).toLocaleString() : '—';
|
||||
html += `<tr>
|
||||
<td style="font-size:11px;white-space:nowrap">${esc(dt)}</td>
|
||||
${hasPriority ? `<td>${priorityBadge(e.priority)}</td>` : ''}
|
||||
${hasProgram ? `<td><code style="font-size:11px">${esc(e.program || '—')}</code></td>` : ''}
|
||||
<td style="font-size:12px;word-break:break-word">${esc(e.message || '—')}</td>
|
||||
</tr>`;
|
||||
});
|
||||
html += '</tbody></table>';
|
||||
$('#wc-log-wrap').html(html);
|
||||
};
|
||||
|
||||
// ── API access log renderer ───────────────────────────────────────────
|
||||
|
||||
const renderApiLog = (entries) => {
|
||||
const methodBadge = (method) => {
|
||||
const colors = { GET:'#27ae60', POST:'#2271b1', PUT:'#e67e22', DELETE:'#c0392b', PATCH:'#8e44ad' };
|
||||
const bg = colors[(method || '').toUpperCase()] || '#555';
|
||||
return `<span style="display:inline-block;padding:1px 7px;border-radius:3px;background:${bg};color:#fff;font-size:11px;font-weight:700;font-family:monospace">${esc(method || '?')}</span>`;
|
||||
};
|
||||
let html = `<div class="woocow-log-toolbar">
|
||||
<strong>API Access Log</strong>
|
||||
<span style="color:#666;font-size:12px">${entries.length} requests</span>
|
||||
</div>
|
||||
<table class="wp-list-table widefat fixed striped woocow-table">
|
||||
<thead><tr>
|
||||
<th style="width:140px">Time</th>
|
||||
<th style="width:72px">Method</th>
|
||||
<th>Endpoint</th>
|
||||
<th style="width:110px">Remote IP</th>
|
||||
<th style="width:160px">Data</th>
|
||||
</tr></thead><tbody>`;
|
||||
entries.forEach(e => {
|
||||
const dt = e.time ? new Date(e.time * 1000).toLocaleString() : '—';
|
||||
html += `<tr>
|
||||
<td style="font-size:11px;white-space:nowrap">${esc(dt)}</td>
|
||||
<td>${methodBadge(e.method)}</td>
|
||||
<td><code style="font-size:11px">${esc(e.uri || '—')}</code></td>
|
||||
<td><code style="font-size:11px">${esc(e.remote || '—')}</code></td>
|
||||
<td style="font-size:11px;color:#888;word-break:break-all">${esc(e.data || '—')}</td>
|
||||
</tr>`;
|
||||
});
|
||||
html += '</tbody></table>';
|
||||
$('#wc-log-wrap').html(html);
|
||||
};
|
||||
|
||||
// ── Autodiscover log renderer ─────────────────────────────────────────
|
||||
|
||||
const renderAutodiscoverLog = (entries) => {
|
||||
const svcBadge = (svc) => {
|
||||
const map = {
|
||||
activesync: ['woocow-badge-blue', 'ActiveSync'],
|
||||
caldav: ['woocow-badge-green', 'CalDAV'],
|
||||
carddav: ['woocow-badge-green', 'CardDAV'],
|
||||
imap: ['woocow-badge-grey', 'IMAP'],
|
||||
smtp: ['woocow-badge-grey', 'SMTP'],
|
||||
};
|
||||
const [cls, label] = map[(svc || '').toLowerCase()] || ['woocow-badge-grey', esc(svc || '—')];
|
||||
return `<span class="woocow-badge ${cls}">${label}</span>`;
|
||||
};
|
||||
let html = `<div class="woocow-log-toolbar">
|
||||
<strong>Autodiscover Log</strong>
|
||||
<span style="color:#666;font-size:12px">${entries.length} requests</span>
|
||||
</div>
|
||||
<table class="wp-list-table widefat fixed striped woocow-table">
|
||||
<thead><tr>
|
||||
<th style="width:140px">Time</th>
|
||||
<th>User</th>
|
||||
<th style="width:120px">Service</th>
|
||||
<th>User Agent</th>
|
||||
</tr></thead><tbody>`;
|
||||
entries.forEach(e => {
|
||||
const dt = e.time ? new Date(e.time * 1000).toLocaleString() : '—';
|
||||
html += `<tr>
|
||||
<td style="font-size:11px;white-space:nowrap">${esc(dt)}</td>
|
||||
<td style="font-size:12px">${esc(e.user || '—')}</td>
|
||||
<td>${svcBadge(e.service)}</td>
|
||||
<td style="font-size:11px;color:#666">${esc(e.ua || '—')}</td>
|
||||
</tr>`;
|
||||
});
|
||||
html += '</tbody></table>';
|
||||
$('#wc-log-wrap').html(html);
|
||||
};
|
||||
|
||||
// ── Watchdog log renderer ─────────────────────────────────────────────
|
||||
|
||||
const renderWatchdogLog = (entries) => {
|
||||
const healthBadge = (lvl) => {
|
||||
const n = parseInt(lvl) || 0;
|
||||
if (n >= 100) return '<span class="woocow-badge woocow-badge-green">Healthy</span>';
|
||||
if (n >= 50) return '<span class="woocow-badge woocow-badge-orange">Degraded</span>';
|
||||
return '<span class="woocow-badge woocow-badge-red">Critical</span>';
|
||||
};
|
||||
let html = `<div class="woocow-log-toolbar">
|
||||
<strong>Watchdog Log</strong>
|
||||
<span style="color:#666;font-size:12px">${entries.length} entries</span>
|
||||
</div>
|
||||
<table class="wp-list-table widefat fixed striped woocow-table">
|
||||
<thead><tr>
|
||||
<th style="width:140px">Time</th>
|
||||
<th style="width:140px">Service</th>
|
||||
<th style="width:90px">Status</th>
|
||||
<th style="width:100px">Processes</th>
|
||||
<th style="width:70px">Change</th>
|
||||
</tr></thead><tbody>`;
|
||||
entries.forEach(e => {
|
||||
const dt = e.time ? new Date(parseInt(e.time) * 1000).toLocaleString() : '—';
|
||||
const diff = parseInt(e.hpdiff) || 0;
|
||||
const diffHtml = diff > 0
|
||||
? `<span style="color:#27ae60;font-weight:700">+${diff}</span>`
|
||||
: diff < 0
|
||||
? `<span style="color:#c0392b;font-weight:700">${diff}</span>`
|
||||
: `<span style="color:#bbb">±0</span>`;
|
||||
html += `<tr>
|
||||
<td style="font-size:11px;white-space:nowrap">${esc(dt)}</td>
|
||||
<td><strong>${esc(e.service || '—')}</strong></td>
|
||||
<td>${healthBadge(e.lvl)}</td>
|
||||
<td style="font-family:monospace">${esc(e.hpnow || '0')}/${esc(e.hptotal || '0')}</td>
|
||||
<td>${diffHtml}</td>
|
||||
</tr>`;
|
||||
});
|
||||
html += '</tbody></table>';
|
||||
$('#wc-log-wrap').html(html);
|
||||
};
|
||||
|
||||
// ── Log load ──────────────────────────────────────────────────────────
|
||||
|
||||
$('#wc-log-load').on('click', () => {
|
||||
@@ -969,24 +1131,14 @@
|
||||
$('#wc-log-wrap').html('<p>No log entries.</p>');
|
||||
return;
|
||||
}
|
||||
|
||||
const typeLabel = $('#wc-log-type option:selected').text();
|
||||
if (type === 'rspamd-history') { renderRspamdLog(entries); return; }
|
||||
if (type === 'ratelimited') { renderRatelimitLog(entries); return; }
|
||||
|
||||
// Plain text log
|
||||
const typeLabel = $('#wc-log-type option:selected').text();
|
||||
const text = entries.map(e => {
|
||||
if (typeof e === 'string') return e;
|
||||
if (e.time && e.message) return `[${e.time}] ${e.message}`;
|
||||
return JSON.stringify(e);
|
||||
}).join('\n');
|
||||
$('#wc-log-wrap').html(`
|
||||
<div class="woocow-log-toolbar">
|
||||
<strong>${esc(typeLabel)} log</strong>
|
||||
<span style="color:#666;font-size:12px">${entries.length} entries</span>
|
||||
</div>
|
||||
<pre class="woocow-log-pre">${esc(text)}</pre>
|
||||
`);
|
||||
if (type === 'api') { renderApiLog(entries); return; }
|
||||
if (type === 'autodiscover') { renderAutodiscoverLog(entries); return; }
|
||||
if (type === 'watchdog') { renderWatchdogLog(entries); return; }
|
||||
// postfix, dovecot, sogo, netfilter, acme → syslog table
|
||||
renderSyslogTable(entries, typeLabel + ' Log');
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user