feat: add basic configuration support

This commit is contained in:
Alexander Navarro 2025-02-26 11:53:07 -03:00
parent 8e1e366964
commit 3ff1f26cc4
6 changed files with 253 additions and 20 deletions

98
src/config.rs Normal file
View file

@ -0,0 +1,98 @@
use std::fmt::Display;
use std::net::SocketAddr;
use std::path::PathBuf;
use crate::Result;
use figment::providers::{Env, Format, Serialized, Toml};
use serde::{Deserialize, Serialize};
use figment::Figment;
#[derive(Debug, Deserialize, Serialize)]
pub struct DBConfig {
user: String,
password: Option<String>,
host: String,
name: String,
string: Option<String>,
}
impl Display for DBConfig {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.generate_db_string(true))
}
}
impl Default for DBConfig {
fn default() -> Self {
DBConfig {
user: "postgres".to_owned(),
password: None,
host: "localhost".to_owned(),
name: "compendium".to_owned(),
string: None,
}
}
}
impl DBConfig {
pub fn generate_db_string(&self, ofuscate: bool) -> String {
if let Some(value) = &self.string {
return value.clone();
}
let db_password = if ofuscate {
let mut db_password = self.password.clone().unwrap();
let password_length = db_password.len();
let visible_characters = 4;
if password_length <= visible_characters {
// Hide all the password
let filler = "*".repeat(password_length);
db_password.replace_range(0..password_length, &filler.to_owned());
} else {
// Hide only a portion of the password
let filler = "*".repeat(password_length - visible_characters);
db_password.replace_range(visible_characters..password_length, &filler.to_owned());
};
db_password
} else {
self.password.clone().unwrap()
};
let db_string = format!(
"postgres://{}:{}@{}/{}",
self.user, db_password, self.host, self.name,
);
db_string.to_owned()
}
}
#[derive(Debug, Deserialize, Serialize)]
pub struct Config {
pub db: DBConfig,
pub addr: SocketAddr,
}
impl Default for Config {
fn default() -> Self {
Config {
db: DBConfig::default(),
addr: "0.0.0.0:3000".parse().unwrap(),
}
}
}
impl Config {
pub fn new(path: PathBuf) -> Result<Self> {
let config: Config = Figment::from(Serialized::defaults(Config::default()))
.merge(Toml::file(path))
.merge(Env::prefixed("CPD_").split("_"))
.extract()?;
Ok(config)
}
}

View file

@ -30,6 +30,9 @@ pub enum Error {
#[error(transparent)]
DatabaseOperation(#[from] sqlx::Error),
#[error(transparent)]
Config(#[from] figment::Error),
}
impl IntoResponse for Error {

View file

@ -1,3 +1,4 @@
pub mod config;
mod error;
pub mod router;

View file

@ -2,7 +2,8 @@
#![allow(dead_code)]
use axum_htmx::AutoVaryLayer;
use compendium::{router, AppState, Error, Link, Result, Tx};
use compendium::config::Config;
use compendium::{config, router, AppState, Error, Link, Result, Tx};
use minijinja::{Environment, Value};
use sqlx::postgres::PgPoolOptions;
use sqlx::PgPool;
@ -41,25 +42,10 @@ fn load_templates() -> Result<Environment<'static>> {
Ok(tmpl_env)
}
async fn init_db() -> Result<PgPool> {
let db_string = format!(
"postgres://{}:{}@{}/{}",
env::var("CPD_DB_USER")?,
env::var("CPD_DB_PASSWORD")?,
env::var("CPD_DB_HOST")?,
env::var("CPD_DB_NAME")?
);
info!("Connecting to database {}", db_string);
Ok(PgPoolOptions::new()
.max_connections(5)
.connect(&db_string)
.await?)
}
#[tokio::main]
async fn main() -> Result<()> {
let config = Config::new("./config.toml".into())?;
// Logs
tracing_subscriber::registry()
.with(
@ -72,7 +58,12 @@ async fn main() -> Result<()> {
let mut tmpl_env = load_templates()?;
let pool = init_db().await?;
info!("Connecting to database {}", config.db);
let pool = PgPoolOptions::new()
.max_connections(5)
.connect(&config.db.generate_db_string(false))
.await?;
let (tx_state, tx_layer) = Tx::setup(pool);
@ -86,7 +77,7 @@ async fn main() -> Result<()> {
#[cfg(debug_assertions)]
let app = app.layer(tower_livereload::LiveReloadLayer::new());
let listener = tokio::net::TcpListener::bind("0.0.0.0:3000").await?;
let listener = tokio::net::TcpListener::bind(config.addr).await?;
info!("listening on {}", listener.local_addr()?);
axum::serve(listener, app).await?;