mod admin; mod howto; mod service; mod state; use crate::admin::example; use crate::service::{handlers, templates}; use crate::state::AppState; use axum::{ extract::State, handler::HandlerWithoutStateExt, response::IntoResponse, routing::get, Router, }; use dotenvy::dotenv; use log::info; use std::env; use std::net::SocketAddr; use std::sync::Arc; 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..."); // Prepare App State let tmpl = templates::Templates::initialize().expect("Template Engine could not be loaded."); let mut admin = admin::state::AdminRegistry::new("admin"); example::register_example(&mut admin); 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", handlers::static_handler.into_service()) .fallback(handlers::not_found_handler) .with_state(state); // 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); axum::Server::bind(&listen_addr) .serve(app.into_make_service()) .with_graceful_shutdown(shutdown_signal()) .await .unwrap(); } 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..."); }