Files
smom-dbis-138/services/token-aggregation/public/omnl-compliance-console.js

220 lines
7.6 KiB
JavaScript

(function () {
const params = new URLSearchParams(window.location.search);
const accessToken = params.get('access_token') || '';
const apiBase =
document.querySelector('meta[name="omnl-api-base"]')?.getAttribute('content')?.trim() || '/api/v1';
const els = {
status: document.getElementById('load-status'),
grid: document.getElementById('posture-grid'),
actions: document.getElementById('pending-actions'),
evidence: document.getElementById('evidence-kv'),
safe: document.getElementById('safe-kv'),
external: document.getElementById('external-kv'),
triple: document.getElementById('triple-summary'),
signoffs: document.getElementById('signoffs-summary'),
raw: document.getElementById('raw-json'),
refreshed: document.getElementById('refreshed-at'),
};
function apiHeaders() {
const h = { Accept: 'application/json' };
if (accessToken) h.Authorization = 'Bearer ' + accessToken;
return h;
}
function apiUrl(path) {
const base = apiBase.replace(/\/$/, '');
const rel = path.startsWith('/') ? path : '/' + path;
const u = new URL(base + rel, window.location.origin);
if (accessToken) u.searchParams.set('access_token', accessToken);
return u.toString();
}
function badge(ok, okLabel, badLabel) {
if (ok === true) return '<span class="badge ok">' + okLabel + '</span>';
if (ok === false) return '<span class="badge bad">' + badLabel + '</span>';
return '<span class="badge neutral">Unknown</span>';
}
function sevBadge(sev) {
const map = { critical: 'bad', high: 'bad', medium: 'warn', low: 'neutral' };
return '<span class="badge ' + (map[sev] || 'neutral') + '">' + sev + '</span>';
}
function renderPosture(data) {
const p = data.posture || {};
els.grid.innerHTML = [
card('Reporting', badge(p.reportingCompliant, 'Compliant', 'Not compliant'), ''),
card('Attestation', badge(!p.attestationStale, 'Fresh', 'Stale'), ''),
card('Triple reconcile', badge(p.tripleAligned, 'Aligned', 'Breaks'), ''),
card('Notary gate', badge(p.requireNotarizedEvidence, 'Enforced', 'Not enforced'), 'requireNotarizedEvidence'),
card('Threshold', '<div class="metric">' + (p.complianceThreshold || '—') + '</div>', 'IPSAS / IFRS / US GAAP'),
card('Policy v', '<div class="metric">' + (p.jurisdictionPolicyVersion ?? '—') + '</div>', 'ID jurisdiction'),
].join('');
}
function card(title, body, sub) {
return (
'<div class="card"><h2>' +
title +
'</h2>' +
body +
(sub ? '<small style="color:var(--muted);font-size:0.78rem">' + sub + '</small>' : '') +
'</div>'
);
}
function renderActions(actions) {
if (!actions || !actions.length) {
els.actions.innerHTML = '<p style="color:var(--muted)">No pending actions.</p>';
return;
}
els.actions.innerHTML =
'<ul class="actions-list">' +
actions
.map(function (a) {
const links = (a.links || [])
.map(function (l) {
const href = l.href.startsWith('http') ? l.href : apiUrl(l.href);
const dl = l.href.indexOf('safe-notary-gate-tx') >= 0 ? ' download="omnl-safe-notary-gate-tx.json"' : '';
return '<a href="' + href + '"' + dl + '>' + l.label + '</a>';
})
.join('');
return (
'<li><h3>' +
sevBadge(a.severity) +
' ' +
esc(a.title) +
'</h3><p>' +
esc(a.detail) +
'</p><div class="link-row">' +
links +
'</div></li>'
);
})
.join('') +
'</ul>';
}
function kv(rows) {
return (
'<dl class="kv">' +
rows
.map(function (r) {
return '<dt>' + esc(r[0]) + '</dt><dd>' + esc(String(r[1] ?? '—')) + '</dd>';
})
.join('') +
'</dl>'
);
}
function renderEvidence(data) {
const pr = data.proofReport;
const gate = data.onChainGate || {};
const triple = data.tripleReconcile;
els.evidence.innerHTML = kv([
['Evidence hash', pr && pr.evidenceHash],
['Merkle root', pr && pr.merkleRoot],
['Package 3-of-3', pr && pr.packageNotarized3of3],
['Reserve 3-of-3', pr && pr.reserveAttested3of3],
['Reserve store', gate.reserveStore],
['Notary registry', gate.notaryRegistry],
['Attestation threshold', gate.attestationThreshold],
['On-chain R', triple && triple.onChain && triple.onChain.r],
]);
}
function renderSafe(data) {
const pr = data.proofReport;
const g = (pr && pr.gnosisSafe) || {};
const sw = data.safeWallet || {};
els.safe.innerHTML = kv([
['Admin Safe', g.address || data.web3.deployed.GnosisSafeAdmin],
['Threshold', g.threshold ? g.threshold + '-of-' + g.owners : '—'],
['Safe Wallet registry', sw.npmRegistryHas138 ? 'chain 138 listed' : 'pending PR #1568'],
['Registry status', sw.status],
]);
}
function renderExternal(data) {
const ev = data.externalVisibility || {};
els.external.innerHTML = kv([
['DefiLlama chain', ev.defiLlama && ev.defiLlama.chainPage],
['Pricing PR', ev.defiLlama && ev.defiLlama.pr12094],
['Bridge TVL PR', ev.defiLlama && ev.defiLlama.pr19451],
['Safe deployments PR', ev.safeDeployments && ev.safeDeployments.pr1568],
]);
}
function renderTriple(data) {
const t = data.tripleReconcile;
if (!t) {
els.triple.textContent = 'Triple-state reconcile unavailable.';
return;
}
const breaks = (t.breaks || []).length;
els.triple.innerHTML =
badge(t.aligned, 'Aligned', breaks + ' break(s)') +
' <span style="color:var(--muted);margin-left:0.5rem;font-size:0.88rem">line ' +
esc(t.lineId).slice(0, 18) +
'…</span>';
}
function renderSignoffs(data) {
const s = data.signoffs || {};
els.signoffs.innerHTML = '<pre class="raw" style="max-height:10rem">' + esc(JSON.stringify(s, null, 2)) + '</pre>';
}
function esc(s) {
return String(s)
.replace(/&/g, '&amp;')
.replace(/</g, '&lt;')
.replace(/>/g, '&gt;')
.replace(/"/g, '&quot;');
}
function setStatus(kind, msg) {
els.status.className = 'status-bar ' + kind;
els.status.textContent = msg;
}
async function load() {
setStatus('loading', 'Loading compliance console…');
try {
const res = await fetch(apiUrl('/omnl/compliance/console'), { headers: apiHeaders() });
const text = await res.text();
let data;
try {
data = JSON.parse(text);
} catch {
throw new Error(res.status + ' — non-JSON response');
}
if (!res.ok) throw new Error(data.error || res.statusText);
renderPosture(data);
renderActions(data.pendingActions);
renderEvidence(data);
renderSafe(data);
renderExternal(data);
renderTriple(data);
renderSignoffs(data);
els.raw.textContent = JSON.stringify(data, null, 2);
els.refreshed.textContent = 'Updated ' + new Date(data.generatedAt).toLocaleString();
setStatus('ok', 'Live — ' + data.pendingActions.length + ' pending action(s)');
} catch (e) {
setStatus('error', 'Failed to load: ' + (e && e.message ? e.message : e));
}
}
document.getElementById('btn-refresh').addEventListener('click', load);
document.getElementById('btn-download-safe').addEventListener('click', function () {
window.location.href = apiUrl('/omnl/compliance/safe-notary-gate-tx');
});
document.getElementById('btn-toggle-raw').addEventListener('click', function () {
document.getElementById('raw-section').classList.toggle('hidden');
});
load();
})();