2020-12-22 14:02:41 +01:00
use std ::env ;
2024-02-15 14:25:25 +01:00
use std ::io ::{ stderr , LineWriter , Write } ;
2024-08-27 17:19:25 +02:00
use std ::num ::NonZeroUsize ;
2022-09-19 18:16:28 +02:00
use std ::path ::PathBuf ;
2024-01-23 09:43:48 +01:00
use std ::str ::FromStr ;
2021-10-29 15:58:06 +02:00
use std ::sync ::Arc ;
2024-08-27 17:19:25 +02:00
use std ::thread ::available_parallelism ;
2020-12-12 13:32:06 +01:00
2022-06-07 03:38:23 +08:00
use actix_web ::http ::KeepAlive ;
2022-09-27 16:33:37 +02:00
use actix_web ::web ::Data ;
2022-10-18 11:57:00 +02:00
use actix_web ::HttpServer ;
2022-09-27 16:33:37 +02:00
use index_scheduler ::IndexScheduler ;
2023-07-03 18:51:42 +02:00
use is_terminal ::IsTerminal ;
2022-12-07 08:20:47 -07:00
use meilisearch ::analytics ::Analytics ;
2024-02-12 11:06:37 +01:00
use meilisearch ::option ::LogMode ;
2024-08-27 17:19:25 +02:00
use meilisearch ::search_queue ::SearchQueue ;
2024-01-30 16:31:42 +01:00
use meilisearch ::{
2024-02-27 18:34:52 +01:00
analytics , create_app , setup_meilisearch , LogRouteHandle , LogRouteType , LogStderrHandle ,
LogStderrType , Opt , SubscriberForSecondLayer ,
2024-01-30 16:31:42 +01:00
} ;
2022-12-22 11:57:44 +01:00
use meilisearch_auth ::{ generate_master_key , AuthController , MASTER_KEY_MIN_SIZE } ;
2024-01-18 18:14:47 +01:00
use mimalloc ::MiMalloc ;
2023-01-12 17:50:39 +01:00
use termcolor ::{ Color , ColorChoice , ColorSpec , StandardStream , WriteColor } ;
2024-01-29 18:45:55 +01:00
use tracing ::level_filters ::LevelFilter ;
2024-01-31 17:46:57 +01:00
use tracing_subscriber ::layer ::SubscriberExt as _ ;
2024-01-23 09:43:48 +01:00
use tracing_subscriber ::Layer ;
2020-12-12 13:32:06 +01:00
#[ global_allocator ]
2024-01-18 18:14:47 +01:00
static ALLOC : MiMalloc = MiMalloc ;
2024-02-12 11:06:37 +01:00
fn default_log_route_layer ( ) -> LogRouteType {
2024-01-30 16:31:42 +01:00
None . with_filter ( tracing_subscriber ::filter ::Targets ::new ( ) . with_target ( " " , LevelFilter ::OFF ) )
2024-01-29 13:36:10 +01:00
}
2024-02-12 11:06:37 +01:00
fn default_log_stderr_layer ( opt : & Opt ) -> LogStderrType {
let layer = tracing_subscriber ::fmt ::layer ( )
2024-02-15 14:25:25 +01:00
. with_writer ( | | LineWriter ::new ( std ::io ::stderr ( ) ) )
2024-02-12 11:06:37 +01:00
. with_span_events ( tracing_subscriber ::fmt ::format ::FmtSpan ::CLOSE ) ;
let layer = match opt . experimental_logs_mode {
LogMode ::Human = > Box ::new ( layer )
as Box < dyn tracing_subscriber ::Layer < SubscriberForSecondLayer > + Send + Sync > ,
LogMode ::Json = > Box ::new ( layer . json ( ) )
as Box < dyn tracing_subscriber ::Layer < SubscriberForSecondLayer > + Send + Sync > ,
} ;
layer . with_filter (
tracing_subscriber ::filter ::Targets ::new ( )
. with_target ( " " , LevelFilter ::from_str ( & opt . log_level . to_string ( ) ) . unwrap ( ) ) ,
)
}
2021-09-27 16:48:03 +02:00
/// does all the setup before meilisearch is launched
2024-02-12 11:06:37 +01:00
fn setup ( opt : & Opt ) -> anyhow ::Result < ( LogRouteHandle , LogStderrHandle ) > {
let ( route_layer , route_layer_handle ) =
tracing_subscriber ::reload ::Layer ::new ( default_log_route_layer ( ) ) ;
2024-01-29 13:36:10 +01:00
let route_layer : tracing_subscriber ::reload ::Layer < _ , _ > = route_layer ;
2024-02-12 11:06:37 +01:00
let ( stderr_layer , stderr_layer_handle ) =
tracing_subscriber ::reload ::Layer ::new ( default_log_stderr_layer ( opt ) ) ;
let route_layer : tracing_subscriber ::reload ::Layer < _ , _ > = route_layer ;
let subscriber = tracing_subscriber ::registry ( ) . with ( route_layer ) . with ( stderr_layer ) ;
2020-12-12 13:32:06 +01:00
2024-01-23 09:43:48 +01:00
// set the subscriber as the default for the application
tracing ::subscriber ::set_global_default ( subscriber ) . unwrap ( ) ;
2021-08-24 12:27:30 +02:00
2024-02-12 11:06:37 +01:00
Ok ( ( route_layer_handle , stderr_layer_handle ) )
2021-09-27 16:48:03 +02:00
}
2024-02-07 17:29:40 +01:00
fn on_panic ( info : & std ::panic ::PanicInfo ) {
let info = info . to_string ( ) . replace ( '\n' , " " ) ;
tracing ::error! ( % info ) ;
}
2021-09-14 18:39:02 +02:00
#[ actix_web::main ]
async fn main ( ) -> anyhow ::Result < ( ) > {
2024-07-29 14:58:39 +02:00
try_main ( ) . await . inspect_err ( | error | {
tracing ::error! ( % error ) ;
let mut current = error . source ( ) ;
let mut depth = 0 ;
while let Some ( source ) = current {
tracing ::info! ( % source , depth , " Error caused by " ) ;
current = source . source ( ) ;
depth + = 1 ;
}
} )
}
async fn try_main ( ) -> anyhow ::Result < ( ) > {
2022-09-19 18:16:28 +02:00
let ( opt , config_read_from ) = Opt ::try_build ( ) ? ;
2022-09-06 09:23:16 +02:00
2024-02-07 17:29:40 +01:00
std ::panic ::set_hook ( Box ::new ( on_panic ) ) ;
2023-05-15 11:23:58 +02:00
anyhow ::ensure! (
! ( cfg! ( windows ) & & opt . experimental_reduce_indexing_memory_usage ) ,
" The `experimental-reduce-indexing-memory-usage` flag is not supported on Windows "
) ;
2024-01-29 17:56:43 +01:00
let log_handle = setup ( & opt ) ? ;
2021-09-14 18:39:02 +02:00
2022-12-22 11:57:44 +01:00
match ( opt . env . as_ref ( ) , & opt . master_key ) {
( " production " , Some ( master_key ) ) if master_key . len ( ) < MASTER_KEY_MIN_SIZE = > {
anyhow ::bail! (
2023-01-12 17:50:39 +01:00
" The master key must be at least {MASTER_KEY_MIN_SIZE} bytes in a production environment. The provided key is only {} bytes.
2022-12-22 11:57:44 +01:00
2023-01-12 17:50:39 +01:00
{ } " ,
2022-12-22 11:57:44 +01:00
master_key . len ( ) ,
2023-01-12 17:50:39 +01:00
generated_master_key_message ( ) ,
2022-12-22 11:57:44 +01:00
)
2020-12-12 13:32:06 +01:00
}
2022-12-22 11:57:44 +01:00
( " production " , None ) = > {
anyhow ::bail! (
2023-01-12 17:50:39 +01:00
" You must provide a master key to secure your instance in a production environment. It can be specified via the MEILI_MASTER_KEY environment variable or the --master-key launch option.
2022-12-22 11:57:44 +01:00
2023-01-12 17:50:39 +01:00
{ } " ,
generated_master_key_message ( )
2022-12-22 11:57:44 +01:00
)
}
// No error; continue
_ = > ( ) ,
2020-12-12 13:32:06 +01:00
}
2022-10-16 03:04:17 +02:00
let ( index_scheduler , auth_controller ) = setup_meilisearch ( & opt ) ? ;
2021-11-08 18:31:27 +01:00
2021-06-16 17:12:49 +02:00
#[ cfg(all(not(debug_assertions), feature = " analytics " )) ]
2022-10-13 15:02:59 +02:00
let analytics = if ! opt . no_analytics {
2023-01-24 18:09:03 +01:00
analytics ::SegmentAnalytics ::new ( & opt , index_scheduler . clone ( ) , auth_controller . clone ( ) )
. await
2021-10-12 13:31:56 +02:00
} else {
2021-10-29 16:46:00 +02:00
analytics ::MockAnalytics ::new ( & opt )
2021-10-12 13:31:56 +02:00
} ;
#[ cfg(any(debug_assertions, not(feature = " analytics " ))) ]
2022-10-13 15:02:59 +02:00
let analytics = analytics ::MockAnalytics ::new ( & opt ) ;
2020-12-12 13:32:06 +01:00
2022-10-13 15:02:59 +02:00
print_launch_resume ( & opt , analytics . clone ( ) , config_read_from ) ;
2020-12-12 13:32:06 +01:00
2024-01-29 17:56:43 +01:00
run_http ( index_scheduler , auth_controller , opt , log_handle , analytics ) . await ? ;
2021-02-26 09:10:04 +01:00
Ok ( ( ) )
}
2021-10-12 14:32:44 +02:00
async fn run_http (
2022-10-18 12:45:06 +02:00
index_scheduler : Arc < IndexScheduler > ,
2023-04-06 13:38:47 +02:00
auth_controller : Arc < AuthController > ,
2021-10-12 14:32:44 +02:00
opt : Opt ,
2024-02-12 11:06:37 +01:00
logs : ( LogRouteHandle , LogStderrHandle ) ,
2021-10-29 15:58:06 +02:00
analytics : Arc < dyn Analytics > ,
2021-10-12 14:32:44 +02:00
) -> anyhow ::Result < ( ) > {
2022-09-27 16:33:37 +02:00
let enable_dashboard = & opt . env = = " development " ;
2021-09-20 15:31:03 +02:00
let opt_clone = opt . clone ( ) ;
2022-10-18 12:45:06 +02:00
let index_scheduler = Data ::from ( index_scheduler ) ;
2023-04-06 13:38:47 +02:00
let auth_controller = Data ::from ( auth_controller ) ;
2024-08-27 17:19:25 +02:00
let search_queue = SearchQueue ::new (
opt . experimental_search_queue_size ,
available_parallelism ( ) . unwrap_or ( NonZeroUsize ::new ( 2 ) . unwrap ( ) ) ,
) ;
let search_queue = Data ::new ( search_queue ) ;
2022-09-27 16:33:37 +02:00
2021-11-08 18:31:27 +01:00
let http_server = HttpServer ::new ( move | | {
2022-10-18 11:57:00 +02:00
create_app (
index_scheduler . clone ( ) ,
auth_controller . clone ( ) ,
2024-08-27 17:19:25 +02:00
search_queue . clone ( ) ,
2022-10-18 11:57:00 +02:00
opt . clone ( ) ,
2024-01-29 17:56:43 +01:00
logs . clone ( ) ,
2022-10-18 11:57:00 +02:00
analytics . clone ( ) ,
enable_dashboard ,
)
2021-11-08 18:31:27 +01:00
} )
// Disable signals allows the server to terminate immediately when a user enter CTRL-C
2022-06-07 03:38:23 +08:00
. disable_signals ( )
. keep_alive ( KeepAlive ::Os ) ;
2020-12-12 13:32:06 +01:00
2022-09-27 16:33:37 +02:00
if let Some ( config ) = opt_clone . get_ssl_config ( ) ? {
2024-07-17 14:27:29 +02:00
http_server . bind_rustls_0_23 ( opt_clone . http_addr , config ) ? . run ( ) . await ? ;
2021-06-23 14:48:33 +02:00
} else {
2022-09-27 16:33:37 +02:00
http_server . bind ( & opt_clone . http_addr ) ? . run ( ) . await ? ;
2020-12-12 13:32:06 +01:00
}
Ok ( ( ) )
}
2022-10-20 18:00:07 +02:00
pub fn print_launch_resume (
opt : & Opt ,
analytics : Arc < dyn Analytics > ,
config_read_from : Option < PathBuf > ,
) {
2024-02-27 18:34:52 +01:00
let build_info = build_info ::BuildInfo ::from_build ( ) ;
2022-10-20 18:00:07 +02:00
let protocol =
if opt . ssl_cert_path . is_some ( ) & & opt . ssl_key_path . is_some ( ) { " https " } else { " http " } ;
2020-12-12 13:32:06 +01:00
let ascii_name = r #"
2022-01-26 18:53:07 +01:00
888 b d888 d8b 888 d8b 888
8888 b d8888 Y8P 888 Y8P 888
88888 b . d88888 888 888
888 Y88888P888 . d88b . 888 888 888 . d8888b . d88b . 8888 b . 888 d888 . d8888b 88888 b .
888 Y888P 888 d8P Y8b 888 888 888 88 K d8P Y8b " 88b 888P " d88P " 888 " 88 b
888 Y8P 888 88888888 888 888 888 " Y8888b. 88888888 .d888888 888 888 888 888
888 " 888 Y8b. 888 888 888 X88 Y8b. 888 888 888 Y88b. 888 888
888 888 " Y8888 888 888 888 88888P' " Y8888 " Y888888 888 " Y8888P 888 888
2020-12-12 13:32:06 +01:00
" #;
eprintln! ( " {} " , ascii_name ) ;
2022-09-19 18:16:28 +02:00
eprintln! (
2022-09-22 09:44:28 +02:00
" Config file path: \t {:?} " ,
2022-09-19 18:16:28 +02:00
config_read_from
. map ( | config_file_path | config_file_path . display ( ) . to_string ( ) )
. unwrap_or_else ( | | " none " . to_string ( ) )
) ;
2020-12-12 13:32:06 +01:00
eprintln! ( " Database path: \t \t {:?} " , opt . db_path ) ;
2022-08-12 16:16:23 +08:00
eprintln! ( " Server listening on: \t \" {} :// {} \" " , protocol , opt . http_addr ) ;
2020-12-12 13:32:06 +01:00
eprintln! ( " Environment: \t \t {:?} " , opt . env ) ;
2024-02-27 18:34:52 +01:00
eprintln! ( " Commit SHA: \t \t {:?} " , build_info . commit_sha1 . unwrap_or ( " unknown " ) ) ;
eprintln! (
" Commit date: \t \t {:?} " ,
build_info
. commit_timestamp
. and_then ( | commit_timestamp | commit_timestamp
2024-03-05 14:56:48 +01:00
. format ( & time ::format_description ::well_known ::Rfc3339 )
2024-02-27 18:34:52 +01:00
. ok ( ) )
. unwrap_or ( " unknown " . into ( ) )
) ;
2022-10-20 18:00:07 +02:00
eprintln! ( " Package version: \t {:?} " , env! ( " CARGO_PKG_VERSION " ) . to_string ( ) ) ;
2024-02-27 18:34:52 +01:00
if let Some ( prototype ) = build_info . describe . and_then ( | describe | describe . as_prototype ( ) ) {
2023-02-07 15:02:04 +01:00
eprintln! ( " Prototype: \t \t {:?} " , prototype ) ;
}
2020-12-12 13:32:06 +01:00
2021-06-16 17:12:49 +02:00
#[ cfg(all(not(debug_assertions), feature = " analytics " )) ]
2021-06-15 15:36:30 +02:00
{
2022-01-12 15:57:31 +01:00
if ! opt . no_analytics {
2021-06-15 15:36:30 +02:00
eprintln! (
"
2022-01-26 17:43:16 +01:00
Thank you for using Meilisearch !
2021-06-16 11:12:46 +02:00
2023-05-03 19:14:57 +02:00
\ nWe collect anonymized analytics to improve our product and your experience . To learn more , including how to turn off analytics , visit our dedicated documentation page : https ://www.meilisearch.com/docs/learn/what_is_meilisearch/telemetry
2021-06-15 15:36:30 +02:00
2021-10-28 18:39:50 +02:00
Anonymous telemetry :\ t \ " Enabled \" "
2021-06-15 15:36:30 +02:00
) ;
2021-12-08 11:23:16 +01:00
} else {
eprintln! ( " Anonymous telemetry: \t \" Disabled \" " ) ;
2020-12-12 13:32:06 +01:00
}
2021-06-15 15:36:30 +02:00
}
2021-10-13 19:38:14 +02:00
2022-10-13 15:02:59 +02:00
if let Some ( instance_uid ) = analytics . instance_uid ( ) {
2022-10-22 16:35:42 +02:00
eprintln! ( " Instance UID: \t \t \" {} \" " , instance_uid ) ;
2021-10-13 19:38:14 +02:00
}
2020-12-12 13:32:06 +01:00
eprintln! ( ) ;
2022-12-22 11:57:44 +01:00
match ( opt . env . as_ref ( ) , & opt . master_key ) {
( " production " , Some ( _ ) ) = > {
2023-01-03 14:45:23 +01:00
eprintln! ( " A master key has been set. Requests to Meilisearch won't be authorized unless you provide an authentication key. " ) ;
2022-12-22 11:57:44 +01:00
}
( " development " , Some ( master_key ) ) = > {
2023-01-03 14:45:23 +01:00
eprintln! ( " A master key has been set. Requests to Meilisearch won't be authorized unless you provide an authentication key. " ) ;
2022-12-22 11:57:44 +01:00
if master_key . len ( ) < MASTER_KEY_MIN_SIZE {
2023-01-12 17:50:39 +01:00
print_master_key_too_short_warning ( )
2022-12-22 11:57:44 +01:00
}
}
2023-01-12 17:50:39 +01:00
( " development " , None ) = > print_missing_master_key_warning ( ) ,
2022-12-22 11:57:44 +01:00
// unreachable because Opt::try_build above would have failed already if any other value had been produced
_ = > unreachable! ( ) ,
2020-12-12 13:32:06 +01:00
}
eprintln! ( ) ;
2023-07-26 15:54:43 +02:00
eprintln! ( " Check out Meilisearch Cloud! \t https://www.meilisearch.com/cloud?utm_campaign=oss&utm_source=engine&utm_medium=cli " ) ;
2023-06-29 17:36:32 +02:00
eprintln! ( " Documentation: \t \t \t https://www.meilisearch.com/docs " ) ;
eprintln! ( " Source code: \t \t \t https://github.com/meilisearch/meilisearch " ) ;
eprintln! ( " Discord: \t \t \t https://discord.meilisearch.com " ) ;
2020-12-12 13:32:06 +01:00
eprintln! ( ) ;
}
2023-01-12 17:50:39 +01:00
const WARNING_BG_COLOR : Option < Color > = Some ( Color ::Ansi256 ( 178 ) ) ;
const WARNING_FG_COLOR : Option < Color > = Some ( Color ::Ansi256 ( 0 ) ) ;
fn print_master_key_too_short_warning ( ) {
2023-07-03 18:51:42 +02:00
let choice = if stderr ( ) . is_terminal ( ) { ColorChoice ::Auto } else { ColorChoice ::Never } ;
2023-01-12 17:50:39 +01:00
let mut stderr = StandardStream ::stderr ( choice ) ;
stderr
. set_color (
ColorSpec ::new ( ) . set_bg ( WARNING_BG_COLOR ) . set_fg ( WARNING_FG_COLOR ) . set_bold ( true ) ,
)
. unwrap ( ) ;
writeln! ( stderr , " \n " ) . unwrap ( ) ;
writeln! (
stderr ,
" Meilisearch started with a master key considered unsafe for use in a production environment.
2023-01-23 10:33:30 +01:00
A master key of at least { MASTER_KEY_MIN_SIZE } bytes will be required when switching to a production environment . "
2023-01-12 17:50:39 +01:00
)
. unwrap ( ) ;
stderr . reset ( ) . unwrap ( ) ;
writeln! ( stderr ) . unwrap ( ) ;
eprintln! ( " \n {} " , generated_master_key_message ( ) ) ;
eprintln! (
" \n Restart Meilisearch with the argument above to use this new and secure master key. "
)
}
fn print_missing_master_key_warning ( ) {
2023-07-03 18:51:42 +02:00
let choice = if stderr ( ) . is_terminal ( ) { ColorChoice ::Auto } else { ColorChoice ::Never } ;
2023-01-12 17:50:39 +01:00
let mut stderr = StandardStream ::stderr ( choice ) ;
stderr
. set_color (
ColorSpec ::new ( ) . set_bg ( WARNING_BG_COLOR ) . set_fg ( WARNING_FG_COLOR ) . set_bold ( true ) ,
)
. unwrap ( ) ;
writeln! ( stderr , " \n " ) . unwrap ( ) ;
writeln! (
stderr ,
" No master key was found. The server will accept unidentified requests.
A master key of at least { MASTER_KEY_MIN_SIZE } bytes will be required when switching to a production environment . "
)
. unwrap ( ) ;
stderr . reset ( ) . unwrap ( ) ;
writeln! ( stderr ) . unwrap ( ) ;
eprintln! ( " \n {} " , generated_master_key_message ( ) ) ;
eprintln! (
" \n Restart Meilisearch with the argument above to use this new and secure master key. "
)
}
fn generated_master_key_message ( ) -> String {
format! (
" We generated a new secure master key for you (you can safely use this token):
> > - - master - key { } < < " ,
generate_master_key ( )
)
}