use std::error::Error; use std::fs; use std::path::Path; use dotenvy::dotenv; use log::{error, info}; use rocket::{catchers, routes}; use sqlx::migrate::Migrator; use sqlx::postgres::PgPoolOptions; use crate::config::TFConfig; pub mod format; pub mod util; pub mod db; pub mod config; pub mod routes; static MIGRATOR: Migrator = sqlx::migrate!(); #[rocket::main] async fn main() -> Result<(), Box> { let _ = rocket::build(); info!("[tfapi] loading config"); let _ = dotenv(); if std::env::var("CONFIG_FILE").is_err() && !Path::new("config.toml").exists() { error!("[tfapi] fatal: the environment variable CONFIG_FILE is not set"); error!("[tfapi] help: try creating a .env file that sets it"); error!("[tfapi] help: or, create a file config.toml with your config, as it is loaded automatically"); std::process::exit(1); } let config_file; if Path::new("config.toml").exists() { config_file = "config.toml".to_string(); } else { config_file = std::env::var("CONFIG_FILE").unwrap(); } let config_data = match fs::read_to_string(&config_file) { Ok(d) => d, Err(e) => { error!("[tfapi] fatal: unable to read config from {}", config_file); error!("[tfapi] fatal: {}", e); std::process::exit(1); } }; let config: TFConfig = match toml::from_str(&config_data) { Ok(c) => c, Err(e) => { error!("[tfapi] fatal: unable to parse config from {}", config_file); error!("[tfapi] fatal: {}", e); std::process::exit(1); } }; info!("[tfapi] connecting to database pool"); let pool = match PgPoolOptions::new().max_connections(5).connect(&config.db_url).await { Ok(p) => p, Err(e) => { error!("[tfapi] fatal: unable to connect to database pool"); error!("[tfapi] fatal: {}", e); std::process::exit(1); } }; info!("[tfapi] running database migrations"); MIGRATOR.run(&pool).await?; info!("[tfapi] building rocket config"); let figment = rocket::Config::figment().merge(("port", config.listen_port)); /* error_handler!(400, "ERR_MALFORMED_REQUEST", "unable to parse the request body, is it properly formatted?"); error_handler!(401, "ERR_AUTHENTICATION_REQUIRED", "this endpoint requires authentication but it was not provided"); error_handler!(403, "ERR_UNAUTHORIZED", "authorization was provided but it is expired or invalid"); error_handler!(404, "ERR_NOT_FOUND", "resource not found"); error_handler!(405, "ERR_METHOD_NOT_ALLOWED", "method not allowed for this endpoint"); error_handler!(500, "ERR_QL_QUERY_FAILED", "graphql query timed out"); error_handler!(501, "ERR_NOT_IMPLEMENTED", "query not supported by this version of graphql"); error_handler!(502, "ERR_PROXY_ERR", "servers under load, please try again later"); error_handler!(503, "ERR_SERVER_OVERLOADED", "servers under load, please try again later"); error_handler!(504, "ERR_PROXY_TIMEOUT", "servers under load, please try again later"); error_handler!(505, "ERR_CLIENT_UNSUPPORTED", "your version of dnclient is out of date, please update"); */ let _ = rocket::custom(figment) .mount("/", routes![ crate::routes::v1::auth::verify_magic_link::verify_magic_link ]) .register("/", catchers![ crate::routes::handler_400, crate::routes::handler_401, crate::routes::handler_403, crate::routes::handler_404, crate::routes::handler_500, crate::routes::handler_501, crate::routes::handler_502, crate::routes::handler_503, crate::routes::handler_504, crate::routes::handler_505, ]) .manage(pool) .manage(config) .launch().await?; Ok(()) }