refactor(account): simplify passkey/token JS and modernize syntax

Replace DOMContentLoaded event-listener block with global arrow
functions and inline onclick handlers. Convert string concatenation
to template literals throughout.
This commit is contained in:
Jon Seager 2026-02-05 12:43:03 +00:00
parent de3e1d4bcb
commit 5acbddd65b
No known key found for this signature in database

View file

@ -59,13 +59,14 @@
<button <button
id="add-passkey-btn" id="add-passkey-btn"
type="button" type="button"
onclick="registerPasskey()"
class="shrink-0 rounded-md bg-amber-600 px-4 py-2 text-sm font-medium text-amber-50 transition hover:bg-amber-500 disabled:opacity-50 disabled:cursor-not-allowed" class="shrink-0 rounded-md bg-amber-600 px-4 py-2 text-sm font-medium text-amber-50 transition hover:bg-amber-500 disabled:opacity-50 disabled:cursor-not-allowed"
> >
Register Register
</button> </button>
<button <button
id="cancel-add-passkey"
type="button" type="button"
onclick="hidePasskeyForm()"
class="shrink-0 rounded-md px-3 py-2 text-sm font-medium text-stone-500 transition hover:text-stone-800" class="shrink-0 rounded-md px-3 py-2 text-sm font-medium text-stone-500 transition hover:text-stone-800"
> >
Cancel Cancel
@ -80,6 +81,7 @@
<button <button
id="show-add-passkey" id="show-add-passkey"
type="button" type="button"
onclick="showPasskeyForm()"
class="mt-3 rounded-md bg-amber-600 px-4 py-2 text-sm font-medium text-amber-50 transition hover:bg-amber-500" class="mt-3 rounded-md bg-amber-600 px-4 py-2 text-sm font-medium text-amber-50 transition hover:bg-amber-500"
> >
Add Passkey Add Passkey
@ -139,13 +141,14 @@
<button <button
id="create-token-btn" id="create-token-btn"
type="button" type="button"
onclick="createToken()"
class="shrink-0 rounded-md bg-amber-600 px-4 py-2 text-sm font-medium text-amber-50 transition hover:bg-amber-500 disabled:opacity-50 disabled:cursor-not-allowed" class="shrink-0 rounded-md bg-amber-600 px-4 py-2 text-sm font-medium text-amber-50 transition hover:bg-amber-500 disabled:opacity-50 disabled:cursor-not-allowed"
> >
Create Create
</button> </button>
<button <button
id="cancel-create-token"
type="button" type="button"
onclick="hideTokenForm()"
class="shrink-0 rounded-md px-3 py-2 text-sm font-medium text-stone-500 transition hover:text-stone-800" class="shrink-0 rounded-md px-3 py-2 text-sm font-medium text-stone-500 transition hover:text-stone-800"
> >
Cancel Cancel
@ -159,16 +162,16 @@
<div class="mt-3 flex items-center gap-2"> <div class="mt-3 flex items-center gap-2">
<code id="token-value" class="flex-1 rounded bg-white px-3 py-2 text-sm font-mono text-stone-800 border border-green-200 break-all select-all"></code> <code id="token-value" class="flex-1 rounded bg-white px-3 py-2 text-sm font-mono text-stone-800 border border-green-200 break-all select-all"></code>
<button <button
id="copy-token-btn"
type="button" type="button"
onclick="copyToken(this)"
class="shrink-0 rounded-md bg-green-600 px-3 py-1.5 text-sm font-medium text-green-50 transition hover:bg-green-500" class="shrink-0 rounded-md bg-green-600 px-3 py-1.5 text-sm font-medium text-green-50 transition hover:bg-green-500"
> >
Copy Copy
</button> </button>
</div> </div>
<button <button
id="dismiss-token"
type="button" type="button"
onclick="window.location.reload()"
class="mt-2 text-sm text-stone-500 hover:text-stone-700" class="mt-2 text-sm text-stone-500 hover:text-stone-700"
> >
Dismiss Dismiss
@ -178,6 +181,7 @@
<button <button
id="show-create-token" id="show-create-token"
type="button" type="button"
onclick="showTokenForm()"
class="mt-3 rounded-md bg-amber-600 px-4 py-2 text-sm font-medium text-amber-50 transition hover:bg-amber-500" class="mt-3 rounded-md bg-amber-600 px-4 py-2 text-sm font-medium text-amber-50 transition hover:bg-amber-500"
> >
Create Token Create Token
@ -247,87 +251,76 @@
</section> </section>
<script> <script>
document.addEventListener("DOMContentLoaded", function () { // --- Passkey form ---
// --- Passkey management ---
const showAddPasskey = document.getElementById("show-add-passkey");
const addPasskeyForm = document.getElementById("add-passkey-form");
const cancelAddPasskey = document.getElementById("cancel-add-passkey");
const addPasskeyBtn = document.getElementById("add-passkey-btn");
const passkeyNameInput = document.getElementById("passkey-name");
const addPasskeyError = document.getElementById("add-passkey-error");
const addPasskeyLoading = document.getElementById("add-passkey-loading");
showAddPasskey.addEventListener("click", function () { const showPasskeyForm = () => {
addPasskeyForm.classList.remove("hidden"); document.getElementById("add-passkey-form").classList.remove("hidden");
showAddPasskey.classList.add("hidden"); document.getElementById("show-add-passkey").classList.add("hidden");
passkeyNameInput.focus(); document.getElementById("passkey-name").focus();
}); };
cancelAddPasskey.addEventListener("click", function () { const hidePasskeyForm = () => {
addPasskeyForm.classList.add("hidden"); document.getElementById("add-passkey-form").classList.add("hidden");
showAddPasskey.classList.remove("hidden"); document.getElementById("show-add-passkey").classList.remove("hidden");
addPasskeyError.classList.add("hidden"); document.getElementById("add-passkey-error").classList.add("hidden");
passkeyNameInput.value = ""; document.getElementById("passkey-name").value = "";
}); };
const registerPasskey = async () => {
const name = document.getElementById("passkey-name").value.trim();
const errorEl = document.getElementById("add-passkey-error");
const loadingEl = document.getElementById("add-passkey-loading");
const btn = document.getElementById("add-passkey-btn");
addPasskeyBtn.addEventListener("click", async function () {
const name = passkeyNameInput.value.trim();
if (!name) { if (!name) {
addPasskeyError.textContent = "Please enter a name for this passkey."; errorEl.textContent = "Please enter a name for this passkey.";
addPasskeyError.classList.remove("hidden"); errorEl.classList.remove("hidden");
return; return;
} }
addPasskeyError.classList.add("hidden"); errorEl.classList.add("hidden");
addPasskeyLoading.classList.remove("hidden"); loadingEl.classList.remove("hidden");
addPasskeyBtn.disabled = true; btn.disabled = true;
try { try {
await addPasskey(name); await addPasskey(name);
window.location.reload(); window.location.reload();
} catch (err) { } catch (err) {
addPasskeyError.textContent = err.message; errorEl.textContent = err.message;
addPasskeyError.classList.remove("hidden"); errorEl.classList.remove("hidden");
addPasskeyLoading.classList.add("hidden"); loadingEl.classList.add("hidden");
addPasskeyBtn.disabled = false; btn.disabled = false;
} }
}); };
// --- Token management --- // --- Token form ---
const showCreateToken = document.getElementById("show-create-token");
const createTokenForm = document.getElementById("create-token-form");
const cancelCreateToken = document.getElementById("cancel-create-token");
const createTokenBtn = document.getElementById("create-token-btn");
const tokenNameInput = document.getElementById("token-name");
const createTokenError = document.getElementById("create-token-error");
const tokenCreated = document.getElementById("token-created");
const tokenValue = document.getElementById("token-value");
const copyTokenBtn = document.getElementById("copy-token-btn");
const dismissToken = document.getElementById("dismiss-token");
showCreateToken.addEventListener("click", function () { const showTokenForm = () => {
createTokenForm.classList.remove("hidden"); document.getElementById("create-token-form").classList.remove("hidden");
showCreateToken.classList.add("hidden"); document.getElementById("show-create-token").classList.add("hidden");
tokenNameInput.focus(); document.getElementById("token-name").focus();
}); };
cancelCreateToken.addEventListener("click", function () { const hideTokenForm = () => {
createTokenForm.classList.add("hidden"); document.getElementById("create-token-form").classList.add("hidden");
showCreateToken.classList.remove("hidden"); document.getElementById("show-create-token").classList.remove("hidden");
createTokenError.classList.add("hidden"); document.getElementById("create-token-error").classList.add("hidden");
tokenNameInput.value = ""; document.getElementById("token-name").value = "";
}); };
const createToken = async () => {
const name = document.getElementById("token-name").value.trim();
const errorEl = document.getElementById("create-token-error");
const btn = document.getElementById("create-token-btn");
createTokenBtn.addEventListener("click", async function () {
const name = tokenNameInput.value.trim();
if (!name) { if (!name) {
createTokenError.textContent = "Please enter a name for this token."; errorEl.textContent = "Please enter a name for this token.";
createTokenError.classList.remove("hidden"); errorEl.classList.remove("hidden");
return; return;
} }
createTokenError.classList.add("hidden"); errorEl.classList.add("hidden");
createTokenBtn.disabled = true; btn.disabled = true;
try { try {
const response = await fetch("/api/v1/tokens", { const response = await fetch("/api/v1/tokens", {
@ -337,54 +330,40 @@
}); });
if (!response.ok) { if (!response.ok) {
throw new Error("Failed to create token (HTTP " + response.status + ")."); throw new Error(`Failed to create token (HTTP ${response.status}).`);
} }
const data = await response.json(); const data = await response.json();
// Show the one-time token value document.getElementById("create-token-form").classList.add("hidden");
createTokenForm.classList.add("hidden"); document.getElementById("token-value").textContent = data.token;
tokenValue.textContent = data.token; document.getElementById("token-created").classList.remove("hidden");
tokenCreated.classList.remove("hidden");
} catch (err) { } catch (err) {
createTokenError.textContent = err.message; errorEl.textContent = err.message;
createTokenError.classList.remove("hidden"); errorEl.classList.remove("hidden");
createTokenBtn.disabled = false; btn.disabled = false;
} }
}); };
copyTokenBtn.addEventListener("click", function () { const copyToken = (btn) => {
const token = tokenValue.textContent; const token = document.getElementById("token-value").textContent;
if (navigator.clipboard) { if (navigator.clipboard) {
navigator.clipboard.writeText(token).then(function () { navigator.clipboard.writeText(token).then(() => {
copyTokenBtn.textContent = "Copied!"; btn.textContent = "Copied!";
setTimeout(function () { setTimeout(() => { btn.textContent = "Copy"; }, 2000);
copyTokenBtn.textContent = "Copy";
}, 2000);
}); });
} else { } else {
// Fallback: select the text
const range = document.createRange(); const range = document.createRange();
range.selectNodeContents(tokenValue); range.selectNodeContents(document.getElementById("token-value"));
const selection = window.getSelection(); const selection = window.getSelection();
selection.removeAllRanges(); selection.removeAllRanges();
selection.addRange(range); selection.addRange(range);
} }
}); };
dismissToken.addEventListener("click", function () { // --- Data management ---
tokenCreated.classList.add("hidden");
showCreateToken.classList.remove("hidden");
tokenNameInput.value = "";
createTokenBtn.disabled = false;
window.location.reload();
});
}); const restoreFromFile = async (input) => {
// --- Global functions for inline onclick/onchange handlers ---
async function restoreFromFile(input) {
const file = input.files[0]; const file = input.files[0];
if (!file) return; if (!file) return;
input.value = ""; input.value = "";
@ -412,7 +391,7 @@
throw new Error("Database is not empty. Restore requires an empty database."); throw new Error("Database is not empty. Restore requires an empty database.");
} }
if (!response.ok) { if (!response.ok) {
throw new Error("Restore failed (HTTP " + response.status + ")."); throw new Error(`Restore failed (HTTP ${response.status}).`);
} }
status.textContent = "Backup restored successfully."; status.textContent = "Backup restored successfully.";
@ -421,13 +400,13 @@
error.textContent = err instanceof SyntaxError ? "Invalid JSON file." : err.message; error.textContent = err instanceof SyntaxError ? "Invalid JSON file." : err.message;
error.classList.remove("hidden"); error.classList.remove("hidden");
} }
} };
async function deletePasskey(id, name) { const deletePasskey = async (id, name) => {
if (!confirm('Delete passkey "' + name + '"? This cannot be undone.')) return; if (!confirm(`Delete passkey "${name}"? This cannot be undone.`)) return;
try { try {
const response = await fetch("/api/v1/passkeys/" + id, { method: "DELETE" }); const response = await fetch(`/api/v1/passkeys/${id}`, { method: "DELETE" });
if (response.ok) { if (response.ok) {
window.location.reload(); window.location.reload();
} else if (response.status === 409) { } else if (response.status === 409) {
@ -436,23 +415,23 @@
alert("Failed to delete passkey."); alert("Failed to delete passkey.");
} }
} catch (err) { } catch (err) {
alert("Failed to delete passkey: " + err.message); alert(`Failed to delete passkey: ${err.message}`);
}
} }
};
async function revokeToken(id, name) { const revokeToken = async (id, name) => {
if (!confirm('Revoke token "' + name + '"? This cannot be undone.')) return; if (!confirm(`Revoke token "${name}"? This cannot be undone.`)) return;
try { try {
const response = await fetch("/api/v1/tokens/" + id + "/revoke", { method: "POST" }); const response = await fetch(`/api/v1/tokens/${id}/revoke`, { method: "POST" });
if (response.ok) { if (response.ok) {
window.location.reload(); window.location.reload();
} else { } else {
alert("Failed to revoke token."); alert("Failed to revoke token.");
} }
} catch (err) { } catch (err) {
alert("Failed to revoke token: " + err.message); alert(`Failed to revoke token: ${err.message}`);
}
} }
};
</script> </script>
{% endblock %} {% endblock %}