Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
140 changes: 96 additions & 44 deletions example_app/ui/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -542,6 +542,14 @@ <h1>AI Security Command Center</h1>
Admin JWT:
<input id="token" type="password" placeholder="Paste admin JWT for ledger + evidence" />
</label>
<label>
Data source:
<select id="dataSource">
<option value="auto">Auto</option>
<option value="live">Live only</option>
<option value="demo">Demo only</option>
</select>
</label>
<button id="saveToken">Save Token</button>
<button id="refreshAll" class="primary">Refresh</button>
</div>
Expand Down Expand Up @@ -699,7 +707,9 @@ <h2>Evidence & ABOM</h2>
const activityEl = document.getElementById("activity");
const demoBannerEl = document.getElementById("demoBanner");
const topRisksEl = document.getElementById("topRisks");
const dataSourceEl = document.getElementById("dataSource");
const tokenKey = "ocpa_admin_token";
const dataSourceKey = "ocpa_data_source";
let demoMode = false;

function setStatus(msg) {
Expand Down Expand Up @@ -806,6 +816,17 @@ <h2>Evidence & ABOM</h2>
return localStorage.getItem(tokenKey) || "";
}

function getDataSource() {
return localStorage.getItem(dataSourceKey) || "auto";
}

function setDataSource(value) {
localStorage.setItem(dataSourceKey, value);
if (dataSourceEl) {
dataSourceEl.value = value;
}
}

function authHeaders() {
const token = getToken();
if (!token) {
Expand Down Expand Up @@ -1200,6 +1221,7 @@ <h2>Evidence & ABOM</h2>
let summaryFallback = false;
let demoInjected = false;
let demoEvents = null;
const dataMode = getDataSource();
try {
const health = await fetchJson("/health");
renderOpaStatus(health.opa_status);
Expand Down Expand Up @@ -1245,52 +1267,67 @@ <h2>Evidence & ABOM</h2>
errors.push(`inventory: ${err.message}`);
}

try {
const summary = await fetchJson("/risk/summary?limit=500", true);
renderSummary(document.getElementById("summary"), summary);
} catch (err) {
summaryError = err;
summaryFallback = demoMode;
if (!demoMode) {
errors.push(`summary: ${err.message}`);
if (summaryMetaEl && /401|403/.test(err.message)) {
summaryMetaEl.textContent = "Admin JWT required to view counts.";
}
if (postureBadgeEl) {
postureBadgeEl.textContent = "Locked";
postureBadgeEl.className = "posture-value posture-locked";
}
if (postureSignalsEl) {
postureSignalsEl.textContent = "Signals in window: --";
if (dataMode === "demo") {
demoInjected = true;
demoEvents = buildDemoEvents();
renderSummary(document.getElementById("summary"), buildDemoSummary(demoEvents));
} else {
try {
const summary = await fetchJson("/risk/summary?limit=500", true);
renderSummary(document.getElementById("summary"), summary);
} catch (err) {
summaryError = err;
summaryFallback = demoMode && dataMode === "auto";
if (!(demoMode && dataMode === "auto")) {
errors.push(`summary: ${err.message}`);
if (summaryMetaEl && /401|403/.test(err.message)) {
summaryMetaEl.textContent = "Admin JWT required to view counts.";
}
if (postureBadgeEl) {
postureBadgeEl.textContent = "Locked";
postureBadgeEl.className = "posture-value posture-locked";
}
if (postureSignalsEl) {
postureSignalsEl.textContent = "Signals in window: --";
}
}
}
}

try {
const ledger = await fetchJson("/ui/ledger?limit=200", true);
if (demoMode && (!ledger.events || ledger.events.length === 0)) {
demoInjected = true;
demoEvents = buildDemoEvents();
renderLedger(document.getElementById("ledger-body"), demoEvents);
renderRiskDrivers(demoEvents);
renderActivity(demoEvents);
renderTopRisks(demoEvents);
} else {
renderLedger(document.getElementById("ledger-body"), ledger.events);
renderRiskDrivers(ledger.events);
renderActivity(ledger.events);
renderTopRisks(ledger.events);
}
} catch (err) {
if (demoMode) {
demoInjected = true;
demoEvents = buildDemoEvents();
renderLedger(document.getElementById("ledger-body"), demoEvents);
renderRiskDrivers(demoEvents);
renderActivity(demoEvents);
renderTopRisks(demoEvents);
} else {
errors.push(`ledger: ${err.message}`);
if (dataMode === "demo") {
demoInjected = true;
demoEvents = demoEvents || buildDemoEvents();
renderLedger(document.getElementById("ledger-body"), demoEvents);
renderRiskDrivers(demoEvents);
renderActivity(demoEvents);
renderTopRisks(demoEvents);
} else {
try {
const ledger = await fetchJson("/ui/ledger?limit=200", true);
if (dataMode === "auto" && demoMode && (!ledger.events || ledger.events.length === 0)) {
demoInjected = true;
demoEvents = buildDemoEvents();
renderLedger(document.getElementById("ledger-body"), demoEvents);
renderRiskDrivers(demoEvents);
renderActivity(demoEvents);
renderTopRisks(demoEvents);
} else {
renderLedger(document.getElementById("ledger-body"), ledger.events);
renderRiskDrivers(ledger.events);
renderActivity(ledger.events);
renderTopRisks(ledger.events);
}
} catch (err) {
if (demoMode && dataMode === "auto") {
demoInjected = true;
demoEvents = buildDemoEvents();
renderLedger(document.getElementById("ledger-body"), demoEvents);
renderRiskDrivers(demoEvents);
renderActivity(demoEvents);
renderTopRisks(demoEvents);
} else {
errors.push(`ledger: ${err.message}`);
}
}
}

Expand Down Expand Up @@ -1318,10 +1355,18 @@ <h2>Evidence & ABOM</h2>
errors.push(`summary: ${summaryError.message}`);
}

const sourceLabel =
demoInjected || dataMode === "demo"
? "demo"
: dataMode === "live"
? "live"
: demoMode
? "auto"
: "live";
if (errors.length) {
setStatus(`Updated with warnings: ${errors.join(" | ")}`);
setStatus(`Updated (${sourceLabel}) with warnings: ${errors.join(" | ")}`);
} else {
setStatus(demoInjected ? "Updated (demo data active)." : "Updated.");
setStatus(`Updated (${sourceLabel}).`);
}
if (lastUpdatedEl) {
lastUpdatedEl.textContent = `Last refresh: ${new Date().toLocaleTimeString()}`;
Expand Down Expand Up @@ -1385,6 +1430,13 @@ <h2>Evidence & ABOM</h2>
document.getElementById("downloadOpenApi").addEventListener("click", () => downloadOpenApi("json"));

tokenEl.value = getToken();
if (dataSourceEl) {
dataSourceEl.value = getDataSource();
dataSourceEl.addEventListener("change", (event) => {
setDataSource(event.target.value);
refreshAll();
});
}
refreshAll();
</script>
</body>
Expand Down