diff --git a/Cargo.lock b/Cargo.lock index 5c41a3406..04749dfd2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2285,6 +2285,7 @@ dependencies = [ "assert-json-diff", "async-stream", "async-trait", + "atty", "brotli", "bstr 1.1.0", "byte-unit", @@ -2344,6 +2345,7 @@ dependencies = [ "tar", "temp-env", "tempfile", + "termcolor", "thiserror", "time", "tokio", diff --git a/meilisearch/Cargo.toml b/meilisearch/Cargo.toml index 9a0c9bd0b..1161c2dd6 100644 --- a/meilisearch/Cargo.toml +++ b/meilisearch/Cargo.toml @@ -73,6 +73,8 @@ walkdir = "2.3.2" yaup = "0.2.0" serde_urlencoded = "0.7.1" actix-utils = "3.0.1" +atty = "0.2.14" +termcolor = "1.1.3" [dev-dependencies] actix-rt = "2.7.0" diff --git a/meilisearch/src/main.rs b/meilisearch/src/main.rs index 050c825a8..a04e40bc7 100644 --- a/meilisearch/src/main.rs +++ b/meilisearch/src/main.rs @@ -1,4 +1,5 @@ use std::env; +use std::io::Write; use std::path::PathBuf; use std::sync::Arc; @@ -9,6 +10,7 @@ use index_scheduler::IndexScheduler; use meilisearch::analytics::Analytics; use meilisearch::{analytics, create_app, setup_meilisearch, Opt}; use meilisearch_auth::{generate_master_key, AuthController, MASTER_KEY_MIN_SIZE}; +use termcolor::{Color, ColorChoice, ColorSpec, StandardStream, WriteColor}; #[global_allocator] static ALLOC: mimalloc::MiMalloc = mimalloc::MiMalloc; @@ -32,24 +34,19 @@ async fn main() -> anyhow::Result<()> { match (opt.env.as_ref(), &opt.master_key) { ("production", Some(master_key)) if master_key.len() < MASTER_KEY_MIN_SIZE => { anyhow::bail!( - "In production mode, the master key must be of at least {MASTER_KEY_MIN_SIZE} bytes, but the provided key is only {} bytes long + "The master key must be at least {MASTER_KEY_MIN_SIZE} bytes in a production environment. The provided key is only {} bytes. -We generated a secure master key for you (you can safely copy this token): - ->> export MEILI_MASTER_KEY={} <<", +{}", master_key.len(), - generate_master_key(), + generated_master_key_message(), ) } ("production", None) => { anyhow::bail!( - "In production mode, you must provide a master key to secure your instance. It can be specified via the MEILI_MASTER_KEY environment variable or the --master-key launch option. + "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. -We generated a secure master key for you (you can safely copy this token): - ->> export MEILI_MASTER_KEY={} << -", - generate_master_key() +{}", + generated_master_key_message() ) } // No error; continue @@ -147,7 +144,7 @@ pub fn print_launch_resume( " Thank you for using Meilisearch! -We 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://docs.meilisearch.com/learn/what_is_meilisearch/telemetry.html +\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://docs.meilisearch.com/learn/what_is_meilisearch/telemetry.html Anonymous telemetry:\t\"Enabled\"" ); @@ -170,16 +167,10 @@ Anonymous telemetry:\t\"Enabled\"" eprintln!("A master key has been set. Requests to Meilisearch won't be authorized unless you provide an authentication key."); if master_key.len() < MASTER_KEY_MIN_SIZE { - eprintln!(); - log::warn!("The provided master key is too short (< {MASTER_KEY_MIN_SIZE} bytes)"); - eprintln!("A master key of at least {MASTER_KEY_MIN_SIZE} bytes will be required when switching to the production environment."); + print_master_key_too_short_warning() } } - ("development", None) => { - log::warn!("No master key found; The server will accept unidentified requests"); - eprintln!("If you need some protection in development mode, please export a key:\n\nexport MEILI_MASTER_KEY={}", generate_master_key()); - eprintln!("\nA master key of at least {MASTER_KEY_MIN_SIZE} bytes will be required when switching to the production environment."); - } + ("development", None) => print_missing_master_key_warning(), // unreachable because Opt::try_build above would have failed already if any other value had been produced _ => unreachable!(), } @@ -190,3 +181,67 @@ Anonymous telemetry:\t\"Enabled\"" eprintln!("Contact:\t\thttps://docs.meilisearch.com/resources/contact.html"); eprintln!(); } + +const WARNING_BG_COLOR: Option = Some(Color::Ansi256(178)); +const WARNING_FG_COLOR: Option = 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. + +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!( + "\nRestart 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!( + "\nRestart 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() + ) +}