use clap::Parser; use s3::creds::Credentials; use s3::{Bucket, Region}; /// GeneIT backend API #[derive(Parser, Debug, Clone)] #[clap(author, version, about, long_about = None)] pub struct AppConfig { /// Listen address #[clap(short, long, env, default_value = "0.0.0.0:8000")] pub listen_address: String, /// Website origin #[clap(short, long, env, default_value = "http://localhost:3000")] pub website_origin: String, /// Proxy IP, might end with a star "*" #[clap(short, long, env)] pub proxy_ip: Option, /// Secret key, used to sign some resources. Must be randomly generated #[clap(long, env, default_value = "")] secret: String, /// PostgreSQL database host #[clap(long, env, default_value = "localhost")] db_host: String, /// PostgreSQL database port #[clap(long, env, default_value_t = 5432)] db_port: u16, /// PostgreSQL username #[clap(long, env, default_value = "user")] db_username: String, /// PostgreSQL password #[clap(long, env, default_value = "pass")] db_password: String, /// PostgreSQL database name #[clap(long, env, default_value = "geneit")] db_name: String, /// Redis connection hostname #[clap(long, env, default_value = "localhost")] redis_hostname: String, /// Redis connection port #[clap(long, env, default_value_t = 6379)] redis_port: u16, /// Redis database number #[clap(long, env, default_value_t = 0)] redis_db_number: i64, /// Redis username #[clap(long, env)] redis_username: Option, /// Redis password #[clap(long, env, default_value = "secretredis")] redis_password: String, /// Mail sender #[clap(long, env, default_value = "geneit@example.com")] pub mail_sender: String, /// SMTP relay #[clap(long, env, default_value = "localhost")] pub smtp_relay: String, /// SMTP port #[clap(long, env, default_value_t = 1025)] pub smtp_port: u16, /// SMTP use TLS to connect to relay #[clap(long, env)] pub smtp_tls: bool, /// SMTP username #[clap(long, env)] pub smtp_username: Option, /// SMTP password #[clap(long, env)] pub smtp_password: Option, /// Password reset URL #[clap(long, env, default_value = "APP_ORIGIN/reset_password#TOKEN")] pub reset_password_url: String, /// Delete account URL #[clap(long, env, default_value = "APP_ORIGIN/delete_account#TOKEN")] pub delete_account_url: String, /// URL where the OpenID configuration can be found #[arg( long, env, default_value = "http://localhost:9001/.well-known/openid-configuration" )] pub oidc_configuration_url: String, /// Disable OpenID authentication #[arg(long, env)] pub disable_oidc: bool, /// OpenID provider name #[arg(long, env, default_value = "3rd party provider")] pub oidc_provider_name: String, /// OpenID client ID #[arg(long, env, default_value = "foo")] pub oidc_client_id: String, /// OpenID client secret #[arg(long, env, default_value = "bar")] pub oidc_client_secret: String, /// OpenID login redirect URL #[arg(long, env, default_value = "APP_ORIGIN/oidc_cb")] oidc_redirect_url: String, /// S3 Bucket name #[arg(long, env, default_value = "geneit-data")] s3_bucket_name: String, /// S3 region (if not using Minio) #[arg(long, env, default_value = "eu-central-1")] s3_region: String, /// S3 API endpoint #[arg(long, env, default_value = "http://localhost:9000")] s3_endpoint: String, /// S3 access key #[arg(long, env, default_value = "topsecret")] s3_access_key: String, /// S3 secret key #[arg(long, env, default_value = "topsecret")] s3_secret_key: String, /// S3 skip auto create bucket if not existing #[arg(long, env)] pub s3_skip_auto_create_bucket: bool, /// Directory where temporary files are stored #[arg(long, env, default_value = "/tmp")] pub temp_dir: String, } lazy_static::lazy_static! { static ref ARGS: AppConfig = { AppConfig::parse() }; } impl AppConfig { /// Get parsed command line arguments pub fn get() -> &'static AppConfig { &ARGS } /// Get app secret pub fn secret(&self) -> &str { let mut secret = self.secret.as_str(); if cfg!(debug_assertions) && secret.is_empty() { secret = "DEBUGKEYDEBUGKEYDEBUGKEYDEBUGKEYDEBUGKEY"; } if secret.is_empty() { panic!("SECRET is undefined or too short (min 30 chars)!") } secret } /// Get full db connection chain pub fn db_connection_chain(&self) -> String { format!( "postgres://{}:{}@{}:{}/{}", self.db_username, self.db_password, self.db_host, self.db_port, self.db_name ) } /// Get Redis connection configuration pub fn redis_connection_config(&self) -> redis::ConnectionInfo { redis::ConnectionInfo { addr: redis::ConnectionAddr::Tcp(self.redis_hostname.clone(), self.redis_port), redis: redis::RedisConnectionInfo { db: self.redis_db_number, username: self.redis_username.clone(), password: Some(self.redis_password.clone()), }, } } /// Get password reset URL pub fn get_password_reset_url(&self, token: &str) -> String { self.reset_password_url .replace("APP_ORIGIN", &self.website_origin) .replace("TOKEN", token) } /// Get account delete URL pub fn get_account_delete_url(&self, token: &str) -> String { self.delete_account_url .replace("APP_ORIGIN", &self.website_origin) .replace("TOKEN", token) } /// Get OpenID providers configuration pub fn openid_providers(&self) -> Vec> { if self.disable_oidc { return vec![]; } vec![OIDCProvider { id: "first_prov", client_id: self.oidc_client_id.as_str(), client_secret: self.oidc_client_secret.as_str(), configuration_url: self.oidc_configuration_url.as_str(), name: self.oidc_provider_name.as_str(), }] } /// Get OIDC callback URL pub fn oidc_redirect_url(&self) -> String { self.oidc_redirect_url .replace("APP_ORIGIN", &self.website_origin) } /// Get s3 credentials pub fn s3_credentials(&self) -> anyhow::Result { Ok(Credentials::new( Some(&self.s3_access_key), Some(&self.s3_secret_key), None, None, None, )?) } /// Get S3 bucket pub fn s3_bucket(&self) -> anyhow::Result { Ok(Bucket::new( &self.s3_bucket_name, Region::Custom { region: self.s3_region.to_string(), endpoint: self.s3_endpoint.to_string(), }, self.s3_credentials()?, )? .with_path_style()) } } #[derive(Debug, Clone, serde::Serialize)] pub struct OIDCProvider<'a> { pub id: &'a str, #[serde(skip_serializing)] pub client_id: &'a str, #[serde(skip_serializing)] pub client_secret: &'a str, #[serde(skip_serializing)] pub configuration_url: &'a str, pub name: &'a str, }