← back to Robet Site
public/admin.js
65 lines
// ROBERT admin — API-key entry (externalized so CSP can stay strict, no inline script).
const $ = (s) => document.querySelector(s);
const api = (p, opt) => fetch(p, opt).then((r) => r.json());
async function load() {
const rows = await api('/api/keys');
const t = $('#table');
if (!rows.length) { t.innerHTML = '<p class="mono" style="font-weight:600">No keys stored yet.</p>'; return; }
t.innerHTML =
'<div class="keyrow head"><div>Name</div><div>Value</div><div>Action</div></div>' +
rows.map((r) =>
`<div class="keyrow">
<div class="mono">${r.name}</div>
<div class="mono">${r.masked}</div>
<div><button class="btn warn" data-del="${r.name}">Delete</button></div>
</div>`).join('');
t.querySelectorAll('[data-del]').forEach((b) =>
b.addEventListener('click', async () => {
await api('/api/keys/' + encodeURIComponent(b.dataset.del), { method: 'DELETE' });
load();
}));
}
// ── show/hide value toggle ──
const toggle = $('#toggleVal'), kVal = $('#kVal');
toggle.addEventListener('click', () => {
const show = kVal.type === 'password';
kVal.type = show ? 'text' : 'password';
toggle.textContent = show ? 'hide' : 'show';
toggle.setAttribute('aria-pressed', String(show));
toggle.setAttribute('aria-label', show ? 'Hide value' : 'Show value');
kVal.focus();
});
// ── inline validation (live) ──
const NAME_RE = /^[A-Za-z0-9_]+$/;
const hint = $('#hint');
function validate() {
const name = $('#kName').value.trim(), value = kVal.value;
let ok = true, m = 'Name: letters, digits, underscores only. Value required.';
if (name && !NAME_RE.test(name)) { ok = false; m = '✗ Name allows only letters, digits, underscores (no spaces).'; }
else if (!name || !value) { ok = false; }
$('#saveBtn').disabled = !ok;
hint.textContent = m;
hint.style.color = (name && !NAME_RE.test(name)) ? 'var(--accent2)' : '';
return ok;
}
$('#kName').addEventListener('input', validate);
kVal.addEventListener('input', validate);
$('#saveBtn').addEventListener('click', async () => {
const name = $('#kName').value.trim(), value = kVal.value;
const msg = $('#msg');
if (!validate()) { msg.textContent = '✗ Fix the highlighted field'; return; }
const j = await api('/api/keys', {
method: 'POST', headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ name, value }),
});
msg.textContent = j.ok ? `✓ Saved ${j.name} (${j.masked})` : '✗ ' + (j.error || 'Failed');
if (j.ok) { $('#kName').value = ''; kVal.value = ''; validate(); load(); }
});
validate();
load();