From ec52428e0654fb2d7bfffba9d331b2347514fdb4 Mon Sep 17 00:00:00 2001 From: Ginger Date: Sun, 22 Mar 2026 19:34:13 -0400 Subject: [PATCH] feat: Add a webpage for threepid validation links --- src/service/firstrun/mod.rs | 2 +- src/service/mailer/messages.rs | 8 ++-- .../mail/{_base.txt.j2 => _base.txt} | 0 .../{change_email.txt.j2 => change_email.txt} | 2 +- .../{new_account.txt.j2 => new_account.txt} | 2 +- ...ssword_reset.txt.j2 => password_reset.txt} | 2 +- .../templates/mail/{test.txt.j2 => test.txt} | 2 +- .../templates/{welcome.md.j2 => welcome.md} | 0 src/web/mod.rs | 1 + src/web/pages/index.rs | 9 ++--- src/web/pages/mod.rs | 3 ++ src/web/pages/password_reset.rs | 11 +++--- .../templates/threepid_validation.html.j2 | 8 ++++ src/web/pages/threepid.rs | 39 +++++++++++++++++++ 14 files changed, 68 insertions(+), 21 deletions(-) rename src/service/templates/mail/{_base.txt.j2 => _base.txt} (100%) rename src/service/templates/mail/{change_email.txt.j2 => change_email.txt} (92%) rename src/service/templates/mail/{new_account.txt.j2 => new_account.txt} (92%) rename src/service/templates/mail/{password_reset.txt.j2 => password_reset.txt} (93%) rename src/service/templates/mail/{test.txt.j2 => test.txt} (76%) rename src/service/templates/{welcome.md.j2 => welcome.md} (100%) create mode 100644 src/web/pages/templates/threepid_validation.html.j2 create mode 100644 src/web/pages/threepid.rs diff --git a/src/service/firstrun/mod.rs b/src/service/firstrun/mod.rs index 146d4228f..c4aa1ac29 100644 --- a/src/service/firstrun/mod.rs +++ b/src/service/firstrun/mod.rs @@ -122,7 +122,7 @@ impl Service { /// if they were not. pub async fn empower_first_user(&self, user: &UserId) -> Result { #[derive(Template)] - #[template(path = "welcome.md.j2")] + #[template(path = "welcome.md")] struct WelcomeMessage<'a> { config: &'a Dep, domain: &'a str, diff --git a/src/service/mailer/messages.rs b/src/service/mailer/messages.rs index 2f5047825..441c38490 100644 --- a/src/service/mailer/messages.rs +++ b/src/service/mailer/messages.rs @@ -6,7 +6,7 @@ pub trait MessageTemplate: Template { } #[derive(Template)] -#[template(path = "mail/change_email.txt.j2")] +#[template(path = "mail/change_email.txt")] pub struct ChangeEmail<'a> { pub user_id: &'a UserId, pub verification_link: String, @@ -17,7 +17,7 @@ impl MessageTemplate for ChangeEmail<'_> { } #[derive(Template)] -#[template(path = "mail/new_account.txt.j2")] +#[template(path = "mail/new_account.txt")] pub struct NewAccount<'a> { pub server_name: &'a str, pub verification_link: String, @@ -28,7 +28,7 @@ impl MessageTemplate for NewAccount<'_> { } #[derive(Template)] -#[template(path = "mail/password_reset.txt.j2")] +#[template(path = "mail/password_reset.txt")] pub struct PasswordReset<'a> { pub display_name: Option<&'a str>, pub user_id: &'a UserId, @@ -40,7 +40,7 @@ impl MessageTemplate for PasswordReset<'_> { } #[derive(Template)] -#[template(path = "mail/test.txt.j2")] +#[template(path = "mail/test.txt")] pub struct Test; impl MessageTemplate for Test { diff --git a/src/service/templates/mail/_base.txt.j2 b/src/service/templates/mail/_base.txt similarity index 100% rename from src/service/templates/mail/_base.txt.j2 rename to src/service/templates/mail/_base.txt diff --git a/src/service/templates/mail/change_email.txt.j2 b/src/service/templates/mail/change_email.txt similarity index 92% rename from src/service/templates/mail/change_email.txt.j2 rename to src/service/templates/mail/change_email.txt index 8168e7c60..1b744cdc8 100644 --- a/src/service/templates/mail/change_email.txt.j2 +++ b/src/service/templates/mail/change_email.txt @@ -1,4 +1,4 @@ -{% extends "_base.txt.j2" %} +{% extends "_base.txt" %} {% block content -%} Hello! diff --git a/src/service/templates/mail/new_account.txt.j2 b/src/service/templates/mail/new_account.txt similarity index 92% rename from src/service/templates/mail/new_account.txt.j2 rename to src/service/templates/mail/new_account.txt index 258390b30..032b78fef 100644 --- a/src/service/templates/mail/new_account.txt.j2 +++ b/src/service/templates/mail/new_account.txt @@ -1,4 +1,4 @@ -{% extends "_base.txt.j2" %} +{% extends "_base.txt" %} {% block content -%} Hello! diff --git a/src/service/templates/mail/password_reset.txt.j2 b/src/service/templates/mail/password_reset.txt similarity index 93% rename from src/service/templates/mail/password_reset.txt.j2 rename to src/service/templates/mail/password_reset.txt index 532623305..e3b954cce 100644 --- a/src/service/templates/mail/password_reset.txt.j2 +++ b/src/service/templates/mail/password_reset.txt @@ -1,4 +1,4 @@ -{% extends "_base.txt.j2" %} +{% extends "_base.txt" %} {% block content -%} {%- if let Some(display_name) = display_name -%} diff --git a/src/service/templates/mail/test.txt.j2 b/src/service/templates/mail/test.txt similarity index 76% rename from src/service/templates/mail/test.txt.j2 rename to src/service/templates/mail/test.txt index 9bd09868e..8b4b8f1fd 100644 --- a/src/service/templates/mail/test.txt.j2 +++ b/src/service/templates/mail/test.txt @@ -1,4 +1,4 @@ -{% extends "_base.txt.j2" %} +{% extends "_base.txt" %} {% block content -%} If you're seeing this, SMTP is configured correctly. :3 diff --git a/src/service/templates/welcome.md.j2 b/src/service/templates/welcome.md similarity index 100% rename from src/service/templates/welcome.md.j2 rename to src/service/templates/welcome.md diff --git a/src/web/mod.rs b/src/web/mod.rs index c5932a130..c6864a99b 100644 --- a/src/web/mod.rs +++ b/src/web/mod.rs @@ -90,6 +90,7 @@ pub fn build() -> Router { .merge(resources::build()) .merge(password_reset::build()) .merge(debug::build()) + .merge(threepid::build()) .fallback(async || WebError::NotFound), ) .layer(CatchPanicLayer::custom(|panic: Box| { diff --git a/src/web/pages/index.rs b/src/web/pages/index.rs index 6070dac2f..fa4b52973 100644 --- a/src/web/pages/index.rs +++ b/src/web/pages/index.rs @@ -1,17 +1,14 @@ -use askama::Template; use axum::{Router, extract::State, response::IntoResponse, routing::get}; use crate::{WebError, template}; pub(crate) fn build() -> Router { Router::new() - .route("/", get(index_handler)) - .route("/_continuwuity/", get(index_handler)) + .route("/", get(index)) + .route("/_continuwuity/", get(index)) } -async fn index_handler( - State(services): State, -) -> Result { +async fn index(State(services): State) -> Result { template! { struct Index<'a> use "index.html.j2" { server_name: &'a str, diff --git a/src/web/pages/mod.rs b/src/web/pages/mod.rs index 58977fd06..e2bfacd46 100644 --- a/src/web/pages/mod.rs +++ b/src/web/pages/mod.rs @@ -3,6 +3,7 @@ pub(super) mod debug; pub(super) mod index; pub(super) mod password_reset; pub(super) mod resources; +pub(super) mod threepid; #[derive(Debug)] pub(crate) struct TemplateContext { @@ -43,6 +44,8 @@ macro_rules! template { #[allow(single_use_lifetimes)] impl$(<$lifetime>)? axum::response::IntoResponse for $name$(<$lifetime>)? { fn into_response(self) -> axum::response::Response { + use askama::Template; + match self.render() { Ok(rendered) => axum::response::Html(rendered).into_response(), Err(err) => $crate::WebError::from(err).into_response() diff --git a/src/web/pages/password_reset.rs b/src/web/pages/password_reset.rs index 1c33f0f8f..fc6eb7bf7 100644 --- a/src/web/pages/password_reset.rs +++ b/src/web/pages/password_reset.rs @@ -1,4 +1,3 @@ -use askama::Template; use axum::{ Router, extract::{ @@ -20,11 +19,6 @@ use crate::{ const INVALID_TOKEN_ERROR: &str = "Invalid reset token. Your reset link may have expired."; -#[derive(Deserialize)] -struct PasswordResetQuery { - token: String, -} - template! { struct PasswordReset<'a> use "password_reset.html.j2" { user_card: UserCard<'a>, @@ -63,6 +57,11 @@ pub(crate) fn build() -> Router { .route("/account/reset_password", get(get_password_reset).post(post_password_reset)) } +#[derive(Deserialize)] +struct PasswordResetQuery { + token: String, +} + async fn password_reset_form( services: crate::State, query: PasswordResetQuery, diff --git a/src/web/pages/templates/threepid_validation.html.j2 b/src/web/pages/templates/threepid_validation.html.j2 new file mode 100644 index 000000000..fbf0a2540 --- /dev/null +++ b/src/web/pages/templates/threepid_validation.html.j2 @@ -0,0 +1,8 @@ +{% extends "_layout.html.j2" %} + +{%- block content -%} +
+

Email verification

+

Your email address has been verified. Return to your Matrix client to continue.

+
+{%- endblock content -%} diff --git a/src/web/pages/threepid.rs b/src/web/pages/threepid.rs new file mode 100644 index 000000000..b660f11a7 --- /dev/null +++ b/src/web/pages/threepid.rs @@ -0,0 +1,39 @@ +use axum::{ + Router, + extract::{Query, State, rejection::QueryRejection}, + response::IntoResponse, + routing::get, +}; +use ruma::OwnedSessionId; +use serde::Deserialize; + +use crate::{WebError, template}; + +template! { + struct ThreepidValidation use "threepid_validation.html.j2" {} +} + +pub(crate) fn build() -> Router { + Router::new().route("/3pid/email/validate", get(threepid_validation)) +} + +#[derive(Deserialize)] +struct ThreepidValidationQuery { + session: OwnedSessionId, + token: String, +} + +async fn threepid_validation( + State(services): State, + query: Result, QueryRejection>, +) -> Result { + let Query(query) = query?; + + services + .threepid + .try_validate_session(&query.session, &query.token) + .await + .map_err(|message| WebError::BadRequest(message.into_owned()))?; + + Ok(ThreepidValidation::new(&services)) +}