mirror of
https://forgejo.ellis.link/continuwuation/continuwuity.git
synced 2026-05-26 20:49:55 +00:00
feat: Implement mailer service for sending emails
This commit is contained in:
Generated
+123
@@ -72,6 +72,12 @@ dependencies = [
|
|||||||
"alloc-no-stdlib",
|
"alloc-no-stdlib",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "allocator-api2"
|
||||||
|
version = "0.2.21"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "android_system_properties"
|
name = "android_system_properties"
|
||||||
version = "0.1.5"
|
version = "0.1.5"
|
||||||
@@ -113,6 +119,15 @@ version = "1.0.102"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "7f202df86484c868dbad7eaa557ef785d5c66295e41b460ef922eca0723b842c"
|
checksum = "7f202df86484c868dbad7eaa557ef785d5c66295e41b460ef922eca0723b842c"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "ar_archive_writer"
|
||||||
|
version = "0.5.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7eb93bbb63b9c227414f6eb3a0adfddca591a8ce1e9b60661bb08969b87e340b"
|
||||||
|
dependencies = [
|
||||||
|
"object",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "arbitrary"
|
name = "arbitrary"
|
||||||
version = "1.4.2"
|
version = "1.4.2"
|
||||||
@@ -820,6 +835,16 @@ dependencies = [
|
|||||||
"num-traits",
|
"num-traits",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "chumsky"
|
||||||
|
version = "0.9.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "8eebd66744a15ded14960ab4ccdbfb51ad3b81f51f3f04a80adac98c985396c9"
|
||||||
|
dependencies = [
|
||||||
|
"hashbrown 0.14.5",
|
||||||
|
"stacker",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "clang-sys"
|
name = "clang-sys"
|
||||||
version = "1.8.1"
|
version = "1.8.1"
|
||||||
@@ -1034,6 +1059,7 @@ dependencies = [
|
|||||||
"hyper-util",
|
"hyper-util",
|
||||||
"ipaddress",
|
"ipaddress",
|
||||||
"itertools 0.14.0",
|
"itertools 0.14.0",
|
||||||
|
"lettre",
|
||||||
"libc",
|
"libc",
|
||||||
"libloading 0.9.0",
|
"libloading 0.9.0",
|
||||||
"lock_api",
|
"lock_api",
|
||||||
@@ -1150,6 +1176,7 @@ dependencies = [
|
|||||||
"ipaddress",
|
"ipaddress",
|
||||||
"itertools 0.14.0",
|
"itertools 0.14.0",
|
||||||
"ldap3",
|
"ldap3",
|
||||||
|
"lettre",
|
||||||
"log",
|
"log",
|
||||||
"loole",
|
"loole",
|
||||||
"lru-cache",
|
"lru-cache",
|
||||||
@@ -1757,6 +1784,22 @@ dependencies = [
|
|||||||
"serde",
|
"serde",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "email-encoding"
|
||||||
|
version = "0.4.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "9298e6504d9b9e780ed3f7dfd43a61be8cd0e09eb07f7706a945b0072b6670b6"
|
||||||
|
dependencies = [
|
||||||
|
"base64 0.22.1",
|
||||||
|
"memchr",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "email_address"
|
||||||
|
version = "0.2.9"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e079f19b08ca6239f47f8ba8509c11cf3ea30095831f7fed61441475edd8c449"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "encoding_rs"
|
name = "encoding_rs"
|
||||||
version = "0.8.35"
|
version = "0.8.35"
|
||||||
@@ -2228,6 +2271,16 @@ version = "0.1.2+12"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "647deb1583b14d160f85f3ff626f20b6edd366e3852c9843b06077388f794cb6"
|
checksum = "647deb1583b14d160f85f3ff626f20b6edd366e3852c9843b06077388f794cb6"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "hashbrown"
|
||||||
|
version = "0.14.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1"
|
||||||
|
dependencies = [
|
||||||
|
"ahash",
|
||||||
|
"allocator-api2",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "hashbrown"
|
name = "hashbrown"
|
||||||
version = "0.15.5"
|
version = "0.15.5"
|
||||||
@@ -2899,6 +2952,37 @@ version = "0.5.3"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "7a79a3332a6609480d7d0c9eab957bca6b455b91bb84e66d19f5ff66294b85b8"
|
checksum = "7a79a3332a6609480d7d0c9eab957bca6b455b91bb84e66d19f5ff66294b85b8"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "lettre"
|
||||||
|
version = "0.11.19"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "9e13e10e8818f8b2a60f52cb127041d388b89f3a96a62be9ceaffa22262fef7f"
|
||||||
|
dependencies = [
|
||||||
|
"async-trait",
|
||||||
|
"base64 0.22.1",
|
||||||
|
"chumsky",
|
||||||
|
"email-encoding",
|
||||||
|
"email_address",
|
||||||
|
"fastrand",
|
||||||
|
"futures-io",
|
||||||
|
"futures-util",
|
||||||
|
"hostname",
|
||||||
|
"httpdate",
|
||||||
|
"idna",
|
||||||
|
"mime",
|
||||||
|
"nom 8.0.0",
|
||||||
|
"percent-encoding",
|
||||||
|
"quoted_printable",
|
||||||
|
"rustls",
|
||||||
|
"rustls-native-certs",
|
||||||
|
"serde",
|
||||||
|
"socket2 0.6.3",
|
||||||
|
"tokio",
|
||||||
|
"tokio-rustls",
|
||||||
|
"tracing",
|
||||||
|
"url",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "libc"
|
name = "libc"
|
||||||
version = "0.2.183"
|
version = "0.2.183"
|
||||||
@@ -4023,6 +4107,16 @@ dependencies = [
|
|||||||
"prost",
|
"prost",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "psm"
|
||||||
|
version = "0.1.30"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "3852766467df634d74f0b2d7819bf8dc483a0eb2e3b0f50f756f9cfe8b0d18d8"
|
||||||
|
dependencies = [
|
||||||
|
"ar_archive_writer",
|
||||||
|
"cc",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "pulldown-cmark"
|
name = "pulldown-cmark"
|
||||||
version = "0.13.1"
|
version = "0.13.1"
|
||||||
@@ -4127,6 +4221,12 @@ dependencies = [
|
|||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "quoted_printable"
|
||||||
|
version = "0.5.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "640c9bd8497b02465aeef5375144c26062e0dcd5939dfcbb0f5db76cb8c17c73"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "r-efi"
|
name = "r-efi"
|
||||||
version = "5.3.0"
|
version = "5.3.0"
|
||||||
@@ -5271,6 +5371,20 @@ version = "1.2.1"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "6ce2be8dc25455e1f91df71bfa12ad37d7af1092ae736f3a6cd0e37bc7810596"
|
checksum = "6ce2be8dc25455e1f91df71bfa12ad37d7af1092ae736f3a6cd0e37bc7810596"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "stacker"
|
||||||
|
version = "0.1.23"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "08d74a23609d509411d10e2176dc2a4346e3b4aea2e7b1869f19fdedbc71c013"
|
||||||
|
dependencies = [
|
||||||
|
"cc",
|
||||||
|
"cfg-if",
|
||||||
|
"libc",
|
||||||
|
"psm",
|
||||||
|
"windows-sys 0.52.0",
|
||||||
|
"windows-sys 0.59.0",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "strict"
|
name = "strict"
|
||||||
version = "0.2.0"
|
version = "0.2.0"
|
||||||
@@ -6406,6 +6520,15 @@ dependencies = [
|
|||||||
"windows-targets 0.52.6",
|
"windows-targets 0.52.6",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows-sys"
|
||||||
|
version = "0.59.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b"
|
||||||
|
dependencies = [
|
||||||
|
"windows-targets 0.52.6",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows-sys"
|
name = "windows-sys"
|
||||||
version = "0.60.2"
|
version = "0.60.2"
|
||||||
|
|||||||
+5
-1
@@ -556,6 +556,11 @@ version = "1.0.1"
|
|||||||
[workspace.dependencies.askama]
|
[workspace.dependencies.askama]
|
||||||
version = "0.15.0"
|
version = "0.15.0"
|
||||||
|
|
||||||
|
[workspace.dependencies.lettre]
|
||||||
|
version = "0.11.19"
|
||||||
|
default-features = false
|
||||||
|
features = ["smtp-transport", "pool", "hostname", "builder", "rustls", "aws-lc-rs", "rustls-native-certs", "tokio1", "tokio1-rustls", "tracing", "serde"]
|
||||||
|
|
||||||
#
|
#
|
||||||
# Patches
|
# Patches
|
||||||
#
|
#
|
||||||
@@ -916,7 +921,6 @@ fn_to_numeric_cast_any = "warn"
|
|||||||
format_push_string = "warn"
|
format_push_string = "warn"
|
||||||
get_unwrap = "warn"
|
get_unwrap = "warn"
|
||||||
impl_trait_in_params = "warn"
|
impl_trait_in_params = "warn"
|
||||||
let_underscore_untyped = "warn"
|
|
||||||
lossy_float_literal = "warn"
|
lossy_float_literal = "warn"
|
||||||
mem_forget = "warn"
|
mem_forget = "warn"
|
||||||
missing_assert_message = "warn"
|
missing_assert_message = "warn"
|
||||||
|
|||||||
@@ -2041,3 +2041,27 @@
|
|||||||
# web->synapseHTTPAntispam->authorization
|
# web->synapseHTTPAntispam->authorization
|
||||||
#
|
#
|
||||||
#secret =
|
#secret =
|
||||||
|
|
||||||
|
#[global.smtp]
|
||||||
|
|
||||||
|
# A `smtp://`` URI which will be used to connect to a mail server. Setting
|
||||||
|
# this option enables features which depend on the ability to send email,
|
||||||
|
# such as self-service password resets.
|
||||||
|
#
|
||||||
|
# For most modern mail servers, format the URI like this:
|
||||||
|
# `smtps://username:password@hostname:port`
|
||||||
|
#
|
||||||
|
# For a guide on the accepted URI syntax, consult Lettre's documentation:
|
||||||
|
# https://docs.rs/lettre/latest/lettre/transport/smtp/struct.AsyncSmtpTransport.html#method.from_url
|
||||||
|
#
|
||||||
|
#connection_uri =
|
||||||
|
|
||||||
|
# The outgoing address which will be used for sending emails.
|
||||||
|
#
|
||||||
|
# For a syntax guide, see https://datatracker.ietf.org/doc/html/rfc2822#section-3.4
|
||||||
|
#
|
||||||
|
# ...or if you don't want to read the RFC, for some reason:
|
||||||
|
# - `Name <address@domain.org>` to specify a sender name
|
||||||
|
# - `address@domain.org` to not use a name
|
||||||
|
#
|
||||||
|
#sender =
|
||||||
|
|||||||
@@ -84,6 +84,7 @@ libc.workspace = true
|
|||||||
libloading.workspace = true
|
libloading.workspace = true
|
||||||
libloading.optional = true
|
libloading.optional = true
|
||||||
log.workspace = true
|
log.workspace = true
|
||||||
|
lettre.workspace = true
|
||||||
num-traits.workspace = true
|
num-traits.workspace = true
|
||||||
rand.workspace = true
|
rand.workspace = true
|
||||||
rand_core = { version = "0.6.4", features = ["getrandom"] }
|
rand_core = { version = "0.6.4", features = ["getrandom"] }
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ use either::{
|
|||||||
};
|
};
|
||||||
use figment::providers::{Env, Format, Toml};
|
use figment::providers::{Env, Format, Toml};
|
||||||
pub use figment::{Figment, value::Value as FigmentValue};
|
pub use figment::{Figment, value::Value as FigmentValue};
|
||||||
|
use lettre::message::Mailbox;
|
||||||
use regex::RegexSet;
|
use regex::RegexSet;
|
||||||
use ruma::{
|
use ruma::{
|
||||||
OwnedRoomId, OwnedRoomOrAliasId, OwnedServerName, OwnedUserId, RoomVersionId,
|
OwnedRoomId, OwnedRoomOrAliasId, OwnedServerName, OwnedUserId, RoomVersionId,
|
||||||
@@ -760,6 +761,9 @@ pub struct Config {
|
|||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
pub well_known: WellKnownConfig,
|
pub well_known: WellKnownConfig,
|
||||||
|
|
||||||
|
/// display: nested
|
||||||
|
pub smtp: Option<SmtpConfig>,
|
||||||
|
|
||||||
/// Enable OpenTelemetry OTLP tracing export. This replaces the deprecated
|
/// Enable OpenTelemetry OTLP tracing export. This replaces the deprecated
|
||||||
/// Jaeger exporter. Traces will be sent via OTLP to a collector (such as
|
/// Jaeger exporter. Traces will be sent via OTLP to a collector (such as
|
||||||
/// Jaeger) that supports the OpenTelemetry Protocol.
|
/// Jaeger) that supports the OpenTelemetry Protocol.
|
||||||
@@ -2444,6 +2448,34 @@ pub struct DraupnirConfig {
|
|||||||
pub secret: String,
|
pub secret: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, Deserialize)]
|
||||||
|
#[config_example_generator(
|
||||||
|
filename = "conduwuit-example.toml",
|
||||||
|
section = "global.smtp",
|
||||||
|
optional = "true"
|
||||||
|
)]
|
||||||
|
pub struct SmtpConfig {
|
||||||
|
/// A `smtp://`` URI which will be used to connect to a mail server. Setting
|
||||||
|
/// this option enables features which depend on the ability to send email,
|
||||||
|
/// such as self-service password resets.
|
||||||
|
///
|
||||||
|
/// For most modern mail servers, format the URI like this:
|
||||||
|
/// `smtps://username:password@hostname:port`
|
||||||
|
///
|
||||||
|
/// For a guide on the accepted URI syntax, consult Lettre's documentation:
|
||||||
|
/// https://docs.rs/lettre/latest/lettre/transport/smtp/struct.AsyncSmtpTransport.html#method.from_url
|
||||||
|
pub connection_uri: String,
|
||||||
|
|
||||||
|
/// The outgoing address which will be used for sending emails.
|
||||||
|
///
|
||||||
|
/// For a syntax guide, see https://datatracker.ietf.org/doc/html/rfc2822#section-3.4
|
||||||
|
///
|
||||||
|
/// ...or if you don't want to read the RFC, for some reason:
|
||||||
|
/// - `Name <address@domain.org>` to specify a sender name
|
||||||
|
/// - `address@domain.org` to not use a name
|
||||||
|
pub sender: Mailbox,
|
||||||
|
}
|
||||||
|
|
||||||
const DEPRECATED_KEYS: &[&str] = &[
|
const DEPRECATED_KEYS: &[&str] = &[
|
||||||
"cache_capacity",
|
"cache_capacity",
|
||||||
"conduit_cache_capacity_modifier",
|
"conduit_cache_capacity_modifier",
|
||||||
|
|||||||
@@ -123,6 +123,7 @@ blurhash.workspace = true
|
|||||||
blurhash.optional = true
|
blurhash.optional = true
|
||||||
recaptcha-verify = { version = "0.2.0", default-features = false }
|
recaptcha-verify = { version = "0.2.0", default-features = false }
|
||||||
yansi.workspace = true
|
yansi.workspace = true
|
||||||
|
lettre.workspace = true
|
||||||
|
|
||||||
[target.'cfg(all(unix, target_os = "linux"))'.dependencies]
|
[target.'cfg(all(unix, target_os = "linux"))'.dependencies]
|
||||||
sd-notify.workspace = true
|
sd-notify.workspace = true
|
||||||
|
|||||||
@@ -0,0 +1,48 @@
|
|||||||
|
use askama::Template;
|
||||||
|
use ruma::UserId;
|
||||||
|
|
||||||
|
pub trait MessageTemplate: Template {
|
||||||
|
fn subject(&self) -> String;
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Template)]
|
||||||
|
#[template(path = "mail/change_email.txt.j2")]
|
||||||
|
pub struct ChangeEmail<'a> {
|
||||||
|
user_id: &'a UserId,
|
||||||
|
verification_link: &'a str,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl MessageTemplate for ChangeEmail<'_> {
|
||||||
|
fn subject(&self) -> String { "Verify your email address".to_owned() }
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Template)]
|
||||||
|
#[template(path = "mail/new_account.txt.j2")]
|
||||||
|
pub struct NewAccount<'a> {
|
||||||
|
server_name: &'a str,
|
||||||
|
verification_link: &'a str,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl MessageTemplate for NewAccount<'_> {
|
||||||
|
fn subject(&self) -> String { "Create your new Matrix account".to_owned() }
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Template)]
|
||||||
|
#[template(path = "mail/password_reset.txt.j2")]
|
||||||
|
pub struct PasswordReset<'a> {
|
||||||
|
display_name: &'a str,
|
||||||
|
user_id: &'a UserId,
|
||||||
|
verification_link: &'a str,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl MessageTemplate for PasswordReset<'_> {
|
||||||
|
fn subject(&self) -> String { format!("Password reset request for {}", &self.user_id) }
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Template)]
|
||||||
|
#[template(path = "mail/test.txt.j2")]
|
||||||
|
pub struct Test;
|
||||||
|
|
||||||
|
impl MessageTemplate for Test {
|
||||||
|
fn subject(&self) -> String { "Test message".to_owned() }
|
||||||
|
}
|
||||||
@@ -0,0 +1,102 @@
|
|||||||
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
use conduwuit::{Err, Result, err, info};
|
||||||
|
use lettre::{
|
||||||
|
AsyncSmtpTransport, AsyncTransport, Tokio1Executor,
|
||||||
|
message::{Mailbox, MessageBuilder},
|
||||||
|
};
|
||||||
|
|
||||||
|
use crate::{Args, mailer::messages::MessageTemplate};
|
||||||
|
|
||||||
|
pub mod messages;
|
||||||
|
|
||||||
|
type Transport = AsyncSmtpTransport<Tokio1Executor>;
|
||||||
|
type TransportError = lettre::transport::smtp::Error;
|
||||||
|
|
||||||
|
pub struct Service {
|
||||||
|
transport: Option<(Mailbox, Transport)>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[async_trait::async_trait]
|
||||||
|
impl crate::Service for Service {
|
||||||
|
fn build(args: Args<'_>) -> Result<Arc<Self>> {
|
||||||
|
let transport = args
|
||||||
|
.server
|
||||||
|
.config
|
||||||
|
.smtp
|
||||||
|
.as_ref()
|
||||||
|
.map(|config| {
|
||||||
|
Ok((config.sender.clone(), Transport::from_url(&config.connection_uri)?.build()))
|
||||||
|
})
|
||||||
|
.transpose()
|
||||||
|
.map_err(|err: TransportError| err!("Failed to set up SMTP transport: {err}"))?;
|
||||||
|
|
||||||
|
Ok(Arc::new(Self { transport }))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn name(&self) -> &str { crate::service::make_name(std::module_path!()) }
|
||||||
|
|
||||||
|
async fn worker(self: Arc<Self>) -> Result<()> {
|
||||||
|
if let Some((_, ref transport)) = self.transport {
|
||||||
|
match transport.test_connection().await {
|
||||||
|
| Ok(true) => {
|
||||||
|
info!("SMTP connection test successful");
|
||||||
|
Ok(())
|
||||||
|
},
|
||||||
|
| Ok(false) => {
|
||||||
|
Err!("SMTP connection test failed")
|
||||||
|
},
|
||||||
|
| Err(err) => {
|
||||||
|
Err!("SMTP connection test failed: {err}")
|
||||||
|
},
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
info!("SMTP is not configured, email functionality will be unavailable");
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Service {
|
||||||
|
/// Returns a mailer which allows email to be sent, if SMTP is configured.
|
||||||
|
#[must_use]
|
||||||
|
pub fn mailer(&self) -> Option<Mailer<'_>> {
|
||||||
|
self.transport
|
||||||
|
.as_ref()
|
||||||
|
.map(|(sender, transport)| Mailer { sender, transport })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Mailer<'a> {
|
||||||
|
sender: &'a Mailbox,
|
||||||
|
transport: &'a Transport,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Mailer<'_> {
|
||||||
|
/// Sends an email.
|
||||||
|
pub async fn send<Template: MessageTemplate>(
|
||||||
|
&self,
|
||||||
|
recipient: Mailbox,
|
||||||
|
message: Template,
|
||||||
|
) -> Result<()> {
|
||||||
|
let subject = message.subject();
|
||||||
|
let body = message
|
||||||
|
.render()
|
||||||
|
.map_err(|err| err!("Failed to render message template: {err}"))?;
|
||||||
|
|
||||||
|
let message = MessageBuilder::new()
|
||||||
|
.from(self.sender.clone())
|
||||||
|
.to(recipient)
|
||||||
|
.subject(subject)
|
||||||
|
.date_now()
|
||||||
|
.body(body)
|
||||||
|
.expect("should have been able to construct message");
|
||||||
|
|
||||||
|
self.transport
|
||||||
|
.send(message)
|
||||||
|
.await
|
||||||
|
.map_err(|err: TransportError| err!("Failed to send message: {err}"))?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -21,6 +21,7 @@ pub mod federation;
|
|||||||
pub mod firstrun;
|
pub mod firstrun;
|
||||||
pub mod globals;
|
pub mod globals;
|
||||||
pub mod key_backups;
|
pub mod key_backups;
|
||||||
|
pub mod mailer;
|
||||||
pub mod media;
|
pub mod media;
|
||||||
pub mod moderation;
|
pub mod moderation;
|
||||||
pub mod password_reset;
|
pub mod password_reset;
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ use tokio::sync::Mutex;
|
|||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
account_data, admin, announcements, antispam, appservice, client, config, emergency,
|
account_data, admin, announcements, antispam, appservice, client, config, emergency,
|
||||||
federation, firstrun, globals, key_backups,
|
federation, firstrun, globals, key_backups, mailer,
|
||||||
manager::Manager,
|
manager::Manager,
|
||||||
media, moderation, password_reset, presence, pusher, registration_tokens, resolver, rooms,
|
media, moderation, password_reset, presence, pusher, registration_tokens, resolver, rooms,
|
||||||
sending, server_keys,
|
sending, server_keys,
|
||||||
@@ -28,6 +28,7 @@ pub struct Services {
|
|||||||
pub key_backups: Arc<key_backups::Service>,
|
pub key_backups: Arc<key_backups::Service>,
|
||||||
pub media: Arc<media::Service>,
|
pub media: Arc<media::Service>,
|
||||||
pub password_reset: Arc<password_reset::Service>,
|
pub password_reset: Arc<password_reset::Service>,
|
||||||
|
pub mailer: Arc<mailer::Service>,
|
||||||
pub presence: Arc<presence::Service>,
|
pub presence: Arc<presence::Service>,
|
||||||
pub pusher: Arc<pusher::Service>,
|
pub pusher: Arc<pusher::Service>,
|
||||||
pub registration_tokens: Arc<registration_tokens::Service>,
|
pub registration_tokens: Arc<registration_tokens::Service>,
|
||||||
@@ -83,6 +84,7 @@ impl Services {
|
|||||||
key_backups: build!(key_backups::Service),
|
key_backups: build!(key_backups::Service),
|
||||||
media: build!(media::Service),
|
media: build!(media::Service),
|
||||||
password_reset: build!(password_reset::Service),
|
password_reset: build!(password_reset::Service),
|
||||||
|
mailer: build!(mailer::Service),
|
||||||
presence: build!(presence::Service),
|
presence: build!(presence::Service),
|
||||||
pusher: build!(pusher::Service),
|
pusher: build!(pusher::Service),
|
||||||
registration_tokens: build!(registration_tokens::Service),
|
registration_tokens: build!(registration_tokens::Service),
|
||||||
|
|||||||
@@ -0,0 +1,3 @@
|
|||||||
|
{%- block content %}{% endblock %}
|
||||||
|
|
||||||
|
Message sent by Continuwuity {{ env!("CARGO_PKG_VERSION") }}. 🐈
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
{% extends "_base.txt.j2" %}
|
||||||
|
|
||||||
|
{% block content -%}
|
||||||
|
Hello!
|
||||||
|
|
||||||
|
Somebody, probably you, tried to associate this email address with the Matrix account {{ user_id }}.
|
||||||
|
If that's your account, and this is your email address, click this link to proceed:
|
||||||
|
{{ verification_link }}
|
||||||
|
Otherwise, you can ignore this email. The above link will expire in one hour.
|
||||||
|
{%- endblock %}
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
{% extends "_base.txt.j2" %}
|
||||||
|
|
||||||
|
{% block content -%}
|
||||||
|
Hello!
|
||||||
|
|
||||||
|
Somebody, probably you, tried to create a Matrix account on {{ server_name }} using this email address.
|
||||||
|
Use the link below to proceed with creating your account:
|
||||||
|
{{ verification_link }}
|
||||||
|
If you are not trying to create an account, you can ignore this email. The above link will expire in one hour.
|
||||||
|
{%- endblock %}
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
{% extends "_base.txt.j2" %}
|
||||||
|
|
||||||
|
{% block content -%}
|
||||||
|
Hello {{ display_name }} ({{ user_id }}),
|
||||||
|
|
||||||
|
Somebody, probably you, tried to reset your Matrix account's password.
|
||||||
|
If you requested for your password to be reset, click this link to proceed:
|
||||||
|
{{ verification_link }}
|
||||||
|
Otherwise, you can ignore this email. The above link will expire in one hour.
|
||||||
|
{%- endblock %}
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
{% extends "_base.txt.j2" %}
|
||||||
|
|
||||||
|
{% block content -%}
|
||||||
|
If you're seeing this, SMTP is configured correctly. :3
|
||||||
|
{%- endblock %}
|
||||||
Reference in New Issue
Block a user