//! Axum router assembly. use std::sync::Arc; use axum::Router; use tower_http::compression::{CompressionLayer, CompressionLevel}; use tower_http::cors::CorsLayer; use utoipa::OpenApi; use utoipa_swagger_ui::SwaggerUi; use crate::observability::openapi::ApiDoc; use crate::observability::openapi_modules::ModuleApiDoc; use crate::presentation::state::AppState; /// Build the main application router with all routes, middleware, and Swagger UI. pub fn build_router(app_state: Arc) -> anyhow::Result { let mut openapi = ApiDoc::openapi(); openapi.merge(ModuleApiDoc::openapi()); let app = Router::new() // Anime routes .route( "/api/anime", axum::routing::get(crate::presentation::handler::anime::anime_index), ) .route( "/api/anime/genre_list", axum::routing::get(crate::presentation::handler::anime::genres), ) .route( "/api/anime/detail/{slug}", axum::routing::get(crate::presentation::handler::anime::detail_slug), ) .route( "/api/anime/complete_anime/{slug}", axum::routing::get(crate::presentation::handler::anime::complete_anime_slug), ) .route( "/api/anime/full/{slug}", axum::routing::get(crate::presentation::handler::anime::full_slug), ) .route( "/api/anime/ongoing_anime/{slug}", axum::routing::get(crate::presentation::handler::anime::ongoing_anime_slug), ) .route( "/api/anime/latest/{slug}", axum::routing::get(crate::presentation::handler::anime::latest_slug), ) .route( "/api/anime/search/{slug}", axum::routing::get(crate::presentation::handler::anime::search_slug_index), ) .route( "/api/anime/search/{slug}/{page}", axum::routing::get(crate::presentation::handler::anime::search_slug_page), ) .route( "/api/anime/genre/{slug}", axum::routing::get(crate::presentation::handler::anime::genre_slug_index), ) .route( "/api/anime/genre/{slug}/{page}", axum::routing::get(crate::presentation::handler::anime::genre_slug_page), ) // Anime2 routes .route( "/api/anime2", axum::routing::get(crate::presentation::handler::anime2::index), ) .route( "/api/anime2/complete_anime/{slug}", axum::routing::get(crate::presentation::handler::anime2::complete_anime_slug), ) .route( "/api/anime2/detail/{slug}", axum::routing::get(crate::presentation::handler::anime2::detail_slug), ) .route( "/api/anime2/filter", axum::routing::get(crate::presentation::handler::anime2::filter), ) .route( "/api/anime2/genre_list", axum::routing::get(crate::presentation::handler::anime2::genre_list), ) .route( "/api/anime2/genre/{slug}", axum::routing::get(crate::presentation::handler::anime2::genre_slug_index), ) .route( "/api/anime2/genre/{slug}/{page}", axum::routing::get(crate::presentation::handler::anime2::genre_slug_page), ) .route( "/api/anime2/latest/{slug}", axum::routing::get(crate::presentation::handler::anime2::latest_slug), ) .route( "/api/anime2/ongoing_anime/{slug}", axum::routing::get(crate::presentation::handler::anime2::ongoing_anime_slug), ) .route( "/api/anime2/search/{slug}", axum::routing::get(crate::presentation::handler::anime2::search_slug_index), ) .route( "/api/anime2/search/{slug}/{page}", axum::routing::get(crate::presentation::handler::anime2::search_slug_page), ) // Komik routes .route( "/api/komik/genre_list", axum::routing::get(crate::presentation::handler::komik::genre_list), ) .route( "/api/komik/chapter/{slug}", axum::routing::get(crate::presentation::handler::komik::chapter_slug), ) .route( "/api/komik/detail/{slug}", axum::routing::get(crate::presentation::handler::komik::detail_slug), ) .route( "/api/komik/genre/{slug}", axum::routing::get(crate::presentation::handler::komik::genre_slug), ) .route( "/api/komik/genre/{slug}/{page}", axum::routing::get(crate::presentation::handler::komik::genre_slug_page), ) .route( "/api/komik/manga/{slug}", axum::routing::get(crate::presentation::handler::komik::manga_slug), ) .route( "/api/komik/manhua/{slug}", axum::routing::get(crate::presentation::handler::komik::manhua_slug), ) .route( "/api/komik/manhwa/{slug}", axum::routing::get(crate::presentation::handler::komik::manhwa_slug), ) .route( "/api/komik/popular/{slug}", axum::routing::get(crate::presentation::handler::komik::popular_slug), ) .route( "/api/komik/search/{slug}", axum::routing::get(crate::presentation::handler::komik::search_slug), ) .route( "/api/komik/search/{slug}/{page}", axum::routing::get(crate::presentation::handler::komik::search_slug_page), ) // Proxy routes // (all proxy routes removed) // Downloader routes .route( "/download", axum::routing::get(crate::presentation::handler::downloader::download), ) .route( "/download/detect", axum::routing::get(crate::presentation::handler::downloader::detect_platform_handler), ) .route( "/download/instagram", axum::routing::get(crate::presentation::handler::downloader::download_instagram), ) .route( "/download/facebook", axum::routing::get(crate::presentation::handler::downloader::download_facebook), ) .route( "/download/tiktok", axum::routing::get(crate::presentation::handler::downloader::download_tiktok), ) .route( "/download/youtube", axum::routing::get(crate::presentation::handler::downloader::download_youtube), ) .route( "/download/youtube/mp3", axum::routing::get(crate::presentation::handler::downloader::download_youtube_mp3), ) .route( "/download/spotify", axum::routing::get(crate::presentation::handler::downloader::download_spotify), ) .route( "/download/twitter", axum::routing::get(crate::presentation::handler::downloader::download_twitter), ) .route( "/download/pinterest", axum::routing::get(crate::presentation::handler::downloader::download_pinterest), ) .route( "/download/mega", axum::routing::get(crate::presentation::handler::downloader::download_mega), ) .route( "/download/terabox", axum::routing::get(crate::presentation::handler::downloader::download_terabox), ) .route( "/download/gdrive", axum::routing::get(crate::presentation::handler::downloader::download_gdrive), ) .route( "/download/mediafire", axum::routing::get(crate::presentation::handler::downloader::download_mediafire), ) .route( "/download/pixeldrain", axum::routing::get(crate::presentation::handler::downloader::download_pixeldrain), ) .route( "/download/threads", axum::routing::get(crate::presentation::handler::downloader::download_threads), ) .route( "/download/dood", axum::routing::get(crate::presentation::handler::downloader::download_doodstream), ) .route( "/download/kraken", axum::routing::get(crate::presentation::handler::downloader::download_krakenfiles), ) .route( "/download/danbooru", axum::routing::get(crate::presentation::handler::downloader::download_danbooru), ) .route( "/download/soundcloud", axum::routing::get(crate::presentation::handler::downloader::download_soundcloud), ) .route( "/download/dailymotion", axum::routing::get(crate::presentation::handler::downloader::download_dailymotion), ) .route( "/download/reddit", axum::routing::get(crate::presentation::handler::downloader::download_reddit), ) .route( "/download/streamable", axum::routing::get(crate::presentation::handler::downloader::download_streamable), ) .route( "/download/videy", axum::routing::get(crate::presentation::handler::downloader::download_videy), ) .route( "/download/bilibili", axum::routing::get(crate::presentation::handler::downloader::download_bilibili), ) // Misc routes .route( "/misc/currency-converter", axum::routing::get(crate::presentation::handler::misc::currency_converter_handler), ) .route( "/misc/harga-emas", axum::routing::get(crate::presentation::handler::misc::harga_emas_handler), ) .route( "/misc/kurs-bca", axum::routing::get(crate::presentation::handler::misc::kurs_bca_handler), ) .route( "/misc/server-info", axum::routing::get(crate::presentation::handler::misc::server_info_handler), ) // Stalk routes .route( "/stalk/github", axum::routing::get(crate::presentation::handler::stalk::github_handler), ) .route( "/stalk/youtube", axum::routing::get(crate::presentation::handler::stalk::youtube_handler), ) // Health .route( "/health", axum::routing::get(crate::presentation::handler::health::health_check), ) // Swagger UI .merge(SwaggerUi::new("/docs").url("/api-docs/openapi.json", openapi)) .with_state(app_state) .layer(axum::middleware::from_fn( crate::observability::metrics::otel_metrics_middleware, )) .layer(CompressionLayer::new().quality(CompressionLevel::Fastest)) .layer(CorsLayer::permissive()); Ok(app) }