diff --git a/Cargo.lock b/Cargo.lock index 79d4ec0..8eb5f3c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -534,6 +534,7 @@ dependencies = [ "nom", "once_cell", "quoted_printable", + "serde", "socket2", "tokio", "tracing", @@ -918,6 +919,7 @@ dependencies = [ "diesel", "diesel_migrations", "lettre", + "once_cell", "serde", "time", "tokio", diff --git a/Cargo.toml b/Cargo.toml index f963920..7521f5b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -14,7 +14,7 @@ anyhow = "1" tracing = "0.1" tracing-subscriber = "0.3" time = { version = "0.3", features = ["serde-human-readable"] } -lettre = { version = "0.10", features = ["tracing"] } +lettre = { version = "0.10", features = ["serde", "tracing"] } diesel = { version = "2", features = ["postgres", "r2d2", "time"] } diesel_migrations = "2" serde = { version = "1", features = ["derive"] } @@ -25,3 +25,4 @@ tokio = { version = "1", features = ["full"] } axum = "0.6" tower = { version = "0.4", features = ["util"] } tower-http = { version = "0.3", features = ["trace"] } +once_cell = "1" diff --git a/config.toml b/config.toml index 89cd8e1..39f5584 100644 --- a/config.toml +++ b/config.toml @@ -8,3 +8,6 @@ pass = "remindrs" # [server] # address = "::1" # port = 8080 + +# [email] +# from = "remindrs@localhost" diff --git a/src/api.rs b/src/api.rs index a0eabda..b8acd0a 100644 --- a/src/api.rs +++ b/src/api.rs @@ -7,6 +7,7 @@ use axum::{ Json, }; use diesel::prelude::*; +use lettre::message::Mailbox; use serde::Deserialize; use time::OffsetDateTime; use tracing::{error, trace}; @@ -35,7 +36,7 @@ pub struct Reminder { planned: OffsetDateTime, title: String, message: String, - receiver: String, + receiver: Mailbox, } #[allow(clippy::unused_async)] @@ -48,7 +49,7 @@ pub async fn create_reminder( planned: data.planned, title: &data.title, message: &data.message, - receiver: &data.receiver, + receiver: &data.receiver.to_string(), }; trace!(?data, "received data"); diff --git a/src/config.rs b/src/config.rs index 381b295..a34c8c0 100644 --- a/src/config.rs +++ b/src/config.rs @@ -1,4 +1,6 @@ -use anyhow::{bail, Error, Result}; +use anyhow::{anyhow, bail, Error, Result}; +use lettre::{message::Mailbox, Address}; +use once_cell::sync::OnceCell; use serde::Deserialize; use std::{ env, fs, @@ -7,6 +9,8 @@ use std::{ }; use tracing::{error, info, trace, warn}; +static CONFIG: OnceCell = OnceCell::new(); + fn default_database_host() -> String { "localhost".to_string() } @@ -75,15 +79,42 @@ impl Default for Server { } } +fn default_email_from() -> Mailbox { + Mailbox::new( + Some(env!("CARGO_BIN_NAME").to_string()), + Address::new(env!("CARGO_BIN_NAME"), "localhost").unwrap(), + ) +} + +#[derive(Debug, Deserialize)] +#[serde(deny_unknown_fields)] +pub struct Email { + /// Address the notification emails are sent from + #[serde(default = "default_email_from")] + pub from: Mailbox, +} + +impl Default for Email { + fn default() -> Self { + Self { + from: default_email_from(), + } + } +} + #[derive(Debug, Deserialize)] #[serde(deny_unknown_fields)] pub struct Config { /// Database configuration pub database: Database, - /// server configuration + /// Server configuration #[serde(default)] pub server: Server, + + /// Email configuration + #[serde(default)] + pub email: Email, } impl Config { @@ -130,4 +161,15 @@ impl Config { Ok(Self::try_paths()?) } } + + pub fn init(file: Option) -> Result<()> { + CONFIG + .set(Self::load_config(file)?) + .map_err(|_| anyhow!("configuration already initialized"))?; + Ok(()) + } + + pub fn get() -> &'static Self { + CONFIG.get().expect("config is not initialized") + } } diff --git a/src/main.rs b/src/main.rs index e502974..50cc044 100644 --- a/src/main.rs +++ b/src/main.rs @@ -51,14 +51,14 @@ fn remind( .load::(&mut db_pool.get()?)?; if result.is_empty() { - info!("no reminders present, parking indefinitely"); + info!("no reminders present, waiting for new reminder"); std::thread::park(); } else { for reminder in result { trace!(?reminder, "checking reminder"); if reminder.planned <= OffsetDateTime::now_utc() { let email = Message::builder() - .from(format!("{}@localhost", env!("CARGO_BIN_NAME")).parse()?) + .from(Config::get().email.from.clone()) .to(reminder.receiver.parse()?) .subject(&reminder.title) .body(reminder.message.to_string())?; @@ -105,8 +105,9 @@ async fn main() -> Result<()> { env!("CARGO_PKG_VERSION") ); - let config = Config::load_config(args.config)?; - let mut db_pool = get_connection_pool(&config)?; + Config::init(args.config)?; + + let mut db_pool = get_connection_pool(Config::get())?; trace!(migrations = ?db_pool .get()? .run_pending_migrations(MIGRATIONS) @@ -120,7 +121,7 @@ async fn main() -> Result<()> { } }); - let db_pool = get_connection_pool(&config)?; + let db_pool = get_connection_pool(Config::get())?; let app = Router::new() .route("/v1/reminder", post(api::create_reminder)) .layer(ServiceBuilder::new().layer(TraceLayer::new_for_http())) @@ -129,7 +130,7 @@ async fn main() -> Result<()> { db_pool, reminder: Arc::new(reminder), }); - let addr = SocketAddr::from((config.server.address, config.server.port)); + let addr = SocketAddr::from((Config::get().server.address, Config::get().server.port)); info!("{} listening on {}", env!("CARGO_PKG_NAME"), addr);