2026-07-21 12:54:23 +07:00
|
|
|
//! 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<AppState>) -> anyhow::Result<Router> {
|
|
|
|
|
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
|
2026-07-22 14:51:08 +07:00
|
|
|
// (all proxy routes removed)
|
2026-09-01 00:54:06 +07:00
|
|
|
// 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),
|
|
|
|
|
)
|
2026-09-01 18:05:35 +07:00
|
|
|
.route(
|
|
|
|
|
"/download/dailymotion",
|
|
|
|
|
axum::routing::get(crate::presentation::handler::downloader::download_dailymotion),
|
|
|
|
|
)
|
|
|
|
|
.route(
|
|
|
|
|
"/download/reddit",
|
|
|
|
|
axum::routing::get(crate::presentation::handler::downloader::download_reddit),
|
|
|
|
|
)
|
2026-09-01 18:23:49 +07:00
|
|
|
.route(
|
|
|
|
|
"/download/streamable",
|
|
|
|
|
axum::routing::get(crate::presentation::handler::downloader::download_streamable),
|
|
|
|
|
)
|
2026-09-01 18:36:54 +07:00
|
|
|
.route(
|
|
|
|
|
"/download/videy",
|
|
|
|
|
axum::routing::get(crate::presentation::handler::downloader::download_videy),
|
|
|
|
|
)
|
|
|
|
|
.route(
|
|
|
|
|
"/download/tiktok/v2",
|
|
|
|
|
axum::routing::get(crate::presentation::handler::downloader::download_tiktok_v2),
|
|
|
|
|
)
|
|
|
|
|
.route(
|
|
|
|
|
"/download/twitter/v2",
|
|
|
|
|
axum::routing::get(crate::presentation::handler::downloader::download_twitter_v2),
|
|
|
|
|
)
|
2026-09-01 00:54:06 +07:00
|
|
|
.route(
|
|
|
|
|
"/download/bilibili",
|
|
|
|
|
axum::routing::get(crate::presentation::handler::downloader::download_bilibili),
|
|
|
|
|
)
|
2026-07-21 12:54:23 +07:00
|
|
|
// 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)
|
|
|
|
|
}
|