trifid/trifid-api/src/main.rs

148 lines
4.3 KiB
Rust

use crate::config::Config;
use crate::error::APIErrorResponse;
use actix_web::middleware::Logger;
use actix_web::web::{Data, JsonConfig};
use actix_web::{App, Error, HttpResponse, HttpServer};
use diesel::Connection;
use diesel_async::async_connection_wrapper::AsyncConnectionWrapper;
use diesel_async::pooled_connection::bb8::Pool;
use diesel_async::pooled_connection::AsyncDieselConnectionManager;
use diesel_async::AsyncPgConnection;
use diesel_migrations::{embed_migrations, EmbeddedMigrations, MigrationHarness};
use log::{error, info};
use std::fs;
use std::path::PathBuf;
pub mod error;
#[macro_use]
pub mod response;
pub mod config;
pub mod models;
pub mod routes;
pub mod schema;
#[macro_use]
pub mod id;
pub mod auth;
pub mod email;
#[derive(Clone)]
pub struct AppState {
pub config: Config,
pub pool: bb8::Pool<AsyncDieselConnectionManager<AsyncPgConnection>>,
}
pub const MIGRATIONS: EmbeddedMigrations = embed_migrations!("migrations");
#[actix_web::main]
async fn main() {
env_logger::init();
info!("Trifid API v{} starting up", env!("CARGO_PKG_VERSION"));
let mut args = std::env::args();
let config_path = match args.nth(1) {
Some(path) => path,
None => {
eprintln!("usage: trifid-api <config_path>");
std::process::exit(1);
}
};
let config_pathbuf = PathBuf::from(config_path);
info!("Loading config from {}", config_pathbuf.display());
let config_str = match fs::read_to_string(&config_pathbuf) {
Ok(c_str) => c_str,
Err(e) => {
error!(
"Error loading configuration from {}: {}",
config_pathbuf.display(),
e
);
std::process::exit(1);
}
};
let config: Config = match toml::from_str(&config_str) {
Ok(config) => config,
Err(e) => {
error!(
"Error parsing configuration in {}: {}",
config_pathbuf.display(),
e
);
std::process::exit(1);
}
};
info!("Connecting to the database...");
let pool_config = AsyncDieselConnectionManager::<AsyncPgConnection>::new(&config.database.url);
let pool = match Pool::builder().build(pool_config).await {
Ok(pool) => pool,
Err(e) => {
error!("Error while creating database pool: {}", e);
std::process::exit(1);
}
};
info!("Running pending migrations...");
let local_config = config.clone();
let db_url = config.database.url.clone();
match actix_web::rt::task::spawn_blocking(move || {
// Lock block
let mut conn = match AsyncConnectionWrapper::<AsyncPgConnection>::establish(&db_url) {
Ok(conn) => conn,
Err(e) => {
error!("Error acquiring connection from pool: {}", e);
std::process::exit(1);
}
};
match conn.run_pending_migrations(MIGRATIONS) {
Ok(_) => (),
Err(e) => {
error!("Failed to run pending migrations: {}", e);
std::process::exit(1);
}
}
})
.await
{
Ok(_) => (),
Err(e) => {
error!("Error waiting for migrations: {}", e);
std::process::exit(1);
}
}
let app_state = Data::new(AppState { config, pool });
let server = HttpServer::new(move || {
App::new()
.app_data(JsonConfig::default().error_handler(|err, _rq| {
Error::from({
let err2: APIErrorResponse = (&err).into();
actix_web::error::InternalError::from_response(
err,
HttpResponse::BadRequest().json(err2),
)
})
}))
.service(routes::v1::signup::signup_req)
.service(routes::v1::auth::verify_magic_link::verify_link_req)
.service(routes::v1::auth::magic_link::login_req)
.service(routes::v1::totp_authenticators::create_totp_auth_req)
.wrap(Logger::default())
.wrap(actix_cors::Cors::permissive())
.app_data(app_state.clone())
})
.bind((local_config.server.bind.ip, local_config.server.bind.port))
.unwrap();
server.run().await.unwrap();
info!("Goodbye!");
}