shoutdotdev/src/settings.rs

126 lines
3.1 KiB
Rust

use anyhow::{Context as _, Result};
use axum::extract::FromRef;
use config::{Config, Environment};
use dotenvy::dotenv;
use serde::Deserialize;
use crate::app_state::AppState;
#[derive(Clone, Debug, Deserialize)]
pub struct Settings {
/// Prefix under which to nest all routes. If specified, include leading
/// slash but no trailing slash, for example "/app". For default behavior,
/// leave as empty string.
#[serde(default)]
pub base_path: String,
/// postgresql:// URL.
pub database_url: String,
/// When set to 1, embedded Diesel migrations will be run on startup.
pub run_database_migrations: Option<u8>,
/// Address for server to bind to
#[serde(default = "default_host")]
pub host: String,
/// Port for server to bind to
#[serde(default = "default_port")]
pub port: u16,
/// Host visible to end users, for example "https://shout.dev"
pub frontend_host: String,
pub auth: AuthSettings,
pub email: EmailSettings,
}
fn default_port() -> u16 {
3000
}
fn default_host() -> String {
"127.0.0.1".to_string()
}
#[derive(Clone, Debug, Deserialize)]
pub struct AuthSettings {
pub client_id: String,
pub client_secret: String,
pub auth_url: String,
pub token_url: String,
pub userinfo_url: String,
pub logout_url: Option<String>,
#[serde(default = "default_cookie_name")]
pub cookie_name: String,
}
fn default_cookie_name() -> String {
"SHOUT_DOT_DEV_SESSION".to_string()
}
#[derive(Clone, Debug, Deserialize)]
pub struct SmtpSettings {
pub server: String,
pub username: String,
pub password: String,
}
#[derive(Clone, Debug, Deserialize)]
pub struct PostmarkSettings {
pub server_token: String,
}
#[derive(Clone, Debug, Deserialize)]
pub struct EmailSettings {
pub verification_from: lettre::message::Mailbox,
pub message_from: lettre::message::Mailbox,
pub smtp: Option<SmtpSettings>,
pub postmark: Option<PostmarkSettings>,
}
pub struct SlackSettings {
pub client_id: String,
pub client_secret: String,
pub redirect_url: String,
pub auth_url: String,
pub token_url: String,
}
impl Settings {
pub fn load() -> Result<Self> {
match dotenv() {
Err(err) => {
if err.not_found() {
tracing::info!("no .env file found");
} else {
return Err(err).context("dotenvy error");
}
}
Ok(pathbuf) => {
tracing::info!(
"using env file {}",
pathbuf
.to_str()
.ok_or(anyhow::anyhow!("pathbuf is not valid unicode"))?
);
}
}
let s = Config::builder()
.add_source(Environment::default().separator("__"))
.build()
.context("config error")?;
s.try_deserialize().context("deserialize error")
}
}
impl<S> FromRef<S> for Settings
where
S: Into<AppState> + Clone,
{
fn from_ref(state: &S) -> Self {
Into::<AppState>::into(state.clone()).settings.clone()
}
}