Use once_cell to keep the configuration

In order to keep the configuration globally available use
`once_cell`. Also check for a valid email address in the part which is
receiving the data from the api.
This commit is contained in:
finga 2023-02-06 09:56:33 +01:00
parent 05e63acca5
commit 7ad6f610d1
6 changed files with 61 additions and 11 deletions

2
Cargo.lock generated
View file

@ -534,6 +534,7 @@ dependencies = [
"nom", "nom",
"once_cell", "once_cell",
"quoted_printable", "quoted_printable",
"serde",
"socket2", "socket2",
"tokio", "tokio",
"tracing", "tracing",
@ -918,6 +919,7 @@ dependencies = [
"diesel", "diesel",
"diesel_migrations", "diesel_migrations",
"lettre", "lettre",
"once_cell",
"serde", "serde",
"time", "time",
"tokio", "tokio",

View file

@ -14,7 +14,7 @@ anyhow = "1"
tracing = "0.1" tracing = "0.1"
tracing-subscriber = "0.3" tracing-subscriber = "0.3"
time = { version = "0.3", features = ["serde-human-readable"] } 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 = { version = "2", features = ["postgres", "r2d2", "time"] }
diesel_migrations = "2" diesel_migrations = "2"
serde = { version = "1", features = ["derive"] } serde = { version = "1", features = ["derive"] }
@ -25,3 +25,4 @@ tokio = { version = "1", features = ["full"] }
axum = "0.6" axum = "0.6"
tower = { version = "0.4", features = ["util"] } tower = { version = "0.4", features = ["util"] }
tower-http = { version = "0.3", features = ["trace"] } tower-http = { version = "0.3", features = ["trace"] }
once_cell = "1"

View file

@ -8,3 +8,6 @@ pass = "remindrs"
# [server] # [server]
# address = "::1" # address = "::1"
# port = 8080 # port = 8080
# [email]
# from = "remindrs@localhost"

View file

@ -7,6 +7,7 @@ use axum::{
Json, Json,
}; };
use diesel::prelude::*; use diesel::prelude::*;
use lettre::message::Mailbox;
use serde::Deserialize; use serde::Deserialize;
use time::OffsetDateTime; use time::OffsetDateTime;
use tracing::{error, trace}; use tracing::{error, trace};
@ -35,7 +36,7 @@ pub struct Reminder {
planned: OffsetDateTime, planned: OffsetDateTime,
title: String, title: String,
message: String, message: String,
receiver: String, receiver: Mailbox,
} }
#[allow(clippy::unused_async)] #[allow(clippy::unused_async)]
@ -48,7 +49,7 @@ pub async fn create_reminder(
planned: data.planned, planned: data.planned,
title: &data.title, title: &data.title,
message: &data.message, message: &data.message,
receiver: &data.receiver, receiver: &data.receiver.to_string(),
}; };
trace!(?data, "received data"); trace!(?data, "received data");

View file

@ -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 serde::Deserialize;
use std::{ use std::{
env, fs, env, fs,
@ -7,6 +9,8 @@ use std::{
}; };
use tracing::{error, info, trace, warn}; use tracing::{error, info, trace, warn};
static CONFIG: OnceCell<Config> = OnceCell::new();
fn default_database_host() -> String { fn default_database_host() -> String {
"localhost".to_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)] #[derive(Debug, Deserialize)]
#[serde(deny_unknown_fields)] #[serde(deny_unknown_fields)]
pub struct Config { pub struct Config {
/// Database configuration /// Database configuration
pub database: Database, pub database: Database,
/// server configuration /// Server configuration
#[serde(default)] #[serde(default)]
pub server: Server, pub server: Server,
/// Email configuration
#[serde(default)]
pub email: Email,
} }
impl Config { impl Config {
@ -130,4 +161,15 @@ impl Config {
Ok(Self::try_paths()?) Ok(Self::try_paths()?)
} }
} }
pub fn init(file: Option<PathBuf>) -> 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")
}
} }

View file

@ -51,14 +51,14 @@ fn remind(
.load::<Reminder>(&mut db_pool.get()?)?; .load::<Reminder>(&mut db_pool.get()?)?;
if result.is_empty() { if result.is_empty() {
info!("no reminders present, parking indefinitely"); info!("no reminders present, waiting for new reminder");
std::thread::park(); std::thread::park();
} else { } else {
for reminder in result { for reminder in result {
trace!(?reminder, "checking reminder"); trace!(?reminder, "checking reminder");
if reminder.planned <= OffsetDateTime::now_utc() { if reminder.planned <= OffsetDateTime::now_utc() {
let email = Message::builder() let email = Message::builder()
.from(format!("{}@localhost", env!("CARGO_BIN_NAME")).parse()?) .from(Config::get().email.from.clone())
.to(reminder.receiver.parse()?) .to(reminder.receiver.parse()?)
.subject(&reminder.title) .subject(&reminder.title)
.body(reminder.message.to_string())?; .body(reminder.message.to_string())?;
@ -105,8 +105,9 @@ async fn main() -> Result<()> {
env!("CARGO_PKG_VERSION") env!("CARGO_PKG_VERSION")
); );
let config = Config::load_config(args.config)?; Config::init(args.config)?;
let mut db_pool = get_connection_pool(&config)?;
let mut db_pool = get_connection_pool(Config::get())?;
trace!(migrations = ?db_pool trace!(migrations = ?db_pool
.get()? .get()?
.run_pending_migrations(MIGRATIONS) .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() let app = Router::new()
.route("/v1/reminder", post(api::create_reminder)) .route("/v1/reminder", post(api::create_reminder))
.layer(ServiceBuilder::new().layer(TraceLayer::new_for_http())) .layer(ServiceBuilder::new().layer(TraceLayer::new_for_http()))
@ -129,7 +130,7 @@ async fn main() -> Result<()> {
db_pool, db_pool,
reminder: Arc::new(reminder), 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); info!("{} listening on {}", env!("CARGO_PKG_NAME"), addr);