diff --git a/crates/api/src/site.rs b/crates/api/src/site.rs index 401082647..83037d65b 100644 --- a/crates/api/src/site.rs +++ b/crates/api/src/site.rs @@ -583,7 +583,9 @@ impl Perform for GetSiteConfig { // Only let admins read this is_admin(context.pool(), user.id).await?; - let config_hjson = Settings::read_config_file()?; + // TODO: should make the response field optional? + // or include env vars, but then we cant really save it + let config_hjson = Settings::read_config_file().unwrap_or("".to_string()); Ok(GetSiteConfigResponse { config_hjson }) } diff --git a/crates/utils/src/settings/environment.rs b/crates/utils/src/settings/environment.rs index 89ae2866f..9721212ff 100644 --- a/crates/utils/src/settings/environment.rs +++ b/crates/utils/src/settings/environment.rs @@ -48,7 +48,7 @@ pub(in crate::settings) fn parse_from_env() -> SettingsOpt { admin_username: env_var("SETUP__ADMIN_USERNAME"), admin_password: env_var("SETUP__ADMIN_PASSWORD"), admin_email: Some(env_var("SETUP__ADMIN_EMAIL")), - site_name: env_var("SETUP__ADMIN_SITE_NAME"), + site_name: env_var("SETUP__SITE_NAME"), }), database: Some(DatabaseConfigOpt { user: env_var("DATABASE__USER"), diff --git a/crates/utils/src/settings/merge.rs b/crates/utils/src/settings/merge.rs index 7cd1762ff..189fc546b 100644 --- a/crates/utils/src/settings/merge.rs +++ b/crates/utils/src/settings/merge.rs @@ -1,107 +1,141 @@ use crate::settings::{structs::*, structs_opt::*}; pub(in crate::settings) trait Merge { - fn merge(&mut self, opt: T); + fn merge(self, opt: T) -> Self; } impl Merge for Settings { - fn merge(&mut self, opt: SettingsOpt) { - overwrite_if_some(&mut self.hostname, opt.hostname); - overwrite_if_some(&mut self.bind, opt.bind); - overwrite_if_some(&mut self.port, opt.port); - overwrite_if_some(&mut self.tls_enabled, opt.tls_enabled); - overwrite_if_some(&mut self.jwt_secret, opt.jwt_secret); - overwrite_if_some(&mut self.pictrs_url, opt.pictrs_url); - overwrite_if_some(&mut self.iframely_url, opt.iframely_url); - merge_if_some(&mut self.captcha, opt.captcha); - merge_if_some(&mut self.rate_limit, opt.rate_limit); - merge_if_some_opt(&mut self.email, opt.email); - merge_if_some_opt(&mut self.setup, opt.setup); - merge_if_some(&mut self.federation, opt.federation); - merge_if_some(&mut self.database, opt.database); + fn merge(self, opt: SettingsOpt) -> Self { + Settings { + setup: merge_structs(self.setup, opt.setup), + database: merge_structs(self.database, opt.database), + hostname: opt.hostname.unwrap_or(self.hostname), + bind: opt.bind.unwrap_or(self.bind), + port: opt.port.unwrap_or(self.port), + tls_enabled: opt.tls_enabled.unwrap_or(self.tls_enabled), + jwt_secret: opt.jwt_secret.unwrap_or(self.jwt_secret), + pictrs_url: opt.pictrs_url.unwrap_or(self.pictrs_url), + iframely_url: opt.iframely_url.unwrap_or(self.iframely_url), + rate_limit: merge_structs(self.rate_limit, opt.rate_limit), + email: merge_structs(self.email, opt.email), + federation: merge_structs(self.federation, opt.federation), + captcha: merge_structs(self.captcha, opt.captcha), + } } } impl Merge for RateLimitConfig { - fn merge(&mut self, opt: RateLimitConfigOpt) { - overwrite_if_some(&mut self.message, opt.message); - overwrite_if_some(&mut self.message_per_second, opt.message_per_second); - overwrite_if_some(&mut self.post, opt.post); - overwrite_if_some(&mut self.post_per_second, opt.post_per_second); - overwrite_if_some(&mut self.register, opt.register); - overwrite_if_some(&mut self.register_per_second, opt.register_per_second); - overwrite_if_some(&mut self.image, opt.image); - overwrite_if_some(&mut self.image_per_second, opt.image_per_second); + fn merge(self, opt: RateLimitConfigOpt) -> Self { + RateLimitConfig { + message: opt.message.unwrap_or(self.message), + message_per_second: opt.message_per_second.unwrap_or(self.message_per_second), + post: opt.post.unwrap_or(self.post), + post_per_second: opt.post_per_second.unwrap_or(self.post_per_second), + register: opt.register.unwrap_or(self.register), + register_per_second: opt.register_per_second.unwrap_or(self.register_per_second), + image: opt.image.unwrap_or(self.image), + image_per_second: opt.image_per_second.unwrap_or(self.image_per_second), + } } } -impl Merge for Setup { - fn merge(&mut self, opt: SetupOpt) { - overwrite_if_some(&mut self.admin_username, opt.admin_username); - overwrite_if_some(&mut self.admin_password, opt.admin_password); - overwrite_if_some(&mut self.admin_email, opt.admin_email); - overwrite_if_some(&mut self.site_name, opt.site_name); +impl Merge for Option { + fn merge(self, opt: SetupOpt) -> Self { + if let Some(setup) = self { + Some(Setup { + admin_username: opt.admin_username.unwrap_or(setup.admin_username), + admin_password: opt.admin_password.unwrap_or(setup.admin_password), + admin_email: opt.admin_email.unwrap_or(setup.admin_email), + site_name: opt.site_name.unwrap_or(setup.site_name), + }) + } else if let (Some(admin_username), Some(admin_password), Some(site_name)) = + (opt.admin_username, opt.admin_password, opt.site_name) + { + Some(Setup { + admin_username, + admin_password, + admin_email: opt.admin_email.flatten(), + site_name, + }) + } else { + None + } } } -impl Merge for EmailConfig { - fn merge(&mut self, opt: EmailConfigOpt) { - overwrite_if_some(&mut self.smtp_server, opt.smtp_server); - overwrite_if_some(&mut self.smtp_login, opt.smtp_login); - overwrite_if_some(&mut self.smtp_password, opt.smtp_password); - overwrite_if_some(&mut self.smtp_from_address, opt.smtp_from_address); - overwrite_if_some(&mut self.use_tls, opt.use_tls); +impl Merge for Option { + fn merge(self, opt: EmailConfigOpt) -> Self { + if let Some(email_config) = self { + Some(EmailConfig { + smtp_server: opt.smtp_server.unwrap_or(email_config.smtp_server), + smtp_login: opt.smtp_login.unwrap_or(email_config.smtp_login), + smtp_password: opt.smtp_password.unwrap_or(email_config.smtp_password), + smtp_from_address: opt + .smtp_from_address + .unwrap_or(email_config.smtp_from_address), + use_tls: opt.use_tls.unwrap_or(email_config.use_tls), + }) + } else if let (Some(smtp_server), Some(smtp_from_address), Some(use_tls)) = + (opt.smtp_server, opt.smtp_from_address, opt.use_tls) + { + Some(EmailConfig { + smtp_server, + smtp_login: opt + .smtp_login + .or(self.clone().map(|s| s.smtp_login)) + .flatten(), + smtp_password: opt + .smtp_password + .or(self.map(|s| s.smtp_password)) + .flatten(), + smtp_from_address, + use_tls, + }) + } else { + None + } } } impl Merge for DatabaseConfig { - fn merge(&mut self, opt: DatabaseConfigOpt) { - overwrite_if_some(&mut self.user, opt.user); - overwrite_if_some(&mut self.password, opt.password); - overwrite_if_some(&mut self.host, opt.host); - overwrite_if_some(&mut self.port, opt.port); - overwrite_if_some(&mut self.database, opt.database); - overwrite_if_some(&mut self.pool_size, opt.pool_size); + fn merge(self, opt: DatabaseConfigOpt) -> Self { + DatabaseConfig { + user: opt.user.unwrap_or(self.user), + password: opt.password.unwrap_or(self.password), + host: opt.host.unwrap_or(self.host), + port: opt.port.unwrap_or(self.port), + database: opt.database.unwrap_or(self.database), + pool_size: opt.pool_size.unwrap_or(self.pool_size), + } } } impl Merge for FederationConfig { - fn merge(&mut self, opt: FederationConfigOpt) { - overwrite_if_some(&mut self.enabled, opt.enabled); - overwrite_if_some(&mut self.allowed_instances, opt.allowed_instances); - overwrite_if_some(&mut self.blocked_instances, opt.blocked_instances); + fn merge(self, opt: FederationConfigOpt) -> Self { + FederationConfig { + enabled: opt.enabled.unwrap_or(self.enabled), + allowed_instances: opt.allowed_instances.unwrap_or(self.allowed_instances), + blocked_instances: opt.blocked_instances.unwrap_or(self.blocked_instances), + } } } impl Merge for CaptchaConfig { - fn merge(&mut self, opt: CaptchaConfigOpt) { - overwrite_if_some(&mut self.enabled, opt.enabled); - overwrite_if_some(&mut self.difficulty, opt.difficulty); - } -} - -fn overwrite_if_some(lhs: &mut T, rhs: Option) { - if let Some(x) = rhs { - *lhs = x; - } -} - -fn merge_if_some(lhs: &mut T, rhs: Option) -where - T: Merge, -{ - if let Some(x) = rhs { - lhs.merge(x); - } -} - -fn merge_if_some_opt(lhs: &mut Option, rhs: Option) -where - T: Merge, -{ - if let Some(x) = rhs { - if let Some(y) = lhs { - y.merge(x) + fn merge(self, opt: CaptchaConfigOpt) -> Self { + CaptchaConfig { + enabled: opt.enabled.unwrap_or(self.enabled), + difficulty: opt.difficulty.unwrap_or(self.difficulty), } } } + +fn merge_structs(lhs: T, rhs: Option) -> T +where + T: Merge + std::clone::Clone, +{ + if let Some(x) = rhs { + lhs.merge(x) + } else { + lhs.to_owned() + } +} diff --git a/crates/utils/src/settings/mod.rs b/crates/utils/src/settings/mod.rs index 2111213ae..db3ebe89d 100644 --- a/crates/utils/src/settings/mod.rs +++ b/crates/utils/src/settings/mod.rs @@ -10,7 +10,7 @@ use crate::{ }; use anyhow::{anyhow, Context}; use deser_hjson::from_str; -use std::{env, fs, io::Error, sync::RwLock}; +use std::{env, fs, sync::RwLock}; pub mod defaults; mod environment; @@ -39,10 +39,12 @@ impl Settings { let mut config = Settings::default(); // Read the config file - config.merge(from_str::(&Self::read_config_file()?)?); + if let Some(config_file) = &Self::read_config_file() { + config = config.merge(from_str::(config_file)?); + } // Read env vars - config.merge(parse_from_env()); + config = config.merge(parse_from_env()); if config.hostname == Settings::default().hostname { return Err(anyhow!("Hostname variable is not set!").into()); @@ -68,8 +70,8 @@ impl Settings { env::var("LEMMY_CONFIG_LOCATION").unwrap_or_else(|_| CONFIG_FILE.to_string()) } - pub fn read_config_file() -> Result { - fs::read_to_string(Self::get_config_location()) + pub fn read_config_file() -> Option { + fs::read_to_string(Self::get_config_location()).ok() } pub fn get_allowed_instances(&self) -> Vec { @@ -133,7 +135,7 @@ impl Settings { ) } - pub fn save_config_file(data: &str) -> Result { + pub fn save_config_file(data: &str) -> Result { fs::write(CONFIG_FILE, data)?; // Reload the new settings @@ -144,6 +146,6 @@ impl Settings { Err(e) => panic!("{}", e), }; - Self::read_config_file() + Self::read_config_file().ok_or(anyhow!("Failed to read config").into()) } } diff --git a/docker/federation/docker-compose.yml b/docker/federation/docker-compose.yml index 72e6c5cec..6f47572c8 100644 --- a/docker/federation/docker-compose.yml +++ b/docker/federation/docker-compose.yml @@ -39,6 +39,7 @@ services: lemmy-alpha: image: lemmy-federation:latest environment: + - RUST_BACKTRACE=full - LEMMY_HOSTNAME=lemmy-alpha:8541 - LEMMY_DATABASE_URL=postgres://lemmy:password@postgres_alpha:5432/lemmy - LEMMY_JWT_SECRET=changeme diff --git a/docker/federation/start-local-instances.bash b/docker/federation/start-local-instances.bash index e963792ad..8de4348a3 100755 --- a/docker/federation/start-local-instances.bash +++ b/docker/federation/start-local-instances.bash @@ -8,4 +8,5 @@ for Item in alpha beta gamma delta epsilon ; do sudo chown -R 991:991 volumes/pictrs_$Item done -sudo docker-compose up +sudo docker-compose up -d +sudo docker-compose logs -f lemmy-alpha