This commit is contained in:
Mateusz Gruszczyński
2026-07-26 23:32:44 +02:00
parent 364af50a7c
commit 2857006949
28 changed files with 961 additions and 152 deletions
+43 -1
View File
@@ -84,6 +84,7 @@ document.querySelector("#workspace-form").addEventListener("submit", async (even
handleAccountConfirmationToken();
handleResetToken();
{ const params=new URLSearchParams(location.search); const token=params.get("account_action_token"); if(token){ api("/api/auth/account-action/confirm",{method:"POST",body:JSON.stringify({token})}).then(r=>alert(r.message)).catch(e=>alert(e.message)).finally(()=>{params.delete("account_action_token");history.replaceState({},"",`${location.pathname}${params.size?`?${params}`:""}${location.hash}`);}); } }
const identityDialog = document.querySelector("#identity-dialog");
const guestAccount = document.querySelector("#footer-account-guest");
@@ -94,6 +95,9 @@ const registrationEnabled = document.body.dataset.registrationEnabled === "true"
const resourcesDialog = document.querySelector("#resources-dialog");
const resourcesList = document.querySelector("#resources-list");
const resourcesError = document.querySelector("#resources-error");
const profileDialog = document.querySelector("#profile-dialog");
const profileForm = document.querySelector("#profile-form");
let currentSession = null;
function authHeaders() { const token = getAuthToken(); return token ? { Authorization: `Bearer ${token}` } : {}; }
function escapeHtml(value) { const node = document.createElement("div"); node.textContent = String(value ?? ""); return node.innerHTML; }
@@ -179,7 +183,7 @@ async function loadResources() {
linkRow.querySelector("[data-revoke-link]").addEventListener("click", async () => { try { await api("/api/auth/resources/share-links", { method: "DELETE", headers: authHeaders(), body: JSON.stringify({ kind: item.kind, slug: item.slug, token: linkRow.dataset.linkToken }) }); setDialogMessage("Link revoked.", "success"); await refresh(); } catch (err) { setDialogMessage(err.message, "error"); } });
});
};
userForm.addEventListener("submit", async event => { event.preventDefault(); try { await api("/api/auth/resources/sharing", { method: "POST", headers: authHeaders(), body: JSON.stringify({ kind: item.kind, slug: item.slug, emails: userForm.emails.value, permission: userForm.permission.value }) }); userForm.emails.value = ""; setDialogMessage("Access granted.", "success"); await refresh(); } catch (err) { setDialogMessage(err.message, "error"); } });
userForm.addEventListener("submit", async event => { event.preventDefault(); try { const result = await api("/api/auth/resources/sharing", { method: "POST", headers: authHeaders(), body: JSON.stringify({ kind: item.kind, slug: item.slug, emails: userForm.emails.value, permission: userForm.permission.value }) }); userForm.emails.value = ""; setDialogMessage(result.confirmation_required ? "Invitation sent. Access will appear after the recipient accepts it." : "Access granted.", "success"); await refresh(); } catch (err) { setDialogMessage(err.message, "error"); } });
linkForm.addEventListener("submit", async event => { event.preventDefault(); try { const expires_at = shareExpiry(linkForm.hours.value, linkForm.forever.checked); const result = await api("/api/auth/resources/share-links", { method: "POST", headers: authHeaders(), body: JSON.stringify({ kind: item.kind, slug: item.slug, permission: linkForm.permission.value, expires_at }) }); const absolute = new URL(result.url, location.origin).href; await copyText(absolute); setDialogMessage("Link created and copied. It remains visible below.", "success"); await refresh(); } catch (err) { setDialogMessage(err.message, "error"); } });
dialog.showModal();
try { await refresh(); } catch (err) { setDialogMessage(err.message, "error"); }
@@ -232,6 +236,7 @@ async function loadResources() {
function renderAccount(session) {
currentSession = session;
guestAccount.hidden = Boolean(session);
userAccount.hidden = !session;
if (session) userLabel.textContent = `Signed in as ${session.nickname}`;
@@ -252,6 +257,43 @@ if (identityDialog) {
authDialog.setMode("register");
identityDialog.showModal();
});
document.querySelector("#footer-profile")?.addEventListener("click", () => {
document.querySelector("#profile-nickname").value = currentSession?.nickname || "";
document.querySelector("#profile-email").value = "";
document.querySelector("#profile-new-password").value = "";
document.querySelector("#profile-password").value = "";
const profileMessage = document.querySelector("#profile-message");
profileMessage.textContent = "";
profileMessage.classList.remove("success", "error");
const directoryManaged = Boolean(currentSession?.directory_managed);
document.querySelector("#profile-copy").textContent = directoryManaged
? "Directory account details are read-only. You can change only the displayed nickname."
: "Manage your local RustPad account.";
document.querySelectorAll("[data-local-profile-field]").forEach(element => { element.hidden = directoryManaged; });
document.querySelectorAll("[data-directory-profile-field]").forEach(element => { element.hidden = !directoryManaged; });
document.querySelector("#profile-directory-name").value = currentSession?.directory_display_name || "";
document.querySelector("#profile-directory-organization").value = currentSession?.directory_organization || "";
const suggestion = currentSession?.suggested_nickname;
const suggestionElement = document.querySelector("#profile-nickname-suggestion");
suggestionElement.textContent = suggestion ? `Suggested nickname: ${suggestion}` : "No automatic nickname suggestion is available.";
document.querySelector("#profile-password").required = !directoryManaged;
profileDialog.showModal();
});
document.querySelector("#close-profile")?.addEventListener("click", () => profileDialog.close());
profileDialog?.addEventListener("click", event => { if (event.target === profileDialog) profileDialog.close(); });
profileForm?.addEventListener("submit", async event => {
event.preventDefault(); const message=document.querySelector("#profile-message"); message.textContent=""; message.classList.remove("success", "error");
try { const result=await api("/api/auth/profile",{method:"POST",headers:authHeaders(),body:JSON.stringify({nickname:document.querySelector("#profile-nickname").value.trim(),new_email:currentSession?.directory_managed?null:(document.querySelector("#profile-email").value.trim()||null),new_password:currentSession?.directory_managed?null:(document.querySelector("#profile-new-password").value||null),password:currentSession?.directory_managed?"":document.querySelector("#profile-password").value})}); message.textContent=result.message; message.classList.add("success"); currentSession.nickname=result.nickname; renderAccount(currentSession); } catch(e){ message.textContent=e.message; message.classList.add("error"); }
});
document.querySelector("#profile-delete")?.addEventListener("click", async () => {
const message=document.querySelector("#profile-message"); const password=document.querySelector("#profile-password").value;
message.classList.remove("success", "error");
if (!password) { message.textContent="Enter the current password first."; message.classList.add("error"); return; }
if (!confirm("Send an e-mail link to permanently delete this account?")) return;
try { const result=await api("/api/auth/account/delete",{method:"POST",headers:authHeaders(),body:JSON.stringify({password})}); message.textContent=result.message; message.classList.add("success"); } catch(e){ message.textContent=e.message; message.classList.add("error"); }
});
document.querySelector("#footer-resources")?.addEventListener("click", async () => { resourcesDialog.showModal(); await loadResources(); });
document.querySelector("#close-resources")?.addEventListener("click", () => resourcesDialog.close());
resourcesDialog?.addEventListener("click", (event) => { if (event.target === resourcesDialog) resourcesDialog.close(); });