2023-11-19 03:51:45 +00:00
|
|
|
use std::fs;
|
|
|
|
use std::path::PathBuf;
|
|
|
|
use actix_web::{App, Error, HttpResponse, HttpServer};
|
|
|
|
use actix_web::middleware::Logger;
|
|
|
|
use actix_web::web::{Data, JsonConfig};
|
|
|
|
use diesel::Connection;
|
|
|
|
use diesel_async::async_connection_wrapper::AsyncConnectionWrapper;
|
|
|
|
use diesel_async::AsyncPgConnection;
|
|
|
|
use diesel_async::pooled_connection::AsyncDieselConnectionManager;
|
|
|
|
use diesel_async::pooled_connection::bb8::Pool;
|
|
|
|
use diesel_migrations::{embed_migrations, EmbeddedMigrations, MigrationHarness};
|
|
|
|
use log::{error, info};
|
|
|
|
use crate::config::Config;
|
|
|
|
use crate::error::APIErrorResponse;
|
|
|
|
|
2023-04-02 17:06:16 +00:00
|
|
|
pub mod error;
|
2023-11-19 15:49:08 +00:00
|
|
|
#[macro_use]
|
2023-08-19 03:55:27 +00:00
|
|
|
pub mod response;
|
2023-11-19 03:51:45 +00:00
|
|
|
pub mod config;
|
|
|
|
pub mod routes;
|
2023-11-19 15:49:08 +00:00
|
|
|
pub mod schema;
|
|
|
|
pub mod models;
|
|
|
|
#[macro_use]
|
|
|
|
pub mod id;
|
2023-11-19 16:31:30 +00:00
|
|
|
pub mod email;
|
2023-11-19 15:49:08 +00:00
|
|
|
|
2023-04-02 19:25:52 +00:00
|
|
|
|
2023-11-19 03:51:45 +00:00
|
|
|
#[derive(Clone)]
|
2023-04-02 19:25:52 +00:00
|
|
|
pub struct AppState {
|
2023-11-19 03:51:45 +00:00
|
|
|
pub config: Config,
|
|
|
|
pub pool: bb8::Pool<AsyncDieselConnectionManager<AsyncPgConnection>>
|
2023-04-02 19:25:52 +00:00
|
|
|
}
|
2023-02-20 18:42:15 +00:00
|
|
|
|
2023-11-19 03:51:45 +00:00
|
|
|
pub const MIGRATIONS: EmbeddedMigrations = embed_migrations!("migrations");
|
|
|
|
|
2023-04-02 16:08:36 +00:00
|
|
|
#[actix_web::main]
|
2023-11-19 03:51:45 +00:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
};
|
2023-02-03 02:39:41 +00:00
|
|
|
|
2023-11-19 03:51:45 +00:00
|
|
|
let config_pathbuf = PathBuf::from(config_path);
|
|
|
|
info!("Loading config from {}", config_pathbuf.display());
|
2023-02-03 02:39:41 +00:00
|
|
|
|
2023-11-19 03:51:45 +00:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
};
|
2023-02-03 02:39:41 +00:00
|
|
|
|
2023-11-19 03:51:45 +00:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
};
|
2023-02-03 02:39:41 +00:00
|
|
|
|
2023-11-19 03:51:45 +00:00
|
|
|
info!("Connecting to the database...");
|
2023-04-02 17:06:16 +00:00
|
|
|
|
2023-11-19 03:51:45 +00:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
};
|
2023-04-02 17:06:16 +00:00
|
|
|
|
2023-11-19 03:51:45 +00:00
|
|
|
info!("Running pending migrations...");
|
|
|
|
|
2023-11-19 15:49:08 +00:00
|
|
|
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) {
|
2023-11-19 03:51:45 +00:00
|
|
|
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) => {
|
2023-11-19 15:49:08 +00:00
|
|
|
error!("Failed to run pending migrations: {}", e);
|
|
|
|
std::process::exit(1);
|
2023-11-19 03:51:45 +00:00
|
|
|
}
|
|
|
|
}
|
2023-11-19 15:49:08 +00:00
|
|
|
}).await {
|
|
|
|
Ok(_) => (),
|
|
|
|
Err(e) => {
|
|
|
|
error!("Error waiting for migrations: {}", e);
|
|
|
|
std::process::exit(1);
|
|
|
|
}
|
2023-11-19 03:51:45 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
let app_state = Data::new(AppState {
|
|
|
|
config,
|
|
|
|
pool
|
|
|
|
});
|
|
|
|
|
|
|
|
let server = HttpServer::new(move || {
|
2023-04-02 17:06:16 +00:00
|
|
|
App::new()
|
2023-11-19 03:51:45 +00:00
|
|
|
.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),
|
|
|
|
)
|
|
|
|
})
|
|
|
|
}))
|
2023-11-19 15:49:08 +00:00
|
|
|
.service(routes::v1::signup::signup_req)
|
2023-11-20 01:58:46 +00:00
|
|
|
.service(routes::v1::auth::verify_magic_link::verify_link_req)
|
2023-11-19 03:51:45 +00:00
|
|
|
.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!");
|
2023-04-02 16:08:36 +00:00
|
|
|
}
|