Add signed licensing enforcement and ISO notes

This commit is contained in:
Jino Jose committed 2026-07-08 10:47:49 +05:30
1 parent dee4486826
commit d645249a13
14 files changed
+1526 -103

No files matched your search

+69 -2
View File
@@ -170,7 +170,7 @@
</div>
</div>
</nav>
<a href="notifications.html" style="position:relative">🔔</a>
<a href="notifications.html" class="nav-icon-btn" style="position:relative" aria-label="Notifications"><span class="icon-svg icon-sm" aria-hidden="true"><svg viewBox="0 0 24 24"><path d="M15 17h5l-1.4-1.4A2 2 0 0 1 18 14.2V11a6 6 0 1 0-12 0v3.2a2 2 0 0 1-.6 1.4L4 17h5"/><path d="M10 17a2 2 0 0 0 4 0"/></svg></span></a>
<span class="badge" data-brand="tier">Basic Tier</span>
<div id="nav-org-logo" class="nav-org-logo"></div>
</header>
@@ -186,6 +186,9 @@
<button class="ops-btn primary" onclick="loadAll()">Refresh</button>
<button class="ops-btn" onclick="createBackup()">Create Backup</button>
<button class="ops-btn" onclick="exportReadiness()">Export Readiness JSON</button>
<button class="ops-btn" onclick="uploadLicense()">Upload License</button>
<button class="ops-btn" onclick="reverifyLicense()">Reverify License</button>
<button class="ops-btn" onclick="reconcileLicense()">Reconcile Provisioning</button>
</div>
<div class="ops-grid">
@@ -217,6 +220,16 @@
<div class="ops-list" id="hardware-list"><div class="empty">Loading…</div></div>
</section>
<section class="ops-card span-6">
<h2>Entitlement</h2>
<div class="ops-list" id="entitlement-list"><div class="empty">Loading…</div></div>
</section>
<section class="ops-card span-6">
<h2>Feature Access</h2>
<div class="ops-list" id="features-list"><div class="empty">Loading…</div></div>
</section>
<section class="ops-card span-8">
<h2>Readiness Checks</h2>
<div id="checks-list"><div class="empty">Loading…</div></div>
@@ -271,12 +284,13 @@ function renderReadiness(d) {
readinessData = d;
const score = d.readiness?.score ?? 0;
const status = d.readiness?.status || 'unknown';
const entitlement = d.entitlement || d.license?.entitlement || {};
document.getElementById('readiness-score').textContent = score + '%';
const statusEl = document.getElementById('readiness-status');
statusEl.textContent = status.replace(/_/g, ' ');
statusEl.className = 'pill ' + (status === 'ready' ? 'ok' : status === 'limited' ? 'warn' : 'bad');
document.getElementById('license-tier').textContent = d.license?.label || '—';
document.getElementById('license-position').textContent = d.license?.positioning || '—';
document.getElementById('license-position').textContent = `${d.license?.positioning || '—'} · status: ${entitlement.license_status || d.license?.license_status || 'unknown'}`;
const auditFit = d.commercial_fit || {};
const rec = d.feasibility?.recommendation || {};
@@ -299,6 +313,22 @@ function renderReadiness(d) {
['GPU', gpu ? `${gpu.name || 'GPU'} · ${gpu.vram_gb || 0} GB` : 'None detected'],
].map(row => `<div class="ops-row"><div class="ops-label">${esc(row[0])}</div><div class="ops-value">${esc(row[1])}</div></div>`).join('');
document.getElementById('entitlement-list').innerHTML = [
['License status', entitlement.license_status || '—'],
['Customer', entitlement.license_record?.customer_name || '—'],
['Licensed tier', entitlement.licensed_tier || '—'],
['Provisioned tier', entitlement.provisioned_tier || '—'],
['Hardware tier', entitlement.hardware_recommended_tier || '—'],
['Install type', entitlement.install_type || '—'],
['Support until', entitlement.license_record?.support_until || '—'],
['Compliance', (entitlement.compliance_state || []).join(', ') || '—'],
].map(row => `<div class="ops-row"><div class="ops-label">${esc(row[0])}</div><div class="ops-value">${esc(row[1])}</div></div>`).join('');
const featureEntries = Object.entries(entitlement.effective_features || {});
document.getElementById('features-list').innerHTML = featureEntries.length ? featureEntries.map(([k,v]) =>
`<div class="ops-row"><div class="ops-label">${esc(k.replace(/_/g,' '))}</div><div class="ops-value">${esc(v)}</div></div>`
).join('') : '<div class="empty">No feature details available.</div>';
document.getElementById('checks-list').innerHTML = (d.readiness?.checks || []).map(c => `
<div class="check-row">
<span class="check-dot ${c.passed ? 'ok' : ''}"></span>
@@ -343,6 +373,43 @@ async function loadAll() {
}
}
async function uploadLicense() {
const license = prompt('Paste signed cezen.license.v2 JSON');
if (!license) return;
const override = prompt('Optional: paste signed cezen.override.v1 JSON');
try {
await api('/api/license/upload', {
method: 'POST',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify({ license_json: license, override_json: override || '' })
});
toast('License uploaded');
loadAll();
} catch (err) {
toast('License upload failed: ' + err.message, false);
}
}
async function reverifyLicense() {
try {
await api('/api/license/reverify', { method: 'POST' });
toast('License reverified');
loadAll();
} catch (err) {
toast('Reverify failed: ' + err.message, false);
}
}
async function reconcileLicense() {
try {
const d = await api('/api/license/reconcile', { method: 'POST' });
toast(`Reconcile ${d.reconcile?.status || 'queued'}`);
loadAll();
} catch (err) {
toast('Reconcile failed: ' + err.message, false);
}
}
async function createBackup() {
try {
toast('Creating backup…');