paths in mail
This commit is contained in:
Generated
+1
-1
@@ -2581,7 +2581,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "rustpad"
|
||||
version = "0.1.12"
|
||||
version = "0.1.13"
|
||||
dependencies = [
|
||||
"argon2",
|
||||
"aws-config",
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "rustpad"
|
||||
version = "0.1.12"
|
||||
version = "0.1.13"
|
||||
edition = "2024"
|
||||
rust-version = "1.94"
|
||||
description = "Collaborative Markdown notepad built with Axum, WebSockets and SQLite, PostgreSQL and MySQL"
|
||||
|
||||
@@ -43,6 +43,9 @@ pub fn router(
|
||||
|
||||
Router::new()
|
||||
.route("/", get(home))
|
||||
.route("/auth/confirm/{token}", get(home))
|
||||
.route("/auth/reset-password/{token}", get(home))
|
||||
.route("/auth/account-action/{token}", get(home))
|
||||
.route("/p/{slug}", get(pad))
|
||||
.route("/s/{token}", get(public_page))
|
||||
.route("/w/{workspace_slug}", get(workspace))
|
||||
|
||||
+3
-3
@@ -883,7 +883,7 @@ async fn send_account_action(
|
||||
token: &str,
|
||||
) -> Result<(), AuthError> {
|
||||
let site = smtp.public_url.trim_end_matches('/');
|
||||
let url = format!("{site}/?account_action_token={token}");
|
||||
let url = format!("{site}/auth/account-action/{token}");
|
||||
let sender = smtp
|
||||
.from
|
||||
.parse::<Mailbox>()
|
||||
@@ -1954,7 +1954,7 @@ async fn send_registration_email(
|
||||
|
||||
let message = match token {
|
||||
Some(token) => {
|
||||
let confirmation_url = format!("{site}/?confirm_token={token}");
|
||||
let confirmation_url = format!("{site}/auth/confirm/{token}");
|
||||
let subject = "Confirm your RustPad account";
|
||||
let text_body = format!(
|
||||
"Hello {},\n\nYour RustPad account has been created.\nNickname: {}\nSite: {}\n\nConfirm the account within 24 hours by opening this link:\n{}\n",
|
||||
@@ -2142,7 +2142,7 @@ async fn send_message(smtp: &SmtpConfig, message: Message, label: &str) -> Resul
|
||||
|
||||
async fn send_reset(smtp: &SmtpConfig, user: &User, token: &str) -> Result<(), AuthError> {
|
||||
let site = smtp.public_url.trim_end_matches('/');
|
||||
let reset_url = format!("{site}/?reset_token={token}");
|
||||
let reset_url = format!("{site}/auth/reset-password/{token}");
|
||||
let sender = smtp
|
||||
.from
|
||||
.parse::<Mailbox>()
|
||||
|
||||
+50
-13
@@ -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
@@ -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");
|
||||
|
||||
Reference in New Issue
Block a user