Generate config docs from code (#1786)

* Generate config docs from code

* update library

* move settings comments from config.hjson to code

* updte doku to 0.10

* update doku
This commit is contained in:
Nutomic 2021-10-08 15:07:24 +00:00 committed by GitHub
parent 53a2b6d013
commit 454d3987a5
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 118 additions and 127 deletions

24
Cargo.lock generated
View file

@ -1000,6 +1000,28 @@ version = "1.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "31ad93652f40969dead8d4bf897a41e9462095152eb21c56e5830537e41179dd" checksum = "31ad93652f40969dead8d4bf897a41e9462095152eb21c56e5830537e41179dd"
[[package]]
name = "doku"
version = "0.10.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b44778199365a299b026bf1c16d80579adaeba3c484244e1561f3a93de43451a"
dependencies = [
"doku-derive",
"serde",
]
[[package]]
name = "doku-derive"
version = "0.10.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "949dc0bf36de2fe276d42c79ff81cf4326c45b98b091c0edec75e857b5775626"
dependencies = [
"darling 0.13.0",
"proc-macro2 1.0.29",
"quote 1.0.10",
"syn 1.0.80",
]
[[package]] [[package]]
name = "either" name = "either"
version = "1.6.1" version = "1.6.1"
@ -1924,6 +1946,7 @@ dependencies = [
"clokwerk", "clokwerk",
"diesel", "diesel",
"diesel_migrations", "diesel_migrations",
"doku",
"env_logger", "env_logger",
"http-signature-normalization-actix", "http-signature-normalization-actix",
"lemmy_api", "lemmy_api",
@ -1960,6 +1983,7 @@ dependencies = [
"comrak", "comrak",
"deser-hjson", "deser-hjson",
"diesel", "diesel",
"doku",
"futures", "futures",
"http", "http",
"itertools", "itertools",

View file

@ -64,6 +64,7 @@ activitystreams = "0.7.0-alpha.11"
actix-rt = { version = "2.2.0", default-features = false } actix-rt = { version = "2.2.0", default-features = false }
serde_json = { version = "1.0.68", features = ["preserve_order"] } serde_json = { version = "1.0.68", features = ["preserve_order"] }
clokwerk = "0.3.5" clokwerk = "0.3.5"
doku = "0.10.1"
[dev-dependencies.cargo-husky] [dev-dependencies.cargo-husky]
version = "1.5.0" version = "1.5.0"

View file

@ -1,95 +1,5 @@
# See the documentation for available config fields and descriptions:
# https://join-lemmy.org/docs/en/administration/configuration.html
{ {
# # optional: parameters for automatic configuration of new instance (only used at first start)
# setup: {
# # username for the admin user
# admin_username: ""
# # password for the admin user
# admin_password: ""
# # optional: email for the admin user (can be omitted and set later through the website)
# admin_email: ""
# # name of the site (can be changed later)
# site_name: ""
# }
# settings related to the postgresql database
database: {
# username to connect to postgres
user: "lemmy"
# password to connect to postgres
password: "password"
# host where postgres is running
host: "localhost"
# port where postgres can be accessed
port: 5432
# name of the postgres database for lemmy
database: "lemmy"
# maximum number of active sql connections
pool_size: 5
}
# the domain name of your instance (eg "lemmy.ml")
hostname: lemmy-alpha hostname: lemmy-alpha
# address where lemmy should listen for incoming requests
bind: "0.0.0.0"
# port where lemmy should listen for incoming requests
port: 8536
# whether tls is required for activitypub. only disable this for debugging, never for producion.
tls_enabled: true
# address where pictrs is available
pictrs_url: "http://pictrs:8080"
# maximum length of local community and user names
actor_name_max_length: 20
# rate limits for various user actions, by user ip
rate_limit: {
# maximum number of messages created in interval
message: 180
# interval length for message limit
message_per_second: 60
# maximum number of posts created in interval
post: 6
# interval length for post limit
post_per_second: 600
# maximum number of registrations in interval
register: 3
# interval length for registration limit
register_per_second: 3600
# maximum number of image uploads in interval
image: 6
# interval length for image uploads
image_per_second: 3600
}
# settings related to activitypub federation
federation: {
# whether to enable activitypub federation.
enabled: false
# Allows and blocks are described here:
# https://join-lemmy.org/docs/en/federation/administration.html#instance-allowlist-and-blocklist
#
# list of instances with which federation is allowed
# allowed_instances: ["instance1.tld","instance2.tld"]
# instances which we never federate anything with (but previously federated objects are unaffected)
# blocked_instances: []
# If true, only federate with instances on the allowlist and block everything else. If false,
# use allowlist only for remote communities, and posts/comments in local communities.
# strict_allowlist: true
}
captcha: {
enabled: true
difficulty: medium # Can be easy, medium, or hard
}
# # email sending configuration
# email: {
# # hostname and port of the smtp server
# smtp_server: ""
# # login name for smtp server
# smtp_login: ""
# # password to login to the smtp server
# smtp_password: ""
# # address to send emails from, eg "noreply@your-instance.com"
# smtp_from_address: ""
# # whether or not smtp connections should use tls
# use_tls: true
# }
# additional_slurs:
# '''
# (\bThis\b)|(\bis\b)|(\bsample\b)
# '''
} }

View file

@ -39,3 +39,4 @@ deser-hjson = "1.0.2"
smart-default = "0.6.0" smart-default = "0.6.0"
webpage = { version = "1.3.0", default-features = false, features = ["serde"] } webpage = { version = "1.3.0", default-features = false, features = ["serde"] }
jsonwebtoken = "7.2.0" jsonwebtoken = "7.2.0"
doku = "0.10.1"

View file

@ -11,6 +11,16 @@ static DEFAULT_CONFIG_FILE: &str = "config/config.hjson";
lazy_static! { lazy_static! {
static ref SETTINGS: RwLock<Settings> = static ref SETTINGS: RwLock<Settings> =
RwLock::new(Settings::init().expect("Failed to load settings file")); RwLock::new(Settings::init().expect("Failed to load settings file"));
static ref WEBFINGER_COMMUNITY_REGEX: Regex = Regex::new(&format!(
"^group:([a-z0-9_]{{3,}})@{}$",
Settings::get().hostname
))
.expect("compile webfinger regex");
static ref WEBFINGER_USER_REGEX: Regex = Regex::new(&format!(
"^acct:([a-z0-9_]{{3,}})@{}$",
Settings::get().hostname
))
.expect("compile webfinger regex");
} }
impl Settings { impl Settings {
@ -21,22 +31,12 @@ impl Settings {
/// Warning: Only call this once. /// Warning: Only call this once.
pub fn init() -> Result<Self, LemmyError> { pub fn init() -> Result<Self, LemmyError> {
// Read the config file // Read the config file
let mut config = from_str::<Settings>(&Self::read_config_file()?)?; let config = from_str::<Settings>(&Self::read_config_file()?)?;
if config.hostname == "unset" { if config.hostname == "unset" {
return Err(anyhow!("Hostname variable is not set!").into()); return Err(anyhow!("Hostname variable is not set!").into());
} }
// Initialize the regexes
config.webfinger_community_regex = Some(
Regex::new(&format!("^group:([a-z0-9_]{{3,}})@{}$", config.hostname))
.expect("compile webfinger regex"),
);
config.webfinger_username_regex = Some(
Regex::new(&format!("^acct:([a-z0-9_]{{3,}})@{}$", config.hostname))
.expect("compile webfinger regex"),
);
Ok(config) Ok(config)
} }
@ -106,17 +106,11 @@ impl Settings {
} }
pub fn webfinger_community_regex(&self) -> Regex { pub fn webfinger_community_regex(&self) -> Regex {
self WEBFINGER_COMMUNITY_REGEX.to_owned()
.webfinger_community_regex
.to_owned()
.expect("compile webfinger regex")
} }
pub fn webfinger_username_regex(&self) -> Regex { pub fn webfinger_username_regex(&self) -> Regex {
self WEBFINGER_USER_REGEX.to_owned()
.webfinger_username_regex
.to_owned()
.expect("compile webfinger regex")
} }
pub fn slur_regex(&self) -> Regex { pub fn slur_regex(&self) -> Regex {

View file

@ -1,118 +1,168 @@
use regex::Regex; use doku::Document;
use serde::Deserialize; use serde::{Deserialize, Serialize};
use std::net::{IpAddr, Ipv4Addr}; use std::net::{IpAddr, Ipv4Addr};
#[derive(Debug, Deserialize, Clone, SmartDefault)] #[derive(Debug, Deserialize, Serialize, Clone, SmartDefault, Document)]
#[serde(default)] #[serde(default)]
pub struct Settings { pub struct Settings {
/// settings related to the postgresql database
#[serde(default)] #[serde(default)]
pub database: DatabaseConfig, pub database: DatabaseConfig,
#[default(Some(RateLimitConfig::default()))] #[default(Some(RateLimitConfig::default()))]
/// rate limits for various user actions, by user ip
pub rate_limit: Option<RateLimitConfig>, pub rate_limit: Option<RateLimitConfig>,
/// Settings related to activitypub federation
#[default(FederationConfig::default())] #[default(FederationConfig::default())]
pub federation: FederationConfig, pub federation: FederationConfig,
#[default(CaptchaConfig::default())] #[default(CaptchaConfig::default())]
pub captcha: CaptchaConfig, pub captcha: CaptchaConfig,
/// Email sending configuration. All options except login/password are mandatory
#[default(None)] #[default(None)]
pub email: Option<EmailConfig>, pub email: Option<EmailConfig>,
/// Parameters for automatic configuration of new instance (only used at first start)
#[default(None)] #[default(None)]
pub setup: Option<SetupConfig>, pub setup: Option<SetupConfig>,
/// the domain name of your instance (mandatory)
#[default("unset")] #[default("unset")]
#[doku(example = "example.com")]
pub hostname: String, pub hostname: String,
/// Address where lemmy should listen for incoming requests
#[default(IpAddr::V4(Ipv4Addr::new(0, 0, 0, 0)))] #[default(IpAddr::V4(Ipv4Addr::new(0, 0, 0, 0)))]
#[doku(as = "String")]
pub bind: IpAddr, pub bind: IpAddr,
/// Port where lemmy should listen for incoming requests
#[default(8536)] #[default(8536)]
pub port: u16, pub port: u16,
/// Whether the site is available over TLS. Needs to be true for federation to work.
#[default(true)] #[default(true)]
pub tls_enabled: bool, pub tls_enabled: bool,
/// Address where pictrs is available (for image hosting)
#[default(None)] #[default(None)]
#[doku(example = "http://localhost:8080")]
pub pictrs_url: Option<String>, pub pictrs_url: Option<String>,
/// Regex for slurs which are prohibited. Example: `(\bThis\b)|(\bis\b)|(\bsample\b)`
#[default(None)] #[default(None)]
pub additional_slurs: Option<String>, pub additional_slurs: Option<String>,
/// Maximum length of local community and user names
#[default(20)] #[default(20)]
pub actor_name_max_length: usize, pub actor_name_max_length: usize,
#[default(None)]
#[serde(skip)]
pub webfinger_community_regex: Option<Regex>,
#[default(None)]
#[serde(skip)]
pub webfinger_username_regex: Option<Regex>,
} }
#[derive(Debug, Deserialize, Clone, SmartDefault)] #[derive(Debug, Deserialize, Serialize, Clone, SmartDefault, Document)]
#[serde(default)] #[serde(default)]
pub struct CaptchaConfig { pub struct CaptchaConfig {
/// Whether captcha is required for signup
#[default(false)] #[default(false)]
pub enabled: bool, pub enabled: bool,
/// Can be easy, medium, or hard
#[default("medium")] #[default("medium")]
pub difficulty: String, pub difficulty: String,
} }
#[derive(Debug, Deserialize, Clone, SmartDefault)] #[derive(Debug, Deserialize, Serialize, Clone, SmartDefault, Document)]
#[serde(default)] #[serde(default)]
pub struct DatabaseConfig { pub struct DatabaseConfig {
/// Username to connect to postgres
#[default("lemmy")] #[default("lemmy")]
pub(super) user: String, pub(super) user: String,
/// Password to connect to postgres
#[default("password")] #[default("password")]
pub password: String, pub password: String,
#[default("localhost")] #[default("localhost")]
/// Host where postgres is running
pub host: String, pub host: String,
/// Port where postgres can be accessed
#[default(5432)] #[default(5432)]
pub(super) port: i32, pub(super) port: i32,
/// Name of the postgres database for lemmy
#[default("lemmy")] #[default("lemmy")]
pub(super) database: String, pub(super) database: String,
/// Maximum number of active sql connections
#[default(5)] #[default(5)]
pub pool_size: u32, pub pool_size: u32,
} }
#[derive(Debug, Deserialize, Clone)] #[derive(Debug, Deserialize, Serialize, Clone, Document)]
pub struct EmailConfig { pub struct EmailConfig {
/// Hostname and port of the smtp server
#[doku(example = "localhost:25")]
pub smtp_server: String, pub smtp_server: String,
/// Login name for smtp server
pub smtp_login: Option<String>, pub smtp_login: Option<String>,
/// Password to login to the smtp server
pub smtp_password: Option<String>, pub smtp_password: Option<String>,
#[doku(example = "noreply@example.com")]
/// Address to send emails from, eg "noreply@your-instance.com"
pub smtp_from_address: String, pub smtp_from_address: String,
/// Whether or not smtp connections should use tls
pub use_tls: bool, pub use_tls: bool,
} }
#[derive(Debug, Deserialize, Clone, SmartDefault)] #[derive(Debug, Deserialize, Serialize, Clone, SmartDefault, Document)]
#[serde(default)] #[serde(default)]
pub struct FederationConfig { pub struct FederationConfig {
/// Whether to enable activitypub federation.
#[default(false)] #[default(false)]
pub enabled: bool, pub enabled: bool,
/// Allows and blocks are described here:
/// https://join-lemmy.org/docs/en/federation/administration.html///instance-allowlist-and-blocklist
///
/// list of instances with which federation is allowed
#[default(None)] #[default(None)]
#[doku(example = "instance1.tld")]
#[doku(example = "instance2.tld")]
pub allowed_instances: Option<Vec<String>>, pub allowed_instances: Option<Vec<String>>,
/// Instances which we never federate anything with (but previously federated objects are unaffected)
#[default(None)] #[default(None)]
pub blocked_instances: Option<Vec<String>>, pub blocked_instances: Option<Vec<String>>,
/// If true, only federate with instances on the allowlist and block everything else. If false,
/// use allowlist only for remote communities, and posts/comments in local communities
/// (meaning remote communities will show content from arbitrary instances).
#[default(true)] #[default(true)]
pub strict_allowlist: bool, pub strict_allowlist: bool,
} }
#[derive(Debug, Deserialize, Clone, SmartDefault)] #[derive(Debug, Deserialize, Serialize, Clone, SmartDefault, Document)]
#[serde(default)] #[serde(default)]
pub struct RateLimitConfig { pub struct RateLimitConfig {
/// Maximum number of messages created in interval
#[default(180)] #[default(180)]
pub message: i32, pub message: i32,
/// Interval length for message limit, in seconds
#[default(60)] #[default(60)]
pub message_per_second: i32, pub message_per_second: i32,
/// Maximum number of posts created in interval
#[default(6)] #[default(6)]
pub post: i32, pub post: i32,
/// Interval length for post limit, in seconds
#[default(600)] #[default(600)]
pub post_per_second: i32, pub post_per_second: i32,
/// Maximum number of registrations in interval
#[default(3)] #[default(3)]
pub register: i32, pub register: i32,
/// Interval length for registration limit, in seconds
#[default(3600)] #[default(3600)]
pub register_per_second: i32, pub register_per_second: i32,
/// Maximum number of image uploads in interval
#[default(6)] #[default(6)]
pub image: i32, pub image: i32,
/// Interval length for image uploads, in seconds
#[default(3600)] #[default(3600)]
pub image_per_second: i32, pub image_per_second: i32,
} }
#[derive(Debug, Deserialize, Clone, SmartDefault)] #[derive(Debug, Deserialize, Serialize, Clone, SmartDefault, Document)]
pub struct SetupConfig { pub struct SetupConfig {
/// Username for the admin user
#[doku(example = "admin")]
pub admin_username: String, pub admin_username: String,
/// Password for the admin user
#[doku(example = "my_passwd")]
pub admin_password: String, pub admin_password: String,
/// Name of the site (can be changed later)
#[doku(example = "My Lemmy Instance")]
pub site_name: String, pub site_name: String,
/// Email for the admin user (optional, can be omitted and set later through the website)
#[default(None)] #[default(None)]
pub admin_email: Option<String>, pub admin_email: Option<String>,
#[default(None)] #[default(None)]

View file

@ -7,6 +7,7 @@ use diesel::{
r2d2::{ConnectionManager, Pool}, r2d2::{ConnectionManager, Pool},
PgConnection, PgConnection,
}; };
use doku::json::{AutoComments, Formatting};
use lemmy_api::match_websocket_operation; use lemmy_api::match_websocket_operation;
use lemmy_api_common::blocking; use lemmy_api_common::blocking;
use lemmy_api_crud::match_websocket_operation_crud; use lemmy_api_crud::match_websocket_operation_crud;
@ -23,13 +24,23 @@ use lemmy_utils::{
}; };
use lemmy_websocket::{chat_server::ChatServer, LemmyContext}; use lemmy_websocket::{chat_server::ChatServer, LemmyContext};
use reqwest::Client; use reqwest::Client;
use std::{sync::Arc, thread}; use std::{env, sync::Arc, thread};
use tokio::sync::Mutex; use tokio::sync::Mutex;
embed_migrations!(); embed_migrations!();
#[actix_web::main] #[actix_web::main]
async fn main() -> Result<(), LemmyError> { async fn main() -> Result<(), LemmyError> {
let args: Vec<String> = env::args().collect();
if args.len() == 2 && args[1] == "--print-config-docs" {
let fmt = Formatting {
auto_comments: AutoComments::none(),
..Default::default()
};
println!("{}", doku::to_json_fmt_val(&fmt, &Settings::default()));
return Ok(());
}
env_logger::init(); env_logger::init();
let settings = Settings::init().expect("Couldn't initialize settings."); let settings = Settings::init().expect("Couldn't initialize settings.");