@lifeaitools/clauth 0.3.6 → 0.3.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/cli/commands/serve.js +71 -1
- package/package.json +1 -1
package/cli/commands/serve.js
CHANGED
|
@@ -110,6 +110,11 @@ function dashboardHtml(port, whitelist) {
|
|
|
110
110
|
.btn-cancel{background:transparent;color:#64748b;padding:6px 10px}.btn-cancel:hover{color:#94a3b8}
|
|
111
111
|
.btn-check{background:#0f2d2d;color:#34d399;border:1px solid #064e3b;padding:7px 16px;font-size:.85rem;border-radius:7px;cursor:pointer;font-weight:500;transition:all .15s}
|
|
112
112
|
.btn-check:hover{background:#134e4a;border-color:#34d399}.btn-check:disabled{opacity:.5;cursor:not-allowed}
|
|
113
|
+
.btn-enable{background:#14291a;color:#4ade80;border:1px solid #166534}.btn-enable:hover{background:#1a3d22;border-color:#4ade80}
|
|
114
|
+
.btn-disable{background:#2d1f1f;color:#f87171;border:1px solid #7f1d1d}.btn-disable:hover{background:#3d2525;border-color:#f87171}
|
|
115
|
+
.svc-badge{font-size:.7rem;font-weight:600;padding:2px 7px;border-radius:4px;letter-spacing:.4px;text-transform:uppercase}
|
|
116
|
+
.svc-badge.on{background:rgba(74,222,128,.12);color:#4ade80;border:1px solid rgba(74,222,128,.25)}
|
|
117
|
+
.svc-badge.off{background:rgba(248,113,113,.1);color:#f87171;border:1px solid rgba(248,113,113,.2)}
|
|
113
118
|
.status-dot{width:8px;height:8px;border-radius:50%;flex-shrink:0;opacity:0;transition:opacity .3s;margin-top:4px;cursor:default}
|
|
114
119
|
.status-dot.checking{background:#f59e0b;opacity:1;animation:pulse 1s infinite}
|
|
115
120
|
.status-dot.ok{background:#22c55e;opacity:1}
|
|
@@ -294,7 +299,10 @@ async function loadServices() {
|
|
|
294
299
|
<div style="display:flex;align-items:flex-start;justify-content:space-between">
|
|
295
300
|
<div>
|
|
296
301
|
<div class="card-name">\${s.name}</div>
|
|
297
|
-
<div
|
|
302
|
+
<div style="display:flex;align-items:center;gap:6px;margin-top:2px">
|
|
303
|
+
<div class="card-type">\${s.key_type || "secret"}</div>
|
|
304
|
+
<span class="svc-badge \${s.enabled === false ? "off" : "on"}" id="badge-\${s.name}">\${s.enabled === false ? "disabled" : "enabled"}</span>
|
|
305
|
+
</div>
|
|
298
306
|
\${KEY_URLS[s.name] ? \`<a class="card-getkey" href="\${KEY_URLS[s.name]}" target="_blank" rel="noopener">↗ Get / rotate key</a>\` : ""}
|
|
299
307
|
</div>
|
|
300
308
|
<div class="status-dot" id="sdot-\${s.name}" title=""></div>
|
|
@@ -304,6 +312,7 @@ async function loadServices() {
|
|
|
304
312
|
<button class="btn btn-reveal" onclick="reveal('\${s.name}', this)">Reveal</button>
|
|
305
313
|
<button class="btn btn-copy" id="copybtn-\${s.name}" style="display:none" onclick="copyKey('\${s.name}')">Copy</button>
|
|
306
314
|
<button class="btn btn-set" onclick="toggleSet('\${s.name}')">Set</button>
|
|
315
|
+
<button class="btn \${s.enabled === false ? "btn-enable" : "btn-disable"}" id="togbtn-\${s.name}" onclick="toggleService('\${s.name}')">\${s.enabled === false ? "Enable" : "Disable"}</button>
|
|
307
316
|
</div>
|
|
308
317
|
<div class="set-panel" id="set-panel-\${s.name}">
|
|
309
318
|
<label>New value for <strong>\${s.name}</strong> — paste here, never in chat</label>
|
|
@@ -392,6 +401,39 @@ async function saveKey(name) {
|
|
|
392
401
|
}
|
|
393
402
|
}
|
|
394
403
|
|
|
404
|
+
// ── Enable / Disable service ────────────────
|
|
405
|
+
async function toggleService(name) {
|
|
406
|
+
const badge = document.getElementById("badge-" + name);
|
|
407
|
+
const btn = document.getElementById("togbtn-" + name);
|
|
408
|
+
const currently = badge.classList.contains("on");
|
|
409
|
+
const newState = !currently;
|
|
410
|
+
|
|
411
|
+
btn.disabled = true; btn.textContent = "…";
|
|
412
|
+
|
|
413
|
+
try {
|
|
414
|
+
const r = await fetch(BASE + "/toggle/" + name, {
|
|
415
|
+
method: "POST",
|
|
416
|
+
headers: { "Content-Type": "application/json" },
|
|
417
|
+
body: JSON.stringify({ enabled: newState })
|
|
418
|
+
}).then(r => r.json());
|
|
419
|
+
|
|
420
|
+
if (r.locked) { showLockScreen(); return; }
|
|
421
|
+
if (r.error) throw new Error(r.error);
|
|
422
|
+
|
|
423
|
+
badge.className = "svc-badge " + (newState ? "on" : "off");
|
|
424
|
+
badge.textContent = newState ? "enabled" : "disabled";
|
|
425
|
+
btn.className = "btn " + (newState ? "btn-disable" : "btn-enable");
|
|
426
|
+
btn.textContent = newState ? "Disable" : "Enable";
|
|
427
|
+
} catch (e) {
|
|
428
|
+
btn.textContent = "Error";
|
|
429
|
+
setTimeout(() => {
|
|
430
|
+
btn.textContent = currently ? "Disable" : "Enable";
|
|
431
|
+
}, 2000);
|
|
432
|
+
} finally {
|
|
433
|
+
btn.disabled = false;
|
|
434
|
+
}
|
|
435
|
+
}
|
|
436
|
+
|
|
395
437
|
// ── Check all credentials ───────────────────
|
|
396
438
|
async function checkAll() {
|
|
397
439
|
const btn = document.getElementById("check-btn");
|
|
@@ -674,6 +716,34 @@ function createServer(initPassword, whitelist, port) {
|
|
|
674
716
|
return ok(res, { ok: true, locked: true });
|
|
675
717
|
}
|
|
676
718
|
|
|
719
|
+
// POST /toggle/:service — enable or disable a service
|
|
720
|
+
const toggleMatch = reqPath.match(/^\/toggle\/([a-zA-Z0-9_-]+)$/);
|
|
721
|
+
if (method === "POST" && toggleMatch) {
|
|
722
|
+
if (lockedGuard(res)) return;
|
|
723
|
+
const service = toggleMatch[1].toLowerCase();
|
|
724
|
+
|
|
725
|
+
let body;
|
|
726
|
+
try { body = await readBody(req); } catch {
|
|
727
|
+
res.writeHead(400, { "Content-Type": "application/json", ...CORS });
|
|
728
|
+
return res.end(JSON.stringify({ error: "Invalid JSON body" }));
|
|
729
|
+
}
|
|
730
|
+
|
|
731
|
+
const { enabled } = body;
|
|
732
|
+
if (typeof enabled !== "boolean") {
|
|
733
|
+
res.writeHead(400, { "Content-Type": "application/json", ...CORS });
|
|
734
|
+
return res.end(JSON.stringify({ error: "enabled must be boolean" }));
|
|
735
|
+
}
|
|
736
|
+
|
|
737
|
+
try {
|
|
738
|
+
const { token, timestamp } = deriveToken(password, machineHash);
|
|
739
|
+
const result = await api.enable(password, machineHash, token, timestamp, service, enabled);
|
|
740
|
+
if (result.error) return strike(res, 502, result.error);
|
|
741
|
+
return ok(res, { ok: true, service, enabled });
|
|
742
|
+
} catch (err) {
|
|
743
|
+
return strike(res, 502, err.message);
|
|
744
|
+
}
|
|
745
|
+
}
|
|
746
|
+
|
|
677
747
|
// GET /check-all — soft health check, no strikes for missing/disabled keys
|
|
678
748
|
if (method === "GET" && reqPath === "/check-all") {
|
|
679
749
|
if (lockedGuard(res)) return;
|