aipackage/cezen-portal/audit.html
2026-06-30 10:51:41 +05:30

300 lines
13 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Audit Log — Nexus One AI</title>
<link rel="stylesheet" href="style.css?v=4">
<style>
.audit-toolbar { display:flex; align-items:center; gap:10px; flex-wrap:wrap; margin-bottom:24px; }
.audit-filter { padding:8px 12px; border:1.5px solid var(--bdr); border-radius:8px; font-size:13px; font-family:inherit; color:var(--ink); background:var(--navy2); outline:none; min-width:140px; }
.audit-filter:focus { border-color:var(--teal); }
.audit-btn { padding:8px 16px; border-radius:8px; font-size:13px; font-weight:600; cursor:pointer; border:1px solid var(--bdr); background:var(--navy2); font-family:inherit; color:var(--med); transition:all .15s; }
.audit-btn:hover { border-color:var(--teal); color:var(--teal); }
.audit-btn.primary { background:var(--teal); color:var(--ink); border:none; }
.audit-btn.primary:hover { background:#0B7A70; }
.audit-table-wrap { background:var(--navy2); border:1px solid var(--bdr); border-radius:14px; overflow:hidden; }
table.audit-table { width:100%; border-collapse:collapse; }
.audit-table th { background:rgba(255,255,255,.03); padding:10px 14px; text-align:left; font-size:11px; font-weight:700; text-transform:uppercase; letter-spacing:.5px; color:var(--lt); border-bottom:1px solid var(--bdr); white-space:nowrap; }
.audit-table td { padding:11px 14px; border-bottom:1px solid var(--bdr); font-size:13px; color:var(--ink); vertical-align:middle; }
.audit-table tr:last-child td { border-bottom:none; }
.audit-table tr:hover td { background:rgba(255,255,255,.03); }
.audit-table td.mono { font-family:monospace; font-size:12px; }
.result-pill { display:inline-block; font-size:10px; font-weight:700; padding:2px 8px; border-radius:8px; text-transform:uppercase; letter-spacing:.3px; }
.result-pill.success { background:rgba(34,197,94,.15); color:#15803D; }
.result-pill.failure { background:#FEE2E2; color:#991B1B; }
.result-pill.info { background:#DBEAFE; color:#93C5FD; }
.action-tag { display:inline-block; font-size:11px; font-weight:600; padding:2px 8px; border-radius:6px; background:rgba(255,255,255,.03); color:var(--med); border:1px solid var(--bdr); }
/* Pagination */
.audit-pager { display:flex; align-items:center; gap:8px; justify-content:center; padding:16px; border-top:1px solid var(--bdr); background:rgba(255,255,255,.03); }
.page-btn { padding:5px 12px; border:1px solid var(--bdr); border-radius:6px; background:var(--navy2); font-size:13px; cursor:pointer; font-family:inherit; color:var(--med); transition:all .15s; }
.page-btn:hover:not(:disabled) { border-color:var(--teal); color:var(--teal); }
.page-btn.active { background:var(--teal); color:var(--ink); border-color:var(--teal); }
.page-btn:disabled { opacity:.4; cursor:not-allowed; }
.page-info { font-size:13px; color:var(--lt); padding:0 6px; }
.audit-empty { padding:32px; text-align:center; color:var(--lt); font-size:14px; }
</style>
</head>
<body data-role="admin">
<header class="topnav">
<a href="index.html" class="brand">Nexus One <span>AI</span></a>
<nav>
<a href="index.html">Home</a>
<a href="quickstart.html">Quick Start</a>
<a href="prompts.html">Prompt Library</a>
<a href="usecases.html">Use Cases</a>
<span class="nav-sep"></span>
<div class="nav-dropdown">
<button class="nav-drop-btn">Help ▾</button>
<div class="nav-drop-menu">
<span class="nav-drop-cat">LEARN /</span>
<a href="quickstart.html">Quick Start</a>
<a href="models.html">Models</a>
<span class="nav-drop-cat">SUPPORT /</span>
<a href="troubleshooting.html">Troubleshoot</a>
<a href="faq.html">FAQ</a>
<span class="nav-drop-cat">MORE /</span>
<a href="glossary.html">Glossary</a>
<a href="whats-new.html">What's New</a>
</div>
</div>
<div class="nav-dropdown">
<button class="nav-drop-btn active">Admin ▾</button>
<div class="nav-drop-menu nav-drop-menu-wide">
<span class="nav-drop-cat">DOCS /</span>
<a href="security.html">Security & Privacy</a>
<a href="admin.html">Admin Guide</a>
<span class="nav-drop-cat">MONITOR /</span>
<a href="dashboard.html">Dashboard</a>
<a href="analytics.html">Usage Analytics</a>
<a href="audit.html" class="active">Audit Log</a>
<span class="nav-drop-cat">MANAGE /</span>
<a href="users.html">Users</a>
<a href="teams.html">Teams</a>
<a href="models-admin.html">Model Manager</a>
<a href="training.html">Training</a>
<a href="knowledge.html">Knowledge Base</a>
<span class="nav-drop-cat">TOOLS /</span>
<a href="apikeys.html">API Keys</a>
<a href="benchmark.html">Benchmarking</a>
<a href="model-compare.html">Model Compare</a>
<a href="api-playground.html">API Playground</a>
<a href="guardrails.html">Guardrails</a>
<a href="rag-quality.html">RAG Quality</a>
<a href="router.html">Model Router</a>
<a href="connectors.html">Connectors</a>
<span class="nav-drop-cat">SYSTEM /</span>
<a href="appliance.html">Appliance Ops</a>
<a href="console.html">Console</a>
<a href="settings.html">Settings</a>
</div>
</div>
<div class="nav-dropdown">
<button class="nav-drop-btn">AI Tools ▾</button>
<div class="nav-drop-menu">
<span class="nav-drop-cat">INTELLIGENCE /</span>
<a href="documents.html">Document Intelligence</a>
<a href="chat-multi.html">Multimodal Chat</a>
<a href="prompt-studio.html">Prompt Studio</a>
<a href="meeting.html">Meeting Assistant</a>
<span class="nav-drop-cat">AUTOMATION /</span>
<a href="agents.html">Agent Builder</a>
<a href="schedules.html">Scheduled Jobs</a>
<a href="workflows.html">Workflow Automation</a>
<span class="nav-drop-cat">QUALITY /</span>
<a href="evals.html">AI Eval Suite</a>
<a href="chatrooms.html">Chat Rooms</a>
</div>
</div>
</nav>
<a href="notifications.html" style="position:relative">🔔</a>
<span class="badge" data-brand="tier">Basic Tier</span>
<div id="nav-org-logo" class="nav-org-logo"></div>
</header>
<div class="page-hero">
<div class="label">Admin · Audit Log</div>
<h1>Audit Log</h1>
<p>Full history of authentication events, model operations, and administrative actions.</p>
</div>
<div class="content">
<div class="audit-toolbar">
<select class="audit-filter" id="filter-action" onchange="applyFilters()">
<option value="">All Actions</option>
<option value="login">Login</option>
<option value="logout">Logout</option>
<option value="login_failed">Login Failed</option>
<option value="create_user">Create User</option>
<option value="delete_user">Delete User</option>
<option value="reset_password">Reset Password</option>
<option value="change_password">Change Password</option>
<option value="model_pull">Model Pull</option>
<option value="model_delete">Model Delete</option>
<option value="session_revoke">Session Revoke</option>
</select>
<select class="audit-filter" id="filter-result" onchange="applyFilters()">
<option value="">All Results</option>
<option value="success">Success</option>
<option value="failure">Failure</option>
<option value="info">Info</option>
</select>
<input class="audit-filter" id="filter-user" type="text" placeholder="Filter by user…" oninput="applyFilters()">
<button class="audit-btn" onclick="resetFilters()">Clear</button>
<button class="audit-btn" onclick="load()">↺ Refresh</button>
<button class="audit-btn primary" onclick="exportCSV()">↓ Export CSV</button>
</div>
<div class="audit-table-wrap">
<table class="audit-table">
<thead>
<tr>
<th>Time</th>
<th>User</th>
<th>Action</th>
<th>Target</th>
<th>IP</th>
<th>Result</th>
<th>Detail</th>
</tr>
</thead>
<tbody id="audit-tbody">
<tr><td colspan="7"><div class="audit-empty">Loading…</div></td></tr>
</tbody>
</table>
<div class="audit-pager" id="pager" style="display:none">
<button class="page-btn" id="btn-prev" onclick="goPage(currentPage-1)"> Prev</button>
<span class="page-info" id="page-info"></span>
<button class="page-btn" id="btn-next" onclick="goPage(currentPage+1)">Next </button>
</div>
</div>
</div>
<footer>
<p>Nexus One AI &nbsp;·&nbsp; Powered by Cezen &nbsp;·&nbsp; Basic Tier</p>
</footer>
<script>
const _API = '/api';
const PAGE_SIZE = 50;
let allRows = [];
let filtered = [];
let currentPage = 1;
function fmt(iso) {
if (!iso) return '—';
return new Date(iso).toLocaleString([], {dateStyle:'short', timeStyle:'medium'});
}
function resultClass(r) {
if (r === 'success') return 'success';
if (r === 'failure') return 'failure';
return 'info';
}
async function load() {
try {
const res = await fetch(`${_API}/audit?limit=1000`, { credentials:'include' });
const data = await res.json();
allRows = Array.isArray(data) ? data : (Array.isArray(data.events) ? data.events : (Array.isArray(data.audit) ? data.audit : []));
} catch {
allRows = [];
}
applyFilters();
}
function applyFilters() {
const action = document.getElementById('filter-action').value.toLowerCase();
const result = document.getElementById('filter-result').value.toLowerCase();
const user = document.getElementById('filter-user').value.toLowerCase();
filtered = allRows.filter(r => {
if (action && r.action !== action) return false;
if (result && r.result !== result) return false;
if (user && !(r.username || '').toLowerCase().includes(user)) return false;
return true;
});
currentPage = 1;
render();
}
function resetFilters() {
document.getElementById('filter-action').value = '';
document.getElementById('filter-result').value = '';
document.getElementById('filter-user').value = '';
applyFilters();
}
function goPage(p) {
const total = Math.max(1, Math.ceil(filtered.length / PAGE_SIZE));
currentPage = Math.max(1, Math.min(p, total));
render();
}
function render() {
const total = Math.ceil(filtered.length / PAGE_SIZE) || 1;
const start = (currentPage - 1) * PAGE_SIZE;
const page = filtered.slice(start, start + PAGE_SIZE);
const tbody = document.getElementById('audit-tbody');
if (!page.length) {
tbody.innerHTML = '<tr><td colspan="7"><div class="audit-empty">No events match the current filter.</div></td></tr>';
document.getElementById('pager').style.display = 'none';
return;
}
tbody.innerHTML = page.map(r => `
<tr>
<td class="mono">${fmt(r.created_at)}</td>
<td><strong>${r.username || '—'}</strong></td>
<td><span class="action-tag">${r.action}</span></td>
<td style="color:var(--med)">${r.target || '—'}</td>
<td class="mono">${r.ip_address || '—'}</td>
<td><span class="result-pill ${resultClass(r.result)}">${r.result}</span></td>
<td style="color:var(--lt);font-size:12px;max-width:200px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap" title="${r.detail || ''}">${r.detail || '—'}</td>
</tr>
`).join('');
// Pager
const pager = document.getElementById('pager');
if (filtered.length > PAGE_SIZE) {
pager.style.display = 'flex';
document.getElementById('page-info').textContent = `Page ${currentPage} of ${total} (${filtered.length} events)`;
document.getElementById('btn-prev').disabled = currentPage <= 1;
document.getElementById('btn-next').disabled = currentPage >= total;
} else {
pager.style.display = filtered.length ? 'flex' : 'none';
document.getElementById('page-info').textContent = `${filtered.length} events`;
document.getElementById('btn-prev').disabled = true;
document.getElementById('btn-next').disabled = true;
}
}
function exportCSV() {
const headers = ['Time','User','Action','Target','IP','Result','Detail'];
const rows = filtered.map(r => [
fmt(r.created_at), r.username||'', r.action, r.target||'', r.ip_address||'', r.result, (r.detail||'').replace(/"/g,"'")
].map(v => `"${v}"`).join(','));
const csv = [headers.join(','), ...rows].join('\n');
const blob = new Blob([csv], {type:'text/csv'});
const a = document.createElement('a');
a.href = URL.createObjectURL(blob);
a.download = `cezen-audit-${new Date().toISOString().slice(0,10)}.csv`;
a.click();
}
load();
</script>
<script src="auth.js"></script>
<script src="branding.js"></script>
</body>
</html>