MeiliSearch/meilisearch-http/src/lib.rs

172 lines
8.3 KiB
Rust
Raw Normal View History

//! # MeiliSearch
//! Hello there, future contributors. If you are here and see this code, it's probably because you want to add a super new fancy feature in MeiliSearch or fix a bug and first of all, thank you for that!
//!
//! To help you in this task, we'll try to do a little overview of the project.
//! ## Milli
2021-07-29 14:40:41 +02:00
//! [Milli](https://github.com/meilisearch/milli) is the core library of MeiliSearch. It's where we actually index documents and perform searches. Its purpose is to do these two tasks as fast as possible. You can give an update to milli, and it'll uses as many cores as provided to perform it as fast as possible. Nothing more. You can perform searches at the same time (search only uses one core).
//! As you can see, we're missing quite a lot of features here; milli does not handle multiples indexes, it can't queue updates, it doesn't provide any web / API frontend, it doesn't implement dumps or snapshots, etc...
//!
//! ## `Index` module
2021-07-29 14:40:41 +02:00
//! The [index] module is what encapsulates one milli index. It abstracts over its transaction and isolates a task that can be run into a thread. This is the unit of interaction with milli.
//! If you add a feature to milli, you'll probably need to add it in this module too before exposing it to the rest of meilisearch.
//!
//! ## `IndexController` module
2021-07-29 14:40:41 +02:00
//! To handle multiple indexes, we created an [index_controller]. It's in charge of creating new indexes, keeping references to all its indexes, forward asynchronous updates to its indexes, and provide an API to search in its indexes synchronously.
//! To achieves this goal, we use an [actor model](https://en.wikipedia.org/wiki/Actor_model).
//!
//! ### The actor model
//! Every actor is composed of at least three files:
//! - `mod.rs` declare and import all the files used by the actor. We also describe the interface (= all the methods) used to interact with the actor. If you are not modifying anything inside of an actor, this is usually all you need to see.
//! - `handle_impl.rs` implements the interface described in the `mod.rs`; in reality, there is no code logic in this file. Every method is only wrapping its parameters in a structure that is sent to the actor. This is useful for test and futureproofing.
//! - `message.rs` contains an enum that describes all the interactions you can have with the actor.
//! - `actor.rs` is used to create and execute the actor. It's where we'll write the loop looking for new messages and actually perform the tasks.
//!
//! MeiliSearch currently uses four actors:
//! - [`uuid_resolver`](index_controller/uuid_resolver/index.html) hold the association between the user-provided indexes name and the internal [`uuid`](https://en.wikipedia.org/wiki/Universally_unique_identifier) representation we use.
//! - [`index_actor`](index_controller::index_actor) is our representation of multiples indexes. Any request made to MeiliSearch that needs to talk to milli will pass through this actor.
//! - [`update_actor`](index_controller/update_actor/index.html) is in charge of indexes updates. Since updates can take a long time to receive and process, we need to:
//! 1. Store them as fast as possible so we can continue to receive other updates even if nothing has been processed
//! 2. Feed the `index_actor` with a new update every time it finished its current job.
//! - [`dump_actor`](index_controller/dump_actor/index.html) this actor handle the [dumps](https://docs.meilisearch.com/reference/api/dump.html). It needs to contact all the others actors and create a dump of everything that was currently happening.
//!
//! ## Data module
2021-07-29 14:40:41 +02:00
//! The [data] module provide a unified interface to communicate with the index controller and other services (snapshot, dumps, ...), initialize the MeiliSearch instance
//!
2021-07-29 14:40:41 +02:00
//! ## HTTP server
//! To handle the web and API part, we are using [actix-web](https://docs.rs/actix-web/); you can find all routes in the [routes] module.
2021-07-29 14:40:41 +02:00
//! Currently, the configuration of actix-web is made in the [lib.rs](crate).
//! Most of the routes use [extractors] to handle the authentication.
#![allow(rustdoc::private_intra_doc_links)]
2020-12-12 13:32:06 +01:00
pub mod data;
2021-06-15 17:55:27 +02:00
#[macro_use]
2020-12-12 13:32:06 +01:00
pub mod error;
2021-06-24 14:22:12 +02:00
#[macro_use]
2021-06-23 14:56:02 +02:00
pub mod extractors;
2020-12-12 13:32:06 +01:00
pub mod helpers;
2021-03-04 11:23:41 +01:00
mod index;
2021-01-13 17:50:36 +01:00
mod index_controller;
2021-03-15 18:11:10 +01:00
pub mod option;
pub mod routes;
2020-12-12 13:32:06 +01:00
2021-06-16 17:12:49 +02:00
#[cfg(all(not(debug_assertions), feature = "analytics"))]
pub mod analytics;
2021-06-24 14:22:12 +02:00
use crate::extractors::authentication::AuthConfig;
2020-12-12 13:32:06 +01:00
pub use self::data::Data;
2021-03-15 18:11:10 +01:00
pub use option::Opt;
2021-03-10 11:56:51 +01:00
2021-06-23 14:56:02 +02:00
use actix_web::web;
2021-06-24 14:22:12 +02:00
use extractors::authentication::policies::*;
2021-06-24 16:25:52 +02:00
use extractors::payload::PayloadConfig;
2021-06-23 13:21:48 +02:00
pub fn configure_data(config: &mut web::ServiceConfig, data: Data) {
let http_payload_size_limit = data.http_payload_size_limit();
config
2021-06-24 14:22:12 +02:00
.data(data.clone())
.app_data(data)
2021-06-23 13:21:48 +02:00
.app_data(
web::JsonConfig::default()
2021-06-23 15:02:00 +02:00
.limit(http_payload_size_limit)
2021-06-23 13:21:48 +02:00
.content_type(|_mime| true) // Accept all mime types
.error_handler(|err, _req| error::payload_error_handler(err).into()),
)
2021-06-23 13:58:22 +02:00
.app_data(PayloadConfig::new(http_payload_size_limit))
2021-06-23 13:21:48 +02:00
.app_data(
web::QueryConfig::default()
.error_handler(|err, _req| error::payload_error_handler(err).into()),
);
}
2021-03-10 11:56:51 +01:00
2021-06-24 14:22:12 +02:00
pub fn configure_auth(config: &mut web::ServiceConfig, data: &Data) {
let keys = data.api_keys();
2021-06-24 16:25:52 +02:00
let auth_config = if let Some(ref master_key) = keys.master {
2021-06-24 14:22:12 +02:00
let private_key = keys.private.as_ref().unwrap();
let public_key = keys.public.as_ref().unwrap();
let mut policies = init_policies!(Public, Private, Admin);
create_users!(
policies,
master_key.as_bytes() => { Admin, Private, Public },
private_key.as_bytes() => { Private, Public },
public_key.as_bytes() => { Public }
);
AuthConfig::Auth(policies)
} else {
AuthConfig::NoAuth
};
config.app_data(auth_config);
2021-06-23 19:35:26 +02:00
}
2021-06-23 13:21:48 +02:00
#[cfg(feature = "mini-dashboard")]
pub fn dashboard(config: &mut web::ServiceConfig, enable_frontend: bool) {
2021-06-23 13:55:16 +02:00
use actix_web::HttpResponse;
2021-06-24 16:25:52 +02:00
use actix_web_static_files::Resource;
2021-04-21 13:49:21 +02:00
2021-06-23 14:48:33 +02:00
mod generated {
2021-06-23 13:21:48 +02:00
include!(concat!(env!("OUT_DIR"), "/generated.rs"));
}
2021-04-21 13:49:21 +02:00
2021-06-23 13:21:48 +02:00
if enable_frontend {
2021-06-23 14:48:33 +02:00
let generated = generated::generate();
2021-06-24 16:25:52 +02:00
let mut scope = web::scope("/");
// Generate routes for mini-dashboard assets
for (path, resource) in generated.into_iter() {
let Resource {
mime_type, data, ..
} = resource;
// Redirect index.html to /
if path == "index.html" {
config.service(web::resource("/").route(
web::get().to(move || HttpResponse::Ok().content_type(mime_type).body(data)),
));
} else {
scope = scope.service(web::resource(path).route(
web::get().to(move || HttpResponse::Ok().content_type(mime_type).body(data)),
));
2021-06-23 13:21:48 +02:00
}
2021-06-24 16:25:52 +02:00
}
config.service(scope);
2021-06-23 13:21:48 +02:00
} else {
2021-06-24 19:02:28 +02:00
config.service(web::resource("/").route(web::get().to(routes::running)));
2021-06-23 13:21:48 +02:00
}
}
#[cfg(not(feature = "mini-dashboard"))]
pub fn dashboard(config: &mut web::ServiceConfig, _enable_frontend: bool) {
2021-06-24 19:02:28 +02:00
config.service(web::resource("/").route(web::get().to(routes::running)));
2021-06-23 13:21:48 +02:00
}
2021-04-21 13:49:21 +02:00
2021-06-23 13:21:48 +02:00
#[macro_export]
macro_rules! create_app {
($data:expr, $enable_frontend:expr) => {{
use actix_cors::Cors;
use actix_web::middleware::TrailingSlash;
use actix_web::App;
use actix_web::{middleware, web};
2021-07-05 14:29:20 +02:00
use meilisearch_http::routes;
2021-06-24 16:25:52 +02:00
use meilisearch_http::{configure_auth, configure_data, dashboard};
2021-04-21 13:49:21 +02:00
2021-06-23 13:21:48 +02:00
App::new()
.configure(|s| configure_data(s, $data.clone()))
2021-06-24 14:22:12 +02:00
.configure(|s| configure_auth(s, &$data))
2021-07-05 14:29:20 +02:00
.configure(routes::configure)
2021-06-23 13:21:48 +02:00
.configure(|s| dashboard(s, $enable_frontend))
.wrap(
2021-06-22 21:48:51 +02:00
Cors::default()
2021-06-23 14:48:33 +02:00
.send_wildcard()
.allowed_headers(vec!["content-type", "x-meili-api-key"])
.allow_any_origin()
.allow_any_method()
.max_age(86_400), // 24h
2021-06-22 21:48:51 +02:00
)
2021-06-23 13:21:48 +02:00
.wrap(middleware::Logger::default())
.wrap(middleware::Compress::default())
2021-06-23 14:48:33 +02:00
.wrap(middleware::NormalizePath::new(
middleware::TrailingSlash::Trim,
))
2021-06-23 13:21:48 +02:00
}};
2021-03-10 11:56:51 +01:00
}