Skip to content

Commit 1bc00ce

Browse files
committed
feat!: [#591] extract new types for SMTP server configuration
From: ```toml [mail] email_verification_enabled = false from = "example@email.com" password = "" port = 25 reply_to = "noreply@email.com" server = "" username = "" ``` To: ```toml [mail] email_verification_enabled = false from = "example@email.com" reply_to = "noreply@email.com" [mail.smtp] port = 25 server = "" [mail.smtp.credentials] password = "" username = "" ```
1 parent cd8248a commit 1bc00ce

15 files changed

+188
-68
lines changed

share/default/config/index.container.mysql.toml

+2-1
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ log_level = "info"
44
[database]
55
connect_url = "mysql://root:root_secret_password@mysql:3306/torrust_index"
66

7-
[mail]
7+
[mail.smtp]
88
port = 1025
99
server = "mailcatcher"
10+

share/default/config/index.container.sqlite3.toml

+1-1
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,6 @@ log_level = "info"
44
[database]
55
connect_url = "sqlite:///var/lib/torrust/index/database/sqlite3.db?mode=rwc"
66

7-
[mail]
7+
[mail.smtp]
88
port = 1025
99
server = "mailcatcher"

share/default/config/index.private.e2e.container.sqlite3.toml

+1-1
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,6 @@ url = "http://tracker:7070"
99
[database]
1010
connect_url = "sqlite:///var/lib/torrust/index/database/e2e_testing_sqlite3.db?mode=rwc"
1111

12-
[mail]
12+
[mail.smtp]
1313
port = 1025
1414
server = "mailcatcher"

share/default/config/index.public.e2e.container.mysql.toml

+1-1
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,6 @@ url = "udp://tracker:6969"
88
[database]
99
connect_url = "mysql://root:root_secret_password@mysql:3306/torrust_index_e2e_testing"
1010

11-
[mail]
11+
[mail.smtp]
1212
port = 1025
1313
server = "mailcatcher"

share/default/config/index.public.e2e.container.sqlite3.toml

+1-1
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,6 @@ url = "udp://tracker:6969"
88
[database]
99
connect_url = "sqlite:///var/lib/torrust/index/database/e2e_testing_sqlite3.db?mode=rwc"
1010

11-
[mail]
11+
[mail.smtp]
1212
port = 1025
1313
server = "mailcatcher"

src/bootstrap/logging.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ use std::sync::Once;
1111
use tracing::info;
1212
use tracing::level_filters::LevelFilter;
1313

14-
use crate::config::v1::logging::LogLevel;
14+
use crate::config::LogLevel;
1515

1616
static INIT: Once = Once::new();
1717

src/config/mod.rs

+25-8
Original file line numberDiff line numberDiff line change
@@ -19,18 +19,33 @@ use url::Url;
1919
use crate::web::api::server::DynError;
2020

2121
pub type Settings = v1::Settings;
22+
2223
pub type Api = v1::api::Api;
24+
2325
pub type Auth = v1::auth::Auth;
26+
pub type EmailOnSignup = v1::auth::EmailOnSignup;
27+
pub type SecretKey = v1::auth::SecretKey;
28+
pub type PasswordConstraints = v1::auth::PasswordConstraints;
29+
2430
pub type Database = v1::database::Database;
31+
2532
pub type ImageCache = v1::image_cache::ImageCache;
33+
2634
pub type Mail = v1::mail::Mail;
35+
pub type Smtp = v1::mail::Smtp;
36+
pub type Credentials = v1::mail::Credentials;
37+
2738
pub type Network = v1::net::Network;
39+
2840
pub type TrackerStatisticsImporter = v1::tracker_statistics_importer::TrackerStatisticsImporter;
41+
2942
pub type Tracker = v1::tracker::Tracker;
43+
pub type ApiToken = v1::tracker::ApiToken;
44+
3045
pub type Logging = v1::logging::Logging;
46+
pub type LogLevel = v1::logging::LogLevel;
47+
3148
pub type Website = v1::website::Website;
32-
pub type EmailOnSignup = v1::auth::EmailOnSignup;
33-
pub type PasswordConstraints = v1::auth::PasswordConstraints;
3449

3550
/// Prefix for env vars that overwrite configuration options.
3651
const CONFIG_OVERRIDE_PREFIX: &str = "TORRUST_INDEX_CONFIG_OVERRIDE_";
@@ -321,9 +336,7 @@ mod tests {
321336

322337
use url::Url;
323338

324-
use crate::config::v1::auth::SecretKey;
325-
use crate::config::v1::tracker::ApiToken;
326-
use crate::config::{Configuration, ConfigurationPublic, Info, Settings};
339+
use crate::config::{ApiToken, Configuration, ConfigurationPublic, Info, SecretKey, Settings};
327340

328341
#[cfg(test)]
329342
fn default_config_toml() -> String {
@@ -358,10 +371,14 @@ mod tests {
358371
email_verification_enabled = false
359372
from = "example@email.com"
360373
reply_to = "noreply@email.com"
361-
username = ""
362-
password = ""
363-
server = ""
374+
375+
[mail.smtp]
364376
port = 25
377+
server = ""
378+
379+
[mail.smtp.credentials]
380+
password = ""
381+
username = ""
365382
366383
[image_cache]
367384
max_request_timeout_ms = 1000

src/config/v1/mail.rs

+64-20
Original file line numberDiff line numberDiff line change
@@ -13,18 +13,9 @@ pub struct Mail {
1313
/// The email address to reply to.
1414
#[serde(default = "Mail::default_reply_to")]
1515
pub reply_to: Mailbox,
16-
/// The username to use for SMTP authentication.
17-
#[serde(default = "Mail::default_username")]
18-
pub username: String,
19-
/// The password to use for SMTP authentication.
20-
#[serde(default = "Mail::default_password")]
21-
pub password: String,
22-
/// The SMTP server to use.
23-
#[serde(default = "Mail::default_server")]
24-
pub server: String,
25-
/// The SMTP port to use.
26-
#[serde(default = "Mail::default_port")]
27-
pub port: u16,
16+
/// The SMTP server configuration.
17+
#[serde(default = "Mail::default_smtp")]
18+
pub smtp: Smtp,
2819
}
2920

3021
impl Default for Mail {
@@ -33,10 +24,7 @@ impl Default for Mail {
3324
email_verification_enabled: Self::default_email_verification_enabled(),
3425
from: Self::default_from(),
3526
reply_to: Self::default_reply_to(),
36-
username: Self::default_username(),
37-
password: Self::default_password(),
38-
server: Self::default_server(),
39-
port: Self::default_port(),
27+
smtp: Self::default_smtp(),
4028
}
4129
}
4230
}
@@ -54,19 +42,75 @@ impl Mail {
5442
"noreply@email.com".parse().expect("valid mailbox")
5543
}
5644

57-
fn default_username() -> String {
58-
String::default()
45+
fn default_smtp() -> Smtp {
46+
Smtp::default()
5947
}
48+
}
6049

61-
fn default_password() -> String {
62-
String::default()
50+
/// SMTP configuration.
51+
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
52+
pub struct Smtp {
53+
/// The SMTP port to use.
54+
#[serde(default = "Smtp::default_port")]
55+
pub port: u16,
56+
/// The SMTP server to use.
57+
#[serde(default = "Smtp::default_server")]
58+
pub server: String,
59+
/// The SMTP server credentials.
60+
#[serde(default = "Smtp::default_credentials")]
61+
pub credentials: Credentials,
62+
}
63+
64+
impl Default for Smtp {
65+
fn default() -> Self {
66+
Self {
67+
server: Self::default_server(),
68+
port: Self::default_port(),
69+
credentials: Self::default_credentials(),
70+
}
6371
}
72+
}
6473

74+
impl Smtp {
6575
fn default_server() -> String {
6676
String::default()
6777
}
6878

6979
fn default_port() -> u16 {
7080
25
7181
}
82+
83+
fn default_credentials() -> Credentials {
84+
Credentials::default()
85+
}
86+
}
87+
88+
/// SMTP configuration.
89+
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
90+
pub struct Credentials {
91+
/// The password to use for SMTP authentication.
92+
#[serde(default = "Credentials::default_password")]
93+
pub password: String,
94+
/// The username to use for SMTP authentication.
95+
#[serde(default = "Credentials::default_username")]
96+
pub username: String,
97+
}
98+
99+
impl Default for Credentials {
100+
fn default() -> Self {
101+
Self {
102+
username: Self::default_username(),
103+
password: Self::default_password(),
104+
}
105+
}
106+
}
107+
108+
impl Credentials {
109+
fn default_username() -> String {
110+
String::default()
111+
}
112+
113+
fn default_password() -> String {
114+
String::default()
115+
}
72116
}

src/config/v1/mod.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ impl Settings {
6464
if let Some(_password) = self.database.connect_url.password() {
6565
let _ = self.database.connect_url.set_password(Some("***"));
6666
}
67-
"***".clone_into(&mut self.mail.password);
67+
"***".clone_into(&mut self.mail.smtp.credentials.password);
6868
self.auth.secret_key = SecretKey::new("***");
6969
}
7070
}

src/lib.rs

+7-3
Original file line numberDiff line numberDiff line change
@@ -194,10 +194,14 @@
194194
//! email_verification_enabled = false
195195
//! from = "example@email.com"
196196
//! reply_to = "noreply@email.com"
197-
//! username = ""
198-
//! password = ""
199-
//! server = ""
197+
//!
198+
//! [mail.smtp]
200199
//! port = 25
200+
//! server = ""
201+
//!
202+
//! [mail.smtp.credentials]
203+
//! password = ""
204+
//! username = ""
201205
//!
202206
//! [image_cache]
203207
//! max_request_timeout_ms = 1000

src/mailer.rs

+9-6
Original file line numberDiff line numberDiff line change
@@ -70,19 +70,22 @@ impl Service {
7070
async fn get_mailer(cfg: &Configuration) -> Mailer {
7171
let settings = cfg.settings.read().await;
7272

73-
if !settings.mail.username.is_empty() && !settings.mail.password.is_empty() {
73+
if !settings.mail.smtp.credentials.username.is_empty() && !settings.mail.smtp.credentials.password.is_empty() {
7474
// SMTP authentication
75-
let creds = Credentials::new(settings.mail.username.clone(), settings.mail.password.clone());
75+
let creds = Credentials::new(
76+
settings.mail.smtp.credentials.username.clone(),
77+
settings.mail.smtp.credentials.password.clone(),
78+
);
7679

77-
AsyncSmtpTransport::<Tokio1Executor>::builder_dangerous(&settings.mail.server)
78-
.port(settings.mail.port)
80+
AsyncSmtpTransport::<Tokio1Executor>::builder_dangerous(&settings.mail.smtp.server)
81+
.port(settings.mail.smtp.port)
7982
.credentials(creds)
8083
.authentication(vec![Mechanism::Login, Mechanism::Xoauth2, Mechanism::Plain])
8184
.build()
8285
} else {
8386
// SMTP without authentication
84-
AsyncSmtpTransport::<Tokio1Executor>::builder_dangerous(&settings.mail.server)
85-
.port(settings.mail.port)
87+
AsyncSmtpTransport::<Tokio1Executor>::builder_dangerous(&settings.mail.smtp.server)
88+
.port(settings.mail.smtp.port)
8689
.build()
8790
}
8891
}

src/web/api/client/v1/contexts/settings/mod.rs

+36-9
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,10 @@ use url::Url;
55

66
use crate::config::v1::tracker::ApiToken;
77
use crate::config::{
8-
Api as DomainApi, Auth as DomainAuth, Database as DomainDatabase, ImageCache as DomainImageCache, Mail as DomainMail,
9-
Network as DomainNetwork, PasswordConstraints as DomainPasswordConstraints, Settings as DomainSettings,
10-
Tracker as DomainTracker, TrackerStatisticsImporter as DomainTrackerStatisticsImporter, Website as DomainWebsite,
8+
Api as DomainApi, Auth as DomainAuth, Credentials as DomainCredentials, Database as DomainDatabase,
9+
ImageCache as DomainImageCache, Mail as DomainMail, Network as DomainNetwork,
10+
PasswordConstraints as DomainPasswordConstraints, Settings as DomainSettings, Smtp as DomainSmtp, Tracker as DomainTracker,
11+
TrackerStatisticsImporter as DomainTrackerStatisticsImporter, Website as DomainWebsite,
1112
};
1213

1314
#[derive(Deserialize, Serialize, PartialEq, Debug, Clone)]
@@ -66,10 +67,20 @@ pub struct Mail {
6667
pub email_verification_enabled: bool,
6768
pub from: String,
6869
pub reply_to: String,
69-
pub username: String,
70-
pub password: String,
70+
pub smtp: Smtp,
71+
}
72+
73+
#[derive(Deserialize, Serialize, PartialEq, Debug, Clone)]
74+
pub struct Smtp {
7175
pub server: String,
7276
pub port: u16,
77+
pub credentials: Credentials,
78+
}
79+
80+
#[derive(Deserialize, Serialize, PartialEq, Debug, Clone)]
81+
pub struct Credentials {
82+
pub username: String,
83+
pub password: String,
7384
}
7485

7586
#[derive(Deserialize, Serialize, PartialEq, Debug, Clone)]
@@ -169,10 +180,26 @@ impl From<DomainMail> for Mail {
169180
email_verification_enabled: mail.email_verification_enabled,
170181
from: mail.from.to_string(),
171182
reply_to: mail.reply_to.to_string(),
172-
username: mail.username,
173-
password: mail.password,
174-
server: mail.server,
175-
port: mail.port,
183+
smtp: Smtp::from(mail.smtp),
184+
}
185+
}
186+
}
187+
188+
impl From<DomainSmtp> for Smtp {
189+
fn from(smtp: DomainSmtp) -> Self {
190+
Self {
191+
server: smtp.server,
192+
port: smtp.port,
193+
credentials: Credentials::from(smtp.credentials),
194+
}
195+
}
196+
}
197+
198+
impl From<DomainCredentials> for Credentials {
199+
fn from(credentials: DomainCredentials) -> Self {
200+
Self {
201+
username: credentials.username,
202+
password: credentials.password,
176203
}
177204
}
178205
}

0 commit comments

Comments
 (0)