2
0
Fork 0
mirror of https://github.com/LemmyNet/lemmy synced 2025-02-20 16:08:59 +00:00

Move middleware code into api_routes for faster compilation ()

* Move middleware code into api_routes for faster compilation

* fix wasm

* cleanup
This commit is contained in:
Nutomic 2025-01-23 10:44:05 +00:00 committed by GitHub
parent 1fdc229338
commit a531e384b1
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
23 changed files with 196 additions and 204 deletions

32
Cargo.lock generated
View file

@ -2080,9 +2080,9 @@ checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9"
[[package]]
name = "hyper"
version = "0.14.30"
version = "0.14.32"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a152ddd61dfaec7273fe8419ab357f33aee0d914c5f4efbf0d96fa749eea5ec9"
checksum = "41dfc780fdec9373c01bae43289ea34c972e40ee3c9f6b3c8801a35f35586ce7"
dependencies = [
"bytes",
"futures-channel",
@ -2129,7 +2129,7 @@ checksum = "ec3efd23720e2049821a693cbc7e65ea87c72f1c58ff2f9522ff332b1491e590"
dependencies = [
"futures-util",
"http 0.2.12",
"hyper 0.14.30",
"hyper 0.14.32",
"rustls 0.21.12",
"tokio",
"tokio-rustls 0.24.1",
@ -2555,7 +2555,6 @@ version = "0.19.6-beta.7"
dependencies = [
"activitypub_federation",
"actix-web",
"actix-web-httpauth",
"anyhow",
"base64 0.22.1",
"bcrypt",
@ -2585,6 +2584,7 @@ version = "0.19.6-beta.7"
dependencies = [
"activitypub_federation",
"actix-web",
"actix-web-httpauth",
"anyhow",
"chrono",
"encoding_rs",
@ -2614,6 +2614,7 @@ dependencies = [
"url",
"urlencoding",
"uuid",
"webmention",
"webpage",
]
@ -2640,7 +2641,6 @@ dependencies = [
"tracing",
"url",
"uuid",
"webmention",
]
[[package]]
@ -2833,20 +2833,29 @@ name = "lemmy_routes"
version = "0.19.6-beta.7"
dependencies = [
"activitypub_federation",
"actix-cors",
"actix-web",
"actix-web-prom",
"anyhow",
"chrono",
"clokwerk",
"diesel",
"diesel-async",
"futures",
"futures-util",
"http 1.2.0",
"lemmy_api_common",
"lemmy_db_schema",
"lemmy_db_views",
"lemmy_db_views_actor",
"lemmy_utils",
"pretty_assertions",
"prometheus",
"reqwest 0.12.12",
"reqwest-middleware",
"rss",
"serde",
"serial_test",
"tokio",
"tracing",
"url",
@ -2857,15 +2866,8 @@ name = "lemmy_server"
version = "0.19.6-beta.7"
dependencies = [
"activitypub_federation",
"actix-cors",
"actix-web",
"actix-web-prom",
"chrono",
"clap",
"clokwerk",
"diesel",
"diesel-async",
"futures-util",
"lemmy_api",
"lemmy_api_common",
"lemmy_api_crud",
@ -2874,18 +2876,14 @@ dependencies = [
"lemmy_federate",
"lemmy_routes",
"lemmy_utils",
"pretty_assertions",
"prometheus",
"reqwest-middleware",
"reqwest-tracing",
"rustls 0.23.21",
"serde_json",
"serial_test",
"tokio",
"tracing",
"tracing-actix-web",
"tracing-subscriber",
"url",
]
[[package]]
@ -4096,7 +4094,7 @@ dependencies = [
"h2",
"http 0.2.12",
"http-body 0.4.6",
"hyper 0.14.30",
"hyper 0.14.32",
"hyper-rustls 0.24.2",
"ipnet",
"js-sys",

View file

@ -140,6 +140,7 @@ tokio = { version = "1.43.0", features = ["full"] }
regex = "1.11.1"
diesel-derive-newtype = "2.1.2"
diesel-derive-enum = { version = "2.1.0", features = ["postgres"] }
enum-map = { version = "2.7" }
strum = { version = "0.26.3", features = ["derive"] }
itertools = "0.14.0"
futures = "0.3.31"
@ -155,7 +156,6 @@ futures-util = "0.3.31"
tokio-postgres = "0.7.12"
tokio-postgres-rustls = "0.13.0"
urlencoding = "2.1.3"
enum-map = "2.7"
moka = { version = "0.12.10", features = ["future"] }
i-love-jesus = { version = "0.1.0" }
clap = { version = "4.5.27", features = ["derive", "env"] }
@ -174,26 +174,13 @@ lemmy_api_common = { workspace = true }
lemmy_routes = { workspace = true }
lemmy_federate = { workspace = true }
activitypub_federation = { workspace = true }
diesel = { workspace = true }
diesel-async = { workspace = true }
actix-web = { workspace = true }
tracing = { workspace = true }
tracing-actix-web = { workspace = true }
tracing-subscriber = { workspace = true }
url = { workspace = true }
reqwest-middleware = { workspace = true }
reqwest-tracing = { workspace = true }
clokwerk = { workspace = true }
serde_json = { workspace = true }
rustls = { workspace = true }
tokio.workspace = true
actix-cors = "0.7.0"
futures-util = { workspace = true }
chrono = { workspace = true }
prometheus = { version = "0.13.4", features = ["process"] }
serial_test = { workspace = true }
clap = { workspace = true }
actix-web-prom = "0.9.0"
[dev-dependencies]
pretty_assertions = { workspace = true }

View file

@ -36,7 +36,6 @@ url = { workspace = true }
hound = "3.5.1"
sitemap-rs = "0.2.2"
totp-rs = { version = "5.6.0", features = ["gen_secret", "otpauth"] }
actix-web-httpauth = "0.8.2"
[dev-dependencies]
serial_test = { workspace = true }

View file

@ -1,14 +1,11 @@
use activitypub_federation::config::Data;
use actix_web::{http::header::Header, HttpRequest};
use actix_web_httpauth::headers::authorization::{Authorization, Bearer};
use base64::{engine::general_purpose::STANDARD_NO_PAD as base64, Engine};
use captcha::Captcha;
use lemmy_api_common::{
claims::Claims,
community::BanFromCommunity,
context::LemmyContext,
send_activity::{ActivityChannel, SendActivityData},
utils::{check_expire_time, check_user_valid, local_site_to_slur_regex, AUTH_COOKIE_NAME},
utils::{check_expire_time, local_site_to_slur_regex},
};
use lemmy_db_schema::{
source::{
@ -26,7 +23,7 @@ use lemmy_db_schema::{
};
use lemmy_db_views::structs::LocalUserView;
use lemmy_utils::{
error::{LemmyErrorExt, LemmyErrorExt2, LemmyErrorType, LemmyResult},
error::{LemmyErrorExt, LemmyErrorType, LemmyResult},
utils::slurs::check_slurs,
};
use std::io::Cursor;
@ -95,21 +92,6 @@ pub(crate) fn check_report_reason(reason: &str, local_site: &LocalSite) -> Lemmy
}
}
pub fn read_auth_token(req: &HttpRequest) -> LemmyResult<Option<String>> {
// Try reading jwt from auth header
if let Ok(header) = Authorization::<Bearer>::parse(req) {
Ok(Some(header.as_ref().token().to_string()))
}
// If that fails, try to read from cookie
else if let Some(cookie) = &req.cookie(AUTH_COOKIE_NAME) {
Ok(Some(cookie.value().to_string()))
}
// Otherwise, there's no auth
else {
Ok(None)
}
}
pub(crate) fn check_totp_2fa_valid(
local_user_view: &LocalUserView,
totp_token: &Option<String>,
@ -243,20 +225,6 @@ pub(crate) async fn ban_nonlocal_user_from_local_communities(
Ok(())
}
#[tracing::instrument(skip_all)]
pub async fn local_user_view_from_jwt(
jwt: &str,
context: &LemmyContext,
) -> LemmyResult<LocalUserView> {
let local_user_id = Claims::validate(jwt, context)
.await
.with_lemmy_type(LemmyErrorType::NotLoggedIn)?;
let local_user_view = LocalUserView::read(&mut context.pool(), local_user_id).await?;
check_user_valid(&local_user_view.person)?;
Ok(local_user_view)
}
#[cfg(test)]
mod tests {

View file

@ -1,7 +1,10 @@
use crate::read_auth_token;
use activitypub_federation::config::Data;
use actix_web::{cookie::Cookie, HttpRequest, HttpResponse};
use lemmy_api_common::{context::LemmyContext, utils::AUTH_COOKIE_NAME, SuccessResponse};
use lemmy_api_common::{
context::LemmyContext,
utils::{read_auth_token, AUTH_COOKIE_NAME},
SuccessResponse,
};
use lemmy_db_schema::source::login_token::LoginToken;
use lemmy_db_views::structs::LocalUserView;
use lemmy_utils::error::{LemmyErrorType, LemmyResult};

View file

@ -1,9 +1,12 @@
use crate::{local_user_view_from_jwt, read_auth_token};
use actix_web::{
web::{Data, Json},
HttpRequest,
};
use lemmy_api_common::{context::LemmyContext, SuccessResponse};
use lemmy_api_common::{
context::LemmyContext,
utils::{local_user_view_from_jwt, read_auth_token},
SuccessResponse,
};
use lemmy_utils::error::{LemmyErrorType, LemmyResult};
/// Returns an error message if the auth token is invalid for any reason. Necessary because other

View file

@ -37,6 +37,8 @@ full = [
"jsonwebtoken",
"mime",
"moka",
"actix-web-httpauth",
"webmention",
]
[dependencies]
@ -61,8 +63,8 @@ reqwest = { workspace = true, optional = true }
ts-rs = { workspace = true, optional = true }
moka = { workspace = true, optional = true }
anyhow.workspace = true
actix-web = { workspace = true, optional = true }
enum-map = { workspace = true }
actix-web = { workspace = true, optional = true }
urlencoding = { workspace = true }
mime = { version = "0.3.17", optional = true }
mime_guess = "2.0.5"
@ -72,6 +74,8 @@ webpage = { version = "2.0", default-features = false, optional = true, features
] }
encoding_rs = { version = "0.8.35", optional = true }
jsonwebtoken = { version = "9.3.0", optional = true }
actix-web-httpauth = { version = "0.8.2", optional = true }
webmention = { version = "0.6.0", optional = true }
[dev-dependencies]
serial_test = { workspace = true }

View file

@ -1,4 +1,5 @@
use crate::{
claims::Claims,
context::LemmyContext,
request::{
delete_image_from_pictrs,
@ -7,6 +8,8 @@ use crate::{
},
site::{FederatedInstances, InstanceWithFederationState},
};
use actix_web::{http::header::Header, HttpRequest};
use actix_web_httpauth::headers::authorization::{Authorization, Bearer};
use chrono::{DateTime, Days, Local, TimeZone, Utc};
use enum_map::{enum_map, EnumMap};
use lemmy_db_schema::{
@ -39,6 +42,7 @@ use lemmy_db_schema::{
},
traits::{Crud, Likeable},
utils::DbPool,
CommunityVisibility,
FederationMode,
RegistrationMode,
};
@ -54,12 +58,13 @@ use lemmy_db_views_actor::structs::{
};
use lemmy_utils::{
email::{send_email, translations::Lang},
error::{LemmyError, LemmyErrorExt, LemmyErrorType, LemmyResult},
error::{LemmyError, LemmyErrorExt, LemmyErrorExt2, LemmyErrorType, LemmyResult},
rate_limit::{ActionType, BucketConfig},
settings::{
structs::{PictrsImageMode, Settings},
SETTINGS,
},
spawn_try_task,
utils::{
markdown::{image_links::markdown_rewrite_image_links, markdown_check_for_blocked_urls},
slurs::{build_slur_regex, remove_slurs},
@ -72,9 +77,10 @@ use moka::future::Cache;
use regex::{escape, Regex, RegexSet};
use rosetta_i18n::{Language, LanguageId};
use std::sync::LazyLock;
use tracing::warn;
use tracing::{warn, Instrument};
use url::{ParseError, Url};
use urlencoding::encode;
use webmention::{Webmention, WebmentionError};
pub const AUTH_COOKIE_NAME: &str = "jwt";
@ -1145,6 +1151,55 @@ fn build_proxied_image_url(
))
}
#[tracing::instrument(skip_all)]
pub async fn local_user_view_from_jwt(
jwt: &str,
context: &LemmyContext,
) -> LemmyResult<LocalUserView> {
let local_user_id = Claims::validate(jwt, context)
.await
.with_lemmy_type(LemmyErrorType::NotLoggedIn)?;
let local_user_view = LocalUserView::read(&mut context.pool(), local_user_id).await?;
check_user_valid(&local_user_view.person)?;
Ok(local_user_view)
}
pub fn read_auth_token(req: &HttpRequest) -> LemmyResult<Option<String>> {
// Try reading jwt from auth header
if let Ok(header) = Authorization::<Bearer>::parse(req) {
Ok(Some(header.as_ref().token().to_string()))
}
// If that fails, try to read from cookie
else if let Some(cookie) = &req.cookie(AUTH_COOKIE_NAME) {
Ok(Some(cookie.value().to_string()))
}
// Otherwise, there's no auth
else {
Ok(None)
}
}
pub fn send_webmention(post: Post, community: Community) {
if let Some(url) = post.url.clone() {
if community.visibility == CommunityVisibility::Public {
spawn_try_task(async move {
let mut webmention = Webmention::new::<Url>(post.ap_id.clone().into(), url.clone().into())?;
webmention.set_checked(true);
match webmention
.send()
.instrument(tracing::info_span!("Sending webmention"))
.await
{
Err(WebmentionError::NoEndpointDiscovered(_)) => Ok(()),
Ok(_) => Ok(()),
Err(e) => Err(e).with_lemmy_type(LemmyErrorType::CouldntSendWebmention),
}
});
}
};
}
#[cfg(test)]
mod tests {

View file

@ -27,7 +27,6 @@ futures.workspace = true
uuid = { workspace = true }
anyhow.workspace = true
chrono.workspace = true
webmention = "0.6.0"
accept-language = "3.1.0"
regex = { workspace = true }
serde_json = { workspace = true }

View file

@ -13,6 +13,7 @@ use lemmy_api_common::{
honeypot_check,
local_site_to_slur_regex,
process_markdown_opt,
send_webmention,
},
};
use lemmy_db_schema::{
@ -25,13 +26,11 @@ use lemmy_db_schema::{
},
traits::{Crud, Likeable},
utils::diesel_url_create,
CommunityVisibility,
};
use lemmy_db_views::structs::LocalUserView;
use lemmy_db_views_actor::structs::CommunityModeratorView;
use lemmy_utils::{
error::{LemmyErrorExt, LemmyErrorType, LemmyResult},
spawn_try_task,
utils::{
mention::scrape_text_for_mentions,
slurs::check_slurs,
@ -44,9 +43,6 @@ use lemmy_utils::{
},
},
};
use tracing::Instrument;
use url::Url;
use webmention::{Webmention, WebmentionError};
#[tracing::instrument(skip(context))]
pub async fn create_post(
@ -170,23 +166,3 @@ pub async fn create_post(
build_post_response(&context, community_id, local_user_view, post_id).await
}
pub fn send_webmention(post: Post, community: Community) {
if let Some(url) = post.url.clone() {
if community.visibility == CommunityVisibility::Public {
spawn_try_task(async move {
let mut webmention = Webmention::new::<Url>(post.ap_id.clone().into(), url.clone().into())?;
webmention.set_checked(true);
match webmention
.send()
.instrument(tracing::info_span!("Sending webmention"))
.await
{
Err(WebmentionError::NoEndpointDiscovered(_)) => Ok(()),
Ok(_) => Ok(()),
Err(e) => Err(e).with_lemmy_type(LemmyErrorType::CouldntSendWebmention),
}
});
}
};
}

View file

@ -1,4 +1,4 @@
use super::{convert_published_time, create::send_webmention};
use super::convert_published_time;
use activitypub_federation::config::Data;
use actix_web::web::Json;
use chrono::Utc;
@ -13,6 +13,7 @@ use lemmy_api_common::{
get_url_blocklist,
local_site_to_slur_regex,
process_markdown_opt,
send_webmention,
},
};
use lemmy_db_schema::{

View file

@ -32,5 +32,16 @@ serde = { workspace = true }
url = { workspace = true }
tracing = { workspace = true }
tokio = { workspace = true }
futures-util.workspace = true
http.workspace = true
diesel.workspace = true
diesel-async.workspace = true
clokwerk = "0.4.0"
prometheus = { version = "0.13.4", features = ["process"] }
rss = "2.0.11"
actix-web-prom = "0.9.0"
actix-cors = "0.7.0"
[dev-dependencies]
pretty_assertions.workspace = true
serial_test.workspace = true

View file

@ -1,8 +1,10 @@
use crate::local_user_view_from_jwt;
use actix_web::{error::ErrorBadRequest, web, Error, HttpRequest, HttpResponse, Result};
use anyhow::anyhow;
use chrono::{DateTime, Utc};
use lemmy_api_common::{context::LemmyContext, utils::check_private_instance};
use lemmy_api_common::{
context::LemmyContext,
utils::{check_private_instance, local_user_view_from_jwt},
};
use lemmy_db_schema::{
source::{community::Community, person::Person},
traits::ApubActor,

View file

@ -1,17 +1,6 @@
use lemmy_api_common::{claims::Claims, context::LemmyContext, utils::check_user_valid};
use lemmy_db_views::structs::LocalUserView;
use lemmy_utils::error::LemmyResult;
pub mod feeds;
pub mod images;
pub mod middleware;
pub mod nodeinfo;
pub mod utils;
pub mod webfinger;
#[tracing::instrument(skip_all)]
async fn local_user_view_from_jwt(jwt: &str, context: &LemmyContext) -> LemmyResult<LocalUserView> {
let local_user_id = Claims::validate(jwt, context).await?;
let local_user_view = LocalUserView::read(&mut context.pool(), local_user_id).await?;
check_user_valid(&local_user_view.person)?;
Ok(local_user_view)
}

View file

@ -0,0 +1,2 @@
pub mod idempotency;
pub mod session;

View file

@ -7,8 +7,10 @@ use actix_web::{
};
use core::future::Ready;
use futures_util::future::LocalBoxFuture;
use lemmy_api::{local_user_view_from_jwt, read_auth_token};
use lemmy_api_common::context::LemmyContext;
use lemmy_api_common::{
context::LemmyContext,
utils::{local_user_view_from_jwt, read_auth_token},
};
use std::{future::ready, rc::Rc};
#[derive(Clone)]
@ -67,9 +69,8 @@ where
if let Some(jwt) = &jwt {
// Ignore any invalid auth so the site can still be used
// TODO: this means it will be impossible to get any error message for invalid jwt. Need
// to add a separate endpoint for that.
// https://github.com/LemmyNet/lemmy/issues/3702
// This means it is be impossible to get any error message for invalid jwt. Need
// to use `/api/v4/account/validate_auth` for that.
let local_user_view = local_user_view_from_jwt(jwt, &context).await.ok();
if let Some(local_user_view) = local_user_view {
req.extensions_mut().insert(local_user_view);
@ -99,9 +100,8 @@ where
#[cfg(test)]
mod tests {
use crate::tests::test_context;
use actix_web::test::TestRequest;
use lemmy_api_common::claims::Claims;
use lemmy_api_common::{claims::Claims, context::LemmyContext};
use lemmy_db_schema::{
source::{
instance::Instance,
@ -117,7 +117,7 @@ mod tests {
#[tokio::test]
#[serial]
async fn test_session_auth() -> LemmyResult<()> {
let context = test_context().await;
let context = LemmyContext::init_test_context().await;
let inserted_instance =
Instance::read_or_create(&mut context.pool(), "my_domain.tld".to_string()).await?;

View file

@ -0,0 +1,38 @@
use actix_cors::Cors;
use lemmy_utils::settings::structs::Settings;
pub mod code_migrations;
pub mod prometheus_metrics;
pub mod scheduled_tasks;
pub fn cors_config(settings: &Settings) -> Cors {
let self_origin = settings.get_protocol_and_hostname();
let cors_origin_setting = settings.cors_origin();
// A default setting for either wildcard, or None
let cors_default = Cors::default()
.allow_any_origin()
.allow_any_method()
.allow_any_header()
.expose_any_header()
.max_age(3600);
match (cors_origin_setting.clone(), cfg!(debug_assertions)) {
(Some(origin), false) => {
// Need to call send_wildcard() explicitly, passing this into allowed_origin() results in
// error
if origin == "*" {
cors_default
} else {
Cors::default()
.allowed_origin(&origin)
.allowed_origin(&self_origin)
.allow_any_method()
.allow_any_header()
.expose_any_header()
.max_age(3600)
}
}
_ => cors_default,
}
}

View file

@ -1,10 +1,21 @@
use actix_web::{rt::System, web, App, HttpServer};
use lemmy_api_common::context::LemmyContext;
use actix_web_prom::{PrometheusMetrics, PrometheusMetricsBuilder};
use lemmy_api_common::{context::LemmyContext, LemmyErrorType};
use lemmy_utils::{error::LemmyResult, settings::structs::PrometheusConfig};
use prometheus::{default_registry, Encoder, Gauge, Opts, TextEncoder};
use std::{sync::Arc, thread};
use tracing::error;
/// Creates a middleware that populates http metrics for each path, method, and status code
pub fn new_prometheus_metrics() -> LemmyResult<PrometheusMetrics> {
Ok(
PrometheusMetricsBuilder::new("lemmy_api")
.registry(default_registry().clone())
.build()
.map_err(|e| LemmyErrorType::Unknown(format!("Should always be buildable: {e}")))?,
)
}
struct PromContext {
lemmy: LemmyContext,
db_pool_metrics: DbPoolMetrics,

View file

@ -1,3 +1,4 @@
use crate::nodeinfo::{NodeInfo, NodeInfoWellKnown};
use activitypub_federation::config::Data;
use chrono::{DateTime, TimeZone, Utc};
use clokwerk::{AsyncScheduler, TimeUnits as CTimeUnits};
@ -16,8 +17,8 @@ use diesel_async::{AsyncPgConnection, RunQueryDsl};
use lemmy_api_common::{
context::LemmyContext,
send_activity::{ActivityChannel, SendActivityData},
utils::send_webmention,
};
use lemmy_api_crud::post::create::send_webmention;
use lemmy_db_schema::{
schema::{
captcha_answer,
@ -48,7 +49,6 @@ use lemmy_db_schema::{
DELETED_REPLACEMENT_TEXT,
},
};
use lemmy_routes::nodeinfo::{NodeInfo, NodeInfoWellKnown};
use lemmy_utils::error::{LemmyErrorType, LemmyResult};
use reqwest_middleware::ClientWithMiddleware;
use std::time::Duration;
@ -553,7 +553,6 @@ async fn build_update_instance_form(
mod tests {
use super::*;
use crate::{scheduled_tasks::build_update_instance_form, tests::test_context};
use lemmy_api_common::request::client_builder;
use lemmy_utils::{
error::{LemmyErrorType, LemmyResult},
@ -586,7 +585,7 @@ mod tests {
#[tokio::test]
#[serial]
async fn test_scheduled_tasks_no_errors() -> LemmyResult<()> {
let context = test_context().await;
let context = LemmyContext::init_test_context().await;
startup_jobs(&mut context.pool()).await?;
update_instance_software(&mut context.pool(), context.client()).await?;

View file

@ -82,7 +82,7 @@ lettre = { version = "0.11.11", default-features = false, features = [
], optional = true }
markdown-it = { version = "0.6.1", optional = true }
ts-rs = { workspace = true, optional = true }
enum-map = { workspace = true, optional = true }
enum-map = { version = "2.7", optional = true }
cfg-if = "1"
clearurls = { version = "0.0.4", features = ["linkify"] }
markdown-it-block-spoiler = "1.0.1"

View file

@ -1,14 +1,7 @@
pub mod api_routes_v3;
pub mod api_routes_v4;
pub mod code_migrations;
pub mod idempotency_middleware;
pub mod prometheus_metrics;
pub mod scheduled_tasks;
pub mod session_middleware;
use crate::{code_migrations::run_advanced_migrations, session_middleware::SessionMiddleware};
use activitypub_federation::config::{FederationConfig, FederationMiddleware};
use actix_cors::Cors;
use actix_web::{
dev::{ServerHandle, ServiceResponse},
middleware::{self, Condition, ErrorHandlerResponse, ErrorHandlers},
@ -17,9 +10,7 @@ use actix_web::{
HttpResponse,
HttpServer,
};
use actix_web_prom::PrometheusMetricsBuilder;
use clap::{Parser, Subcommand};
use idempotency_middleware::{IdempotencyMiddleware, IdempotencySet};
use lemmy_api::sitemap::get_sitemap;
use lemmy_api_common::{
context::LemmyContext,
@ -39,7 +30,21 @@ use lemmy_apub::{
};
use lemmy_db_schema::{schema_setup, source::secret::Secret, utils::build_db_pool};
use lemmy_federate::{Opts, SendManager};
use lemmy_routes::{feeds, nodeinfo, webfinger};
use lemmy_routes::{
feeds,
middleware::{
idempotency::{IdempotencyMiddleware, IdempotencySet},
session::SessionMiddleware,
},
nodeinfo,
utils::{
code_migrations::run_advanced_migrations,
cors_config,
prometheus_metrics::{new_prometheus_metrics, serve_prometheus},
scheduled_tasks,
},
webfinger,
};
use lemmy_utils::{
error::{LemmyErrorType, LemmyResult},
rate_limit::RateLimitCell,
@ -47,8 +52,6 @@ use lemmy_utils::{
settings::{structs::Settings, SETTINGS},
VERSION,
};
use prometheus::default_registry;
use prometheus_metrics::serve_prometheus;
use reqwest_middleware::ClientBuilder;
use reqwest_tracing::TracingMiddleware;
use serde_json::json;
@ -175,19 +178,17 @@ pub async fn start_lemmy_server(args: CmdArgs) -> LemmyResult<()> {
// Make sure the local site is set up.
let site_view = SiteView::read_local(&mut (&pool).into()).await?;
let local_site = site_view.local_site;
let federation_enabled = local_site.federation_enabled;
let federation_enabled = site_view.local_site.federation_enabled;
if federation_enabled {
println!("Federation enabled, host is {}", &SETTINGS.hostname);
}
check_private_instance_and_federation_enabled(&local_site)?;
// Set up the rate limiter
let rate_limit_config =
local_site_rate_limit_to_rate_limit_config(&site_view.local_site_rate_limit);
let rate_limit_cell = RateLimitCell::new(rate_limit_config);
check_private_instance_and_federation_enabled(&site_view.local_site)?;
println!(
"Starting HTTP server at {}:{}",
@ -205,7 +206,7 @@ pub async fn start_lemmy_server(args: CmdArgs) -> LemmyResult<()> {
client.clone(),
pictrs_client,
secret.clone(),
rate_limit_cell.clone(),
rate_limit_cell,
);
if let Some(prometheus) = SETTINGS.prometheus.clone() {
@ -221,8 +222,8 @@ pub async fn start_lemmy_server(args: CmdArgs) -> LemmyResult<()> {
.debug(cfg!(debug_assertions))
.http_signature_compat(true)
.url_verifier(Box::new(VerifyUrlData(context.inner_pool().clone())));
if local_site.federation_signed_fetch {
let site: ApubSite = site_view.site.into();
if site_view.local_site.federation_signed_fetch {
let site: ApubSite = site_view.site.clone().into();
federation_config_builder.signed_fetch_actor(&site);
}
let federation_config = federation_config_builder.build().await?;
@ -329,14 +330,8 @@ fn create_http_server(
settings: Settings,
federation_enabled: bool,
) -> LemmyResult<ServerHandle> {
// this must come before the HttpServer creation
// creates a middleware that populates http metrics for each path, method, and status code
let prom_api_metrics = PrometheusMetricsBuilder::new("lemmy_api")
.registry(default_registry().clone())
.build()
.map_err(|e| LemmyErrorType::Unknown(format!("Should always be buildable: {e}")))?;
// Must create this outside of HTTP server so that duplicate requests get detected across threads.
// These must come before HttpServer creation so they can collect data across threads.
let prom_api_metrics = new_prometheus_metrics()?;
let idempotency_set = IdempotencySet::default();
// Create Http server
@ -358,7 +353,6 @@ fn create_http_server(
.wrap(TracingLogger::<DefaultRootSpanBuilder>::new())
.wrap(ErrorHandlers::new().default_handler(jsonify_plain_text_errors))
.app_data(Data::new(context.clone()))
.app_data(Data::new(rate_limit_cell.clone()))
.wrap(FederationMiddleware::new(federation_config.clone()))
.wrap(IdempotencyMiddleware::new(idempotency_set.clone()))
.wrap(SessionMiddleware::new(context.clone()))
@ -392,50 +386,3 @@ fn create_http_server(
tokio::task::spawn(server);
Ok(handle)
}
fn cors_config(settings: &Settings) -> Cors {
let self_origin = settings.get_protocol_and_hostname();
let cors_origin_setting = settings.cors_origin();
// A default setting for either wildcard, or None
let cors_default = Cors::default()
.allow_any_origin()
.allow_any_method()
.allow_any_header()
.expose_any_header()
.max_age(3600);
match (cors_origin_setting.clone(), cfg!(debug_assertions)) {
(Some(origin), false) => {
// Need to call send_wildcard() explicitly, passing this into allowed_origin() results in
// error
if origin == "*" {
cors_default
} else {
Cors::default()
.allowed_origin(&origin)
.allowed_origin(&self_origin)
.allow_any_method()
.allow_any_header()
.expose_any_header()
.max_age(3600)
}
}
_ => cors_default,
}
}
#[cfg(test)]
pub mod tests {
use activitypub_federation::config::Data;
use lemmy_api_common::context::LemmyContext;
use std::env::set_current_dir;
pub async fn test_context() -> Data<LemmyContext> {
// hack, necessary so that config file can be loaded from hardcoded, relative path.
// Ignore errors as this gets called once for every test (so changing dir again would fail).
set_current_dir("crates/utils").ok();
LemmyContext::init_test_context().await
}
}