mod captcha_solutions; mod config; mod errors; mod forms; mod logging; mod mailer; mod routes; mod templates; use anyhow::Result; use axum::extract::Extension; use axum::routing::{get, post}; use axum::{error_handling::HandleErrorLayer, http::StatusCode, BoxError}; use axum::{Router, Server}; use std::net::{IpAddr, Ipv4Addr, SocketAddr}; use std::process; use std::sync::Arc; use std::time::Duration; use tower::buffer::BufferLayer; use tower::limit::RateLimitLayer; use tower::ServiceBuilder; use tower_http::trace::{DefaultOnResponse, TraceLayer}; use tracing::Level; use tracing_appender::non_blocking::WorkerGuard; async fn init() -> Result { let mut config = config::Config::new()?; let path_prefix = config.path_prefix.clone(); let mailer = Arc::new(mailer::Mailer::new(&mut config)?); let address = config.socket_address.address; let socket_address = SocketAddr::new( IpAddr::V4(Ipv4Addr::new( address[0], address[1], address[2], address[3], )), config.socket_address.port, ); let tracing_worker_gurad = logging::init_logger(&config.logging); let config = Arc::new(config); let captcha_solutions = Arc::new(captcha_solutions::SharedCaptchaSolutions::new()); let routes = Router::new() .route("/", get(routes::index)) .route("/submit", post(routes::submit)) .route("/success", get(routes::success)) .layer(TraceLayer::new_for_http().on_response(DefaultOnResponse::new().level(Level::INFO))) .layer( ServiceBuilder::new() .layer(HandleErrorLayer::new(|err: BoxError| async move { ( StatusCode::INTERNAL_SERVER_ERROR, format!("Unhandled error: {}", err), ) })) .layer(BufferLayer::new(1024)) .layer(RateLimitLayer::new(2, Duration::from_secs(3))), ) .layer(Extension(config)) .layer(Extension(mailer)) .layer(Extension(captcha_solutions)); let app = Router::new().nest(&path_prefix, routes); Server::bind(&socket_address) .serve(app.into_make_service()) .await .unwrap(); Ok(tracing_worker_gurad) } #[tokio::main] async fn main() { let _tracing_worker_gurad = init().await.unwrap_or_else(|e| { eprintln!("{e:?}"); process::exit(1); }); }