@lifeaitools/clauth 1.4.4 → 1.4.6
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 +106 -12
- 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}
|
|
@@ -885,16 +893,7 @@ const KEY_URLS = {
|
|
|
885
893
|
};
|
|
886
894
|
|
|
887
895
|
// Extra links shown below the primary KEY_URLS link
|
|
888
|
-
const EXTRA_LINKS = {
|
|
889
|
-
"gmail": [
|
|
890
|
-
{ label: "↗ OAuth Playground (get refresh token)", url: "https://developers.google.com/oauthplayground/" },
|
|
891
|
-
{ label: "↗ Enable Gmail API", url: "https://console.cloud.google.com/apis/library/gmail.googleapis.com" },
|
|
892
|
-
],
|
|
893
|
-
"gcal": [
|
|
894
|
-
{ label: "↗ OAuth Playground (get refresh token)", url: "https://developers.google.com/oauthplayground/" },
|
|
895
|
-
{ label: "↗ Enable Calendar API", url: "https://console.cloud.google.com/apis/library/calendar-json.googleapis.com" },
|
|
896
|
-
],
|
|
897
|
-
};
|
|
896
|
+
const EXTRA_LINKS = {};
|
|
898
897
|
|
|
899
898
|
// ── OAuth import config ─────────────────────
|
|
900
899
|
// Services where Google downloads a JSON file. jsonFields = keys to extract
|
|
@@ -1192,6 +1191,14 @@ function renderServiceGrid(services) {
|
|
|
1192
1191
|
<button class="btn-project" onclick="toggleProjectEdit('\${s.name}')">\${s.project ? "✎ Project" : "+ Project"}</button>
|
|
1193
1192
|
<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
1193
|
<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>
|
|
1194
|
+
<button class="btn-rename" onclick="toggleRename('\${s.name}')" title="Rename service">✎</button>
|
|
1195
|
+
<button class="btn-delete" onclick="deleteService('\${s.name}')" title="Delete service">✕</button>
|
|
1196
|
+
</div>
|
|
1197
|
+
<div class="rename-panel" id="rn-\${s.name}">
|
|
1198
|
+
<input class="rename-input" id="rn-input-\${s.name}" value="\${s.name}" spellcheck="false" autocomplete="off" placeholder="New name…">
|
|
1199
|
+
<button class="btn" onclick="saveRename('\${s.name}')" style="padding:4px 10px;font-size:.8rem">Save</button>
|
|
1200
|
+
<button class="btn" onclick="toggleRename('\${s.name}')" style="padding:4px 10px;font-size:.8rem;background:#1e293b">Cancel</button>
|
|
1201
|
+
<span class="rename-msg" id="rn-msg-\${s.name}"></span>
|
|
1195
1202
|
</div>
|
|
1196
1203
|
<div class="project-edit" id="pe-\${s.name}">
|
|
1197
1204
|
<input type="text" id="pe-input-\${s.name}" value="\${s.project || ""}" placeholder="Project name…" spellcheck="false" autocomplete="off">
|
|
@@ -1397,6 +1404,53 @@ async function clearProject(name) {
|
|
|
1397
1404
|
await saveProject(name);
|
|
1398
1405
|
}
|
|
1399
1406
|
|
|
1407
|
+
// ── Rename service ───────────────────────────
|
|
1408
|
+
function toggleRename(name) {
|
|
1409
|
+
const panel = document.getElementById("rn-" + name);
|
|
1410
|
+
const open = panel.style.display === "block";
|
|
1411
|
+
panel.style.display = open ? "none" : "block";
|
|
1412
|
+
if (!open) {
|
|
1413
|
+
const inp = document.getElementById("rn-input-" + name);
|
|
1414
|
+
inp.value = name;
|
|
1415
|
+
inp.focus(); inp.select();
|
|
1416
|
+
document.getElementById("rn-msg-" + name).textContent = "";
|
|
1417
|
+
}
|
|
1418
|
+
}
|
|
1419
|
+
|
|
1420
|
+
async function saveRename(oldName) {
|
|
1421
|
+
const inp = document.getElementById("rn-input-" + oldName);
|
|
1422
|
+
const msg = document.getElementById("rn-msg-" + oldName);
|
|
1423
|
+
const newName = inp.value.trim();
|
|
1424
|
+
if (!newName || newName === oldName) { toggleRename(oldName); return; }
|
|
1425
|
+
msg.style.color = "#94a3b8"; msg.textContent = "Saving…";
|
|
1426
|
+
try {
|
|
1427
|
+
const r = await fetch(BASE + "/rename/" + oldName, {
|
|
1428
|
+
method: "POST",
|
|
1429
|
+
headers: { "Content-Type": "application/json" },
|
|
1430
|
+
body: JSON.stringify({ new_name: newName })
|
|
1431
|
+
}).then(r => r.json());
|
|
1432
|
+
if (r.locked) { showLockScreen(false); return; }
|
|
1433
|
+
if (r.error) throw new Error(r.error);
|
|
1434
|
+
msg.style.color = "#4ade80"; msg.textContent = "✓ Renamed";
|
|
1435
|
+
setTimeout(() => loadServices(), 800);
|
|
1436
|
+
} catch (e) {
|
|
1437
|
+
msg.style.color = "#f87171"; msg.textContent = "✗ " + e.message;
|
|
1438
|
+
}
|
|
1439
|
+
}
|
|
1440
|
+
|
|
1441
|
+
// ── Delete service ───────────────────────────
|
|
1442
|
+
async function deleteService(name) {
|
|
1443
|
+
if (!confirm(\`Delete service "\${name}"? This cannot be undone.\`)) return;
|
|
1444
|
+
try {
|
|
1445
|
+
const r = await fetch(BASE + "/delete/" + name, { method: "POST" }).then(r => r.json());
|
|
1446
|
+
if (r.locked) { showLockScreen(false); return; }
|
|
1447
|
+
if (r.error) throw new Error(r.error);
|
|
1448
|
+
loadServices();
|
|
1449
|
+
} catch (e) {
|
|
1450
|
+
alert("Delete failed: " + e.message);
|
|
1451
|
+
}
|
|
1452
|
+
}
|
|
1453
|
+
|
|
1400
1454
|
// ── Set key ─────────────────────────────────
|
|
1401
1455
|
function toggleSet(name) {
|
|
1402
1456
|
const panel = document.getElementById("set-panel-" + name);
|
|
@@ -3417,6 +3471,46 @@ function createServer(initPassword, whitelist, port, tunnelHostnameInit = null,
|
|
|
3417
3471
|
return ok(res, { ok: true, locked: true, hard_locked: authHardLocked });
|
|
3418
3472
|
}
|
|
3419
3473
|
|
|
3474
|
+
// POST /rename/:service — rename a service
|
|
3475
|
+
const renameMatch = reqPath.match(/^\/rename\/([a-zA-Z0-9_-]+)$/);
|
|
3476
|
+
if (method === "POST" && renameMatch) {
|
|
3477
|
+
if (lockedGuard(res)) return;
|
|
3478
|
+
const service = renameMatch[1].toLowerCase();
|
|
3479
|
+
let body;
|
|
3480
|
+
try { body = await readBody(req); } catch {
|
|
3481
|
+
res.writeHead(400, { "Content-Type": "application/json", ...CORS });
|
|
3482
|
+
return res.end(JSON.stringify({ error: "Invalid JSON body" }));
|
|
3483
|
+
}
|
|
3484
|
+
const newName = (body.new_name || "").trim().toLowerCase();
|
|
3485
|
+
if (!newName || !/^[a-z0-9_-]+$/.test(newName)) {
|
|
3486
|
+
res.writeHead(400, { "Content-Type": "application/json", ...CORS });
|
|
3487
|
+
return res.end(JSON.stringify({ error: "Invalid name — use lowercase letters, numbers, hyphens, underscores" }));
|
|
3488
|
+
}
|
|
3489
|
+
try {
|
|
3490
|
+
const { token, timestamp } = deriveToken(password, machineHash);
|
|
3491
|
+
const result = await api.updateService(password, machineHash, token, timestamp, service, { name: newName, label: newName });
|
|
3492
|
+
if (result.error) return strike(res, 502, result.error);
|
|
3493
|
+
return ok(res, { ok: true, old_name: service, new_name: newName });
|
|
3494
|
+
} catch (err) {
|
|
3495
|
+
return strike(res, 502, err.message);
|
|
3496
|
+
}
|
|
3497
|
+
}
|
|
3498
|
+
|
|
3499
|
+
// POST /delete/:service — remove a service entirely
|
|
3500
|
+
const deleteMatch = reqPath.match(/^\/delete\/([a-zA-Z0-9_-]+)$/);
|
|
3501
|
+
if (method === "POST" && deleteMatch) {
|
|
3502
|
+
if (lockedGuard(res)) return;
|
|
3503
|
+
const service = deleteMatch[1].toLowerCase();
|
|
3504
|
+
try {
|
|
3505
|
+
const { token, timestamp } = deriveToken(password, machineHash);
|
|
3506
|
+
const result = await api.removeService(password, machineHash, token, timestamp, service, true);
|
|
3507
|
+
if (result.error) return strike(res, 502, result.error);
|
|
3508
|
+
return ok(res, { ok: true, deleted: service });
|
|
3509
|
+
} catch (err) {
|
|
3510
|
+
return strike(res, 502, err.message);
|
|
3511
|
+
}
|
|
3512
|
+
}
|
|
3513
|
+
|
|
3420
3514
|
// POST /toggle/:service — enable or disable a service
|
|
3421
3515
|
const toggleMatch = reqPath.match(/^\/toggle\/([a-zA-Z0-9_-]+)$/);
|
|
3422
3516
|
if (method === "POST" && toggleMatch) {
|