diff options
| author | rtkay123 <dev@kanjala.com> | 2026-02-09 10:24:16 +0200 |
|---|---|---|
| committer | rtkay123 <dev@kanjala.com> | 2026-02-09 10:24:16 +0200 |
| commit | 253c5631ae09fd5ad9fd6b3eff104e6099d4676c (patch) | |
| tree | 56096882cd282b4760480a38750f7c70606918d8 | |
| parent | 9b0c8c23e85930ef1128e73e06713c8a36708625 (diff) | |
| download | sellershut-253c5631ae09fd5ad9fd6b3eff104e6099d4676c.tar.bz2 sellershut-253c5631ae09fd5ad9fd6b3eff104e6099d4676c.zip | |
feat: merge config
| -rw-r--r-- | lib/auth-service/src/client/mod.rs | 22 | ||||
| -rw-r--r-- | sellershut/sellershut.toml | 8 | ||||
| -rw-r--r-- | sellershut/src/config/cli/database.rs | 4 | ||||
| -rw-r--r-- | sellershut/src/config/cli/mod.rs | 10 | ||||
| -rw-r--r-- | sellershut/src/config/cli/oauth/discord.rs | 14 | ||||
| -rw-r--r-- | sellershut/src/config/cli/oauth/mod.rs | 3 | ||||
| -rw-r--r-- | sellershut/src/config/log_level.rs | 68 | ||||
| -rw-r--r-- | sellershut/src/config/mod.rs | 86 | ||||
| -rw-r--r-- | sellershut/src/logging/mod.rs | 19 | ||||
| -rw-r--r-- | sellershut/src/main.rs | 28 |
10 files changed, 218 insertions, 44 deletions
diff --git a/lib/auth-service/src/client/mod.rs b/lib/auth-service/src/client/mod.rs index 25cf16c..af581b0 100644 --- a/lib/auth-service/src/client/mod.rs +++ b/lib/auth-service/src/client/mod.rs @@ -22,18 +22,34 @@ pub struct ClientConfig { auth_url: Url, } +impl ClientConfig { + pub fn new( + client_id: String, + client_secret: SecretString, + token_url: Url, + auth_url: Url, + ) -> Self { + Self { + client_id, + client_secret, + token_url, + auth_url, + } + } +} + impl TryFrom<ClientConfig> for OauthClient { type Error = AuthServiceError; fn try_from(value: ClientConfig) -> Result<Self, Self::Error> { debug!("creating oauth client"); Ok(Self( - oauth2::basic::BasicClient::new(ClientId::new(value.client_id)) + oauth2::basic::BasicClient::new(ClientId::new(value.client_id.to_string())) .set_client_secret(ClientSecret::new( value.client_secret.expose_secret().to_string(), )) - .set_auth_uri(AuthUrl::from_url(value.auth_url)) - .set_token_uri(TokenUrl::from_url(value.token_url)), + .set_auth_uri(AuthUrl::from_url(value.auth_url.to_owned())) + .set_token_uri(TokenUrl::from_url(value.token_url.to_owned())), )) } } diff --git a/sellershut/sellershut.toml b/sellershut/sellershut.toml index b5e4545..32add09 100644 --- a/sellershut/sellershut.toml +++ b/sellershut/sellershut.toml @@ -1,9 +1,11 @@ [server] port = 2210 environment = "dev" +log-level = "trace" [database] - +url = "http://localhost:5432" +pool-size = 10 [oauth] redirect-url = "http://localhost:2210" @@ -11,5 +13,5 @@ redirect-url = "http://localhost:2210" [oauth.discord] client-id = "" client-secret = "" -token-url = "" -auth-url = "" +token-url = "http://localhost" +auth-url = "http://localhost" diff --git a/sellershut/src/config/cli/database.rs b/sellershut/src/config/cli/database.rs index 59fac99..4e8126b 100644 --- a/sellershut/src/config/cli/database.rs +++ b/sellershut/src/config/cli/database.rs @@ -2,13 +2,15 @@ use clap::Parser; use serde::Deserialize; use url::Url; -#[derive(Parser, Deserialize)] +#[derive(Parser, Deserialize, Debug)] #[serde(rename_all = "kebab-case")] pub struct Database { /// Database url #[arg(long, value_name = "DATABASE_URL", env = "DATABASE_URL")] + #[serde(rename = "url")] pub database_url: Option<Url>, /// Database pool size #[arg(long, value_name = "DATABASE_POOL_SIZE", env = "DATABASE_POOL_SIZE")] + #[serde(rename = "pool-size")] pub database_pool_size: Option<u32>, } diff --git a/sellershut/src/config/cli/mod.rs b/sellershut/src/config/cli/mod.rs index 551eb4d..8227407 100644 --- a/sellershut/src/config/cli/mod.rs +++ b/sellershut/src/config/cli/mod.rs @@ -7,8 +7,9 @@ use clap::ValueEnum; use serde::Deserialize; use crate::config::cli::{database::Database, oauth::Oauth}; +use crate::config::log_level::LogLevel; -#[derive(Parser, Deserialize, Default)] +#[derive(Parser, Deserialize, Default, Debug)] /// A federated marketplace platform #[command(version, about, long_about = None)] #[serde(rename_all = "kebab-case")] @@ -25,7 +26,7 @@ pub struct Cli { pub database: Option<Database>, } -#[derive(Parser, Deserialize, Default)] +#[derive(Parser, Deserialize, Default, Debug)] #[serde(rename_all = "kebab-case")] pub struct Server { /// Sets the port that the server listens to @@ -35,9 +36,12 @@ pub struct Server { /// Runtime environment #[arg(short, long, value_name = "ENV")] pub environment: Option<CliEnvironment>, + /// Log Level + #[arg(short, long, value_name = "LOG_LEVEL")] + pub log_level: Option<LogLevel>, } -#[derive(Deserialize, ValueEnum, Clone, Copy, Default)] +#[derive(Deserialize, ValueEnum, Clone, Copy, Default, Debug)] #[serde(rename_all = "lowercase")] pub enum CliEnvironment { #[default] diff --git a/sellershut/src/config/cli/oauth/discord.rs b/sellershut/src/config/cli/oauth/discord.rs index afb4154..90314cd 100644 --- a/sellershut/src/config/cli/oauth/discord.rs +++ b/sellershut/src/config/cli/oauth/discord.rs @@ -2,7 +2,7 @@ use clap::Parser; use serde::Deserialize; use url::Url; -#[derive(Parser, Deserialize)] +#[derive(Parser, Deserialize, Debug)] #[serde(rename_all = "kebab-case")] pub struct DiscordConfig { /// Discord client id @@ -11,26 +11,30 @@ pub struct DiscordConfig { value_name = "OAUTH_DISCORD_CLIENT_ID", env = "OAUTH_DISCORD_CLIENT_ID" )] - discord_client_id: Option<String>, + #[serde(rename = "client-id")] + pub discord_client_id: Option<String>, /// Discord client secret #[arg( long, value_name = "OAUTH_DISCORD_CLIENT_SECRET", env = "OAUTH_DISCORD_CLIENT_SECRET" )] - discord_client_secret: Option<secrecy::SecretString>, + #[serde(rename = "client-secret")] + pub discord_client_secret: Option<secrecy::SecretString>, /// Discord auth url #[arg( long, value_name = "OAUTH_DISCORD_AUTH_URL", env = "OAUTH_DISCORD_AUTH_URL" )] - discord_auth_url: Option<Url>, + #[serde(rename = "auth-url")] + pub discord_auth_url: Option<Url>, /// Discord token url #[arg( long, value_name = "OAUTH_DISCORD_TOKEN_URL", env = "OAUTH_DISCORD_TOKEN_URL" )] - discord_token_url: Option<Url>, + #[serde(rename = "token-url")] + pub discord_token_url: Option<Url>, } diff --git a/sellershut/src/config/cli/oauth/mod.rs b/sellershut/src/config/cli/oauth/mod.rs index cc59231..617ec0d 100644 --- a/sellershut/src/config/cli/oauth/mod.rs +++ b/sellershut/src/config/cli/oauth/mod.rs @@ -6,11 +6,12 @@ use url::Url; use crate::config::cli::oauth::discord::DiscordConfig; -#[derive(Parser, Deserialize)] +#[derive(Parser, Deserialize, Debug)] #[serde(rename_all = "kebab-case")] pub struct Oauth { /// Oauth redirect url #[arg(long, value_name = "OAUTH_REDIRECT_URL", env = "OAUTH_REDIRECT_URL")] + #[serde(rename = "redirect-url")] pub oauth_redirect_url: Option<Url>, #[command(flatten)] pub discord: Option<DiscordConfig>, diff --git a/sellershut/src/config/log_level.rs b/sellershut/src/config/log_level.rs new file mode 100644 index 0000000..3edfbc3 --- /dev/null +++ b/sellershut/src/config/log_level.rs @@ -0,0 +1,68 @@ +use clap::ValueEnum; +use serde::Deserialize; + +#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, ValueEnum, Debug, Default, Deserialize)] +#[serde(rename_all = "lowercase")] +pub enum LogLevel { + /// The "trace" level. + /// + /// Designates very low priority, often extremely verbose, information. + Trace = 0, + /// The "debug" level. + /// + /// Designates lower priority information. + #[default] + Debug = 1, + /// The "info" level. + /// + /// Designates useful information. + Info = 2, + /// The "warn" level. + /// + /// Designates hazardous situations. + Warn = 3, + /// The "error" level. + /// + /// Designates very serious errors. + Error = 4, +} + +impl From<LogLevel> for tracing::Level { + fn from(value: LogLevel) -> Self { + match value { + LogLevel::Trace => tracing::Level::TRACE, + LogLevel::Debug => tracing::Level::DEBUG, + LogLevel::Info => tracing::Level::INFO, + LogLevel::Warn => tracing::Level::WARN, + LogLevel::Error => tracing::Level::ERROR, + } + } +} + +#[cfg(test)] +mod tests { + use super::LogLevel; + + fn check(level: LogLevel, value: &str) { + let level = tracing::Level::from(level); + assert_eq!(level.to_string().to_lowercase(), value); + } + + #[test] + fn loglevel() { + let level = LogLevel::Trace; + check(level, "trace"); + + let level = LogLevel::Debug; + check(level, "debug"); + + let level = LogLevel::Info; + check(level, "info"); + + let level = LogLevel::Warn; + check(level, "warn"); + + let level = LogLevel::Error; + check(level, "error"); + } +} diff --git a/sellershut/src/config/mod.rs b/sellershut/src/config/mod.rs index ee0103e..b013765 100644 --- a/sellershut/src/config/mod.rs +++ b/sellershut/src/config/mod.rs @@ -1,12 +1,13 @@ +pub mod cli; +pub mod log_level; + use auth_service::client::ClientConfig; use clap::ValueEnum; use serde::Deserialize; use tracing::Level; use url::Url; -use crate::config::cli::{Cli, CliEnvironment, oauth}; - -pub mod cli; +use crate::config::cli::{Cli, CliEnvironment}; #[derive(Deserialize, ValueEnum, Clone, Copy)] #[serde(rename_all = "lowercase")] @@ -59,6 +60,13 @@ impl Configuration { missing.push("server.port"); } + let log_level = cli + .server + .as_ref() + .and_then(|v| v.log_level) + .or(file.server.as_ref().and_then(|v| v.log_level)) + .unwrap_or_default(); + let environment = cli .server .as_ref() @@ -66,20 +74,65 @@ impl Configuration { .or(file.server.as_ref().and_then(|v| v.environment)) .unwrap_or_default() .into(); - + let cli_oauth = cli.oauth.as_ref(); let file_oauth = file.oauth.as_ref(); let oauth_redirect_url = cli_oauth .and_then(|value| value.oauth_redirect_url.clone()) - .or(file_oauth - .and_then(|value| value.oauth_redirect_url.clone())); + .or(file_oauth.and_then(|value| value.oauth_redirect_url.clone())); if oauth_redirect_url.is_none() { missing.push("oauth.redirect-url"); } - let discord_config = cli_oauth.and_then(|v| v.discord.as_ref()).or(file_oauth.and_then(|v| v.discord.as_ref())); + let cli_discord = cli_oauth.and_then(|v| v.discord.as_ref()); + let file_discord = file_oauth.and_then(|v| v.discord.as_ref()); + + let discord_client_id = cli_discord + .and_then(|v| v.discord_client_id.clone()) + .or(file_discord.and_then(|v| v.discord_client_id.clone())); + if discord_client_id.is_none() { + missing.push("oauth.discord.client-id"); + } + + let discord_client_secret = cli_discord + .and_then(|v| v.discord_client_secret.clone()) + .or(file_discord.and_then(|v| v.discord_client_secret.clone())); + + if discord_client_secret.is_none() { + missing.push("oauth.discord.client-secret"); + } + + let discord_auth_url = cli_discord + .and_then(|v| v.discord_auth_url.clone()) + .or(file_discord.and_then(|v| v.discord_auth_url.clone())); + if discord_auth_url.is_none() { + missing.push("oauth.discord.auth-url"); + } + + let discord_token_url = cli_discord + .and_then(|v| v.discord_token_url.clone()) + .or(file_discord.and_then(|v| v.discord_token_url.clone())); + if discord_token_url.is_none() { + missing.push("oauth.discord.token-url"); + } + + let cli_db = cli.database.as_ref(); + let file_db = file.database.as_ref(); + + let database_url = cli_db + .and_then(|v| v.database_url.clone()) + .or(file_db.and_then(|v| v.database_url.clone())); + + if database_url.is_none() { + missing.push("database.url"); + } + + let pool_size = cli_db + .and_then(|v| v.database_pool_size) + .or(file_db.and_then(|v| v.database_pool_size)) + .unwrap_or(10); // sensible default if !missing.is_empty() { anyhow::bail!( @@ -92,14 +145,27 @@ impl Configuration { ); } + let client_config = ClientConfig::new( + discord_client_id.unwrap(), + discord_client_secret.unwrap(), + discord_token_url.unwrap(), + discord_auth_url.unwrap(), + ); + Ok(Self { server: Server { port: port.unwrap(), environment, - log_level: Level::INFO, + log_level: log_level.into(), + }, + oauth: Oauth { + redirect_url: oauth_redirect_url.unwrap(), + discord: client_config, + }, + database: Database { + url: database_url.unwrap(), + pool_size, }, - oauth: todo!(), - database: todo!(), }) } } diff --git a/sellershut/src/logging/mod.rs b/sellershut/src/logging/mod.rs new file mode 100644 index 0000000..c4b00c9 --- /dev/null +++ b/sellershut/src/logging/mod.rs @@ -0,0 +1,19 @@ +use tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt}; + +use crate::config::Configuration; + +pub fn initialise_logging(config: &Configuration) { + tracing_subscriber::registry() + .with( + tracing_subscriber::EnvFilter::try_from_default_env().unwrap_or_else(|_| { + format!( + "{}={},tower_http=debug,axum=trace", + env!("CARGO_CRATE_NAME"), + config.server.log_level + ) + .into() + }), + ) + .with(tracing_subscriber::fmt::layer()) + .init(); +} diff --git a/sellershut/src/main.rs b/sellershut/src/main.rs index bd22dd5..86db34d 100644 --- a/sellershut/src/main.rs +++ b/sellershut/src/main.rs @@ -1,6 +1,8 @@ mod config; +mod logging; mod state; +use std::net::{Ipv6Addr, SocketAddr}; use std::time::Duration; use anyhow::Context; @@ -8,10 +10,11 @@ use axum::{Router, routing::get}; use clap::Parser; use tokio::time::sleep; use tokio::{net::TcpListener, signal}; -use tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt}; +use tracing::info; use crate::config::Configuration; use crate::config::cli::Cli; +use crate::logging::initialise_logging; #[tokio::main] async fn main() -> anyhow::Result<()> { @@ -21,31 +24,20 @@ async fn main() -> anyhow::Result<()> { .with_context(|| format!("Failed to read config file: {file:?}"))?; toml::from_str(&contents)? } else { - Cli::default() + let contents = include_str!("../sellershut.toml"); + toml::from_str(contents)? }; let config = Configuration::merge(&cli, &config)?; - - // Enable tracing. - tracing_subscriber::registry() - .with( - tracing_subscriber::EnvFilter::try_from_default_env().unwrap_or_else(|_| { - format!( - "{}=debug,tower_http=debug,axum=trace", - env!("CARGO_CRATE_NAME") - ) - .into() - }), - ) - .with(tracing_subscriber::fmt::layer().without_time()) - .init(); + initialise_logging(&config); // Create a regular axum app. let app = Router::new() .route("/slow", get(|| sleep(Duration::from_secs(5)))) .route("/forever", get(std::future::pending::<()>)); - // Create a `TcpListener` using tokio. - let listener = TcpListener::bind("0.0.0.0:3000").await.unwrap(); + let addr = SocketAddr::from((Ipv6Addr::UNSPECIFIED, config.server.port)); + info!(port = addr.port(), "starting server"); + let listener = TcpListener::bind(addr).await?; // Run the server with graceful shutdown axum::serve(listener, app) |
