mod auth; mod events; mod pages; mod platform; use axum::Router; use axum::http::StatusCode; use axum::response::{Html, IntoResponse, Response}; use minijinja::context; use crate::state::AppState; pub fn router() -> Router { Router::new() .merge(pages::router()) .merge(auth::router()) .merge(platform::router()) .merge(events::router()) } /// Render an error page with the given status code, heading, and message. fn error_page(state: &AppState, status: StatusCode, heading: &str, message: &str) -> Response { error_page_detail(state, status, heading, message, None) } /// Render an error page with optional error detail (shown in a collapsible section). fn error_page_detail( state: &AppState, status: StatusCode, heading: &str, message: &str, detail: Option<&str>, ) -> Response { let html = state.templates.render( "pages/error.html.jinja", context! { title => format!("{} - Forage", heading), description => message, status => status.as_u16(), heading => heading, message => message, detail => detail, }, ); match html { Ok(body) => (status, Html(body)).into_response(), Err(_) => status.into_response(), } } /// Log an error and render a 500 page with the error detail. fn internal_error(state: &AppState, context: &str, err: &dyn std::fmt::Display) -> Response { let detail = format!("{err:#}"); tracing::error!("{context}: {detail}"); error_page_detail( state, StatusCode::INTERNAL_SERVER_ERROR, "Something went wrong", "An internal error occurred. Please try again.", Some(&detail), ) } /// Log a warning for a failed call and return the default value. /// Use for supplementary data where graceful degradation is acceptable. fn warn_default(context: &str, result: Result) -> T { match result { Ok(v) => v, Err(e) => { tracing::warn!("{context}: {e:#}"); T::default() } } }