Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
50 changes: 49 additions & 1 deletion crates/email/src/mailer.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
// Copyright 2025, 2026 Element Creations Ltd.
// Copyright 2024, 2025 New Vector Ltd.
// Copyright 2022-2024 The Matrix.org Foundation C.I.C.
//
Expand All @@ -10,7 +11,9 @@ use lettre::{
AsyncTransport, Message,
message::{Mailbox, MessageBuilder, MultiPart},
};
use mas_templates::{EmailRecoveryContext, EmailVerificationContext, Templates, WithLanguage};
use mas_templates::{
EmailInviteContext, EmailRecoveryContext, EmailVerificationContext, Templates, WithLanguage,
};
use thiserror::Error;

use crate::MailTransport;
Expand Down Expand Up @@ -102,6 +105,28 @@ impl Mailer {
Ok(message)
}

fn prepare_invite_email(
&self,
to: Mailbox,
context: &WithLanguage<EmailInviteContext>,
) -> Result<Message, Error> {
let plain = self.templates.render_email_invite_txt(context)?;

let html = self.templates.render_email_invite_html(context)?;

let multipart = MultiPart::alternative_plain_html(plain, html);

let subject = self.templates.render_email_invite_subject(context)?;

let message = self
.base_message()
.subject(subject.trim())
.to(to)
.multipart(multipart)?;

Ok(message)
}

/// Send the verification email to a user
///
/// # Errors
Expand Down Expand Up @@ -150,6 +175,29 @@ impl Mailer {
Ok(())
}

/// Send an invite email
///
/// # Errors
///
/// Will return `Err` if the email failed rendering or failed sending
#[tracing::instrument(
name = "email.invite.send",
skip_all,
fields(
email.to = %to,
email.language = %context.language(),
),
)]
pub async fn send_invite_email(
&self,
to: Mailbox,
context: &WithLanguage<EmailInviteContext>,
) -> Result<(), Error> {
let message = self.prepare_invite_email(to, context)?;
self.transport.send(message).await?;
Ok(())
}

/// Test the connetion to the mail server
///
/// # Errors
Expand Down
3 changes: 2 additions & 1 deletion crates/handlers/src/admin/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ use mas_axum_utils::InternalError;
use mas_data_model::{AppVersion, BoxRng, SiteConfig};
use mas_http::CorsLayerExt;
use mas_matrix::HomeserverConnection;
use mas_policy::PolicyFactory;
use mas_policy::{Policy, PolicyFactory};
use mas_router::{
ApiDoc, ApiDocCallback, OAuth2AuthorizationEndpoint, OAuth2TokenEndpoint, Route, SimpleRoute,
UrlBuilder,
Expand Down Expand Up @@ -167,6 +167,7 @@ where
PasswordManager: FromRef<S>,
BoxRng: FromRequestParts<S>,
CallContext: FromRequestParts<S>,
Policy: FromRequestParts<S>,
Templates: FromRef<S>,
UrlBuilder: FromRef<S>,
Arc<PolicyFactory>: FromRef<S>,
Expand Down
7 changes: 7 additions & 0 deletions crates/handlers/src/admin/model.rs
Original file line number Diff line number Diff line change
Expand Up @@ -647,6 +647,10 @@ pub struct UserRegistrationToken {
/// identity is then established by verifying their email address.
passwordless: bool,

/// A room opened once the user has registered or signed in through the
/// token's link. If null, the link opens no room.
room_id: Option<String>,

/// Maximum number of times this token can be used
usage_limit: Option<u32>,

Expand Down Expand Up @@ -684,6 +688,7 @@ impl UserRegistrationToken {
username: token.username,
email: token.email,
passwordless: token.passwordless,
room_id: token.room_id,
usage_limit: token.usage_limit,
times_used: token.times_used,
created_at: token.created_at,
Expand Down Expand Up @@ -715,6 +720,7 @@ impl UserRegistrationToken {
username: None,
email: None,
passwordless: false,
room_id: None,
usage_limit: Some(10),
times_used: 5,
created_at: DateTime::default(),
Expand All @@ -730,6 +736,7 @@ impl UserRegistrationToken {
username: None,
email: None,
passwordless: false,
room_id: None,
usage_limit: None,
times_used: 0,
created_at: DateTime::default(),
Expand Down
10 changes: 9 additions & 1 deletion crates/handlers/src/admin/v1/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ use aide::axum::{
use axum::extract::{FromRef, FromRequestParts};
use mas_data_model::{AppVersion, BoxRng, SiteConfig};
use mas_matrix::HomeserverConnection;
use mas_policy::PolicyFactory;
use mas_policy::{Policy, PolicyFactory};
use mas_router::UrlBuilder;

use super::call_context::CallContext;
Expand Down Expand Up @@ -45,6 +45,7 @@ where
UrlBuilder: FromRef<S>,
BoxRng: FromRequestParts<S>,
CallContext: FromRequestParts<S>,
Policy: FromRequestParts<S>,
{
ApiRouter::<S>::new()
.api_route(
Expand Down Expand Up @@ -210,6 +211,13 @@ where
self::user_registration_tokens::add_doc,
),
)
.api_route(
"/user-registration-tokens/invite-emails",
post_with(
self::user_registration_tokens::invite_emails,
self::user_registration_tokens::invite_emails_doc,
),
)
.api_route(
"/user-registration-tokens/{id}",
get_with(
Expand Down
Loading
Loading