upub/core/src/config.rs
alemi 08fdc93d35
feat: optionally fetch and verify relayed activity
relays usually Announce(Create), so the Create is not from them but the
announce is, and it gets processed properly. Lemmy does the correct
thing: it sends Announce(...activity...), so the "topmost" activity
effectively comes from the sending server and can be verified. however
aode relay sends activities as-is, without wrapping. so if we receive
activities from someone else, it won't match the http signature and
we thus can't be sure this wasn't falsified. added an option to directly
fetch such cases. it's probably not great, so defaults to OFF
2025-01-28 13:55:20 +01:00

215 lines
6.8 KiB
Rust

#[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
}
}
}