<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>StudioMCPHub Admin - Logs</title>
<style>
:root{--bg:#0a0a0f;--surface:#12121a;--border:#2a2a3a;--accent:#7c5cff;--teal:#00d4aa;--text:#e0e0e0;--muted:#888;--red:#ff4d6a;--orange:#f0a030;--green:#00d4aa}
*{margin:0;padding:0;box-sizing:border-box}
body{background:var(--bg);color:var(--text);font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',sans-serif;font-size:14px}
a{color:var(--accent);text-decoration:none}
.header{display:flex;align-items:center;justify-content:space-between;padding:12px 24px;background:var(--surface);border-bottom:1px solid var(--border);position:sticky;top:0;z-index:10}
.header h1{font-size:1.1rem;font-weight:600}
.header h1 span{color:var(--accent)}
.nav-links{display:flex;gap:12px}
.nav-links a{padding:6px 12px;border-radius:6px;font-size:0.8rem;color:var(--muted);transition:all 0.2s}
.nav-links a:hover,.nav-links a.active{color:var(--text);background:#1a1a2e}
.controls{display:flex;gap:12px;padding:16px 24px;align-items:center;flex-wrap:wrap}
select,input[type=number]{background:#1a1a2e;border:1px solid var(--border);color:var(--text);padding:8px 12px;border-radius:6px;font-size:0.85rem;outline:none}
select:focus,input:focus{border-color:var(--accent)}
button{padding:8px 16px;background:var(--accent);color:#fff;border:none;border-radius:6px;cursor:pointer;font-size:0.85rem}
button:hover{background:#6a4de0}
.log-container{padding:0 24px 24px}
table{width:100%;border-collapse:collapse}
th{text-align:left;padding:8px 12px;font-size:0.75rem;text-transform:uppercase;letter-spacing:0.05em;color:var(--muted);border-bottom:1px solid var(--border);position:sticky;top:0;background:var(--bg)}
td{padding:8px 12px;border-bottom:1px solid var(--border);font-size:0.8rem;vertical-align:top}
tr:hover{background:#1a1a2e}
.sev-DEFAULT,.sev-DEBUG{color:var(--muted)}
.sev-INFO,.sev-NOTICE{color:var(--teal)}
.sev-WARNING{color:var(--orange)}
.sev-ERROR,.sev-CRITICAL,.sev-ALERT,.sev-EMERGENCY{color:var(--red)}
.msg-cell{max-width:600px;word-break:break-word;font-family:'Fira Code',monospace;font-size:0.75rem}
.empty{text-align:center;padding:40px;color:var(--muted)}
.count{color:var(--muted);font-size:0.8rem;padding:0 24px 8px}
</style>
</head>
<body>
<div class="header">
<h1><span>StudioMCPHub</span> Admin</h1>
<div class="nav-links">
<a href="/admin">Dashboard</a>
<a href="/admin/logs" class="active">Logs</a>
<a href="/admin/data">Data</a>
</div>
<div style="font-size:0.8rem;color:var(--muted)">v{{ version }}</div>
</div>
<div class="controls">
<label style="color:var(--muted);font-size:0.8rem">Service:</label>
<select id="svc">
<option value="studiomcphub">studiomcphub</option>
<option value="data-portal">data-portal</option>
<option value="fluora-mcp">fluora-mcp</option>
<option value="nova-agent">nova-agent</option>
<option value="atlas-agent">atlas-agent</option>
<option value="esrgan-l4">esrgan-gpu</option>
<option value="sd35-l4">sd35-l4</option>
<option value="archivus-agent">archivus-agent</option>
<option value="mintra-agent">mintra-agent</option>
</select>
<label style="color:var(--muted);font-size:0.8rem">Severity:</label>
<select id="sev">
<option value="DEFAULT">All</option>
<option value="INFO">INFO+</option>
<option value="WARNING">WARNING+</option>
<option value="ERROR">ERROR+</option>
</select>
<label style="color:var(--muted);font-size:0.8rem">Limit:</label>
<input type="number" id="lim" value="50" min="10" max="200" style="width:70px">
<button onclick="fetchLogs()">Refresh</button>
<label style="color:var(--muted);font-size:0.8rem"><input type="checkbox" id="auto-refresh" checked> Auto (15s)</label>
</div>
<div class="count" id="count"></div>
<div class="log-container">
<table>
<thead><tr><th style="width:160px">Time</th><th style="width:80px">Severity</th><th style="width:100px">Log</th><th>Message</th></tr></thead>
<tbody id="log-body">
<tr><td colspan="4" class="empty">Loading...</td></tr>
</tbody>
</table>
</div>
<script>
function escapeHtml(s){ return s.replace(/&/g,'&').replace(/</g,'<').replace(/>/g,'>'); }
async function fetchLogs(){
const svc = document.getElementById('svc').value;
const sev = document.getElementById('sev').value;
const lim = document.getElementById('lim').value;
try{
const r = await fetch(`/api/admin/logs?service=${svc}&severity=${sev}&limit=${lim}`);
if(!r.ok) return;
const d = await r.json();
const body = document.getElementById('log-body');
document.getElementById('count').textContent = `${d.count} entries`;
if(!d.entries.length){
body.innerHTML = '<tr><td colspan="4" class="empty">No log entries found</td></tr>';
return;
}
body.innerHTML = d.entries.map(e => {
const ts = e.timestamp ? new Date(e.timestamp).toLocaleString() : '';
return `<tr>
<td style="white-space:nowrap">${ts}</td>
<td class="sev-${e.severity}">${e.severity}</td>
<td style="color:var(--muted)">${e.log_name}</td>
<td class="msg-cell">${escapeHtml(e.message)}</td>
</tr>`;
}).join('');
}catch(e){ console.error('Log fetch error:', e); }
}
fetchLogs();
setInterval(()=>{ if(document.getElementById('auto-refresh').checked) fetchLogs(); }, 15000);
</script>
</body>
</html>