@lifeaitools/clauth 1.4.4 → 1.4.5
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 +107 -2
- package/package.json +1 -1
package/cli/commands/serve.js
CHANGED
|
@@ -502,10 +502,18 @@ function dashboardHtml(port, whitelist, isStaged = false) {
|
|
|
502
502
|
.card:hover{border-color:#3b82f6}
|
|
503
503
|
.card-name{font-size:1rem;font-weight:600;color:#f8fafc;margin-bottom:3px}
|
|
504
504
|
.card-type{font-size:.78rem;color:#64748b;text-transform:uppercase;letter-spacing:.5px}
|
|
505
|
-
.card-getkey{font-size:.75rem;color:#
|
|
506
|
-
.card-getkey:hover{
|
|
505
|
+
.card-getkey{font-size:.75rem;color:#60a5fa;text-decoration:none;transition:color .15s}
|
|
506
|
+
.card-getkey:hover{color:#93c5fd;text-decoration:underline}
|
|
507
507
|
.card-value{font-family:'Courier New',monospace;font-size:.82rem;color:#22c55e;background:#0f172a;border-radius:4px;padding:8px 10px;margin-top:10px;word-break:break-all;max-height:80px;overflow:auto;display:none}
|
|
508
508
|
.card-actions{margin-top:10px;display:flex;gap:7px;flex-wrap:wrap}
|
|
509
|
+
.btn-rename{background:#1e293b;color:#94a3b8;border:1px solid #334155;padding:3px 8px;font-size:.75rem;border-radius:4px;cursor:pointer;transition:color .15s,border-color .15s}
|
|
510
|
+
.btn-rename:hover{color:#e2e8f0;border-color:#60a5fa}
|
|
511
|
+
.btn-delete{background:#1e293b;color:#f87171;border:1px solid #4b2020;padding:3px 8px;font-size:.75rem;border-radius:4px;cursor:pointer;transition:background .15s,border-color .15s}
|
|
512
|
+
.btn-delete:hover{background:#2d1f1f;border-color:#ef4444}
|
|
513
|
+
.rename-panel{display:none;margin-top:8px;background:#0f172a;border-radius:6px;padding:8px 10px;border:1px solid #1e3a5f}
|
|
514
|
+
.rename-input{width:calc(100% - 100px);background:#0a0f1a;border:1px solid #1e3a5f;border-radius:4px;color:#e2e8f0;font-size:.85rem;padding:5px 8px;outline:none}
|
|
515
|
+
.rename-input:focus{border-color:#3b82f6}
|
|
516
|
+
.rename-msg{font-size:.75rem;margin-left:6px}
|
|
509
517
|
.set-panel{display:none;margin-top:10px;background:#0f172a;border-radius:6px;padding:10px;border:1px solid #1e3a5f}
|
|
510
518
|
.set-panel label{font-size:.75rem;color:#64748b;display:block;margin-bottom:6px}
|
|
511
519
|
.set-input{width:100%;background:#0a0f1a;border:1px solid #1e3a5f;border-radius:4px;color:#e2e8f0;font-family:'Courier New',monospace;font-size:.85rem;padding:7px 10px;outline:none;resize:vertical;min-height:58px;transition:border-color .2s}
|
|
@@ -887,10 +895,12 @@ const KEY_URLS = {
|
|
|
887
895
|
// Extra links shown below the primary KEY_URLS link
|
|
888
896
|
const EXTRA_LINKS = {
|
|
889
897
|
"gmail": [
|
|
898
|
+
{ label: "↗ Open Gmail", url: "https://mail.google.com" },
|
|
890
899
|
{ label: "↗ OAuth Playground (get refresh token)", url: "https://developers.google.com/oauthplayground/" },
|
|
891
900
|
{ label: "↗ Enable Gmail API", url: "https://console.cloud.google.com/apis/library/gmail.googleapis.com" },
|
|
892
901
|
],
|
|
893
902
|
"gcal": [
|
|
903
|
+
{ label: "↗ Open Google Calendar", url: "https://calendar.google.com" },
|
|
894
904
|
{ label: "↗ OAuth Playground (get refresh token)", url: "https://developers.google.com/oauthplayground/" },
|
|
895
905
|
{ label: "↗ Enable Calendar API", url: "https://console.cloud.google.com/apis/library/calendar-json.googleapis.com" },
|
|
896
906
|
],
|
|
@@ -1192,6 +1202,14 @@ function renderServiceGrid(services) {
|
|
|
1192
1202
|
<button class="btn-project" onclick="toggleProjectEdit('\${s.name}')">\${s.project ? "✎ Project" : "+ Project"}</button>
|
|
1193
1203
|
<button class="btn \${s.enabled === false ? "btn-enable" : "btn-disable"}" id="togbtn-\${s.name}" onclick="toggleService('\${s.name}')">\${s.enabled === false ? "Enable" : "Disable"}</button>
|
|
1194
1204
|
<button class="btn-rotate" id="rotbtn-\${s.name}" style="display:none;background:#0e7490;border:1px solid #06b6d4;color:#cffafe;font-size:.75rem;padding:3px 8px;border-radius:4px;cursor:pointer" onclick="rotateKey('\${s.name}')">↻ Rotate</button>
|
|
1205
|
+
<button class="btn-rename" onclick="toggleRename('\${s.name}')" title="Rename service">✎</button>
|
|
1206
|
+
<button class="btn-delete" onclick="deleteService('\${s.name}')" title="Delete service">✕</button>
|
|
1207
|
+
</div>
|
|
1208
|
+
<div class="rename-panel" id="rn-\${s.name}">
|
|
1209
|
+
<input class="rename-input" id="rn-input-\${s.name}" value="\${s.name}" spellcheck="false" autocomplete="off" placeholder="New name…">
|
|
1210
|
+
<button class="btn" onclick="saveRename('\${s.name}')" style="padding:4px 10px;font-size:.8rem">Save</button>
|
|
1211
|
+
<button class="btn" onclick="toggleRename('\${s.name}')" style="padding:4px 10px;font-size:.8rem;background:#1e293b">Cancel</button>
|
|
1212
|
+
<span class="rename-msg" id="rn-msg-\${s.name}"></span>
|
|
1195
1213
|
</div>
|
|
1196
1214
|
<div class="project-edit" id="pe-\${s.name}">
|
|
1197
1215
|
<input type="text" id="pe-input-\${s.name}" value="\${s.project || ""}" placeholder="Project name…" spellcheck="false" autocomplete="off">
|
|
@@ -1397,6 +1415,53 @@ async function clearProject(name) {
|
|
|
1397
1415
|
await saveProject(name);
|
|
1398
1416
|
}
|
|
1399
1417
|
|
|
1418
|
+
// ── Rename service ───────────────────────────
|
|
1419
|
+
function toggleRename(name) {
|
|
1420
|
+
const panel = document.getElementById("rn-" + name);
|
|
1421
|
+
const open = panel.style.display === "block";
|
|
1422
|
+
panel.style.display = open ? "none" : "block";
|
|
1423
|
+
if (!open) {
|
|
1424
|
+
const inp = document.getElementById("rn-input-" + name);
|
|
1425
|
+
inp.value = name;
|
|
1426
|
+
inp.focus(); inp.select();
|
|
1427
|
+
document.getElementById("rn-msg-" + name).textContent = "";
|
|
1428
|
+
}
|
|
1429
|
+
}
|
|
1430
|
+
|
|
1431
|
+
async function saveRename(oldName) {
|
|
1432
|
+
const inp = document.getElementById("rn-input-" + oldName);
|
|
1433
|
+
const msg = document.getElementById("rn-msg-" + oldName);
|
|
1434
|
+
const newName = inp.value.trim();
|
|
1435
|
+
if (!newName || newName === oldName) { toggleRename(oldName); return; }
|
|
1436
|
+
msg.style.color = "#94a3b8"; msg.textContent = "Saving…";
|
|
1437
|
+
try {
|
|
1438
|
+
const r = await fetch(BASE + "/rename/" + oldName, {
|
|
1439
|
+
method: "POST",
|
|
1440
|
+
headers: { "Content-Type": "application/json" },
|
|
1441
|
+
body: JSON.stringify({ new_name: newName })
|
|
1442
|
+
}).then(r => r.json());
|
|
1443
|
+
if (r.locked) { showLockScreen(false); return; }
|
|
1444
|
+
if (r.error) throw new Error(r.error);
|
|
1445
|
+
msg.style.color = "#4ade80"; msg.textContent = "✓ Renamed";
|
|
1446
|
+
setTimeout(() => loadServices(), 800);
|
|
1447
|
+
} catch (e) {
|
|
1448
|
+
msg.style.color = "#f87171"; msg.textContent = "✗ " + e.message;
|
|
1449
|
+
}
|
|
1450
|
+
}
|
|
1451
|
+
|
|
1452
|
+
// ── Delete service ───────────────────────────
|
|
1453
|
+
async function deleteService(name) {
|
|
1454
|
+
if (!confirm(\`Delete service "\${name}"? This cannot be undone.\`)) return;
|
|
1455
|
+
try {
|
|
1456
|
+
const r = await fetch(BASE + "/delete/" + name, { method: "POST" }).then(r => r.json());
|
|
1457
|
+
if (r.locked) { showLockScreen(false); return; }
|
|
1458
|
+
if (r.error) throw new Error(r.error);
|
|
1459
|
+
loadServices();
|
|
1460
|
+
} catch (e) {
|
|
1461
|
+
alert("Delete failed: " + e.message);
|
|
1462
|
+
}
|
|
1463
|
+
}
|
|
1464
|
+
|
|
1400
1465
|
// ── Set key ─────────────────────────────────
|
|
1401
1466
|
function toggleSet(name) {
|
|
1402
1467
|
const panel = document.getElementById("set-panel-" + name);
|
|
@@ -3417,6 +3482,46 @@ function createServer(initPassword, whitelist, port, tunnelHostnameInit = null,
|
|
|
3417
3482
|
return ok(res, { ok: true, locked: true, hard_locked: authHardLocked });
|
|
3418
3483
|
}
|
|
3419
3484
|
|
|
3485
|
+
// POST /rename/:service — rename a service
|
|
3486
|
+
const renameMatch = reqPath.match(/^\/rename\/([a-zA-Z0-9_-]+)$/);
|
|
3487
|
+
if (method === "POST" && renameMatch) {
|
|
3488
|
+
if (lockedGuard(res)) return;
|
|
3489
|
+
const service = renameMatch[1].toLowerCase();
|
|
3490
|
+
let body;
|
|
3491
|
+
try { body = await readBody(req); } catch {
|
|
3492
|
+
res.writeHead(400, { "Content-Type": "application/json", ...CORS });
|
|
3493
|
+
return res.end(JSON.stringify({ error: "Invalid JSON body" }));
|
|
3494
|
+
}
|
|
3495
|
+
const newName = (body.new_name || "").trim().toLowerCase();
|
|
3496
|
+
if (!newName || !/^[a-z0-9_-]+$/.test(newName)) {
|
|
3497
|
+
res.writeHead(400, { "Content-Type": "application/json", ...CORS });
|
|
3498
|
+
return res.end(JSON.stringify({ error: "Invalid name — use lowercase letters, numbers, hyphens, underscores" }));
|
|
3499
|
+
}
|
|
3500
|
+
try {
|
|
3501
|
+
const { token, timestamp } = deriveToken(password, machineHash);
|
|
3502
|
+
const result = await api.updateService(password, machineHash, token, timestamp, service, { name: newName, label: newName });
|
|
3503
|
+
if (result.error) return strike(res, 502, result.error);
|
|
3504
|
+
return ok(res, { ok: true, old_name: service, new_name: newName });
|
|
3505
|
+
} catch (err) {
|
|
3506
|
+
return strike(res, 502, err.message);
|
|
3507
|
+
}
|
|
3508
|
+
}
|
|
3509
|
+
|
|
3510
|
+
// POST /delete/:service — remove a service entirely
|
|
3511
|
+
const deleteMatch = reqPath.match(/^\/delete\/([a-zA-Z0-9_-]+)$/);
|
|
3512
|
+
if (method === "POST" && deleteMatch) {
|
|
3513
|
+
if (lockedGuard(res)) return;
|
|
3514
|
+
const service = deleteMatch[1].toLowerCase();
|
|
3515
|
+
try {
|
|
3516
|
+
const { token, timestamp } = deriveToken(password, machineHash);
|
|
3517
|
+
const result = await api.removeService(password, machineHash, token, timestamp, service, true);
|
|
3518
|
+
if (result.error) return strike(res, 502, result.error);
|
|
3519
|
+
return ok(res, { ok: true, deleted: service });
|
|
3520
|
+
} catch (err) {
|
|
3521
|
+
return strike(res, 502, err.message);
|
|
3522
|
+
}
|
|
3523
|
+
}
|
|
3524
|
+
|
|
3420
3525
|
// POST /toggle/:service — enable or disable a service
|
|
3421
3526
|
const toggleMatch = reqPath.match(/^\/toggle\/([a-zA-Z0-9_-]+)$/);
|
|
3422
3527
|
if (method === "POST" && toggleMatch) {
|