From 3cc73dc9eca7db86064f32c4c60b0ccf071ca907 Mon Sep 17 00:00:00 2001 From: Malin Date: Fri, 27 Feb 2026 09:52:17 +0100 Subject: [PATCH] feat: rich log viewers for all remaining log types MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 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 --- assets/js/woocow-admin.js | 184 ++++++++++++++++++++++++++++++++++---- 1 file changed, 168 insertions(+), 16 deletions(-) diff --git a/assets/js/woocow-admin.js b/assets/js/woocow-admin.js index 53a294c..2ead658 100644 --- a/assets/js/woocow-admin.js +++ b/assets/js/woocow-admin.js @@ -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 `${label}`; + }; + + const renderSyslogTable = (entries, label) => { + const hasProgram = entries.some(e => e.program); + const hasPriority = entries.some(e => e.priority); + let html = `
+ ${esc(label)} + ${entries.length} entries +
+ + + + ${hasPriority ? '' : ''} + ${hasProgram ? '' : ''} + + `; + entries.forEach(e => { + const dt = e.time ? new Date(parseInt(e.time) * 1000).toLocaleString() : '—'; + html += ` + + ${hasPriority ? `` : ''} + ${hasProgram ? `` : ''} + + `; + }); + html += '
TimePriorityProcessMessage
${esc(dt)}${priorityBadge(e.priority)}${esc(e.program || '—')}${esc(e.message || '—')}
'; + $('#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 `${esc(method || '?')}`; + }; + let html = `
+ API Access Log + ${entries.length} requests +
+ + + + + + + + `; + entries.forEach(e => { + const dt = e.time ? new Date(e.time * 1000).toLocaleString() : '—'; + html += ` + + + + + + `; + }); + html += '
TimeMethodEndpointRemote IPData
${esc(dt)}${methodBadge(e.method)}${esc(e.uri || '—')}${esc(e.remote || '—')}${esc(e.data || '—')}
'; + $('#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 `${label}`; + }; + let html = `
+ Autodiscover Log + ${entries.length} requests +
+ + + + + + + `; + entries.forEach(e => { + const dt = e.time ? new Date(e.time * 1000).toLocaleString() : '—'; + html += ` + + + + + `; + }); + html += '
TimeUserServiceUser Agent
${esc(dt)}${esc(e.user || '—')}${svcBadge(e.service)}${esc(e.ua || '—')}
'; + $('#wc-log-wrap').html(html); + }; + + // ── Watchdog log renderer ───────────────────────────────────────────── + + const renderWatchdogLog = (entries) => { + const healthBadge = (lvl) => { + const n = parseInt(lvl) || 0; + if (n >= 100) return 'Healthy'; + if (n >= 50) return 'Degraded'; + return 'Critical'; + }; + let html = `
+ Watchdog Log + ${entries.length} entries +
+ + + + + + + + `; + entries.forEach(e => { + const dt = e.time ? new Date(parseInt(e.time) * 1000).toLocaleString() : '—'; + const diff = parseInt(e.hpdiff) || 0; + const diffHtml = diff > 0 + ? `+${diff}` + : diff < 0 + ? `${diff}` + : `±0`; + html += ` + + + + + + `; + }); + html += '
TimeServiceStatusProcessesChange
${esc(dt)}${esc(e.service || '—')}${healthBadge(e.lvl)}${esc(e.hpnow || '0')}/${esc(e.hptotal || '0')}${diffHtml}
'; + $('#wc-log-wrap').html(html); + }; + // ── Log load ────────────────────────────────────────────────────────── $('#wc-log-load').on('click', () => { @@ -969,24 +1131,14 @@ $('#wc-log-wrap').html('

No log entries.

'); 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(` -
- ${esc(typeLabel)} log - ${entries.length} entries -
-
${esc(text)}
- `); + 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'); }); }); }