2026-03-18 02:44:59 +00:00
|
|
|
pub mod api;
|
|
|
|
|
pub mod auth;
|
|
|
|
|
|
|
|
|
|
use std::sync::Arc;
|
|
|
|
|
use std::path::PathBuf;
|
2026-03-23 12:34:27 +03:00
|
|
|
use std::time::Duration;
|
2026-03-18 02:44:59 +00:00
|
|
|
|
2026-04-08 16:51:53 +01:00
|
|
|
use axum::{Router, routing::{get, post}, middleware};
|
2026-03-23 12:34:27 +03:00
|
|
|
use axum::http::{header, Method};
|
2026-03-18 02:44:59 +00:00
|
|
|
use sqlx::PgPool;
|
2026-03-23 12:34:27 +03:00
|
|
|
use tower_http::cors::{Any, CorsLayer};
|
2026-03-18 02:44:59 +00:00
|
|
|
|
|
|
|
|
#[derive(Clone)]
|
|
|
|
|
pub struct AppState {
|
|
|
|
|
pub pool: PgPool,
|
|
|
|
|
#[allow(dead_code)]
|
|
|
|
|
pub storage_dir: Arc<PathBuf>,
|
|
|
|
|
pub oidc: Option<Arc<auth::OidcState>>,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn build_router(state: Arc<AppState>) -> 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))
|
2026-04-08 16:51:53 +01:00
|
|
|
.route("/search", get(api::search))
|
|
|
|
|
.route("/tracks/:slug/play", post(api::record_play));
|
2026-03-18 02:44:59 +00:00
|
|
|
|
2026-04-08 14:51:52 +01:00
|
|
|
let api = Router::new()
|
2026-03-18 02:44:59 +00:00
|
|
|
.nest("/api", library);
|
|
|
|
|
|
2026-03-23 12:07:57 +03:00
|
|
|
let requires_auth = state.oidc.is_some();
|
2026-03-18 02:44:59 +00:00
|
|
|
|
2026-03-23 12:07:57 +03:00
|
|
|
let app = if requires_auth {
|
2026-04-08 14:51:52 +01:00
|
|
|
api.route_layer(middleware::from_fn_with_state(state.clone(), auth::require_auth))
|
2026-03-18 02:44:59 +00:00
|
|
|
} else {
|
2026-04-08 14:51:52 +01:00
|
|
|
api
|
2026-03-18 02:44:59 +00:00
|
|
|
};
|
|
|
|
|
|
2026-03-23 12:34:27 +03:00
|
|
|
let cors = CorsLayer::new()
|
|
|
|
|
.allow_origin(Any)
|
2026-04-08 16:51:53 +01:00
|
|
|
.allow_methods([Method::GET, Method::POST, Method::OPTIONS, Method::HEAD])
|
2026-04-08 14:51:52 +01:00
|
|
|
.allow_headers([header::ACCEPT, header::CONTENT_TYPE, header::AUTHORIZATION])
|
2026-03-23 12:34:27 +03:00
|
|
|
.max_age(Duration::from_secs(600));
|
|
|
|
|
|
2026-03-18 02:44:59 +00:00
|
|
|
Router::new()
|
|
|
|
|
.route("/auth/login", get(auth::oidc_login))
|
|
|
|
|
.route("/auth/callback", get(auth::oidc_callback))
|
|
|
|
|
.merge(app)
|
2026-03-23 12:34:27 +03:00
|
|
|
.layer(cors)
|
2026-03-18 02:44:59 +00:00
|
|
|
.with_state(state)
|
|
|
|
|
}
|