use std::path::{Path, PathBuf};
use config::{ConfigError, File};
use lazy_static::lazy_static;
use serde::{Deserialize, Serialize};
use tracing::info;
use crate::{environment::Environment, logger, Error, Result as AppResult};
lazy_static! {
static ref DEFAULT_FOLDER: PathBuf = PathBuf::from("config");
}
#[derive(Debug, Clone, Deserialize, Serialize)]
pub struct Config {
pub logger: Logger,
pub server: Server,
#[cfg(feature = "with-db")]
pub database: Database,
pub redis: Option<Redis>,
pub auth: Option<Auth>,
pub workers: Workers,
pub mailer: Option<Mailer>,
}
#[derive(Debug, Clone, Deserialize, Serialize)]
pub struct Logger {
pub enable: bool,
pub level: logger::LogLevel,
pub format: logger::Format,
pub override_filter: Option<String>,
}
#[derive(Clone, Default, Serialize, Deserialize, Debug)]
pub enum WorkerMode {
#[default]
BackgroundQueue,
ForegroundBlocking,
BackgroundAsync,
}
#[derive(Debug, Clone, Deserialize, Serialize)]
#[allow(clippy::struct_excessive_bools)]
pub struct Database {
pub uri: String,
pub enable_logging: bool,
pub min_connections: u32,
pub max_connections: u32,
pub connect_timeout: u64,
pub idle_timeout: u64,
#[serde(default)]
pub auto_migrate: bool,
#[serde(default)]
pub dangerously_truncate: bool,
#[serde(default)]
pub dangerously_recreate: bool,
}
#[derive(Debug, Clone, Deserialize, Serialize)]
pub struct Redis {
pub uri: String,
#[serde(default)]
pub dangerously_flush: bool,
}
#[derive(Debug, Clone, Deserialize, Serialize)]
pub struct Auth {
pub jwt: Option<JWT>,
}
#[derive(Debug, Clone, Deserialize, Serialize)]
pub struct JWT {
pub secret: String,
pub expiration: u64,
}
#[derive(Debug, Clone, Deserialize, Serialize)]
pub struct Server {
pub port: i32,
pub host: String,
pub middlewares: Middlewares,
}
#[derive(Debug, Clone, Deserialize, Serialize)]
pub struct Workers {
pub mode: WorkerMode,
pub queues: Option<Vec<String>>,
}
#[derive(Debug, Clone, Deserialize, Serialize)]
pub struct Middlewares {
pub limit_payload: Option<LimitPayloadMiddleware>,
pub logger: Option<EnableMiddleware>,
pub catch_panic: Option<EnableMiddleware>,
pub timeout_request: Option<TimeoutRequestMiddleware>,
}
#[derive(Debug, Clone, Deserialize, Serialize)]
pub struct TimeoutRequestMiddleware {
pub enable: bool,
pub timeout: u64,
}
#[derive(Debug, Clone, Deserialize, Serialize)]
pub struct LimitPayloadMiddleware {
pub enable: bool,
pub body_limit: String,
}
#[derive(Debug, Clone, Deserialize, Serialize)]
pub struct EnableMiddleware {
pub enable: bool,
}
#[derive(Debug, Clone, Deserialize, Serialize)]
pub struct Mailer {
pub smtp: Option<SmtpMailer>,
}
#[derive(Debug, Clone, Deserialize, Serialize)]
pub struct SmtpMailer {
pub enable: bool,
pub host: String,
pub port: u16,
pub secure: bool,
pub auth: Option<MailerAuth>,
}
#[derive(Debug, Clone, Deserialize, Serialize)]
pub struct MailerAuth {
pub user: String,
pub password: String,
}
impl Server {
#[must_use]
pub fn full_url(&self) -> String {
format!("{}:{}", self.host, self.port)
}
}
impl Config {
pub fn new(env: &Environment) -> Result<Self, ConfigError> {
let config = Self::from_folder(env, DEFAULT_FOLDER.as_path())?;
info!(name: "environment_loaded", config = ?config, environment = ?env, "environment loaded");
Ok(config)
}
pub fn from_folder(env: &Environment, path: &Path) -> Result<Self, ConfigError> {
config::Config::builder()
.add_source(
File::with_name(&path.join(format!("{env}.yaml")).display().to_string())
.required(true),
)
.add_source(config::Environment::with_prefix("APP").separator("_"))
.build()?
.try_deserialize()
}
pub fn get_jwt_config(&self) -> AppResult<&JWT> {
self.auth
.as_ref()
.and_then(|auth| auth.jwt.as_ref())
.map_or_else(
|| Err(Error::Any("sending email error".to_string().into())),
Ok,
)
}
}