mod admin_examples; mod db; mod embed; mod howto; mod state; use crate::state::AppState; use admin_examples::static_repository; use admin_examples::user_repository; use admin_examples::file_repository; use axum::{ body::Bytes, extract::MatchedPath, http::{HeaderMap, Request}, response::Response, }; use axum::{ extract::State, handler::HandlerWithoutStateExt, response::IntoResponse, routing::get, Router, }; use dotenvy::dotenv; use log::info; use rear::admin; use rear::service::{handlers, templates}; use std::env; use std::net::SocketAddr; use std::sync::Arc; use std::time::Duration; use tokio::net::TcpListener; use tower_http::{classify::ServerErrorsFailureClass, trace::TraceLayer}; use tracing::{info_span, Span}; async fn home(templates: State) -> impl IntoResponse { templates.render_html("index.html", ()) } async fn hello_world(templates: State) -> impl IntoResponse { templates.render_html("hello_world.html", ()) } #[tokio::main] async fn main() { // Load environment dotenv().ok(); env_logger::init(); info!("Miniweb starting..."); // Database Configuration. let db_connection = db::establish_connection().await; // Prepare Application State Members let tmpl = templates::Templates::initialize().expect("Template Engine could not be loaded."); let mut admin = admin::state::AdminRegistry::new("admin"); // Register Admin Apps static_repository::register(&mut admin); user_repository::register(&mut admin, db_connection); file_repository::register(&mut admin, "static/admin"); // Create global Application State. let state: AppState = AppState { templates: tmpl, admin: Arc::new(admin), }; // Application Route let app = Router::new() .route("/", get(home)) .route("/hello", get(hello_world)) //.merge(admin_router) .nest("/admin", admin::routes()) .nest("/howto", howto::routes()) .route_service("/static/*file", embed::static_handler.into_service()) .fallback(handlers::not_found_handler) .with_state(state) .layer( TraceLayer::new_for_http() .make_span_with(|request: &Request<_>| { // Log the matched route's path (with placeholders not filled in). // Use request.uri() or OriginalUri if you want the real path. let matched_path = request .extensions() .get::() .map(MatchedPath::as_str); info_span!( "http_request", method = ?request.method(), uri = ?request.uri(), matched_path, some_other_field = tracing::field::Empty, ) }) .on_request(|_request: &Request<_>, _span: &Span| { // You can use `_span.record("some_other_field", value)` in one of these // closures to attach a value to the initially empty field in the info_span // created above. info!("Request: {:?}", _request); }) .on_response(|_response: &Response, _latency: Duration, _span: &Span| { // ... info!("Response: {:?}", _response); }) .on_body_chunk(|_chunk: &Bytes, _latency: Duration, _span: &Span| { // ... }) .on_eos( |_trailers: Option<&HeaderMap>, _stream_duration: Duration, _span: &Span| { // ... }, ) .on_failure( |_error: ServerErrorsFailureClass, _latency: Duration, _span: &Span| { // ... }, ), ); // Run Server let app_host: std::net::IpAddr = env::var("APP_HOST") .unwrap_or("0.0.0.0".to_string()) .parse() .expect("IP Address expected in APP_HOST"); let app_port: u16 = env::var("APP_PORT") .unwrap_or("3000".to_string()) .parse() .expect("Port expected in APP_PORT"); // the listen_addr is the address we bind to. This might be multiple domains, like 0.0.0.0 let listen_addr = SocketAddr::from((app_host, app_port)); // the server addr is a concrete address the user can connect to. let server_addr = if app_host.is_unspecified() { SocketAddr::from(([127, 0, 0, 1], app_port)) } else { listen_addr.clone() }; info!("listening on {}", listen_addr); info!("admin on: http://{}/admin", server_addr); let listener = TcpListener::bind(&listen_addr) .await .expect("Could not bind TCP Listener."); axum::serve(listener, app.into_make_service()) .with_graceful_shutdown(shutdown_signal()) .await .expect("Could not start serving Axum"); } async fn shutdown_signal() { let ctrl_c = async { tokio::signal::ctrl_c() .await .expect("failed to install Ctrl+C handler"); }; #[cfg(unix)] let terminate = async { tokio::signal::unix::signal(tokio::signal::unix::SignalKind::terminate()) .expect("failed to install signal handler") .recv() .await; }; #[cfg(not(unix))] let terminate = std::future::pending::<()>(); tokio::select! { _ = ctrl_c => {}, _ = terminate => {}, } info!("shutting down..."); }