Rewrite config implementation

This commit is contained in:
Felix Ableitner 2021-02-19 15:28:08 +01:00
parent f83b5590c7
commit 1bc7317b24
41 changed files with 383 additions and 376 deletions

59
Cargo.lock generated
View file

@ -1,5 +1,7 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 3
[[package]]
name = "activitystreams"
version = "0.7.0-alpha.10"
@ -1101,6 +1103,15 @@ dependencies = [
"termcolor",
]
[[package]]
name = "envy"
version = "0.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3f47e0157f2cb54f5ae1bd371b30a2ae4311e1c028f575cd4e81de7353215965"
dependencies = [
"serde",
]
[[package]]
name = "event-listener"
version = "2.5.1"
@ -1953,6 +1964,7 @@ dependencies = [
"comrak",
"deser-hjson",
"diesel",
"envy",
"futures",
"http",
"itertools",
@ -1960,7 +1972,6 @@ dependencies = [
"lazy_static",
"lettre",
"log",
"merge",
"openssl",
"percent-encoding",
"rand 0.8.3",
@ -2113,28 +2124,6 @@ dependencies = [
"autocfg",
]
[[package]]
name = "merge"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "10bbef93abb1da61525bbc45eeaff6473a41907d19f8f9aa5168d214e10693e9"
dependencies = [
"merge_derive",
"num-traits",
]
[[package]]
name = "merge_derive"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "209d075476da2e63b4b29e72a2ef627b840589588e71400a25e3565c4f849d07"
dependencies = [
"proc-macro-error",
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "migrations_internals"
version = "1.4.1"
@ -2568,30 +2557,6 @@ dependencies = [
"vcpkg",
]
[[package]]
name = "proc-macro-error"
version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c"
dependencies = [
"proc-macro-error-attr",
"proc-macro2",
"quote",
"syn",
"version_check",
]
[[package]]
name = "proc-macro-error-attr"
version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869"
dependencies = [
"proc-macro2",
"quote",
"version_check",
]
[[package]]
name = "proc-macro-hack"
version = "0.5.19"

View file

@ -1,6 +1,13 @@
#!/bin/bash
set -e
export LEMMY_JWT_SECRET=changeme
export LEMMY_FEDERATION__ENABLED=true
export LEMMY_TLS_ENABLED=false
export LEMMY_SETUP__ADMIN_PASSWORD=lemmy
export LEMMY_RATE_LIMIT__POST=99999
export LEMMY_RATE_LIMIT__REGISTER=99999
export LEMMY_CAPTCHA__ENABLED=false
export LEMMY_TEST_SEND_SYNC=1
export RUST_BACKTRACE=1
@ -28,39 +35,52 @@ fi
killall lemmy_server || true
echo "$PWD"
echo "start alpha"
LEMMY_HOSTNAME=lemmy-alpha:8541 \
LEMMY_CONFIG_LOCATION=./docker/federation/lemmy_alpha.hjson \
LEMMY_PORT=8541 \
LEMMY_DATABASE_URL="${LEMMY_DATABASE_URL}/lemmy_alpha" \
target/lemmy_server >/tmp/lemmy_alpha.out 2>&1 &
LEMMY_FEDERATION__ALLOWED_INSTANCES=lemmy-beta,lemmy-gamma,lemmy-delta,lemmy-epsilon \
LEMMY_SETUP__ADMIN_USERNAME=lemmy_alpha \
LEMMY_SETUP__SITE_NAME=lemmy-alpha \
target/lemmy_server >/dev/null 2>&1 &
echo "start beta"
LEMMY_HOSTNAME=lemmy-beta:8551 \
LEMMY_CONFIG_LOCATION=./docker/federation/lemmy_beta.hjson \
LEMMY_PORT=8551 \
LEMMY_DATABASE_URL="${LEMMY_DATABASE_URL}/lemmy_beta" \
target/lemmy_server >/tmp/lemmy_beta.out 2>&1 &
LEMMY_FEDERATION__ALLOWED_INSTANCES=lemmy-alpha,lemmy-gamma,lemmy-delta,lemmy-epsilon \
LEMMY_SETUP__ADMIN_USERNAME=lemmy_beta \
LEMMY_SETUP__SITE_NAME=lemmy-beta \
target/lemmy_server >/dev/null 2>&1 &
echo "start gamma"
LEMMY_HOSTNAME=lemmy-gamma:8561 \
LEMMY_CONFIG_LOCATION=./docker/federation/lemmy_gamma.hjson \
LEMMY_PORT=8561 \
LEMMY_DATABASE_URL="${LEMMY_DATABASE_URL}/lemmy_gamma" \
target/lemmy_server >/tmp/lemmy_gamma.out 2>&1 &
LEMMY_FEDERATION__ALLOWED_INSTANCES=lemmy-alpha,lemmy-beta,lemmy-delta,lemmy-epsilon \
LEMMY_SETUP__ADMIN_USERNAME=lemmy_gamma \
LEMMY_SETUP__SITE_NAME=lemmy-gamma \
target/lemmy_server >/dev/null 2>&1 &
echo "start delta"
# An instance with only an allowlist for beta
LEMMY_HOSTNAME=lemmy-delta:8571 \
LEMMY_CONFIG_LOCATION=./docker/federation/lemmy_delta.hjson \
LEMMY_PORT=8571 \
LEMMY_DATABASE_URL="${LEMMY_DATABASE_URL}/lemmy_delta" \
target/lemmy_server >/tmp/lemmy_delta.out 2>&1 &
LEMMY_FEDERATION__ALLOWED_INSTANCES=lemmy-beta \
LEMMY_SETUP__ADMIN_USERNAME=lemmy_delta \
LEMMY_SETUP__SITE_NAME=lemmy-delta \
target/lemmy_server >/dev/null 2>&1 &
echo "start epsilon"
# An instance who has a blocklist, with lemmy-alpha blocked
LEMMY_HOSTNAME=lemmy-epsilon:8581 \
LEMMY_CONFIG_LOCATION=./docker/federation/lemmy_epsilon.hjson \
LEMMY_PORT=8581 \
LEMMY_DATABASE_URL="${LEMMY_DATABASE_URL}/lemmy_epsilon" \
target/lemmy_server >/tmp/lemmy_epsilon.out 2>&1 &
LEMMY_FEDERATION__BLOCKED_INSTANCES=lemmy-alpha \
LEMMY_SETUP__ADMIN_USERNAME=lemmy_epsilon \
LEMMY_SETUP__SITE_NAME=lemmy-epsilon \
target/lemmy_server >/dev/null 2>&1 &
echo "wait for all instances to start"
while [[ "$(curl -s -o /dev/null -w '%{http_code}' 'localhost:8541/api/v2/site')" != "200" ]]; do sleep 1; done

View file

@ -190,7 +190,7 @@ pub(crate) fn check_optional_url(item: &Option<Option<String>>) -> Result<(), Le
pub(crate) async fn build_federated_instances(
pool: &DbPool,
) -> Result<Option<FederatedInstances>, LemmyError> {
if Settings::get().federation.unwrap_or_default().enabled {
if Settings::get().federation.enabled {
let distinct_communities = blocking(pool, move |conn| {
Community::distinct_federated_communities(conn)
})
@ -205,9 +205,7 @@ pub(crate) async fn build_federated_instances(
.collect::<Result<Vec<String>, LemmyError>>()?;
linked.extend_from_slice(&allowed);
linked.retain(|a| {
!blocked.contains(a) && !a.eq("") && !a.eq(&Settings::get().hostname.unwrap_or_default())
});
linked.retain(|a| !blocked.contains(a) && !a.eq("") && !a.eq(&Settings::get().hostname));
// Sort and remove dupes
linked.sort_unstable();

View file

@ -121,7 +121,7 @@ impl Perform for Login {
// Return the jwt
Ok(LoginResponse {
jwt: Claims::jwt(user.id, Settings::get().hostname.unwrap_or_default())?,
jwt: Claims::jwt(user.id, Settings::get().hostname)?,
})
}
}
@ -161,7 +161,7 @@ impl Perform for Register {
.await??;
// If its not the admin, check the captcha
if !no_admins && Settings::get().captcha.unwrap_or_default().enabled {
if !no_admins && Settings::get().captcha.enabled {
let check = context
.chat_server()
.send(CheckCaptcha {
@ -303,10 +303,7 @@ impl Perform for Register {
// Return the jwt
Ok(LoginResponse {
jwt: Claims::jwt(
inserted_user.id,
Settings::get().hostname.unwrap_or_default(),
)?,
jwt: Claims::jwt(inserted_user.id, Settings::get().hostname)?,
})
}
}
@ -320,7 +317,7 @@ impl Perform for GetCaptcha {
context: &Data<LemmyContext>,
_websocket_id: Option<ConnectionId>,
) -> Result<Self::Response, LemmyError> {
let captcha_settings = Settings::get().captcha.unwrap_or_default();
let captcha_settings = Settings::get().captcha;
if !captcha_settings.enabled {
return Ok(GetCaptchaResponse { ok: None });
@ -479,10 +476,7 @@ impl Perform for SaveUserSettings {
// Return the jwt
Ok(LoginResponse {
jwt: Claims::jwt(
updated_user.id,
Settings::get().hostname.unwrap_or_default(),
)?,
jwt: Claims::jwt(updated_user.id, Settings::get().hostname)?,
})
}
}
@ -1018,10 +1012,7 @@ impl Perform for PasswordChange {
// Return the jwt
Ok(LoginResponse {
jwt: Claims::jwt(
updated_user.id,
Settings::get().hostname.unwrap_or_default(),
)?,
jwt: Claims::jwt(updated_user.id, Settings::get().hostname)?,
})
}
}

View file

@ -88,7 +88,7 @@ where
.await?
.iter()
.unique()
.filter(|inbox| inbox.host_str() != Some(&Settings::get().hostname.unwrap_or_default()))
.filter(|inbox| inbox.host_str() != Some(&Settings::get().hostname))
.filter(|inbox| check_is_apub_id_valid(inbox).is_ok())
.map(|inbox| inbox.to_owned())
.collect();
@ -215,7 +215,7 @@ where
Kind: Serialize,
<T as Extends<Kind>>::Error: From<serde_json::Error> + Send + Sync + 'static,
{
if !Settings::get().federation.unwrap_or_default().enabled || inboxes.is_empty() {
if !Settings::get().federation.enabled || inboxes.is_empty() {
return Ok(());
}

View file

@ -41,8 +41,8 @@ use log::debug;
use url::Url;
/// The types of ActivityPub objects that can be fetched directly by searching for their ID.
#[serde(untagged)]
#[derive(serde::Deserialize, Debug)]
#[serde(untagged)]
enum SearchAcceptedObjects {
Person(Box<PersonExt>),
Group(Box<GroupExt>),

View file

@ -167,7 +167,7 @@ where
let id = activity.id_unchecked().context(location_info!())?;
let activity_domain = id.domain().context(location_info!())?;
if activity_domain == Settings::get().hostname.unwrap_or_default() {
if activity_domain == Settings::get().hostname {
return Err(
anyhow!(
"Error: received activity which was sent by local instance: {:?}",

View file

@ -64,7 +64,7 @@ fn check_is_apub_id_valid(apub_id: &Url) -> Result<(), LemmyError> {
let domain = apub_id.domain().context(location_info!())?.to_string();
let local_instance = settings.get_hostname_without_port()?;
if !settings.federation.unwrap_or_default().enabled {
if !settings.federation.enabled {
return if domain == local_instance {
Ok(())
} else {

View file

@ -194,7 +194,7 @@ where
let domain = object_id.domain().context(location_info!())?;
// if its a local object, return it directly from the database
if Settings::get().hostname.unwrap_or_default() == domain {
if Settings::get().hostname == domain {
let object = blocking(context.pool(), move |conn| {
To::read_from_apub_id(conn, &object_id.into())
})

View file

@ -99,7 +99,7 @@ impl FromApub for User_ {
) -> Result<User_, LemmyError> {
let user_id = person.id_unchecked().context(location_info!())?.to_owned();
let domain = user_id.domain().context(location_info!())?;
if domain == Settings::get().hostname.unwrap_or_default() {
if domain == Settings::get().hostname {
let user = blocking(context.pool(), move |conn| {
User_::read_from_apub_id(conn, &user_id.into())
})

View file

@ -23,11 +23,8 @@ static APUB_JSON_CONTENT_TYPE_LONG: &str =
"application/ld+json; profile=\"https://www.w3.org/ns/activitystreams\"";
pub fn config(cfg: &mut web::ServiceConfig) {
if Settings::get().federation.unwrap_or_default().enabled {
println!(
"federation enabled, host is {}",
Settings::get().hostname.unwrap_or_default()
);
if Settings::get().federation.enabled {
println!("federation enabled, host is {}", Settings::get().hostname);
let digest_verifier = VerifyDigest::new(Sha256::new());
let header_guard_accept = guard::Any(guard::Header("Accept", APUB_JSON_CONTENT_TYPE))

View file

@ -168,7 +168,7 @@ fn get_feed_user(
) -> Result<ChannelBuilder, LemmyError> {
let site_view = SiteView::read(&conn)?;
let user = User_::find_by_username(&conn, &user_name)?;
let user_url = user.get_profile_url(&Settings::get().hostname.unwrap_or_default());
let user_url = user.get_profile_url(&Settings::get().hostname);
let posts = PostQueryBuilder::create(&conn)
.listing_type(&ListingType::All)

View file

@ -54,10 +54,8 @@ async fn upload(
return Ok(HttpResponse::Unauthorized().finish());
};
let mut client_req = client.request_from(
format!("{}/image", Settings::get().pictrs_url.unwrap_or_default()),
req.head(),
);
let mut client_req =
client.request_from(format!("{}/image", Settings::get().pictrs_url), req.head());
if let Some(addr) = req.head().peer_addr {
client_req = client_req.header("X-Forwarded-For", addr.to_string())
@ -80,18 +78,14 @@ async fn full_res(
// If there are no query params, the URL is original
let url = if params.format.is_none() && params.thumbnail.is_none() {
format!(
"{}/image/original/{}",
Settings::get().pictrs_url.unwrap_or_default(),
name,
)
format!("{}/image/original/{}", Settings::get().pictrs_url, name,)
} else {
// Use jpg as a default when none is given
let format = params.format.unwrap_or_else(|| "jpg".to_string());
let mut url = format!(
"{}/image/process.{}?src={}",
Settings::get().pictrs_url.unwrap_or_default(),
Settings::get().pictrs_url,
format,
name,
);
@ -140,7 +134,7 @@ async fn delete(
let url = format!(
"{}/image/delete/{}/{}",
Settings::get().pictrs_url.unwrap_or_default(),
Settings::get().pictrs_url,
&token,
&file
);

View file

@ -31,7 +31,7 @@ async fn node_info(context: web::Data<LemmyContext>) -> Result<HttpResponse, Err
.await?
.map_err(|_| ErrorBadRequest(LemmyError::from(anyhow!("not_found"))))?;
let protocols = if Settings::get().federation.unwrap_or_default().enabled {
let protocols = if Settings::get().federation.enabled {
vec!["activitypub".to_string()]
} else {
vec![]

View file

@ -18,7 +18,7 @@ struct Params {
}
pub fn config(cfg: &mut web::ServiceConfig) {
if Settings::get().federation.unwrap_or_default().enabled {
if Settings::get().federation.enabled {
cfg.route(
".well-known/webfinger",
web::get().to(get_webfinger_response),

View file

@ -152,7 +152,7 @@ pub fn send_email_to_user(user: User_, subject_text: &str, body_text: &str, comm
let subject = &format!(
"{} - {} {}",
subject_text,
Settings::get().hostname.unwrap_or_default(),
Settings::get().hostname,
user.name,
);
let html = &format!(

View file

@ -34,4 +34,4 @@ diesel = "1.4.5"
http = "0.2.3"
jsonwebtoken = "7.2.0"
deser-hjson = "0.1.12"
merge = "0.1.0"
envy = "0.4"

View file

@ -18,7 +18,7 @@ impl Claims {
};
decode::<Claims>(
&jwt,
&DecodingKey::from_secret(Settings::get().jwt_secret.unwrap_or_default().as_ref()),
&DecodingKey::from_secret(Settings::get().jwt_secret.as_ref()),
&v,
)
}
@ -31,7 +31,7 @@ impl Claims {
encode(
&Header::default(),
&my_claims,
&EncodingKey::from_secret(Settings::get().jwt_secret.unwrap_or_default().as_ref()),
&EncodingKey::from_secret(Settings::get().jwt_secret.as_ref()),
)
}
}

View file

@ -20,7 +20,7 @@ pub fn send_email(
html: &str,
) -> Result<(), String> {
let email_config = Settings::get().email.ok_or("no_email_setup")?;
let domain = Settings::get().hostname.unwrap_or_default();
let domain = Settings::get().hostname;
let (smtp_server, smtp_port) = {
let email_and_port = email_config.smtp_server.split(':').collect::<Vec<&str>>();

View file

@ -83,12 +83,12 @@ impl actix_web::error::ResponseError for LemmyError {
lazy_static! {
pub static ref WEBFINGER_COMMUNITY_REGEX: Regex = Regex::new(&format!(
"^group:([a-z0-9_]{{3, 20}})@{}$",
Settings::get().hostname.unwrap_or_default()
Settings::get().hostname
))
.unwrap();
pub static ref WEBFINGER_USER_REGEX: Regex = Regex::new(&format!(
"^acct:([a-z0-9_]{{3, 20}})@{}$",
Settings::get().hostname.unwrap_or_default()
Settings::get().hostname
))
.unwrap();
}

View file

@ -70,7 +70,7 @@ impl RateLimited {
{
// Does not need to be blocking because the RwLock in settings never held across await points,
// and the operation here locks only long enough to clone
let rate_limit: RateLimitConfig = Settings::get().rate_limit.unwrap_or_default();
let rate_limit: RateLimitConfig = Settings::get().rate_limit;
// before
{

View file

@ -58,11 +58,7 @@ pub(crate) async fn fetch_iframely(
client: &Client,
url: &str,
) -> Result<IframelyResponse, LemmyError> {
let fetch_url = format!(
"{}/oembed?url={}",
Settings::get().iframely_url.unwrap_or_default(),
url
);
let fetch_url = format!("{}/oembed?url={}", Settings::get().iframely_url, url);
let response = retry(|| client.get(&fetch_url).send()).await?;
@ -93,7 +89,7 @@ pub(crate) async fn fetch_pictrs(
let fetch_url = format!(
"{}/image/download?url={}",
Settings::get().pictrs_url.unwrap_or_default(),
Settings::get().pictrs_url,
utf8_percent_encode(image_url, NON_ALPHANUMERIC) // TODO this might not be needed
);

View file

@ -4,19 +4,20 @@ use std::net::{IpAddr, Ipv4Addr};
impl Default for Settings {
fn default() -> Self {
Self {
database: Some(DatabaseConfig::default()),
rate_limit: Some(RateLimitConfig::default()),
federation: Some(FederationConfig::default()),
captcha: Some(CaptchaConfig::default()),
database: DatabaseConfig::default(),
rate_limit: RateLimitConfig::default(),
federation: FederationConfig::default(),
captcha: CaptchaConfig::default(),
email: None,
setup: None,
hostname: None,
bind: Some(IpAddr::V4(Ipv4Addr::new(0, 0, 0, 0))),
port: Some(8536),
tls_enabled: Some(true),
jwt_secret: Some("changeme".into()),
pictrs_url: Some("http://pictrs:8080".into()),
iframely_url: Some("http://iframely".into()),
// TODO: not sure how to handle this, its mandatory but not provided by defaults
hostname: "hostname_not_set".into(),
bind: IpAddr::V4(Ipv4Addr::new(0, 0, 0, 0)),
port: 8536,
tls_enabled: true,
jwt_secret: "changeme".into(),
pictrs_url: "http://pictrs:8080".into(),
iframely_url: "http://iframely".into(),
}
}
}
@ -62,8 +63,8 @@ impl Default for FederationConfig {
fn default() -> Self {
Self {
enabled: false,
allowed_instances: Some("".into()),
blocked_instances: Some("".into()),
allowed_instances: "".into(),
blocked_instances: "".into(),
}
}
}

View file

@ -0,0 +1,107 @@
use crate::settings::{structs::*, structs_opt::*};
pub(in crate::settings) trait Merge<T> {
fn merge(&mut self, opt: T);
}
impl Merge<SettingsOpt> 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);
}
}
impl Merge<RateLimitConfigOpt> 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);
}
}
impl Merge<SetupOpt> 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<EmailConfigOpt> 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<DatabaseConfigOpt> 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);
}
}
impl Merge<FederationConfigOpt> 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);
}
}
impl Merge<CaptchaConfigOpt> 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<T>(lhs: &mut T, rhs: Option<T>) {
if let Some(x) = rhs {
*lhs = x;
}
}
fn merge_if_some<T, U>(lhs: &mut T, rhs: Option<U>)
where
T: Merge<U>,
{
if let Some(x) = rhs {
lhs.merge(x);
}
}
fn merge_if_some_opt<T, U>(lhs: &mut Option<T>, rhs: Option<U>)
where
T: Merge<U>,
{
if let Some(x) = rhs {
if let Some(y) = lhs {
y.merge(x)
}
}
}

View file

@ -1,11 +1,16 @@
use crate::{location_info, settings::structs::Settings, LemmyError};
use anyhow::Context;
use crate::{
location_info,
settings::{merge::Merge, structs::Settings, structs_opt::SettingsOpt},
LemmyError,
};
use anyhow::{anyhow, Context};
use deser_hjson::from_str;
use merge::Merge;
use std::{env, fs, io::Error, sync::RwLock};
pub mod defaults;
mod merge;
pub mod structs;
mod structs_opt;
static CONFIG_FILE: &str = "config/config.hjson";
@ -25,13 +30,19 @@ impl Settings {
/// Note: The env var `LEMMY_DATABASE_URL` is parsed in
/// `lemmy_db_queries/src/lib.rs::get_database_url_from_env()`
fn init() -> Result<Self, LemmyError> {
let mut config = Settings::default();
// Read the config file
let mut custom_config = from_str::<Settings>(&Self::read_config_file()?)?;
config.merge(from_str::<SettingsOpt>(&Self::read_config_file()?)?);
// Merge with default
custom_config.merge(Settings::default());
// Read env vars
config.merge(envy::prefixed("LEMMY_").from_env::<SettingsOpt>()?);
Ok(custom_config)
if config.hostname == Settings::default().hostname {
return Err(anyhow!("Hostname variable is not set!").into());
}
Ok(config)
}
/// Returns the config as a struct.
@ -40,7 +51,7 @@ impl Settings {
}
pub fn get_database_url(&self) -> String {
let conf = self.database.to_owned().unwrap_or_default();
let conf = self.database.to_owned();
format!(
"postgres://{}:{}@{}:{}/{}",
conf.user, conf.password, conf.host, conf.port, conf.database,
@ -59,9 +70,7 @@ impl Settings {
let mut allowed_instances: Vec<String> = self
.federation
.to_owned()
.unwrap_or_default()
.allowed_instances
.unwrap_or_default()
.split(',')
.map(|d| d.trim().to_string())
.collect();
@ -74,9 +83,7 @@ impl Settings {
let mut blocked_instances: Vec<String> = self
.federation
.to_owned()
.unwrap_or_default()
.blocked_instances
.unwrap_or_default()
.split(',')
.map(|d| d.trim().to_string())
.collect();
@ -87,12 +94,8 @@ impl Settings {
/// Returns either "http" or "https", depending on tls_enabled setting
pub fn get_protocol_string(&self) -> &'static str {
if let Some(tls_enabled) = self.tls_enabled {
if tls_enabled {
"https"
} else {
"http"
}
if self.tls_enabled {
"https"
} else {
"http"
}
@ -104,7 +107,7 @@ impl Settings {
format!(
"{}://{}",
self.get_protocol_string(),
self.hostname.to_owned().unwrap_or_default()
self.hostname.to_owned()
)
}
@ -116,7 +119,6 @@ impl Settings {
self
.hostname
.to_owned()
.unwrap_or_default()
.split(':')
.collect::<Vec<&str>>()
.first()

View file

@ -1,22 +1,21 @@
use merge::Merge;
use serde::Deserialize;
use std::net::IpAddr;
#[derive(Debug, Deserialize, Clone, Merge)]
#[derive(Debug, Deserialize, Clone)]
pub struct Settings {
pub setup: Option<Setup>,
pub database: Option<DatabaseConfig>,
pub hostname: Option<String>,
pub bind: Option<IpAddr>,
pub port: Option<u16>,
pub tls_enabled: Option<bool>,
pub jwt_secret: Option<String>,
pub pictrs_url: Option<String>,
pub iframely_url: Option<String>,
pub rate_limit: Option<RateLimitConfig>,
pub database: DatabaseConfig,
pub hostname: String,
pub bind: IpAddr,
pub port: u16,
pub tls_enabled: bool,
pub jwt_secret: String,
pub pictrs_url: String,
pub iframely_url: String,
pub rate_limit: RateLimitConfig,
pub email: Option<EmailConfig>,
pub federation: Option<FederationConfig>,
pub captcha: Option<CaptchaConfig>,
pub federation: FederationConfig,
pub captcha: CaptchaConfig,
}
#[derive(Debug, Deserialize, Clone)]
@ -51,6 +50,7 @@ pub struct EmailConfig {
#[derive(Debug, Deserialize, Clone)]
pub struct CaptchaConfig {
pub enabled: bool,
// TODO: use enum for this
pub difficulty: String,
}
@ -67,6 +67,6 @@ pub struct DatabaseConfig {
#[derive(Debug, Deserialize, Clone)]
pub struct FederationConfig {
pub enabled: bool,
pub allowed_instances: Option<String>,
pub blocked_instances: Option<String>,
pub allowed_instances: String,
pub blocked_instances: String,
}

View file

@ -0,0 +1,71 @@
use serde::Deserialize;
use std::net::IpAddr;
#[derive(Debug, Deserialize, Clone)]
pub struct SettingsOpt {
pub setup: Option<SetupOpt>,
pub database: Option<DatabaseConfigOpt>,
pub hostname: Option<String>,
pub bind: Option<IpAddr>,
pub port: Option<u16>,
pub tls_enabled: Option<bool>,
pub jwt_secret: Option<String>,
pub pictrs_url: Option<String>,
pub iframely_url: Option<String>,
pub rate_limit: Option<RateLimitConfigOpt>,
pub email: Option<EmailConfigOpt>,
pub federation: Option<FederationConfigOpt>,
pub captcha: Option<CaptchaConfigOpt>,
}
#[derive(Debug, Deserialize, Clone)]
pub struct SetupOpt {
pub admin_username: Option<String>,
pub admin_password: Option<String>,
pub admin_email: Option<Option<String>>,
pub site_name: Option<String>,
}
#[derive(Debug, Deserialize, Clone)]
pub struct RateLimitConfigOpt {
pub message: Option<i32>,
pub message_per_second: Option<i32>,
pub post: Option<i32>,
pub post_per_second: Option<i32>,
pub register: Option<i32>,
pub register_per_second: Option<i32>,
pub image: Option<i32>,
pub image_per_second: Option<i32>,
}
#[derive(Debug, Deserialize, Clone)]
pub struct EmailConfigOpt {
pub smtp_server: Option<String>,
pub smtp_login: Option<Option<String>>,
pub smtp_password: Option<Option<String>>,
pub smtp_from_address: Option<String>,
pub use_tls: Option<bool>,
}
#[derive(Debug, Deserialize, Clone)]
pub struct CaptchaConfigOpt {
pub enabled: Option<bool>,
pub difficulty: Option<String>,
}
#[derive(Debug, Deserialize, Clone)]
pub struct DatabaseConfigOpt {
pub user: Option<String>,
pub password: Option<String>,
pub host: Option<String>,
pub port: Option<i32>,
pub database: Option<String>,
pub pool_size: Option<u32>,
}
#[derive(Debug, Deserialize, Clone)]
pub struct FederationConfigOpt {
pub enabled: Option<bool>,
pub allowed_instances: Option<String>,
pub blocked_instances: Option<String>,
}

View file

@ -85,10 +85,7 @@ pub struct MentionData {
impl MentionData {
pub fn is_local(&self) -> bool {
Settings::get()
.hostname
.unwrap_or_default()
.eq(&self.domain)
Settings::get().hostname.eq(&self.domain)
}
pub fn full_name(&self) -> String {
format!("@{}@{}", &self.name, &self.domain)

View file

@ -56,6 +56,7 @@ RUN addgroup -g 1000 lemmy
RUN adduser -D -s /bin/sh -u 1000 -G lemmy lemmy
# Copy resources
COPY --chown=lemmy:lemmy config/defaults.hjson /config/defaults.hjson
COPY --chown=lemmy:lemmy --from=builder /app/lemmy_server /app/lemmy
RUN chown lemmy:lemmy /app/lemmy

View file

@ -38,9 +38,20 @@ services:
- lemmy-alpha
lemmy-alpha:
image: lemmy-federation:latest
volumes:
- ./lemmy_alpha.hjson:/config/config.hjson
environment:
- LEMMY_HOSTNAME=lemmy-alpha:8541
- LEMMY_DATABASE_URL=postgres://lemmy:password@postgres_alpha:5432/lemmy
- LEMMY_JWT_SECRET=changeme
- LEMMY_FEDERATION__ENABLED=true
- LEMMY_TLS_ENABLED=false
- LEMMY_FEDERATION__ALLOWED_INSTANCES=lemmy-beta,lemmy-gamma,lemmy-delta,lemmy-epsilon
- LEMMY_PORT=8541
- LEMMY_SETUP__ADMIN_USERNAME=lemmy_alpha
- LEMMY_SETUP__ADMIN_PASSWORD=lemmy
- LEMMY_SETUP__SITE_NAME=lemmy-alpha
- LEMMY_RATE_LIMIT__POST=99999
- LEMMY_RATE_LIMIT__REGISTER=99999
- LEMMY_CAPTCHA__ENABLED=false
- LEMMY_TEST_SEND_SYNC=1
- RUST_BACKTRACE=1
- RUST_LOG=debug
@ -67,9 +78,20 @@ services:
- lemmy-beta
lemmy-beta:
image: lemmy-federation:latest
volumes:
- ./lemmy_beta.hjson:/config/config.hjson
environment:
- LEMMY_HOSTNAME=lemmy-beta:8551
- LEMMY_DATABASE_URL=postgres://lemmy:password@postgres_beta:5432/lemmy
- LEMMY_JWT_SECRET=changeme
- LEMMY_FEDERATION__ENABLED=true
- LEMMY_TLS_ENABLED=false
- LEMMY_FEDERATION__ALLOWED_INSTANCES=lemmy-alpha,lemmy-gamma,lemmy-delta,lemmy-epsilon
- LEMMY_PORT=8551
- LEMMY_SETUP__ADMIN_USERNAME=lemmy_beta
- LEMMY_SETUP__ADMIN_PASSWORD=lemmy
- LEMMY_SETUP__SITE_NAME=lemmy-beta
- LEMMY_RATE_LIMIT__POST=99999
- LEMMY_RATE_LIMIT__REGISTER=99999
- LEMMY_CAPTCHA__ENABLED=false
- LEMMY_TEST_SEND_SYNC=1
- RUST_BACKTRACE=1
- RUST_LOG=debug
@ -96,9 +118,20 @@ services:
- lemmy-gamma
lemmy-gamma:
image: lemmy-federation:latest
volumes:
- ./lemmy_gamma.hjson:/config/config.hjson
environment:
- LEMMY_HOSTNAME=lemmy-gamma:8561
- LEMMY_DATABASE_URL=postgres://lemmy:password@postgres_gamma:5432/lemmy
- LEMMY_JWT_SECRET=changeme
- LEMMY_FEDERATION__ENABLED=true
- LEMMY_TLS_ENABLED=false
- LEMMY_FEDERATION__ALLOWED_INSTANCES=lemmy-alpha,lemmy-beta,lemmy-delta,lemmy-epsilon
- LEMMY_PORT=8561
- LEMMY_SETUP__ADMIN_USERNAME=lemmy_gamma
- LEMMY_SETUP__ADMIN_PASSWORD=lemmy
- LEMMY_SETUP__SITE_NAME=lemmy-gamma
- LEMMY_RATE_LIMIT__POST=99999
- LEMMY_RATE_LIMIT__REGISTER=99999
- LEMMY_CAPTCHA__ENABLED=false
- LEMMY_TEST_SEND_SYNC=1
- RUST_BACKTRACE=1
- RUST_LOG=debug
@ -126,9 +159,20 @@ services:
- lemmy-delta
lemmy-delta:
image: lemmy-federation:latest
volumes:
- ./lemmy_delta.hjson:/config/config.hjson
environment:
- LEMMY_HOSTNAME=lemmy-delta:8571
- LEMMY_DATABASE_URL=postgres://lemmy:password@postgres_delta:5432/lemmy
- LEMMY_JWT_SECRET=changeme
- LEMMY_FEDERATION__ENABLED=true
- LEMMY_TLS_ENABLED=false
- LEMMY_FEDERATION__ALLOWED_INSTANCES=lemmy-beta
- LEMMY_PORT=8571
- LEMMY_SETUP__ADMIN_USERNAME=lemmy_delta
- LEMMY_SETUP__ADMIN_PASSWORD=lemmy
- LEMMY_SETUP__SITE_NAME=lemmy-delta
- LEMMY_RATE_LIMIT__POST=99999
- LEMMY_RATE_LIMIT__REGISTER=99999
- LEMMY_CAPTCHA__ENABLED=false
- LEMMY_TEST_SEND_SYNC=1
- RUST_BACKTRACE=1
- RUST_LOG=debug
@ -156,9 +200,20 @@ services:
- lemmy-epsilon
lemmy-epsilon:
image: lemmy-federation:latest
volumes:
- ./lemmy_epsilon.hjson:/config/config.hjson
environment:
- LEMMY_HOSTNAME=lemmy-epsilon:8581
- LEMMY_DATABASE_URL=postgres://lemmy:password@postgres_epsilon:5432/lemmy
- LEMMY_JWT_SECRET=changeme
- LEMMY_FEDERATION__ENABLED=true
- LEMMY_TLS_ENABLED=false
- LEMMY_FEDERATION__BLOCKED_INSTANCES=lemmy-alpha
- LEMMY_PORT=8581
- LEMMY_SETUP__ADMIN_USERNAME=lemmy_epsilon
- LEMMY_SETUP__ADMIN_PASSWORD=lemmy
- LEMMY_SETUP__SITE_NAME=lemmy-epsilon
- LEMMY_RATE_LIMIT__POST=99999
- LEMMY_RATE_LIMIT__REGISTER=99999
- LEMMY_CAPTCHA__ENABLED=false
- LEMMY_TEST_SEND_SYNC=1
- RUST_BACKTRACE=1
- RUST_LOG=debug

View file

@ -1,37 +0,0 @@
{
hostname: lemmy-alpha:8541
port: 8541
tls_enabled: false
jwt_secret: changeme
setup: {
admin_username: lemmy_alpha
admin_password: lemmy
site_name: lemmy-alpha
}
database: {
database: lemmy
user: lemmy
password: password
host: postgres_alpha
port: 5432
pool_size: 5
}
federation: {
enabled: true
allowed_instances: lemmy-beta,lemmy-gamma,lemmy-delta,lemmy-epsilon
}
captcha: {
enabled: false
difficulty: medium
}
rate_limit: {
message: 180
message_per_second: 60
post: 99999
post_per_second: 600
register: 99999
register_per_second: 3600
image: 6
image_per_second: 3600
}
}

View file

@ -1,37 +0,0 @@
{
hostname: lemmy-beta:8551
port: 8551
tls_enabled: false
jwt_secret: changeme
setup: {
admin_username: lemmy_beta
admin_password: lemmy
site_name: lemmy-beta
}
database: {
database: lemmy
user: lemmy
password: password
host: postgres_beta
port: 5432
pool_size: 5
}
federation: {
enabled: true
allowed_instances: lemmy-alpha,lemmy-gamma,lemmy-delta,lemmy-epsilon
}
captcha: {
enabled: false
difficulty: medium
}
rate_limit: {
message: 180
message_per_second: 60
post: 99999
post_per_second: 600
register: 99999
register_per_second: 3600
image: 6
image_per_second: 3600
}
}

View file

@ -1,37 +0,0 @@
{
hostname: lemmy-delta:8571
port: 8571
tls_enabled: false
jwt_secret: changeme
setup: {
admin_username: lemmy_delta
admin_password: lemmy
site_name: lemmy-delta
}
database: {
database: lemmy
user: lemmy
password: password
host: postgres_delta
port: 5432
pool_size: 5
}
federation: {
enabled: true
allowed_instances: lemmy-beta
}
captcha: {
enabled: false
difficulty: medium
}
rate_limit: {
message: 180
message_per_second: 60
post: 99999
post_per_second: 600
register: 99999
register_per_second: 3600
image: 6
image_per_second: 3600
}
}

View file

@ -1,37 +0,0 @@
{
hostname: lemmy-epsilon:8581
port: 8581
tls_enabled: false
jwt_secret: changeme
setup: {
admin_username: lemmy_epsilon
admin_password: lemmy
site_name: lemmy-epsilon
}
database: {
database: lemmy
user: lemmy
password: password
host: postgres_epsilon
port: 5432
pool_size: 5
}
federation: {
enabled: true
blocked_instances: lemmy-alpha
}
captcha: {
enabled: false
difficulty: medium
}
rate_limit: {
message: 180
message_per_second: 60
post: 99999
post_per_second: 600
register: 99999
register_per_second: 3600
image: 6
image_per_second: 3600
}
}

View file

@ -1,37 +0,0 @@
{
hostname: lemmy-gamma:8561
port: 8561
tls_enabled: false
jwt_secret: changeme
setup: {
admin_username: lemmy_gamma
admin_password: lemmy
site_name: lemmy-gamma
}
database: {
database: lemmy
user: lemmy
password: password
host: postgres_gamma
port: 5432
pool_size: 5
}
federation: {
enabled: true
allowed_instances: lemmy-alpha,lemmy-beta,lemmy-delta,lemmy-epsilon
}
captcha: {
enabled: false
difficulty: medium
}
rate_limit: {
message: 180
message_per_second: 60
post: 99999
post_per_second: 600
register: 99999
register_per_second: 3600
image: 6
image_per_second: 3600
}
}

View file

@ -29,10 +29,6 @@
password: "password"
# host where postgres is running
host: "postgres"
# port where postgres can be accessed
port: 5432
# maximum number of active sql connections
pool_size: 5
}
# # optional: email sending configuration
# email: {

View file

@ -56,6 +56,7 @@ RUN addgroup -g 1000 lemmy
RUN adduser -D -s /bin/sh -u 1000 -G lemmy lemmy
# Copy resources
COPY --chown=lemmy:lemmy config/defaults.hjson /config/defaults.hjson
COPY --chown=lemmy:lemmy --from=builder /app/lemmy_server /app/lemmy
RUN chown lemmy:lemmy /app/lemmy

View file

@ -31,6 +31,7 @@ RUN addgroup --gid 1000 lemmy
RUN adduser --no-create-home --shell /bin/sh --uid 1000 --gid 1000 lemmy
# Copy resources
COPY --chown=lemmy:lemmy config/defaults.hjson /config/defaults.hjson
COPY --chown=lemmy:lemmy --from=builder /app/lemmy_server /app/lemmy
RUN chown lemmy:lemmy /app/lemmy

View file

@ -28,7 +28,7 @@ services:
lemmy-ui:
image: dessalines/lemmy-ui:0.9.7
ports:
- "1235:1234"
- "127.0.0.1:1235:1234"
restart: always
environment:
- LEMMY_INTERNAL_HOST=lemmy:8536

View file

@ -37,7 +37,7 @@ async fn main() -> Result<(), LemmyError> {
};
let manager = ConnectionManager::<PgConnection>::new(&db_url);
let pool = Pool::builder()
.max_size(settings.database.unwrap_or_default().pool_size)
.max_size(settings.database.pool_size)
.build(manager)
.unwrap_or_else(|_| panic!("Error connecting to {}", db_url));
@ -61,8 +61,7 @@ async fn main() -> Result<(), LemmyError> {
println!(
"Starting http server at {}:{}",
settings.bind.unwrap(),
settings.port.unwrap_or_default()
settings.bind, settings.port
);
let activity_queue = create_activity_queue();
@ -95,7 +94,7 @@ async fn main() -> Result<(), LemmyError> {
.configure(nodeinfo::config)
.configure(webfinger::config)
})
.bind((settings.bind.unwrap(), settings.port.unwrap_or_default()))?
.bind((settings.bind, settings.port))?
.run()
.await?;

View file

@ -39,7 +39,7 @@ use lemmy_server::code_migrations::run_advanced_migrations;
use lemmy_utils::{
apub::generate_actor_keypair,
rate_limit::{rate_limiter::RateLimiter, RateLimit},
settings::Settings,
settings::structs::Settings,
};
use lemmy_websocket::{chat_server::ChatServer, LemmyContext};
use reqwest::Client;
@ -58,7 +58,7 @@ fn create_context() -> LemmyContext {
};
let manager = ConnectionManager::<PgConnection>::new(&db_url);
let pool = Pool::builder()
.max_size(settings.database.unwrap_or_default().pool_size)
.max_size(settings.database.pool_size)
.build(manager)
.unwrap();
embedded_migrations::run(&pool.get().unwrap()).unwrap();