Gnosco is a Rust-based escrow and badging application that integrates with the AT Protocol ecosystem..
1//! HTTP server setup with routing and middleware.
2//!
3//! Configures OAuth routes (`/oauth/*`), main interface (`/`), and static files
4//! with CORS, tracing, timeouts, and internationalization middleware.
5//!
6//! ## Route Structure
7//! - `GET /`: Main application interface
8//! - `GET|POST /oauth/login`: OAuth authentication initiation
9//! - `GET /oauth/callback`: OAuth authentication completion
10//! - `GET /oauth/client-metadata.json`: OAuth client metadata
11//! - `GET /.well-known/jwks.json`: JSON Web Key Set
12//! - `POST /xrpc/community.lexicon.escrow.submitSignedRecord`: XRPC endpoint
13//! - Static file serving for assets
14//!
15//! ## Middleware Stack
16//! - Request tracing and error logging
17//! - 30-second request timeout
18//! - CORS headers for external base domain
19//! - HTMX auto-vary support for dynamic responses
20
21use std::time::Duration;
22
23use axum::{
24 Router,
25 http::HeaderValue,
26 routing::{get, post},
27};
28use axum_htmx::AutoVaryLayer;
29use http::{
30 Method,
31 header::{ACCEPT, ACCEPT_LANGUAGE},
32};
33use tower_http::trace::TraceLayer;
34use tower_http::{classify::ServerErrorsFailureClass, timeout::TimeoutLayer};
35use tower_http::{cors::CorsLayer, services::ServeDir};
36use tracing::Span;
37
38use atproto_oauth_axum::{handle_jwks::handle_oauth_jwks, handler_metadata::handle_oauth_metadata};
39
40use crate::http::{
41 context::WebContext, handle_index::handle_index, handle_oauth_callback::handle_oauth_callback,
42 handle_oauth_login::handle_oauth_login,
43 handle_xrpc_submit_signed_record::handle_xrpc_submit_signed_record,
44};
45
46pub fn build_router(web_context: WebContext) -> Router {
47 let serve_dir = ServeDir::new(web_context.config.http_static_path.clone());
48
49 let mut router = Router::new()
50 .route("/", get(handle_index))
51 .route("/oauth/login", get(handle_oauth_login))
52 .route("/oauth/login", post(handle_oauth_login))
53 .route("/oauth/callback", get(handle_oauth_callback))
54 .route(
55 "/xrpc/community.lexicon.ngerakines.escrow.submitSignedRecord",
56 post(handle_xrpc_submit_signed_record),
57 );
58
59 // Only add PDS-specific OAuth endpoints if using PDS backend
60 if matches!(
61 web_context.config.oauth_backend,
62 crate::config::OAuthBackend::Pds
63 ) {
64 router = router
65 .route("/oauth/client-metadata.json", get(handle_oauth_metadata))
66 .route("/.well-known/jwks.json", get(handle_oauth_jwks));
67 }
68
69 router
70 .fallback_service(serve_dir)
71 .layer((
72 TraceLayer::new_for_http().on_failure(
73 |err: ServerErrorsFailureClass, _latency: Duration, _span: &Span| {
74 tracing::error!(error = ?err, "Unhandled error: {err}");
75 },
76 ),
77 TimeoutLayer::new(Duration::from_secs(30)),
78 ))
79 .layer(
80 CorsLayer::new()
81 .allow_origin(
82 web_context
83 .config
84 .external_base
85 .parse::<HeaderValue>()
86 .unwrap(),
87 )
88 .allow_methods([Method::GET, Method::POST])
89 .allow_headers([ACCEPT_LANGUAGE, ACCEPT]),
90 )
91 .layer(AutoVaryLayer)
92 .with_state(web_context.clone())
93}