Add signed licensing enforcement and ISO notes
This commit is contained in:
1 parent
dee4486826
commit
d645249a13
14 files changed
+1526
-103
No files matched your search
@@ -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…');
|
||||
|
||||
Reference in new issue
Block a user