2022-12-09 10:31:47 -05:00
pub mod api_routes_http ;
2020-07-10 14:15:41 -04:00
pub mod code_migrations ;
2023-07-05 07:25:19 -04:00
#[ cfg(feature = " prometheus-metrics " ) ]
pub mod prometheus_metrics ;
2021-12-06 09:54:47 -05:00
pub mod root_span_builder ;
2021-01-29 11:38:27 -05:00
pub mod scheduled_tasks ;
2022-02-04 12:31:38 -05:00
#[ cfg(feature = " console " ) ]
2022-05-10 08:06:32 -04:00
pub mod telemetry ;
2022-12-19 10:54:42 -05:00
use crate ::{ code_migrations ::run_advanced_migrations , root_span_builder ::QuieterRootSpanBuilder } ;
2023-03-21 11:03:05 -04:00
use activitypub_federation ::config ::{ FederationConfig , FederationMiddleware } ;
2023-05-29 17:14:00 -04:00
use actix_cors ::Cors ;
2022-12-19 10:54:42 -05:00
use actix_web ::{ middleware , web ::Data , App , HttpServer , Result } ;
use lemmy_api_common ::{
context ::LemmyContext ,
lemmy_db_views ::structs ::SiteView ,
request ::build_user_agent ,
utils ::{
check_private_instance_and_federation_enabled ,
local_site_rate_limit_to_rate_limit_config ,
} ,
} ;
2023-03-21 11:03:05 -04:00
use lemmy_apub ::{ VerifyUrlData , FEDERATION_HTTP_FETCH_LIMIT } ;
2022-12-19 10:54:42 -05: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-06-26 06:23:21 -04:00
use lemmy_utils ::{ error ::LemmyError , rate_limit ::RateLimitCell , settings ::SETTINGS } ;
2022-12-19 10:54:42 -05:00
use reqwest ::Client ;
use reqwest_middleware ::ClientBuilder ;
use reqwest_tracing ::TracingMiddleware ;
2023-04-13 06:53:55 -04:00
use std ::{ env , thread , time ::Duration } ;
2021-11-23 07:16:47 -05:00
use tracing ::subscriber ::set_global_default ;
2022-12-19 10:54:42 -05:00
use tracing_actix_web ::TracingLogger ;
2021-11-23 07:16:47 -05:00
use tracing_error ::ErrorLayer ;
use tracing_log ::LogTracer ;
2022-01-07 09:53:45 -05:00
use tracing_subscriber ::{ filter ::Targets , layer ::SubscriberExt , Layer , Registry } ;
2022-07-11 16:38:37 -04:00
use url ::Url ;
2023-07-05 07:25:19 -04:00
#[ cfg(feature = " prometheus-metrics " ) ]
use {
actix_web_prom ::PrometheusMetricsBuilder ,
prometheus ::default_registry ,
prometheus_metrics ::serve_prometheus ,
} ;
2021-11-23 07:16:47 -05:00
2022-12-19 10:54:42 -05:00
/// Max timeout for http requests
2023-02-18 09:36:12 -05:00
pub ( crate ) const REQWEST_TIMEOUT : Duration = Duration ::from_secs ( 10 ) ;
2022-12-19 10:54:42 -05: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 05:29:12 -04:00
let scheduled_tasks_enabled = args . get ( 1 ) ! = Some ( & " --disable-scheduled-tasks " . to_string ( ) ) ;
2022-12-19 10:54:42 -05:00
let settings = SETTINGS . to_owned ( ) ;
2023-02-28 16:45:37 -05:00
// Run the DB migrations
2022-12-19 10:54:42 -05:00
let db_url = get_database_url ( Some ( & settings ) ) ;
run_migrations ( & db_url ) ;
2023-02-28 16:45:37 -05:00
// Set up the connection pool
2022-12-19 10:54:42 -05:00
let pool = build_db_pool ( & settings ) . await ? ;
2023-02-28 16:45:37 -05:00
// Run the Code-required migrations
2022-12-19 10:54:42 -05:00
run_advanced_migrations ( & pool , & settings ) . await ? ;
// Initialize the secrets
let secret = Secret ::init ( & pool )
. await
. expect ( " Couldn't initialize secrets. " ) ;
// Make sure the local site is set up.
let site_view = SiteView ::read_local ( & pool )
. 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 09:36:12 -05:00
let user_agent = build_user_agent ( & settings ) ;
2022-12-19 10:54:42 -05:00
let reqwest_client = Client ::builder ( )
2023-02-18 09:36:12 -05:00
. user_agent ( user_agent . clone ( ) )
2022-12-19 10:54:42 -05:00
. timeout ( REQWEST_TIMEOUT )
2023-05-18 07:11:06 -04:00
. connect_timeout ( REQWEST_TIMEOUT )
2022-12-19 10:54:42 -05: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 04:28:20 -04:00
let context = LemmyContext ::create (
pool . clone ( ) ,
client . clone ( ) ,
secret . clone ( ) ,
rate_limit_cell . clone ( ) ,
) ;
2023-06-15 05:29:12 -04:00
if scheduled_tasks_enabled {
// Schedules various cleanup tasks for the DB
2023-06-21 04:28:20 -04: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 05:29:12 -04:00
} ) ;
}
2023-02-18 09:36:12 -05:00
2023-07-05 07:25:19 -04:00
#[ cfg(feature = " prometheus-metrics " ) ]
serve_prometheus ( settings . prometheus . as_ref ( ) , context . clone ( ) ) ;
2023-06-26 04:24:11 -04:00
let settings_bind = settings . clone ( ) ;
2023-06-22 08:35:12 -04: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 04:24:11 -04:00
. worker_count ( settings . worker_count )
. retry_count ( settings . retry_count )
2023-06-22 08:35:12 -04:00
. debug ( cfg! ( debug_assertions ) )
. http_signature_compat ( true )
. url_verifier ( Box ::new ( VerifyUrlData ( context . pool ( ) . clone ( ) ) ) )
. build ( )
2023-06-26 04:24:11 -04:00
. await ? ;
2023-06-22 08:35:12 -04:00
2023-07-05 07:25:19 -04: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 ( )
. unwrap ( ) ;
2022-12-19 10:54:42 -05:00
// Create Http server with websocket support
HttpServer ::new ( move | | {
2023-07-06 07:25:19 -04:00
let cors_origin = std ::env ::var ( " LEMMY_CORS_ORIGIN " ) ;
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 17:14:00 -04:00
} ;
2023-07-05 07:25:19 -04:00
let app = App ::new ( )
2023-06-22 03:34:51 -04: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 06:54:41 -04:00
. wrap ( middleware ::Compress ::default ( ) )
2023-05-29 17:14:00 -04:00
. wrap ( cors_config )
2022-12-19 10:54:42 -05:00
. wrap ( TracingLogger ::< QuieterRootSpanBuilder > ::new ( ) )
2023-06-26 04:24:11 -04:00
. app_data ( Data ::new ( context . clone ( ) ) )
2022-12-19 10:54:42 -05:00
. app_data ( Data ::new ( rate_limit_cell . clone ( ) ) )
2023-07-05 07:25:19 -04:00
. wrap ( FederationMiddleware ::new ( federation_config . clone ( ) ) ) ;
#[ cfg(feature = " prometheus-metrics " ) ]
let app = app . wrap ( prom_api_metrics . clone ( ) ) ;
// The routes
app
2022-12-19 10:54:42 -05: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 ? ;
Ok ( ( ) )
}
2022-07-11 16:38:37 -04:00
pub fn init_logging ( opentelemetry_url : & Option < Url > ) -> Result < ( ) , LemmyError > {
2021-11-23 07:16:47 -05:00
LogTracer ::init ( ) ? ;
2022-01-07 09:53:45 -05: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 07:11:47 -04: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 09:53:45 -05:00
2021-11-23 07:16:47 -05:00
let subscriber = Registry ::default ( )
. with ( format_layer )
2022-02-04 12:31:38 -05:00
. with ( ErrorLayer ::default ( ) ) ;
2022-05-10 08:06:32 -04:00
if let Some ( _url ) = opentelemetry_url {
#[ cfg(feature = " console " ) ]
2022-07-11 16:38:37 -04:00
telemetry ::init_tracing ( _url . as_ref ( ) , subscriber , targets ) ? ;
2022-05-10 08:06:32 -04:00
#[ cfg(not(feature = " console " )) ]
tracing ::error! ( " Feature `console` must be enabled for opentelemetry tracing " ) ;
2022-01-06 14:10:20 -05:00
} else {
set_global_default ( subscriber ) ? ;
}
2021-11-23 07:16:47 -05:00
Ok ( ( ) )
}