2022-12-09 15:31:47 +00:00
pub mod api_routes_http ;
2020-07-10 18:15:41 +00:00
pub mod code_migrations ;
2023-07-05 11:25:19 +00:00
#[ cfg(feature = " prometheus-metrics " ) ]
pub mod prometheus_metrics ;
2021-12-06 14:54:47 +00:00
pub mod root_span_builder ;
2021-01-29 16:38:27 +00:00
pub mod scheduled_tasks ;
2023-08-29 14:47:57 +00:00
pub mod session_middleware ;
2022-02-04 17:31:38 +00:00
#[ cfg(feature = " console " ) ]
2022-05-10 12:06:32 +00:00
pub mod telemetry ;
2023-08-29 14:47:57 +00:00
use crate ::{
code_migrations ::run_advanced_migrations ,
root_span_builder ::QuieterRootSpanBuilder ,
session_middleware ::SessionMiddleware ,
} ;
2023-03-21 15:03:05 +00:00
use activitypub_federation ::config ::{ FederationConfig , FederationMiddleware } ;
2023-05-29 21:14:00 +00:00
use actix_cors ::Cors ;
2023-07-10 20:44:14 +00:00
use actix_web ::{
middleware ::{ self , ErrorHandlers } ,
web ::Data ,
App ,
HttpServer ,
Result ,
} ;
2022-12-19 15:54:42 +00:00
use lemmy_api_common ::{
context ::LemmyContext ,
lemmy_db_views ::structs ::SiteView ,
request ::build_user_agent ,
2023-07-20 15:36:48 +00:00
send_activity ::{ ActivityChannel , MATCH_OUTGOING_ACTIVITIES } ,
2022-12-19 15:54:42 +00:00
utils ::{
check_private_instance_and_federation_enabled ,
local_site_rate_limit_to_rate_limit_config ,
} ,
} ;
2023-07-19 13:49:41 +00:00
use lemmy_apub ::{
activities ::{ handle_outgoing_activities , match_outgoing_activities } ,
VerifyUrlData ,
FEDERATION_HTTP_FETCH_LIMIT ,
} ;
2022-12-19 15:54:42 +00:00
use lemmy_db_schema ::{
source ::secret ::Secret ,
utils ::{ build_db_pool , get_database_url , run_migrations } ,
} ;
use lemmy_routes ::{ feeds , images , nodeinfo , webfinger } ;
2023-07-10 10:27:49 +00:00
use lemmy_utils ::{
error ::LemmyError ,
rate_limit ::RateLimitCell ,
2023-07-10 20:44:14 +00:00
response ::jsonify_plain_text_errors ,
2023-07-10 10:27:49 +00:00
settings ::SETTINGS ,
SYNCHRONOUS_FEDERATION ,
} ;
2022-12-19 15:54:42 +00:00
use reqwest ::Client ;
use reqwest_middleware ::ClientBuilder ;
use reqwest_tracing ::TracingMiddleware ;
2023-04-13 10:53:55 +00:00
use std ::{ env , thread , time ::Duration } ;
2021-11-23 12:16:47 +00:00
use tracing ::subscriber ::set_global_default ;
2022-12-19 15:54:42 +00:00
use tracing_actix_web ::TracingLogger ;
2021-11-23 12:16:47 +00:00
use tracing_error ::ErrorLayer ;
use tracing_log ::LogTracer ;
2022-01-07 14:53:45 +00:00
use tracing_subscriber ::{ filter ::Targets , layer ::SubscriberExt , Layer , Registry } ;
2022-07-11 20:38:37 +00:00
use url ::Url ;
2023-07-05 11:25:19 +00:00
#[ cfg(feature = " prometheus-metrics " ) ]
use {
actix_web_prom ::PrometheusMetricsBuilder ,
prometheus ::default_registry ,
prometheus_metrics ::serve_prometheus ,
} ;
2021-11-23 12:16:47 +00:00
2022-12-19 15:54:42 +00:00
/// Max timeout for http requests
2023-02-18 14:36:12 +00:00
pub ( crate ) const REQWEST_TIMEOUT : Duration = Duration ::from_secs ( 10 ) ;
2022-12-19 15:54:42 +00:00
/// Placing the main function in lib.rs allows other crates to import it and embed Lemmy
pub async fn start_lemmy_server ( ) -> Result < ( ) , LemmyError > {
let args : Vec < String > = env ::args ( ) . collect ( ) ;
2023-06-15 09:29:12 +00:00
let scheduled_tasks_enabled = args . get ( 1 ) ! = Some ( & " --disable-scheduled-tasks " . to_string ( ) ) ;
2022-12-19 15:54:42 +00:00
let settings = SETTINGS . to_owned ( ) ;
2023-02-28 21:45:37 +00:00
// Run the DB migrations
2022-12-19 15:54:42 +00:00
let db_url = get_database_url ( Some ( & settings ) ) ;
run_migrations ( & db_url ) ;
2023-02-28 21:45:37 +00:00
// Set up the connection pool
2022-12-19 15:54:42 +00:00
let pool = build_db_pool ( & settings ) . await ? ;
2023-02-28 21:45:37 +00:00
// Run the Code-required migrations
2023-07-11 13:09:59 +00:00
run_advanced_migrations ( & mut ( & pool ) . into ( ) , & settings ) . await ? ;
2022-12-19 15:54:42 +00:00
// Initialize the secrets
2023-07-11 13:09:59 +00:00
let secret = Secret ::init ( & mut ( & pool ) . into ( ) )
2022-12-19 15:54:42 +00:00
. await
. expect ( " Couldn't initialize secrets. " ) ;
// Make sure the local site is set up.
2023-07-11 13:09:59 +00:00
let site_view = SiteView ::read_local ( & mut ( & pool ) . into ( ) )
2022-12-19 15:54:42 +00:00
. await
. expect ( " local site not set up " ) ;
let local_site = site_view . local_site ;
let federation_enabled = 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 ) . await ;
println! (
" Starting http server at {}:{} " ,
settings . bind , settings . port
) ;
2023-02-18 14:36:12 +00:00
let user_agent = build_user_agent ( & settings ) ;
2022-12-19 15:54:42 +00:00
let reqwest_client = Client ::builder ( )
2023-02-18 14:36:12 +00:00
. user_agent ( user_agent . clone ( ) )
2022-12-19 15:54:42 +00:00
. timeout ( REQWEST_TIMEOUT )
2023-05-18 11:11:06 +00:00
. connect_timeout ( REQWEST_TIMEOUT )
2022-12-19 15:54:42 +00:00
. build ( ) ? ;
let client = ClientBuilder ::new ( reqwest_client . clone ( ) )
. with ( TracingMiddleware ::default ( ) )
. build ( ) ;
// Pictrs cannot use the retry middleware
let pictrs_client = ClientBuilder ::new ( reqwest_client . clone ( ) )
. with ( TracingMiddleware ::default ( ) )
. build ( ) ;
2023-06-21 08:28:20 +00:00
let context = LemmyContext ::create (
pool . clone ( ) ,
client . clone ( ) ,
secret . clone ( ) ,
rate_limit_cell . clone ( ) ,
) ;
2023-06-15 09:29:12 +00:00
if scheduled_tasks_enabled {
// Schedules various cleanup tasks for the DB
2023-06-21 08:28:20 +00:00
thread ::spawn ( {
let context = context . clone ( ) ;
move | | {
scheduled_tasks ::setup ( db_url , user_agent , context )
. expect ( " Couldn't set up scheduled_tasks " ) ;
}
2023-06-15 09:29:12 +00:00
} ) ;
}
2023-02-18 14:36:12 +00:00
2023-07-05 11:25:19 +00:00
#[ cfg(feature = " prometheus-metrics " ) ]
serve_prometheus ( settings . prometheus . as_ref ( ) , context . clone ( ) ) ;
2023-06-26 08:24:11 +00:00
let settings_bind = settings . clone ( ) ;
2023-06-22 12:35:12 +00:00
let federation_config = FederationConfig ::builder ( )
. domain ( settings . hostname . clone ( ) )
. app_data ( context . clone ( ) )
. client ( client . clone ( ) )
. http_fetch_limit ( FEDERATION_HTTP_FETCH_LIMIT )
2023-06-26 08:24:11 +00:00
. worker_count ( settings . worker_count )
. retry_count ( settings . retry_count )
2023-07-10 10:27:49 +00:00
. debug ( * SYNCHRONOUS_FEDERATION )
2023-06-22 12:35:12 +00:00
. http_signature_compat ( true )
2023-07-11 13:09:59 +00:00
. url_verifier ( Box ::new ( VerifyUrlData ( context . inner_pool ( ) . clone ( ) ) ) )
2023-06-22 12:35:12 +00:00
. build ( )
2023-06-26 08:24:11 +00:00
. await ? ;
2023-06-22 12:35:12 +00:00
2023-07-05 11:25:19 +00:00
// this must come before the HttpServer creation
// creates a middleware that populates http metrics for each path, method, and status code
#[ cfg(feature = " prometheus-metrics " ) ]
let prom_api_metrics = PrometheusMetricsBuilder ::new ( " lemmy_api " )
. registry ( default_registry ( ) . clone ( ) )
. build ( )
2023-07-17 15:04:14 +00:00
. expect ( " Should always be buildable " ) ;
2023-07-05 11:25:19 +00:00
2023-07-19 13:49:41 +00:00
MATCH_OUTGOING_ACTIVITIES
. set ( Box ::new ( move | d , c | {
Box ::pin ( match_outgoing_activities ( d , c ) )
} ) )
. expect ( " set function pointer " ) ;
let request_data = federation_config . to_request_data ( ) ;
let outgoing_activities_task = tokio ::task ::spawn ( handle_outgoing_activities ( request_data ) ) ;
2022-12-19 15:54:42 +00:00
// Create Http server with websocket support
HttpServer ::new ( move | | {
2023-07-19 13:49:41 +00:00
let cors_origin = env ::var ( " LEMMY_CORS_ORIGIN " ) ;
2023-07-06 11:25:19 +00:00
let cors_config = match ( cors_origin , cfg! ( debug_assertions ) ) {
( Ok ( origin ) , false ) = > Cors ::default ( )
. allowed_origin ( & origin )
. allowed_origin ( & settings . get_protocol_and_hostname ( ) ) ,
_ = > Cors ::default ( )
. allow_any_origin ( )
. allow_any_method ( )
. allow_any_header ( )
. expose_any_header ( )
. max_age ( 3600 ) ,
2023-05-29 21:14:00 +00:00
} ;
2023-07-05 11:25:19 +00:00
let app = App ::new ( )
2023-06-22 07:34:51 +00:00
. wrap ( middleware ::Logger ::new (
// This is the default log format save for the usage of %{r}a over %a to guarantee to record the client's (forwarded) IP and not the last peer address, since the latter is frequently just a reverse proxy
" %{r}a '%r' %s %b '%{Referer}i' '%{User-Agent}i' %T " ,
) )
2023-06-26 10:54:41 +00:00
. wrap ( middleware ::Compress ::default ( ) )
2023-05-29 21:14:00 +00:00
. wrap ( cors_config )
2022-12-19 15:54:42 +00:00
. wrap ( TracingLogger ::< QuieterRootSpanBuilder > ::new ( ) )
2023-07-10 20:44:14 +00:00
. wrap ( ErrorHandlers ::new ( ) . default_handler ( jsonify_plain_text_errors ) )
2023-06-26 08:24:11 +00:00
. app_data ( Data ::new ( context . clone ( ) ) )
2022-12-19 15:54:42 +00:00
. app_data ( Data ::new ( rate_limit_cell . clone ( ) ) )
2023-08-29 14:47:57 +00:00
. wrap ( FederationMiddleware ::new ( federation_config . clone ( ) ) )
. wrap ( SessionMiddleware ::new ( context . clone ( ) ) ) ;
2023-07-05 11:25:19 +00:00
#[ cfg(feature = " prometheus-metrics " ) ]
let app = app . wrap ( prom_api_metrics . clone ( ) ) ;
// The routes
app
2022-12-19 15:54:42 +00:00
. configure ( | cfg | api_routes_http ::config ( cfg , rate_limit_cell ) )
. configure ( | cfg | {
if federation_enabled {
lemmy_apub ::http ::routes ::config ( cfg ) ;
webfinger ::config ( cfg ) ;
}
} )
. configure ( feeds ::config )
. configure ( | cfg | images ::config ( cfg , pictrs_client . clone ( ) , rate_limit_cell ) )
. configure ( nodeinfo ::config )
} )
. bind ( ( settings_bind . bind , settings_bind . port ) ) ?
. run ( )
. await ? ;
2023-07-19 13:49:41 +00:00
// Wait for outgoing apub sends to complete
2023-07-20 15:36:48 +00:00
ActivityChannel ::close ( outgoing_activities_task ) . await ? ;
2023-07-19 13:49:41 +00:00
2022-12-19 15:54:42 +00:00
Ok ( ( ) )
}
2022-07-11 20:38:37 +00:00
pub fn init_logging ( opentelemetry_url : & Option < Url > ) -> Result < ( ) , LemmyError > {
2021-11-23 12:16:47 +00:00
LogTracer ::init ( ) ? ;
2022-01-07 14:53:45 +00:00
let log_description = std ::env ::var ( " RUST_LOG " ) . unwrap_or_else ( | _ | " info " . into ( ) ) ;
let targets = log_description
. trim ( )
. trim_matches ( '"' )
. parse ::< Targets > ( ) ? ;
2023-07-04 11:11:47 +00:00
let format_layer = {
#[ cfg(feature = " json-log " ) ]
let layer = tracing_subscriber ::fmt ::layer ( ) . json ( ) ;
#[ cfg(not(feature = " json-log " )) ]
let layer = tracing_subscriber ::fmt ::layer ( ) ;
layer . with_filter ( targets . clone ( ) )
} ;
2022-01-07 14:53:45 +00:00
2021-11-23 12:16:47 +00:00
let subscriber = Registry ::default ( )
. with ( format_layer )
2022-02-04 17:31:38 +00:00
. with ( ErrorLayer ::default ( ) ) ;
2022-05-10 12:06:32 +00:00
if let Some ( _url ) = opentelemetry_url {
#[ cfg(feature = " console " ) ]
2022-07-11 20:38:37 +00:00
telemetry ::init_tracing ( _url . as_ref ( ) , subscriber , targets ) ? ;
2022-05-10 12:06:32 +00:00
#[ cfg(not(feature = " console " )) ]
tracing ::error! ( " Feature `console` must be enabled for opentelemetry tracing " ) ;
2022-01-06 19:10:20 +00:00
} else {
set_global_default ( subscriber ) ? ;
}
2021-11-23 12:16:47 +00:00
Ok ( ( ) )
}