//! HTTP server setup with routing and middleware. //! //! Configures OAuth routes (`/oauth/*`), main interface (`/`), and static files //! with CORS, tracing, timeouts, and internationalization middleware. //! //! ## Route Structure //! - `GET /`: Main application interface //! - `GET|POST /oauth/login`: OAuth authentication initiation //! - `GET /oauth/callback`: OAuth authentication completion //! - `GET /oauth/client-metadata.json`: OAuth client metadata //! - `GET /.well-known/jwks.json`: JSON Web Key Set //! - `POST /xrpc/community.lexicon.escrow.submitSignedRecord`: XRPC endpoint //! - Static file serving for assets //! //! ## Middleware Stack //! - Request tracing and error logging //! - 30-second request timeout //! - CORS headers for external base domain //! - HTMX auto-vary support for dynamic responses use std::time::Duration; use axum::{ Router, http::HeaderValue, routing::{get, post}, }; use axum_htmx::AutoVaryLayer; use http::{ Method, header::{ACCEPT, ACCEPT_LANGUAGE}, }; use tower_http::trace::TraceLayer; use tower_http::{classify::ServerErrorsFailureClass, timeout::TimeoutLayer}; use tower_http::{cors::CorsLayer, services::ServeDir}; use tracing::Span; use atproto_oauth_axum::{handle_jwks::handle_oauth_jwks, handler_metadata::handle_oauth_metadata}; use crate::http::{ context::WebContext, handle_index::handle_index, handle_oauth_callback::handle_oauth_callback, handle_oauth_login::handle_oauth_login, handle_xrpc_submit_signed_record::handle_xrpc_submit_signed_record, }; pub fn build_router(web_context: WebContext) -> Router { let serve_dir = ServeDir::new(web_context.config.http_static_path.clone()); let mut router = Router::new() .route("/", get(handle_index)) .route("/oauth/login", get(handle_oauth_login)) .route("/oauth/login", post(handle_oauth_login)) .route("/oauth/callback", get(handle_oauth_callback)) .route( "/xrpc/community.lexicon.ngerakines.escrow.submitSignedRecord", post(handle_xrpc_submit_signed_record), ); // Only add PDS-specific OAuth endpoints if using PDS backend if matches!( web_context.config.oauth_backend, crate::config::OAuthBackend::Pds ) { router = router .route("/oauth/client-metadata.json", get(handle_oauth_metadata)) .route("/.well-known/jwks.json", get(handle_oauth_jwks)); } router .fallback_service(serve_dir) .layer(( TraceLayer::new_for_http().on_failure( |err: ServerErrorsFailureClass, _latency: Duration, _span: &Span| { tracing::error!(error = ?err, "Unhandled error: {err}"); }, ), TimeoutLayer::new(Duration::from_secs(30)), )) .layer( CorsLayer::new() .allow_origin( web_context .config .external_base .parse::() .unwrap(), ) .allow_methods([Method::GET, Method::POST]) .allow_headers([ACCEPT_LANGUAGE, ACCEPT]), ) .layer(AutoVaryLayer) .with_state(web_context.clone()) }