pub mod api; pub mod auth; use std::sync::Arc; use std::path::PathBuf; use std::time::Duration; use axum::{Router, routing::{get, post}, middleware}; use axum::http::{header, Method}; use sqlx::PgPool; use tower_http::cors::{Any, CorsLayer}; #[derive(Clone)] pub struct AppState { pub pool: PgPool, #[allow(dead_code)] pub storage_dir: Arc, pub oidc: Option>, } pub fn build_router(state: Arc) -> Router { let library = Router::new() .route("/artists", get(api::list_artists)) .route("/artists/:slug", get(api::get_artist)) .route("/artists/:slug/albums", get(api::list_artist_albums)) .route("/artists/:slug/tracks", get(api::list_artist_all_tracks)) .route("/albums/:slug", get(api::get_album_tracks)) .route("/albums/:slug/cover", get(api::album_cover)) .route("/tracks/:slug", get(api::get_track_detail)) .route("/tracks/:slug/cover", get(api::track_cover)) .route("/stream/:slug", get(api::stream_track)) .route("/search", get(api::search)) .route("/tracks/:slug/play", post(api::record_play)); let api = Router::new() .nest("/api", library); let requires_auth = state.oidc.is_some(); let app = if requires_auth { api.route_layer(middleware::from_fn_with_state(state.clone(), auth::require_auth)) } else { api }; let cors = CorsLayer::new() .allow_origin(Any) .allow_methods([Method::GET, Method::POST, Method::OPTIONS, Method::HEAD]) .allow_headers([header::ACCEPT, header::CONTENT_TYPE, header::AUTHORIZATION]) .max_age(Duration::from_secs(600)); Router::new() .route("/auth/login", get(auth::oidc_login)) .route("/auth/callback", get(auth::oidc_callback)) .merge(app) .layer(cors) .with_state(state) }