Add audit log

This commit is contained in:
2026-01-15 16:19:09 +01:00
parent 10507c070e
commit 18dff66373
16 changed files with 818 additions and 111 deletions

View File

@@ -2386,3 +2386,98 @@ button:disabled:hover {
justify-content: flex-start;
}
}
.audit-body {
background:
radial-gradient(circle at 0% 30%, rgba(14, 116, 144, 0.12), transparent 32%),
radial-gradient(circle at 85% 0%, rgba(59, 130, 246, 0.16), transparent 30%), #0f172a;
}
.audit-frame {
margin: 0 auto;
min-height: 100vh;
display: flex;
flex-direction: column;
gap: 24px;
padding: 24px clamp(20px, 5vw, 48px) 48px;
}
.audit-topbar {
display: flex;
align-items: center;
justify-content: space-between;
gap: 16px;
padding: 16px 20px;
background: rgba(15, 23, 42, 0.9);
border: 1px solid #1f2937;
border-radius: 16px;
box-shadow: 0 14px 35px rgba(0, 0, 0, 0.35);
}
.audit-title {
display: flex;
flex-direction: column;
gap: 6px;
}
.audit-title h1 {
margin: 0;
}
.audit-actions {
display: flex;
gap: 12px;
flex-wrap: wrap;
}
.audit-content {
display: flex;
}
.audit-panel {
width: 100%;
background: rgba(11, 18, 32, 0.92);
border: 1px solid #1f2937;
border-radius: 18px;
padding: 24px;
box-shadow: 0 20px 45px rgba(0, 0, 0, 0.4);
}
.audit-panel-header h2 {
margin: 0 0 6px;
}
.audit-table-wrapper {
margin-top: 16px;
overflow-x: auto;
}
.audit-table {
width: 100%;
border-collapse: collapse;
font-size: 14px;
}
.audit-table thead {
text-align: left;
background: rgba(15, 23, 42, 0.9);
}
.audit-table th,
.audit-table td {
padding: 12px 14px;
border-bottom: 1px solid rgba(148, 163, 184, 0.2);
vertical-align: top;
}
.audit-table tbody tr:hover {
background: rgba(30, 41, 59, 0.45);
}
.audit-empty {
margin-top: 16px;
padding: 16px;
border-radius: 12px;
background: rgba(30, 41, 59, 0.4);
color: #cbd5e1;
}

View File

@@ -0,0 +1,62 @@
const auditBody = document.getElementById("audit-log-body");
const auditEmpty = document.getElementById("audit-empty");
const formatTimestamp = (value) => {
if (!value) {
return "";
}
const date = new Date(value);
if (Number.isNaN(date.getTime())) {
return value;
}
return date.toLocaleString();
};
const renderEntries = (entries) => {
auditBody.innerHTML = "";
if (!entries || entries.length === 0) {
auditEmpty.classList.remove("hidden");
return;
}
auditEmpty.classList.add("hidden");
entries.forEach((entry) => {
const row = document.createElement("tr");
const timeCell = document.createElement("td");
timeCell.textContent = formatTimestamp(entry.createdAt);
row.appendChild(timeCell);
const actorCell = document.createElement("td");
actorCell.textContent = entry.actor || "system";
row.appendChild(actorCell);
const actionCell = document.createElement("td");
actionCell.textContent = entry.action;
row.appendChild(actionCell);
const detailCell = document.createElement("td");
detailCell.textContent = entry.details || "";
row.appendChild(detailCell);
auditBody.appendChild(row);
});
};
const loadAuditLog = () =>
fetch(`/api/channels/${encodeURIComponent(broadcaster)}/audit`)
.then((response) => {
if (!response.ok) {
throw new Error(`Failed to load audit log (${response.status})`);
}
return response.json();
})
.then((entries) => {
renderEntries(entries);
})
.catch((error) => {
console.error(error);
auditEmpty.textContent = "Unable to load audit entries.";
auditEmpty.classList.remove("hidden");
});
loadAuditLog();