#[serde_inline_default::serde_inline_default] #[derive(Debug, Clone, serde::Deserialize, serde::Serialize, serde_default::DefaultFromSerde)] pub struct Config { #[serde(default)] pub instance: InstanceConfig, #[serde(default)] pub datasource: DatasourceConfig, #[serde(default)] pub security: SecurityConfig, #[serde(default)] pub compat: CompatibilityConfig, #[serde(default)] pub files: FileStorageConfig, #[serde(default)] pub reject: RejectConfig, // TODO should i move app keys here? } #[serde_inline_default::serde_inline_default] #[derive(Debug, Clone, serde::Deserialize, serde::Serialize, serde_default::DefaultFromSerde)] pub struct InstanceConfig { #[serde_inline_default("μpub".into())] /// instance name, shown in noedinfo and instance actor pub name: String, #[serde_inline_default("micro social network, federated".into())] /// description, shown in nodeinfo and instance actor pub description: String, #[serde_inline_default("http://127.0.0.1:3000".into())] /// domain of current instance, must change this for prod pub domain: String, #[serde(default)] /// contact information for an administrator, currently unused pub contact: String, #[serde(default)] /// base url for frontend, will be used to compose pretty urls pub frontend: String, } #[serde_inline_default::serde_inline_default] #[derive(Debug, Clone, serde::Deserialize, serde::Serialize, serde_default::DefaultFromSerde)] pub struct DatasourceConfig { #[serde_inline_default("sqlite://./upub.db?mode=rwc".into())] pub connection_string: String, #[serde_inline_default(32)] pub max_connections: u32, #[serde_inline_default(1)] pub min_connections: u32, #[serde_inline_default(90u64)] pub connect_timeout_seconds: u64, #[serde_inline_default(30u64)] pub acquire_timeout_seconds: u64, #[serde_inline_default(10u64)] /// threshold for queries to be considered slow pub slow_query_warn_seconds: u64, #[serde_inline_default(true)] /// enable logging warn for slow queries pub slow_query_warn_enable: bool, } #[serde_inline_default::serde_inline_default] #[derive(Debug, Clone, serde::Deserialize, serde::Serialize, serde_default::DefaultFromSerde)] pub struct SecurityConfig { #[serde(default)] /// allow new users to register autonomously pub allow_registration: bool, #[serde(default)] // TODO i don't like the name of this /// newly registered users require manual activation pub require_user_approval: bool, #[serde(default)] /// allow anonymous users access to fetch debugger (explore screen) pub allow_public_debugger: bool, #[serde(default)] /// allow anonymous users to perform full-text searches pub allow_public_search: bool, #[serde_inline_default(30)] /// max time, in seconds, before requests fail with timeout pub request_timeout: u64, #[serde_inline_default("definitely-change-this-in-prod".to_string())] /// secret for media proxy, set this to something random pub proxy_secret: String, #[serde_inline_default(true)] /// allow expired tokens to be refreshed pub allow_login_refresh: bool, #[serde_inline_default(7 * 24)] /// how long do login sessions last pub session_duration_hours: i64, #[serde_inline_default(2)] /// how many times we allow an object to redirect pub max_id_redirects: u32, // TODO not sure it fits here #[serde_inline_default(20)] /// how deep should threads be crawled for fetching replies pub thread_crawl_depth: u32, // TODO doesn't really fit here #[serde_inline_default(30)] /// how long before a job is considered stale and dropped pub job_expiration_days: u32, // TODO doesn't really fit here #[serde_inline_default(100)] /// how many times to attempt inserting back incomplete jobs pub reinsertion_attempt_limit: u32, // TODO doesn't really fit here } #[serde_inline_default::serde_inline_default] #[derive(Debug, Clone, serde::Deserialize, serde::Serialize, serde_default::DefaultFromSerde)] pub struct CompatibilityConfig { #[serde_inline_default(true)] /// compatibility with almost everything: set image attachments as images pub fix_attachment_images_media_type: bool, #[serde_inline_default(true)] /// compatibility with mastodon and misskey (and somewhat lemmy?): notify like receiver pub add_explicit_target_to_likes_if_local: bool, #[serde_inline_default(true)] /// compatibility with lemmy: avoid showing images twice pub skip_single_attachment_if_image_is_set: bool, #[serde_inline_default(false)] /// compatibility with most relays: since they send us other server's activities, we must fetch /// them to verify that they aren't falsified by the relay itself. this is quite expensive, as /// relays send a lot of activities and we effectively end up fetching again all these, so this /// defaults to false pub verify_relayed_activities_by_fetching: bool, } #[serde_inline_default::serde_inline_default] #[derive(Debug, Clone, serde::Deserialize, serde::Serialize, serde_default::DefaultFromSerde)] pub struct FileStorageConfig { #[serde_inline_default("files/".to_string())] /// path where media files should be stored pub path: String, } #[serde_inline_default::serde_inline_default] #[derive(Debug, Clone, serde::Deserialize, serde::Serialize, serde_default::DefaultFromSerde)] pub struct RejectConfig { #[serde(default)] /// discard incoming activities from these instances pub incoming: Vec<String>, #[serde(default)] /// prevent fetching content from these instances pub fetch: Vec<String>, #[serde(default)] /// prevent content from these instances from being displayed publicly /// this effectively removes the public (aka NULL) addressing: only other addressees (followers, /// mentions) will be able to see content from these instances on timelines and directly pub public: Vec<String>, #[serde(default)] /// prevent proxying media coming from these instances pub media: Vec<String>, #[serde(default)] /// skip delivering to these instances pub delivery: Vec<String>, #[serde(default)] /// prevent fetching private content from these instances pub access: Vec<String>, #[serde(default)] /// reject any request from these instances (ineffective as they can still fetch anonymously) pub requests: Vec<String>, } impl Config { pub fn load(path: Option<&std::path::PathBuf>) -> Self { let Some(cfg_path) = path else { return Config::default() }; match std::fs::read_to_string(cfg_path) { Ok(x) => match toml::from_str(&x) { Ok(cfg) => return cfg, Err(e) => tracing::error!("failed parsing config file: {e}"), }, Err(e) => tracing::error!("failed reading config file: {e}"), } Config::default() } // TODO this is very magic... can we do better? maybe formalize frontend url as an attribute of // our application? pub fn frontend_url(&self, url: &str) -> Option<String> { if !self.instance.frontend.is_empty() { Some(format!("{}{url}", self.instance.frontend)) } else { None } } }