aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorrtkay123 <dev@kanjala.com>2026-02-09 10:24:16 +0200
committerrtkay123 <dev@kanjala.com>2026-02-09 10:24:16 +0200
commit253c5631ae09fd5ad9fd6b3eff104e6099d4676c (patch)
tree56096882cd282b4760480a38750f7c70606918d8
parent9b0c8c23e85930ef1128e73e06713c8a36708625 (diff)
downloadsellershut-253c5631ae09fd5ad9fd6b3eff104e6099d4676c.tar.bz2
sellershut-253c5631ae09fd5ad9fd6b3eff104e6099d4676c.zip
feat: merge config
-rw-r--r--lib/auth-service/src/client/mod.rs22
-rw-r--r--sellershut/sellershut.toml8
-rw-r--r--sellershut/src/config/cli/database.rs4
-rw-r--r--sellershut/src/config/cli/mod.rs10
-rw-r--r--sellershut/src/config/cli/oauth/discord.rs14
-rw-r--r--sellershut/src/config/cli/oauth/mod.rs3
-rw-r--r--sellershut/src/config/log_level.rs68
-rw-r--r--sellershut/src/config/mod.rs86
-rw-r--r--sellershut/src/logging/mod.rs19
-rw-r--r--sellershut/src/main.rs28
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)