2020-12-22 14:02:41 +01:00
use std ::env ;
2023-01-12 17:50:39 +01:00
use std ::io ::Write ;
2022-09-19 18:16:28 +02:00
use std ::path ::PathBuf ;
2021-10-29 15:58:06 +02:00
use std ::sync ::Arc ;
2020-12-12 13:32:06 +01:00
2022-06-06 21:38:23 +02: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 ;
2022-12-07 16:20:47 +01:00
use meilisearch ::analytics ::Analytics ;
2023-02-07 15:02:04 +01:00
use meilisearch ::{ analytics , create_app , prototype_name , setup_meilisearch , Opt } ;
2022-12-22 11:57:44 +01:00
use meilisearch_auth ::{ generate_master_key , AuthController , MASTER_KEY_MIN_SIZE } ;
2023-01-12 17:50:39 +01:00
use termcolor ::{ Color , ColorChoice , ColorSpec , StandardStream , WriteColor } ;
2020-12-12 13:32:06 +01:00
#[ global_allocator ]
2022-08-23 12:58:10 +02:00
static ALLOC : mimalloc ::MiMalloc = mimalloc ::MiMalloc ;
2020-12-12 13:32:06 +01:00
2021-09-27 16:48:03 +02:00
/// does all the setup before meilisearch is launched
2021-09-14 18:39:02 +02:00
fn setup ( opt : & Opt ) -> anyhow ::Result < ( ) > {
2021-06-23 11:07:19 +02:00
let mut log_builder = env_logger ::Builder ::new ( ) ;
2023-01-03 11:55:34 +01:00
log_builder . parse_filters ( & opt . log_level . to_string ( ) ) ;
2020-12-12 13:32:06 +01:00
2021-08-24 12:27:30 +02:00
log_builder . init ( ) ;
2021-09-27 16:48:03 +02:00
Ok ( ( ) )
}
2021-09-14 18:39:02 +02:00
#[ actix_web::main ]
async fn 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
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 "
) ;
2021-09-14 18:39:02 +02:00
setup ( & opt ) ? ;
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
2022-09-27 16:33:37 +02:00
run_http ( index_scheduler , auth_controller , opt , 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 ,
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 ) ;
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 ( ) ,
opt . clone ( ) ,
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-06 21:38:23 +02: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 ( ) ? {
2022-10-20 18:00:07 +02:00
http_server . bind_rustls ( 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 > ,
) {
2021-08-30 17:41:24 +02:00
let commit_sha = option_env! ( " VERGEN_GIT_SHA " ) . unwrap_or ( " unknown " ) ;
let commit_date = option_env! ( " VERGEN_GIT_COMMIT_TIMESTAMP " ) . unwrap_or ( " unknown " ) ;
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 10:16:23 +02:00
eprintln! ( " Server listening on: \t \" {} :// {} \" " , protocol , opt . http_addr ) ;
2020-12-12 13:32:06 +01:00
eprintln! ( " Environment: \t \t {:?} " , opt . env ) ;
2021-04-09 08:03:25 +02:00
eprintln! ( " Commit SHA: \t \t {:?} " , commit_sha . to_string ( ) ) ;
eprintln! ( " Commit date: \t \t {:?} " , commit_date . to_string ( ) ) ;
2022-10-20 18:00:07 +02:00
eprintln! ( " Package version: \t {:?} " , env! ( " CARGO_PKG_VERSION " ) . to_string ( ) ) ;
2023-02-07 15:02:04 +01:00
if let Some ( prototype ) = prototype_name ( ) {
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-05-03 19:14:57 +02:00
eprintln! ( " Documentation: \t \t https://www.meilisearch.com/docs " ) ;
2020-12-12 13:32:06 +01:00
eprintln! ( " Source code: \t \t https://github.com/meilisearch/meilisearch " ) ;
2023-05-03 19:14:57 +02:00
eprintln! ( " Discord: \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 ( ) {
let choice =
if atty ::is ( atty ::Stream ::Stderr ) { ColorChoice ::Auto } else { ColorChoice ::Never } ;
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 ( ) {
let choice =
if atty ::is ( atty ::Stream ::Stderr ) { ColorChoice ::Auto } else { ColorChoice ::Never } ;
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 ( )
)
}