paths in mail

This commit is contained in:
Mateusz Gruszczyński
2026-07-28 10:13:41 +02:00
parent 351dd081b5
commit d38b5b0b51
6 changed files with 60 additions and 23 deletions
+50 -13
View File
@@ -1,6 +1,6 @@
import { api } from "@rustpad/api";
import * as sessionStore from "@rustpad/session";
import { askInput, showMessage } from "@rustpad/modal";
import { askConfirm, askInput, showMessage } from "@rustpad/modal";
const { getAuthToken, setAuthSession, setNickname } = sessionStore;
const clearAuthSession = sessionStore.clearAuthSession || (() => {
@@ -319,28 +319,44 @@ export async function logoutCurrentSession() {
}
export async function handleAccountConfirmationToken() {
function mailActionToken(pathPrefix, legacyQueryName) {
const url = new URL(location.href);
const prefix = `${pathPrefix}/`;
let token = null;
if (url.pathname.startsWith(prefix)) {
const encodedToken = url.pathname.slice(prefix.length).split("/", 1)[0];
try { token = decodeURIComponent(encodedToken); } catch { token = null; }
}
if (!token) token = url.searchParams.get(legacyQueryName);
return token?.trim() || null;
}
function clearMailActionUrl() {
const url = new URL(location.href);
const token = url.searchParams.get("confirm_token");
if (!token) return;
url.searchParams.delete("confirm_token");
history.replaceState({}, "", `${url.pathname}${url.search}${url.hash}`);
url.searchParams.delete("reset_token");
url.searchParams.delete("account_action_token");
history.replaceState({}, "", `/${url.search}${url.hash}`);
}
export async function handleAccountConfirmationToken() {
const token = mailActionToken("/auth/confirm", "confirm_token");
if (!token) return false;
clearMailActionUrl();
try {
const result = await api("/api/auth/confirm-account", { method: "POST", body: JSON.stringify({ token }) });
await showMessage(result.message, { title: "Account confirmed" });
} catch (error) {
await showMessage(error.message, { title: "Account confirmation failed" });
}
return true;
}
export async function handleResetToken() {
const url = new URL(location.href);
const token = url.searchParams.get("reset_token");
if (!token) return;
// Remove the token immediately. Refreshing or navigating back must not reopen the reset dialog.
url.searchParams.delete("reset_token");
history.replaceState({}, "", `${url.pathname}${url.search}${url.hash}`);
const token = mailActionToken("/auth/reset-password", "reset_token");
if (!token) return false;
clearMailActionUrl();
const password = await askInput({
title: "Set a new password",
@@ -352,11 +368,32 @@ export async function handleResetToken() {
confirmText: "Change password",
bitwardenIgnore: true,
});
if (!password) return;
if (!password) return true;
try {
await api("/api/auth/password-reset/confirm", { method: "POST", body: JSON.stringify({ token, password }) });
await showMessage("Password changed. The reset link has been used and cannot be opened again.", { title: "Password changed" });
} catch (error) {
await showMessage(error.message, { title: "Password reset failed" });
}
return true;
}
export async function handleAccountActionToken() {
const token = mailActionToken("/auth/account-action", "account_action_token");
if (!token) return false;
clearMailActionUrl();
const confirmed = await askConfirm(
"Confirm the requested account action. If you did not request it, cancel and ignore the e-mail.",
{ title: "Confirm account action", confirmText: "Confirm action", danger: true },
);
if (!confirmed) return true;
try {
const result = await api("/api/auth/account-action/confirm", { method: "POST", body: JSON.stringify({ token }) });
await showMessage(result.message, { title: "Account action confirmed" });
} catch (error) {
await showMessage(error.message, { title: "Account action failed" });
}
return true;
}
+2 -5
View File
@@ -1,7 +1,7 @@
import { installGlobalDiagnostics, logInfo } from "@rustpad/logger";
installGlobalDiagnostics();
import { bindIdentityDialog, handleAccountConfirmationToken, handleResetToken, logoutCurrentSession, validateCurrentSession } from "@rustpad/auth-ui";
import { bindIdentityDialog, handleAccountActionToken, handleAccountConfirmationToken, handleResetToken, logoutCurrentSession, validateCurrentSession } from "@rustpad/auth-ui";
import { getAuthToken, setAccessToken } from "@rustpad/session";
import { api } from "@rustpad/api";
import { copyText } from "@rustpad/clipboard";
@@ -83,10 +83,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=>toast(r.message)).catch(e=>toast(e.message)).finally(()=>{params.delete("account_action_token");history.replaceState({},"",`${location.pathname}${params.size?`?${params}`:""}${location.hash}`);}); } }
await handleAccountConfirmationToken() || await handleResetToken() || await handleAccountActionToken();
const identityDialog = document.querySelector("#identity-dialog");
const guestAccount = document.querySelector("#footer-account-guest");