2022-09-27 22:58:25 +08:00
use std ::env ;
2021-09-28 22:22:59 +02:00
use std ::fs ;
2020-12-12 13:32:06 +01:00
use std ::io ::{ BufReader , Read } ;
use std ::path ::PathBuf ;
use std ::sync ::Arc ;
2020-12-22 14:02:41 +01:00
use byte_unit ::Byte ;
2022-01-12 13:54:39 +01:00
use clap ::Parser ;
2022-09-06 09:23:16 +02:00
use meilisearch_lib ::{
export_to_env_if_not_present ,
options ::{ IndexerOpts , SchedulerConfig } ,
} ;
2020-12-12 13:32:06 +01:00
use rustls ::{
2022-01-21 20:44:17 +00:00
server ::{
AllowAnyAnonymousOrAuthenticatedClient , AllowAnyAuthenticatedClient ,
ServerSessionMemoryCache ,
} ,
2020-12-12 13:32:06 +01:00
RootCertStore ,
} ;
2022-01-21 20:44:17 +00:00
use rustls_pemfile ::{ certs , pkcs8_private_keys , rsa_private_keys } ;
2022-09-06 09:23:16 +02:00
use serde ::{ Deserialize , Serialize } ;
2021-01-29 19:14:23 +01:00
2020-12-12 13:32:06 +01:00
const POSSIBLE_ENV : [ & str ; 2 ] = [ " development " , " production " ] ;
2022-09-06 09:23:16 +02:00
const MEILI_DB_PATH : & str = " MEILI_DB_PATH " ;
const MEILI_HTTP_ADDR : & str = " MEILI_HTTP_ADDR " ;
const MEILI_MASTER_KEY : & str = " MEILI_MASTER_KEY " ;
const MEILI_ENV : & str = " MEILI_ENV " ;
#[ cfg(all(not(debug_assertions), feature = " analytics " )) ]
const MEILI_NO_ANALYTICS : & str = " MEILI_NO_ANALYTICS " ;
const MEILI_MAX_INDEX_SIZE : & str = " MEILI_MAX_INDEX_SIZE " ;
const MEILI_MAX_TASK_DB_SIZE : & str = " MEILI_MAX_TASK_DB_SIZE " ;
const MEILI_HTTP_PAYLOAD_SIZE_LIMIT : & str = " MEILI_HTTP_PAYLOAD_SIZE_LIMIT " ;
const MEILI_SSL_CERT_PATH : & str = " MEILI_SSL_CERT_PATH " ;
const MEILI_SSL_KEY_PATH : & str = " MEILI_SSL_KEY_PATH " ;
const MEILI_SSL_AUTH_PATH : & str = " MEILI_SSL_AUTH_PATH " ;
const MEILI_SSL_OCSP_PATH : & str = " MEILI_SSL_OCSP_PATH " ;
const MEILI_SSL_REQUIRE_AUTH : & str = " MEILI_SSL_REQUIRE_AUTH " ;
const MEILI_SSL_RESUMPTION : & str = " MEILI_SSL_RESUMPTION " ;
const MEILI_SSL_TICKETS : & str = " MEILI_SSL_TICKETS " ;
2022-09-07 17:47:15 +02:00
const MEILI_IMPORT_SNAPSHOT : & str = " MEILI_IMPORT_SNAPSHOT " ;
const MEILI_IGNORE_MISSING_SNAPSHOT : & str = " MEILI_IGNORE_MISSING_SNAPSHOT " ;
const MEILI_IGNORE_SNAPSHOT_IF_DB_EXISTS : & str = " MEILI_IGNORE_SNAPSHOT_IF_DB_EXISTS " ;
2022-09-06 09:23:16 +02:00
const MEILI_SNAPSHOT_DIR : & str = " MEILI_SNAPSHOT_DIR " ;
const MEILI_SCHEDULE_SNAPSHOT : & str = " MEILI_SCHEDULE_SNAPSHOT " ;
const MEILI_SNAPSHOT_INTERVAL_SEC : & str = " MEILI_SNAPSHOT_INTERVAL_SEC " ;
2022-09-07 17:47:15 +02:00
const MEILI_IMPORT_DUMP : & str = " MEILI_IMPORT_DUMP " ;
const MEILI_IGNORE_MISSING_DUMP : & str = " MEILI_IGNORE_MISSING_DUMP " ;
const MEILI_IGNORE_DUMP_IF_DB_EXISTS : & str = " MEILI_IGNORE_DUMP_IF_DB_EXISTS " ;
2022-09-06 09:23:16 +02:00
const MEILI_DUMPS_DIR : & str = " MEILI_DUMPS_DIR " ;
const MEILI_LOG_LEVEL : & str = " MEILI_LOG_LEVEL " ;
#[ cfg(feature = " metrics " ) ]
const MEILI_ENABLE_METRICS_ROUTE : & str = " MEILI_ENABLE_METRICS_ROUTE " ;
2022-10-11 21:35:07 +08:00
const DEFAULT_CONFIG_FILE_PATH : & str = " ./config.toml " ;
2022-09-07 11:51:23 +02:00
const DEFAULT_DB_PATH : & str = " ./data.ms " ;
2022-10-05 20:14:37 -04:00
const DEFAULT_HTTP_ADDR : & str = " localhost:7700 " ;
2022-09-07 11:51:23 +02:00
const DEFAULT_ENV : & str = " development " ;
const DEFAULT_MAX_INDEX_SIZE : & str = " 100 GiB " ;
const DEFAULT_MAX_TASK_DB_SIZE : & str = " 100 GiB " ;
const DEFAULT_HTTP_PAYLOAD_SIZE_LIMIT : & str = " 100 MB " ;
const DEFAULT_SNAPSHOT_DIR : & str = " snapshots/ " ;
const DEFAULT_SNAPSHOT_INTERVAL_SEC : u64 = 86400 ;
const DEFAULT_DUMPS_DIR : & str = " dumps/ " ;
2022-09-21 16:31:16 +02:00
const DEFAULT_LOG_LEVEL : & str = " INFO " ;
2022-09-07 11:51:23 +02:00
2022-09-06 09:23:16 +02:00
#[ derive(Debug, Clone, Parser, Serialize, Deserialize) ]
2022-10-14 16:43:01 +08:00
#[ clap(version, next_display_order = None) ]
2022-09-19 18:16:28 +02:00
#[ serde(rename_all = " snake_case " , deny_unknown_fields) ]
2020-12-12 13:32:06 +01:00
pub struct Opt {
2022-10-03 16:07:38 +02:00
/// Designates the location where database files will be created and retrieved.
2022-09-07 11:51:23 +02:00
#[ clap(long, env = MEILI_DB_PATH, default_value_os_t = default_db_path()) ]
#[ serde(default = " default_db_path " ) ]
2020-12-22 17:13:50 +01:00
pub db_path : PathBuf ,
2020-12-12 13:32:06 +01:00
2022-10-05 19:04:33 +02:00
/// Sets the HTTP address and port Meilisearch will use.
2022-09-07 11:51:23 +02:00
#[ clap(long, env = MEILI_HTTP_ADDR, default_value_t = default_http_addr()) ]
#[ serde(default = " default_http_addr " ) ]
2020-12-12 13:32:06 +01:00
pub http_addr : String ,
2022-10-05 19:04:33 +02:00
/// Sets the instance's master key, automatically protecting all routes except `GET /health`.
2022-09-06 09:23:16 +02:00
#[ serde(skip_serializing) ]
#[ clap(long, env = MEILI_MASTER_KEY) ]
2020-12-12 13:32:06 +01:00
pub master_key : Option < String > ,
2022-10-05 18:54:38 +02:00
/// Configures the instance's environment. Value must be either `production` or `development`.
2022-10-04 20:59:57 +08:00
#[ clap(long, env = MEILI_ENV, default_value_t = default_env(), value_parser = POSSIBLE_ENV) ]
2022-09-07 11:51:23 +02:00
#[ serde(default = " default_env " ) ]
2020-12-12 13:32:06 +01:00
pub env : String ,
2022-10-03 16:07:38 +02:00
/// Deactivates Meilisearch's built-in telemetry when provided.
///
/// Meilisearch automatically collects data from all instances that do not opt out using this flag.
/// All gathered data is used solely for the purpose of improving Meilisearch, and can be deleted
/// at any time.
2021-06-16 17:12:49 +02:00
#[ cfg(all(not(debug_assertions), feature = " analytics " )) ]
2022-09-07 11:51:23 +02:00
#[ serde(skip_serializing, default) ] // we can't send true
2022-09-06 09:23:16 +02:00
#[ clap(long, env = MEILI_NO_ANALYTICS) ]
2022-01-12 13:54:39 +01:00
pub no_analytics : bool ,
2020-12-12 13:32:06 +01:00
2022-10-05 18:54:38 +02:00
/// Sets the maximum size of the index. Value must be given in bytes or explicitly stating a base unit (for instance: 107374182400, '107.7Gb', or '107374 Mb').
2022-09-07 11:51:23 +02:00
#[ clap(long, env = MEILI_MAX_INDEX_SIZE, default_value_t = default_max_index_size()) ]
#[ serde(default = " default_max_index_size " ) ]
2021-06-16 19:50:15 +02:00
pub max_index_size : Byte ,
2020-12-12 13:32:06 +01:00
2022-10-03 16:07:38 +02:00
/// Sets the maximum size of the task database. Value must be given in bytes or explicitly stating a
2022-10-05 18:54:38 +02:00
/// base unit (for instance: 107374182400, '107.7Gb', or '107374 Mb').
2022-09-07 11:51:23 +02:00
#[ clap(long, env = MEILI_MAX_TASK_DB_SIZE, default_value_t = default_max_task_db_size()) ]
#[ serde(default = " default_max_task_db_size " ) ]
2021-12-02 16:03:26 +01:00
pub max_task_db_size : Byte ,
2020-12-12 13:32:06 +01:00
2022-10-03 16:07:38 +02:00
/// Sets the maximum size of accepted payloads. Value must be given in bytes or explicitly stating a
2022-10-05 18:54:38 +02:00
/// base unit (for instance: 107374182400, '107.7Gb', or '107374 Mb').
2022-09-07 11:51:23 +02:00
#[ clap(long, env = MEILI_HTTP_PAYLOAD_SIZE_LIMIT, default_value_t = default_http_payload_size_limit()) ]
#[ serde(default = " default_http_payload_size_limit " ) ]
2020-12-22 14:02:41 +01:00
pub http_payload_size_limit : Byte ,
2020-12-12 13:32:06 +01:00
2022-10-03 16:07:38 +02:00
/// Sets the server's SSL certificates.
2022-09-06 09:23:16 +02:00
#[ serde(skip_serializing) ]
2022-10-04 20:59:57 +08:00
#[ clap(long, env = MEILI_SSL_CERT_PATH, value_parser) ]
2020-12-12 13:32:06 +01:00
pub ssl_cert_path : Option < PathBuf > ,
2022-10-03 16:07:38 +02:00
/// Sets the server's SSL key files.
2022-09-06 09:23:16 +02:00
#[ serde(skip_serializing) ]
2022-10-04 20:59:57 +08:00
#[ clap(long, env = MEILI_SSL_KEY_PATH, value_parser) ]
2020-12-12 13:32:06 +01:00
pub ssl_key_path : Option < PathBuf > ,
2022-10-03 16:07:38 +02:00
/// Enables client authentication in the specified path.
2022-09-06 09:23:16 +02:00
#[ serde(skip_serializing) ]
2022-10-04 20:59:57 +08:00
#[ clap(long, env = MEILI_SSL_AUTH_PATH, value_parser) ]
2020-12-12 13:32:06 +01:00
pub ssl_auth_path : Option < PathBuf > ,
2022-10-05 19:04:33 +02:00
/// Sets the server's OCSP file. *Optional*
///
/// Reads DER-encoded OCSP response from OCSPFILE and staple to certificate.
2022-09-06 09:23:16 +02:00
#[ serde(skip_serializing) ]
2022-10-04 20:59:57 +08:00
#[ clap(long, env = MEILI_SSL_OCSP_PATH, value_parser) ]
2020-12-12 13:32:06 +01:00
pub ssl_ocsp_path : Option < PathBuf > ,
2022-10-03 16:07:38 +02:00
/// Makes SSL authentication mandatory.
2022-09-07 11:51:23 +02:00
#[ serde(skip_serializing, default) ]
2022-09-06 09:23:16 +02:00
#[ clap(long, env = MEILI_SSL_REQUIRE_AUTH) ]
2020-12-12 13:32:06 +01:00
pub ssl_require_auth : bool ,
2022-10-03 16:07:38 +02:00
/// Activates SSL session resumption.
2022-09-07 11:51:23 +02:00
#[ serde(skip_serializing, default) ]
2022-09-06 09:23:16 +02:00
#[ clap(long, env = MEILI_SSL_RESUMPTION) ]
2020-12-12 13:32:06 +01:00
pub ssl_resumption : bool ,
2022-10-03 16:07:38 +02:00
/// Activates SSL tickets.
2022-09-07 11:51:23 +02:00
#[ serde(skip_serializing, default) ]
2022-09-06 09:23:16 +02:00
#[ clap(long, env = MEILI_SSL_TICKETS) ]
2020-12-12 13:32:06 +01:00
pub ssl_tickets : bool ,
2022-10-03 16:07:38 +02:00
/// Launches Meilisearch after importing a previously-generated snapshot at the given filepath.
2022-09-07 17:47:15 +02:00
#[ clap(long, env = MEILI_IMPORT_SNAPSHOT) ]
2020-12-12 13:32:06 +01:00
pub import_snapshot : Option < PathBuf > ,
2022-10-03 16:07:38 +02:00
/// Prevents a Meilisearch instance from throwing an error when `--import-snapshot`
/// does not point to a valid snapshot file.
///
/// This command will throw an error if `--import-snapshot` is not defined.
2022-09-01 22:37:07 +02:00
#[ clap(
long ,
2022-09-07 17:47:15 +02:00
env = MEILI_IGNORE_MISSING_SNAPSHOT ,
2022-10-07 00:32:08 +08:00
requires = " import_snapshot "
2022-09-01 22:37:07 +02:00
) ]
2022-09-07 11:51:23 +02:00
#[ serde(default) ]
2020-12-12 13:32:06 +01:00
pub ignore_missing_snapshot : bool ,
2022-10-03 16:07:38 +02:00
/// Prevents a Meilisearch instance with an existing database from throwing an
/// error when using `--import-snapshot`. Instead, the snapshot will be ignored
/// and Meilisearch will launch using the existing database.
///
/// This command will throw an error if `--import-snapshot` is not defined.
2022-09-01 22:37:07 +02:00
#[ clap(
long ,
2022-09-07 17:47:15 +02:00
env = MEILI_IGNORE_SNAPSHOT_IF_DB_EXISTS ,
2022-10-07 00:32:08 +08:00
requires = " import_snapshot "
2022-09-01 22:37:07 +02:00
) ]
2022-09-07 11:51:23 +02:00
#[ serde(default) ]
2020-12-12 13:32:06 +01:00
pub ignore_snapshot_if_db_exists : bool ,
2022-10-03 16:07:38 +02:00
/// Sets the directory where Meilisearch will store snapshots.
2022-09-07 11:51:23 +02:00
#[ clap(long, env = MEILI_SNAPSHOT_DIR, default_value_os_t = default_snapshot_dir()) ]
#[ serde(default = " default_snapshot_dir " ) ]
2020-12-12 13:32:06 +01:00
pub snapshot_dir : PathBuf ,
2022-10-03 16:07:38 +02:00
/// Activates scheduled snapshots when provided. Snapshots are disabled by default.
2022-09-06 09:23:16 +02:00
#[ clap(long, env = MEILI_SCHEDULE_SNAPSHOT) ]
2022-09-07 11:51:23 +02:00
#[ serde(default) ]
2020-12-12 13:32:06 +01:00
pub schedule_snapshot : bool ,
2022-10-03 16:07:38 +02:00
/// Defines the interval between each snapshot. Value must be given in seconds.
2022-09-07 11:51:23 +02:00
#[ clap(long, env = MEILI_SNAPSHOT_INTERVAL_SEC, default_value_t = default_snapshot_interval_sec()) ]
#[ serde(default = " default_snapshot_interval_sec " ) ]
2021-03-17 12:01:56 +01:00
pub snapshot_interval_sec : u64 ,
2020-12-12 13:32:06 +01:00
2022-10-03 16:37:16 +02:00
/// Imports the dump file located at the specified path. Path must point to a `.dump` file.
/// If a database already exists, Meilisearch will throw an error and abort launch.
2022-10-07 00:32:08 +08:00
#[ clap(long, env = MEILI_IMPORT_DUMP, conflicts_with = " import_snapshot " ) ]
2020-12-12 13:32:06 +01:00
pub import_dump : Option < PathBuf > ,
2022-10-03 16:37:16 +02:00
/// Prevents Meilisearch from throwing an error when `--import-dump` does not point to
/// a valid dump file. Instead, Meilisearch will start normally without importing any dump.
///
/// This option will trigger an error if `--import-dump` is not defined.
2022-10-07 00:32:08 +08:00
#[ clap(long, env = MEILI_IGNORE_MISSING_DUMP, requires = " import_dump " ) ]
2022-09-07 11:51:23 +02:00
#[ serde(default) ]
2022-01-20 16:00:14 +01:00
pub ignore_missing_dump : bool ,
2022-10-03 16:37:16 +02:00
/// Prevents a Meilisearch instance with an existing database from throwing an error
/// when using `--import-dump`. Instead, the dump will be ignored and Meilisearch will
/// launch using the existing database.
///
/// This option will trigger an error if `--import-dump` is not defined.
2022-10-07 00:32:08 +08:00
#[ clap(long, env = MEILI_IGNORE_DUMP_IF_DB_EXISTS, requires = " import_dump " ) ]
2022-09-07 11:51:23 +02:00
#[ serde(default) ]
2022-01-20 16:00:14 +01:00
pub ignore_dump_if_db_exists : bool ,
2022-10-05 19:04:33 +02:00
/// Sets the directory where Meilisearch will create dump files.
2022-09-07 11:51:23 +02:00
#[ clap(long, env = MEILI_DUMPS_DIR, default_value_os_t = default_dumps_dir()) ]
#[ serde(default = " default_dumps_dir " ) ]
2022-01-20 16:00:14 +01:00
pub dumps_dir : PathBuf ,
2022-10-03 16:37:16 +02:00
/// Defines how much detail should be present in Meilisearch's logs.
///
2022-10-05 18:54:38 +02:00
/// Meilisearch currently supports five log levels, listed in order of increasing verbosity: ERROR, WARN, INFO, DEBUG, TRACE.
2022-09-07 11:51:23 +02:00
#[ clap(long, env = MEILI_LOG_LEVEL, default_value_t = default_log_level()) ]
#[ serde(default = " default_log_level " ) ]
2021-06-23 11:07:19 +02:00
pub log_level : String ,
2022-08-17 18:49:52 +05:30
/// Enables Prometheus metrics and /metrics route.
2022-08-29 12:36:54 +02:00
#[ cfg(feature = " metrics " ) ]
2022-09-06 09:23:16 +02:00
#[ clap(long, env = MEILI_ENABLE_METRICS_ROUTE) ]
2022-09-07 11:51:23 +02:00
#[ serde(default) ]
2022-08-17 17:14:55 +05:30
pub enable_metrics_route : bool ,
2022-03-24 18:52:36 +00:00
#[ serde(flatten) ]
#[ clap(flatten) ]
2020-12-22 17:13:50 +01:00
pub indexer_options : IndexerOpts ,
2022-01-19 11:21:19 +01:00
2022-02-22 15:45:36 +01:00
#[ serde(flatten) ]
2022-01-19 11:21:19 +01:00
#[ clap(flatten) ]
pub scheduler_options : SchedulerConfig ,
2022-09-06 14:50:49 +02:00
2022-10-05 19:04:33 +02:00
/// Set the path to a configuration file that should be used to setup the engine.
2022-09-06 14:50:49 +02:00
/// Format must be TOML.
#[ serde(skip_serializing) ]
#[ clap(long) ]
2022-09-07 20:22:49 +02:00
pub config_file_path : Option < PathBuf > ,
2020-12-12 13:32:06 +01:00
}
impl Opt {
2022-09-20 22:39:35 +08:00
/// Whether analytics should be enabled or not.
2022-01-19 11:21:19 +01:00
#[ cfg(all(not(debug_assertions), feature = " analytics " )) ]
pub fn analytics ( & self ) -> bool {
! self . no_analytics
}
2022-08-22 10:30:07 +05:30
2022-09-07 18:16:33 +02:00
/// Build a new Opt from config file, env vars and cli args.
2022-09-19 18:16:28 +02:00
pub fn try_build ( ) -> anyhow ::Result < ( Self , Option < PathBuf > ) > {
2022-09-07 18:16:33 +02:00
// Parse the args to get the config_file_path.
2022-09-06 14:50:49 +02:00
let mut opts = Opt ::parse ( ) ;
2022-09-19 18:16:28 +02:00
let mut config_read_from = None ;
2022-10-12 11:58:28 +08:00
let user_specified_config_file_path = opts
2022-09-19 18:16:28 +02:00
. config_file_path
. clone ( )
2022-10-12 11:58:28 +08:00
. or_else ( | | env ::var ( " MEILI_CONFIG_FILE_PATH " ) . map ( PathBuf ::from ) . ok ( ) ) ;
let config_file_path = user_specified_config_file_path
. clone ( )
2022-09-27 22:58:25 +08:00
. unwrap_or_else ( | | PathBuf ::from ( DEFAULT_CONFIG_FILE_PATH ) ) ;
match std ::fs ::read ( & config_file_path ) {
Ok ( config ) = > {
// If the file is successfully read, we deserialize it with `toml`.
let opt_from_config = toml ::from_slice ::< Opt > ( & config ) ? ;
// Return an error if config file contains 'config_file_path'
// Using that key in the config file doesn't make sense bc it creates a logical loop (config file referencing itself)
if opt_from_config . config_file_path . is_some ( ) {
anyhow ::bail! ( " `config_file_path` is not supported in config file " )
2022-09-06 14:50:49 +02:00
}
2022-09-27 22:58:25 +08:00
// We inject the values from the toml in the corresponding env vars if needs be. Doing so, we respect the priority toml < env vars < cli args.
opt_from_config . export_to_env ( ) ;
// Once injected we parse the cli args once again to take the new env vars into scope.
opts = Opt ::parse ( ) ;
config_read_from = Some ( config_file_path ) ;
2022-09-06 09:23:16 +02:00
}
2022-10-12 11:58:28 +08:00
Err ( e ) = > {
2022-10-14 14:16:10 +08:00
if let Some ( path ) = user_specified_config_file_path {
2022-10-12 11:58:28 +08:00
// If we have an error while reading the file defined by the user.
2022-10-14 14:16:10 +08:00
anyhow ::bail! (
2022-10-12 11:58:28 +08:00
" unable to open or read the {:?} configuration file: {}. " ,
path ,
e ,
2022-10-14 14:16:10 +08:00
)
2022-09-06 14:50:49 +02:00
}
2022-09-06 09:23:16 +02:00
}
}
2022-09-19 18:16:28 +02:00
Ok ( ( opts , config_read_from ) )
2022-09-06 09:23:16 +02:00
}
2022-09-07 18:16:33 +02:00
/// Exports the opts values to their corresponding env vars if they are not set.
2022-09-06 09:23:16 +02:00
fn export_to_env ( self ) {
2022-09-19 18:16:28 +02:00
let Opt {
db_path ,
http_addr ,
master_key ,
env ,
max_index_size ,
max_task_db_size ,
http_payload_size_limit ,
ssl_cert_path ,
ssl_key_path ,
ssl_auth_path ,
ssl_ocsp_path ,
ssl_require_auth ,
ssl_resumption ,
ssl_tickets ,
snapshot_dir ,
schedule_snapshot ,
snapshot_interval_sec ,
dumps_dir ,
log_level ,
indexer_options ,
scheduler_options ,
import_snapshot : _ ,
ignore_missing_snapshot : _ ,
ignore_snapshot_if_db_exists : _ ,
import_dump : _ ,
ignore_missing_dump : _ ,
ignore_dump_if_db_exists : _ ,
config_file_path : _ ,
#[ cfg(all(not(debug_assertions), feature = " analytics " )) ]
no_analytics ,
#[ cfg(feature = " metrics " ) ]
enable_metrics_route ,
} = self ;
export_to_env_if_not_present ( MEILI_DB_PATH , db_path ) ;
export_to_env_if_not_present ( MEILI_HTTP_ADDR , http_addr ) ;
if let Some ( master_key ) = master_key {
2022-09-06 09:23:16 +02:00
export_to_env_if_not_present ( MEILI_MASTER_KEY , master_key ) ;
}
2022-09-19 18:16:28 +02:00
export_to_env_if_not_present ( MEILI_ENV , env ) ;
2022-09-06 09:23:16 +02:00
#[ cfg(all(not(debug_assertions), feature = " analytics " )) ]
{
2022-09-19 18:16:28 +02:00
export_to_env_if_not_present ( MEILI_NO_ANALYTICS , no_analytics . to_string ( ) ) ;
2022-09-06 09:23:16 +02:00
}
2022-09-19 18:16:28 +02:00
export_to_env_if_not_present ( MEILI_MAX_INDEX_SIZE , max_index_size . to_string ( ) ) ;
export_to_env_if_not_present ( MEILI_MAX_TASK_DB_SIZE , max_task_db_size . to_string ( ) ) ;
2022-09-06 09:23:16 +02:00
export_to_env_if_not_present (
MEILI_HTTP_PAYLOAD_SIZE_LIMIT ,
2022-09-19 18:16:28 +02:00
http_payload_size_limit . to_string ( ) ,
2022-09-06 09:23:16 +02:00
) ;
2022-09-19 18:16:28 +02:00
if let Some ( ssl_cert_path ) = ssl_cert_path {
2022-09-06 09:23:16 +02:00
export_to_env_if_not_present ( MEILI_SSL_CERT_PATH , ssl_cert_path ) ;
}
2022-09-19 18:16:28 +02:00
if let Some ( ssl_key_path ) = ssl_key_path {
2022-09-06 09:23:16 +02:00
export_to_env_if_not_present ( MEILI_SSL_KEY_PATH , ssl_key_path ) ;
}
2022-09-19 18:16:28 +02:00
if let Some ( ssl_auth_path ) = ssl_auth_path {
2022-09-06 09:23:16 +02:00
export_to_env_if_not_present ( MEILI_SSL_AUTH_PATH , ssl_auth_path ) ;
}
2022-09-19 18:16:28 +02:00
if let Some ( ssl_ocsp_path ) = ssl_ocsp_path {
2022-09-06 09:23:16 +02:00
export_to_env_if_not_present ( MEILI_SSL_OCSP_PATH , ssl_ocsp_path ) ;
}
2022-09-19 18:16:28 +02:00
export_to_env_if_not_present ( MEILI_SSL_REQUIRE_AUTH , ssl_require_auth . to_string ( ) ) ;
export_to_env_if_not_present ( MEILI_SSL_RESUMPTION , ssl_resumption . to_string ( ) ) ;
export_to_env_if_not_present ( MEILI_SSL_TICKETS , ssl_tickets . to_string ( ) ) ;
export_to_env_if_not_present ( MEILI_SNAPSHOT_DIR , snapshot_dir ) ;
export_to_env_if_not_present ( MEILI_SCHEDULE_SNAPSHOT , schedule_snapshot . to_string ( ) ) ;
2022-09-06 09:23:16 +02:00
export_to_env_if_not_present (
MEILI_SNAPSHOT_INTERVAL_SEC ,
2022-09-19 18:16:28 +02:00
snapshot_interval_sec . to_string ( ) ,
2022-09-06 09:23:16 +02:00
) ;
2022-09-19 18:16:28 +02:00
export_to_env_if_not_present ( MEILI_DUMPS_DIR , dumps_dir ) ;
export_to_env_if_not_present ( MEILI_LOG_LEVEL , log_level ) ;
2022-09-06 09:23:16 +02:00
#[ cfg(feature = " metrics " ) ]
{
export_to_env_if_not_present (
MEILI_ENABLE_METRICS_ROUTE ,
2022-09-19 18:16:28 +02:00
enable_metrics_route . to_string ( ) ,
2022-09-06 09:23:16 +02:00
) ;
}
2022-09-19 18:16:28 +02:00
indexer_options . export_to_env ( ) ;
scheduler_options . export_to_env ( ) ;
2022-09-06 09:23:16 +02:00
}
2021-09-14 18:39:02 +02:00
pub fn get_ssl_config ( & self ) -> anyhow ::Result < Option < rustls ::ServerConfig > > {
2020-12-12 13:32:06 +01:00
if let ( Some ( cert_path ) , Some ( key_path ) ) = ( & self . ssl_cert_path , & self . ssl_key_path ) {
2022-01-21 20:44:17 +00:00
let config = rustls ::ServerConfig ::builder ( ) . with_safe_defaults ( ) ;
let config = match & self . ssl_auth_path {
2020-12-12 13:32:06 +01:00
Some ( auth_path ) = > {
let roots = load_certs ( auth_path . to_path_buf ( ) ) ? ;
let mut client_auth_roots = RootCertStore ::empty ( ) ;
for root in roots {
client_auth_roots . add ( & root ) . unwrap ( ) ;
}
if self . ssl_require_auth {
2022-01-21 20:44:17 +00:00
let verifier = AllowAnyAuthenticatedClient ::new ( client_auth_roots ) ;
config . with_client_cert_verifier ( verifier )
2020-12-12 13:32:06 +01:00
} else {
2022-01-21 20:44:17 +00:00
let verifier =
AllowAnyAnonymousOrAuthenticatedClient ::new ( client_auth_roots ) ;
config . with_client_cert_verifier ( verifier )
2020-12-12 13:32:06 +01:00
}
}
2022-01-21 20:44:17 +00:00
None = > config . with_no_client_auth ( ) ,
2020-12-12 13:32:06 +01:00
} ;
let certs = load_certs ( cert_path . to_path_buf ( ) ) ? ;
let privkey = load_private_key ( key_path . to_path_buf ( ) ) ? ;
let ocsp = load_ocsp ( & self . ssl_ocsp_path ) ? ;
2022-01-21 20:44:17 +00:00
let mut config = config
. with_single_cert_with_ocsp_and_sct ( certs , privkey , ocsp , vec! [ ] )
2021-09-14 18:39:02 +02:00
. map_err ( | _ | anyhow ::anyhow! ( " bad certificates/private key " ) ) ? ;
2020-12-12 13:32:06 +01:00
2022-01-21 20:44:17 +00:00
config . key_log = Arc ::new ( rustls ::KeyLogFile ::new ( ) ) ;
2020-12-12 13:32:06 +01:00
if self . ssl_resumption {
2022-01-21 20:44:17 +00:00
config . session_storage = ServerSessionMemoryCache ::new ( 256 ) ;
2020-12-12 13:32:06 +01:00
}
if self . ssl_tickets {
2022-01-21 20:44:17 +00:00
config . ticketer = rustls ::Ticketer ::new ( ) . unwrap ( ) ;
2020-12-12 13:32:06 +01:00
}
Ok ( Some ( config ) )
} else {
Ok ( None )
}
}
}
2021-09-14 18:39:02 +02:00
fn load_certs ( filename : PathBuf ) -> anyhow ::Result < Vec < rustls ::Certificate > > {
2021-09-28 22:22:59 +02:00
let certfile =
fs ::File ::open ( filename ) . map_err ( | _ | anyhow ::anyhow! ( " cannot open certificate file " ) ) ? ;
2020-12-12 13:32:06 +01:00
let mut reader = BufReader ::new ( certfile ) ;
2022-01-21 20:44:17 +00:00
certs ( & mut reader )
. map ( | certs | certs . into_iter ( ) . map ( rustls ::Certificate ) . collect ( ) )
. map_err ( | _ | anyhow ::anyhow! ( " cannot read certificate file " ) )
2020-12-12 13:32:06 +01:00
}
2021-09-14 18:39:02 +02:00
fn load_private_key ( filename : PathBuf ) -> anyhow ::Result < rustls ::PrivateKey > {
2020-12-12 13:32:06 +01:00
let rsa_keys = {
2021-09-28 22:22:59 +02:00
let keyfile = fs ::File ::open ( filename . clone ( ) )
. map_err ( | _ | anyhow ::anyhow! ( " cannot open private key file " ) ) ? ;
2020-12-12 13:32:06 +01:00
let mut reader = BufReader ::new ( keyfile ) ;
2021-09-28 22:22:59 +02:00
rsa_private_keys ( & mut reader )
. map_err ( | _ | anyhow ::anyhow! ( " file contains invalid rsa private key " ) ) ?
2020-12-12 13:32:06 +01:00
} ;
let pkcs8_keys = {
2021-09-28 22:22:59 +02:00
let keyfile = fs ::File ::open ( filename )
. map_err ( | _ | anyhow ::anyhow! ( " cannot open private key file " ) ) ? ;
2020-12-12 13:32:06 +01:00
let mut reader = BufReader ::new ( keyfile ) ;
2021-09-28 22:22:59 +02:00
pkcs8_private_keys ( & mut reader ) . map_err ( | _ | {
anyhow ::anyhow! (
" file contains invalid pkcs8 private key (encrypted keys not supported) "
)
} ) ?
2020-12-12 13:32:06 +01:00
} ;
// prefer to load pkcs8 keys
if ! pkcs8_keys . is_empty ( ) {
2022-01-21 20:44:17 +00:00
Ok ( rustls ::PrivateKey ( pkcs8_keys [ 0 ] . clone ( ) ) )
2020-12-12 13:32:06 +01:00
} else {
assert! ( ! rsa_keys . is_empty ( ) ) ;
2022-01-21 20:44:17 +00:00
Ok ( rustls ::PrivateKey ( rsa_keys [ 0 ] . clone ( ) ) )
2020-12-12 13:32:06 +01:00
}
}
2021-09-14 18:39:02 +02:00
fn load_ocsp ( filename : & Option < PathBuf > ) -> anyhow ::Result < Vec < u8 > > {
2020-12-12 13:32:06 +01:00
let mut ret = Vec ::new ( ) ;
if let Some ( ref name ) = filename {
fs ::File ::open ( name )
2021-09-14 18:39:02 +02:00
. map_err ( | _ | anyhow ::anyhow! ( " cannot open ocsp file " ) ) ?
2020-12-12 13:32:06 +01:00
. read_to_end ( & mut ret )
2021-09-14 18:39:02 +02:00
. map_err ( | _ | anyhow ::anyhow! ( " cannot read oscp file " ) ) ? ;
2020-12-12 13:32:06 +01:00
}
Ok ( ret )
}
2022-03-16 17:01:05 +01:00
2022-09-07 18:16:33 +02:00
/// Functions used to get default value for `Opt` fields, needs to be function because of serde's default attribute.
2022-09-07 11:51:23 +02:00
fn default_db_path ( ) -> PathBuf {
PathBuf ::from ( DEFAULT_DB_PATH )
}
2022-10-06 22:42:20 -04:00
pub fn default_http_addr ( ) -> String {
2022-09-07 11:51:23 +02:00
DEFAULT_HTTP_ADDR . to_string ( )
}
fn default_env ( ) -> String {
DEFAULT_ENV . to_string ( )
}
fn default_max_index_size ( ) -> Byte {
Byte ::from_str ( DEFAULT_MAX_INDEX_SIZE ) . unwrap ( )
}
fn default_max_task_db_size ( ) -> Byte {
Byte ::from_str ( DEFAULT_MAX_TASK_DB_SIZE ) . unwrap ( )
}
fn default_http_payload_size_limit ( ) -> Byte {
Byte ::from_str ( DEFAULT_HTTP_PAYLOAD_SIZE_LIMIT ) . unwrap ( )
}
fn default_snapshot_dir ( ) -> PathBuf {
PathBuf ::from ( DEFAULT_SNAPSHOT_DIR )
}
fn default_snapshot_interval_sec ( ) -> u64 {
DEFAULT_SNAPSHOT_INTERVAL_SEC
}
fn default_dumps_dir ( ) -> PathBuf {
PathBuf ::from ( DEFAULT_DUMPS_DIR )
}
fn default_log_level ( ) -> String {
DEFAULT_LOG_LEVEL . to_string ( )
}
2022-03-16 17:01:05 +01:00
#[ cfg(test) ]
mod test {
2022-10-14 14:49:31 +08:00
2022-03-16 17:01:05 +01:00
use super ::* ;
#[ test ]
fn test_valid_opt ( ) {
assert! ( Opt ::try_parse_from ( Some ( " " ) ) . is_ok ( ) ) ;
}
2022-10-12 11:58:28 +08:00
#[ test ]
fn test_meilli_config_file_path_valid ( ) {
temp_env ::with_vars (
vec! [ ( " MEILI_CONFIG_FILE_PATH " , Some ( " ../config.toml " ) ) ] , // Relative path in meilisearch_http package
| | {
assert! ( Opt ::try_build ( ) . is_ok ( ) ) ;
} ,
) ;
}
#[ test ]
fn test_meilli_config_file_path_invalid ( ) {
temp_env ::with_vars (
vec! [ ( " MEILI_CONFIG_FILE_PATH " , Some ( " ../configgg.toml " ) ) ] ,
| | {
2022-10-14 14:49:31 +08:00
let possible_error_messages = [
" unable to open or read the \" ../configgg.toml \" configuration file: No such file or directory (os error 2). " ,
" unable to open or read the \" ../configgg.toml \" configuration file: The system cannot find the file specified. (os error 2). " , // Windows
] ;
let error_message = Opt ::try_build ( ) . unwrap_err ( ) . to_string ( ) ;
assert! (
possible_error_messages . contains ( & error_message . as_str ( ) ) ,
" Expected onf of {:?}, got {:?}. " ,
possible_error_messages ,
error_message
2022-10-12 11:58:28 +08:00
) ;
} ,
) ;
}
2022-03-16 17:01:05 +01:00
}