105 lines
2.7 KiB
Rust
105 lines
2.7 KiB
Rust
// ©AngelaMos | 2026
|
|
// main.rs
|
|
//
|
|
// Axumortem web server entry point
|
|
//
|
|
// Bootstraps the Axum HTTP server with clap-driven CLI
|
|
// configuration, tracing subscriber initialization with
|
|
// EnvFilter, PostgreSQL connection pool (max 20
|
|
// connections) via SQLx PgPoolOptions, automatic database
|
|
// migrations, and AnalysisEngine initialization. Assembles
|
|
// AppState from the pool, engine, and config, then applies
|
|
// tower layers for HTTP tracing, CORS, and body size
|
|
// limits before binding a TCP listener. Graceful shutdown
|
|
// is handled via ctrl_c signal.
|
|
//
|
|
// Connects to:
|
|
// config.rs - AppConfig (clap Parser)
|
|
// state.rs - AppState
|
|
// db/mod.rs - run_migrations
|
|
// middleware/ - cors::layer
|
|
// routes/mod.rs - api_router
|
|
|
|
mod config;
|
|
mod db;
|
|
mod error;
|
|
mod middleware;
|
|
mod routes;
|
|
mod state;
|
|
|
|
use std::sync::Arc;
|
|
|
|
use anyhow::Context;
|
|
use axum::extract::DefaultBodyLimit;
|
|
use clap::Parser;
|
|
use sqlx::postgres::PgPoolOptions;
|
|
use tower::ServiceBuilder;
|
|
use tower_http::trace::TraceLayer;
|
|
use tracing_subscriber::EnvFilter;
|
|
|
|
use config::AppConfig;
|
|
use state::AppState;
|
|
|
|
const DB_MAX_CONNECTIONS: u32 = 20;
|
|
|
|
#[tokio::main]
|
|
async fn main() -> anyhow::Result<()> {
|
|
let config = AppConfig::parse();
|
|
|
|
tracing_subscriber::fmt()
|
|
.with_env_filter(
|
|
EnvFilter::try_from_default_env()
|
|
.unwrap_or_else(|_| EnvFilter::new("info,tower_http=debug")),
|
|
)
|
|
.init();
|
|
|
|
let db = PgPoolOptions::new()
|
|
.max_connections(DB_MAX_CONNECTIONS)
|
|
.connect(&config.database_url)
|
|
.await
|
|
.context("failed to connect to database")?;
|
|
|
|
db::run_migrations(&db)
|
|
.await
|
|
.context("failed to run database migrations")?;
|
|
|
|
let engine =
|
|
axumortem_engine::AnalysisEngine::new().context("failed to initialize analysis engine")?;
|
|
|
|
let config = Arc::new(config);
|
|
|
|
let state = AppState {
|
|
db,
|
|
engine: Arc::new(engine),
|
|
config: Arc::clone(&config),
|
|
};
|
|
|
|
let layers = ServiceBuilder::new()
|
|
.layer(TraceLayer::new_for_http())
|
|
.layer(middleware::cors::layer(&config))
|
|
.layer(DefaultBodyLimit::max(config.max_upload_size));
|
|
|
|
let app = routes::api_router().layer(layers).with_state(state);
|
|
|
|
let bind_address = config.bind_address();
|
|
let listener = tokio::net::TcpListener::bind(&bind_address)
|
|
.await
|
|
.context("failed to bind TCP listener")?;
|
|
|
|
tracing::info!("listening on {}", bind_address);
|
|
|
|
axum::serve(listener, app)
|
|
.with_graceful_shutdown(shutdown_signal())
|
|
.await
|
|
.context("server error")?;
|
|
|
|
Ok(())
|
|
}
|
|
|
|
async fn shutdown_signal() {
|
|
tokio::signal::ctrl_c()
|
|
.await
|
|
.expect("failed to install ctrl+c handler");
|
|
tracing::info!("shutdown signal received");
|
|
}
|