docs: add utoipa OpenAPI annotations to all scraper endpoints
Deploy Scraper / build-and-deploy (push) Canceled after 0s
Deploy Scraper / build-and-deploy (push) Canceled after 0s
Add #[utoipa::path] + IntoParams to all new category handlers: - downloader (25 routes): /download, /detect-platform, 23 platform handlers - misc (4): currency-converter, harga-emas, kurs-bca, server-info - stalk (8): github, youtube, ml, free-fire, genshin, twitter, tiktok, instagram - weebs (5): anime-info, manga-info, whatanime, waifu (sfw/nsfw) - search (5): bmkg, jadwal-sholat, weather, google, yt - tool (6): whois, ip-location, tinyurl, check-hosting, hargapangan, cek-resi - image (2): brat, brat-animated - uploader (2): ryzencdn, serve-file - ai (3): chat, gemini, image Register all in ModuleApiDoc paths() with per-category tags. Fix clippy warnings in touched handlers (expect/unwrap -> map_err, needless lifetimes). /docs now documents the full scraper API surface (was only anime/komik).
This commit is contained in:
@@ -4,7 +4,7 @@ use utoipa::OpenApi;
|
||||
#[derive(OpenApi)]
|
||||
#[openapi(
|
||||
paths(
|
||||
// Anime module handlers
|
||||
// ── Anime module ──────────────────────────────────────────
|
||||
crate::presentation::handler::anime::anime_index,
|
||||
crate::presentation::handler::anime::genres,
|
||||
crate::presentation::handler::anime::detail_slug,
|
||||
@@ -16,7 +16,7 @@ use utoipa::OpenApi;
|
||||
crate::presentation::handler::anime::search_slug_page,
|
||||
crate::presentation::handler::anime::genre_slug_index,
|
||||
crate::presentation::handler::anime::genre_slug_page,
|
||||
// Anime2 module handlers
|
||||
// ── Anime2 module ─────────────────────────────────────────
|
||||
crate::presentation::handler::anime2::index,
|
||||
crate::presentation::handler::anime2::genre_list,
|
||||
crate::presentation::handler::anime2::filter,
|
||||
@@ -28,7 +28,7 @@ use utoipa::OpenApi;
|
||||
crate::presentation::handler::anime2::latest_slug,
|
||||
crate::presentation::handler::anime2::ongoing_anime_slug,
|
||||
crate::presentation::handler::anime2::complete_anime_slug,
|
||||
// Komik module handlers
|
||||
// ── Komik module ──────────────────────────────────────────
|
||||
crate::presentation::handler::komik::genre_list,
|
||||
crate::presentation::handler::komik::chapter_slug,
|
||||
crate::presentation::handler::komik::detail_slug,
|
||||
@@ -40,19 +40,95 @@ use utoipa::OpenApi;
|
||||
crate::presentation::handler::komik::popular_slug,
|
||||
crate::presentation::handler::komik::search_slug,
|
||||
crate::presentation::handler::komik::search_slug_page,
|
||||
// Proxy module handlers
|
||||
// ── Download module ────────────────────────────────────────
|
||||
crate::presentation::handler::downloader::download,
|
||||
crate::presentation::handler::downloader::detect_platform_handler,
|
||||
crate::presentation::handler::downloader::download_instagram,
|
||||
crate::presentation::handler::downloader::download_facebook,
|
||||
crate::presentation::handler::downloader::download_tiktok,
|
||||
crate::presentation::handler::downloader::download_youtube,
|
||||
crate::presentation::handler::downloader::download_youtube_mp3,
|
||||
crate::presentation::handler::downloader::download_spotify,
|
||||
crate::presentation::handler::downloader::download_twitter,
|
||||
crate::presentation::handler::downloader::download_pinterest,
|
||||
crate::presentation::handler::downloader::download_mega,
|
||||
crate::presentation::handler::downloader::download_terabox,
|
||||
crate::presentation::handler::downloader::download_gdrive,
|
||||
crate::presentation::handler::downloader::download_mediafire,
|
||||
crate::presentation::handler::downloader::download_pixeldrain,
|
||||
crate::presentation::handler::downloader::download_threads,
|
||||
crate::presentation::handler::downloader::download_doodstream,
|
||||
crate::presentation::handler::downloader::download_krakenfiles,
|
||||
crate::presentation::handler::downloader::download_danbooru,
|
||||
crate::presentation::handler::downloader::download_soundcloud,
|
||||
crate::presentation::handler::downloader::download_dailymotion,
|
||||
crate::presentation::handler::downloader::download_reddit,
|
||||
crate::presentation::handler::downloader::download_streamable,
|
||||
crate::presentation::handler::downloader::download_videy,
|
||||
crate::presentation::handler::downloader::download_bilibili,
|
||||
// ── Misc module ────────────────────────────────────────────
|
||||
crate::presentation::handler::misc::currency_converter_handler,
|
||||
crate::presentation::handler::misc::harga_emas_handler,
|
||||
crate::presentation::handler::misc::kurs_bca_handler,
|
||||
crate::presentation::handler::misc::server_info_handler,
|
||||
// ── Stalk module ───────────────────────────────────────────
|
||||
crate::presentation::handler::stalk::github_handler,
|
||||
crate::presentation::handler::stalk::youtube_handler,
|
||||
crate::presentation::handler::stalk::mobile_legends_handler,
|
||||
crate::presentation::handler::stalk::free_fire_handler,
|
||||
crate::presentation::handler::stalk::genshin_handler,
|
||||
crate::presentation::handler::stalk::twitter_handler,
|
||||
crate::presentation::handler::stalk::tiktok_handler,
|
||||
crate::presentation::handler::stalk::instagram_handler,
|
||||
// ── Weebs module ───────────────────────────────────────────
|
||||
crate::presentation::handler::weebs::anime_info_handler,
|
||||
crate::presentation::handler::weebs::manga_info_handler,
|
||||
crate::presentation::handler::weebs::whatanime_handler,
|
||||
crate::presentation::handler::weebs::sfw_waifu_handler,
|
||||
crate::presentation::handler::weebs::nsfw_waifu_handler,
|
||||
// ── Search module ──────────────────────────────────────────
|
||||
crate::presentation::handler::search::bmkg_handler,
|
||||
crate::presentation::handler::search::jadwal_sholat_handler,
|
||||
crate::presentation::handler::search::weather_handler,
|
||||
crate::presentation::handler::search::google_handler,
|
||||
crate::presentation::handler::search::yt_handler,
|
||||
// ── Tool module ────────────────────────────────────────────
|
||||
crate::presentation::handler::tools::whois_handler,
|
||||
crate::presentation::handler::tools::ip_location_handler,
|
||||
crate::presentation::handler::tools::tinyurl_handler,
|
||||
crate::presentation::handler::tools::check_hosting_handler,
|
||||
crate::presentation::handler::tools::hargapangan_handler,
|
||||
crate::presentation::handler::tools::cek_resi_handler,
|
||||
// ── Image module ───────────────────────────────────────────
|
||||
crate::presentation::handler::image::brat_handler,
|
||||
crate::presentation::handler::image::brat_animated_handler,
|
||||
// ── Uploader module ────────────────────────────────────────
|
||||
crate::presentation::handler::uploader::ryzencdn_handler,
|
||||
crate::presentation::handler::uploader::serve_file_handler,
|
||||
// ── AI module ──────────────────────────────────────────────
|
||||
crate::presentation::handler::ai::chat_handler,
|
||||
crate::presentation::handler::ai::gemini_handler,
|
||||
crate::presentation::handler::ai::image_handler,
|
||||
),
|
||||
components(
|
||||
schemas(
|
||||
// Application response wrapper
|
||||
crate::presentation::dto::common::ApiResponse<String>,
|
||||
crate::presentation::dto::downloader::DownloadResponse,
|
||||
)
|
||||
),
|
||||
tags(
|
||||
(name = "anime", description = "Anime endpoints"),
|
||||
(name = "anime2", description = "Anime2 endpoints"),
|
||||
(name = "komik", description = "Komik endpoints"),
|
||||
(name = "proxy", description = "Proxy endpoints"),
|
||||
(name = "download", description = "Media download endpoints (23 platforms)"),
|
||||
(name = "misc", description = "Miscellaneous utilities (currency, gold, server)"),
|
||||
(name = "stalk", description = "User profile stalking (GitHub, YT, Twitter, TikTok, IG, FF, ML, Genshin)"),
|
||||
(name = "weebs", description = "Weeb endpoints (anime info, manga, anime search, waifu)"),
|
||||
(name = "search", description = "Search endpoints (BMKG, prayer times, weather, YouTube, Google)"),
|
||||
(name = "tool", description = "Utility tools (WHOIS, IP lookup, URL shortener, tracking, hosting)"),
|
||||
(name = "image", description = "Image generation (brat static & animated)"),
|
||||
(name = "uploader", description = "File upload and serve"),
|
||||
(name = "ai", description = "AI endpoints (chat, gemini, image generation)"),
|
||||
)
|
||||
)]
|
||||
pub struct ModuleApiDoc;
|
||||
|
||||
@@ -23,6 +23,18 @@ pub struct AiParams {
|
||||
}
|
||||
|
||||
/// GET /ai/chat?text=...&prompt=...&model=...
|
||||
#[utoipa::path(
|
||||
get,
|
||||
path = "/ai/chat",
|
||||
tag = "ai",
|
||||
operation_id = "ai_chat_handler",
|
||||
responses(
|
||||
(status = 200, description = "OK", body = serde_json::Value),
|
||||
(status = 400, description = "Bad Request"),
|
||||
(status = 502, description = "Upstream error"),
|
||||
)
|
||||
)]
|
||||
|
||||
pub async fn chat_handler(Query(params): Query<AiParams>) -> Result<Json<Value>, AppError> {
|
||||
let text = params
|
||||
.text
|
||||
@@ -32,6 +44,18 @@ pub async fn chat_handler(Query(params): Query<AiParams>) -> Result<Json<Value>,
|
||||
}
|
||||
|
||||
/// GET /ai/gemini?text=...&prompt=...&image_url=...
|
||||
#[utoipa::path(
|
||||
get,
|
||||
path = "/ai/gemini",
|
||||
tag = "ai",
|
||||
operation_id = "ai_gemini_handler",
|
||||
responses(
|
||||
(status = 200, description = "OK", body = serde_json::Value),
|
||||
(status = 400, description = "Bad Request"),
|
||||
(status = 502, description = "Upstream error"),
|
||||
)
|
||||
)]
|
||||
|
||||
pub async fn gemini_handler(Query(params): Query<AiParams>) -> Result<Json<Value>, AppError> {
|
||||
let text = params
|
||||
.text
|
||||
@@ -42,6 +66,18 @@ pub async fn gemini_handler(Query(params): Query<AiParams>) -> Result<Json<Value
|
||||
}
|
||||
|
||||
/// GET /ai/image?prompt=...&model=...&width=...&height=... — returns image bytes.
|
||||
#[utoipa::path(
|
||||
get,
|
||||
path = "/ai/image",
|
||||
tag = "ai",
|
||||
operation_id = "ai_image_handler",
|
||||
responses(
|
||||
(status = 200, description = "OK", body = serde_json::Value),
|
||||
(status = 400, description = "Bad Request"),
|
||||
(status = 502, description = "Upstream error"),
|
||||
)
|
||||
)]
|
||||
|
||||
pub async fn image_handler(Query(params): Query<AiParams>) -> Result<Response<Body>, AppError> {
|
||||
let prompt = params
|
||||
.prompt
|
||||
@@ -59,9 +95,9 @@ pub async fn image_handler(Query(params): Query<AiParams>) -> Result<Response<Bo
|
||||
} else {
|
||||
"application/octet-stream"
|
||||
};
|
||||
Ok(Response::builder()
|
||||
Response::builder()
|
||||
.status(StatusCode::OK)
|
||||
.header(header::CONTENT_TYPE, mime)
|
||||
.body(Body::from(bytes))
|
||||
.unwrap())
|
||||
.map_err(|e| AppError::Internal(format!("build response: {e}")))
|
||||
}
|
||||
|
||||
@@ -7,17 +7,15 @@
|
||||
use axum::extract::Query;
|
||||
use axum::Json;
|
||||
use serde::Deserialize;
|
||||
use serde_json::Value;
|
||||
use utoipa::IntoParams;
|
||||
|
||||
use crate::application::downloader as use_cases;
|
||||
use crate::presentation::dto::downloader::DownloadResponse;
|
||||
use crate::presentation::error::AppError;
|
||||
|
||||
// ============================================================================
|
||||
// DownloadParams + response
|
||||
// ============================================================================
|
||||
|
||||
/// Request params for downloader endpoints.
|
||||
#[derive(Debug, Deserialize)]
|
||||
#[derive(Debug, Deserialize, IntoParams)]
|
||||
pub struct DownloadParams {
|
||||
pub url: String,
|
||||
pub cookies: Option<String>,
|
||||
@@ -25,11 +23,24 @@ pub struct DownloadParams {
|
||||
pub quality: Option<String>,
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// ========================================================================
|
||||
// Handlers
|
||||
// ============================================================================
|
||||
// ========================================================================
|
||||
|
||||
/// All-in-one downloader with auto-detection.
|
||||
/// All-in-one auto-detect downloader.
|
||||
/// `/download?url=...`
|
||||
#[utoipa::path(
|
||||
get,
|
||||
path = "/download",
|
||||
tag = "download",
|
||||
operation_id = "dl_download",
|
||||
params(DownloadParams),
|
||||
responses(
|
||||
(status = 200, description = "Download result", body = DownloadResponse),
|
||||
(status = 400, description = "Bad URL or platform"),
|
||||
(status = 502, description = "Upstream error"),
|
||||
)
|
||||
)]
|
||||
pub async fn download(
|
||||
Query(params): Query<DownloadParams>,
|
||||
) -> Result<Json<DownloadResponse>, AppError> {
|
||||
@@ -37,10 +48,23 @@ pub async fn download(
|
||||
Ok(Json(DownloadResponse::ok(result)))
|
||||
}
|
||||
|
||||
/// Platform detection endpoint.
|
||||
/// Detect platform from URL.
|
||||
/// `/detect-platform?url=...`
|
||||
#[utoipa::path(
|
||||
get,
|
||||
path = "/detect-platform",
|
||||
tag = "download",
|
||||
operation_id = "dl_detect_platform_handler",
|
||||
params(DownloadParams),
|
||||
responses(
|
||||
(status = 200, description = "Download result", body = DownloadResponse),
|
||||
(status = 400, description = "Bad URL or platform"),
|
||||
(status = 502, description = "Upstream error"),
|
||||
)
|
||||
)]
|
||||
pub async fn detect_platform_handler(
|
||||
Query(params): Query<DownloadParams>,
|
||||
) -> Result<Json<serde_json::Value>, AppError> {
|
||||
) -> Result<Json<Value>, AppError> {
|
||||
let platform = use_cases::detect_platform(¶ms.url);
|
||||
Ok(Json(serde_json::json!({
|
||||
"platform": platform,
|
||||
@@ -48,8 +72,20 @@ pub async fn detect_platform_handler(
|
||||
})))
|
||||
}
|
||||
|
||||
/// Generate handler for each downloader platform.
|
||||
/// Handler for _instagram.
|
||||
/// Download Instagram media.
|
||||
/// `/download/instagram?url=...`
|
||||
#[utoipa::path(
|
||||
get,
|
||||
path = "/download/instagram",
|
||||
tag = "download",
|
||||
operation_id = "dl_download_instagram",
|
||||
params(DownloadParams),
|
||||
responses(
|
||||
(status = 200, description = "Download result", body = DownloadResponse),
|
||||
(status = 400, description = "Bad URL or platform"),
|
||||
(status = 502, description = "Upstream error"),
|
||||
)
|
||||
)]
|
||||
pub async fn download_instagram(
|
||||
Query(params): Query<DownloadParams>,
|
||||
) -> Result<Json<DownloadResponse>, AppError> {
|
||||
@@ -57,7 +93,20 @@ pub async fn download_instagram(
|
||||
Ok(Json(DownloadResponse::ok(result)))
|
||||
}
|
||||
|
||||
/// Handler for _facebook.
|
||||
/// Download Facebook video.
|
||||
/// `/download/facebook?url=...`
|
||||
#[utoipa::path(
|
||||
get,
|
||||
path = "/download/facebook",
|
||||
tag = "download",
|
||||
operation_id = "dl_download_facebook",
|
||||
params(DownloadParams),
|
||||
responses(
|
||||
(status = 200, description = "Download result", body = DownloadResponse),
|
||||
(status = 400, description = "Bad URL or platform"),
|
||||
(status = 502, description = "Upstream error"),
|
||||
)
|
||||
)]
|
||||
pub async fn download_facebook(
|
||||
Query(params): Query<DownloadParams>,
|
||||
) -> Result<Json<DownloadResponse>, AppError> {
|
||||
@@ -65,7 +114,20 @@ pub async fn download_facebook(
|
||||
Ok(Json(DownloadResponse::ok(result)))
|
||||
}
|
||||
|
||||
/// Handler for _tiktok.
|
||||
/// Download TikTok video.
|
||||
/// `/download/tiktok?url=...`
|
||||
#[utoipa::path(
|
||||
get,
|
||||
path = "/download/tiktok",
|
||||
tag = "download",
|
||||
operation_id = "dl_download_tiktok",
|
||||
params(DownloadParams),
|
||||
responses(
|
||||
(status = 200, description = "Download result", body = DownloadResponse),
|
||||
(status = 400, description = "Bad URL or platform"),
|
||||
(status = 502, description = "Upstream error"),
|
||||
)
|
||||
)]
|
||||
pub async fn download_tiktok(
|
||||
Query(params): Query<DownloadParams>,
|
||||
) -> Result<Json<DownloadResponse>, AppError> {
|
||||
@@ -73,7 +135,20 @@ pub async fn download_tiktok(
|
||||
Ok(Json(DownloadResponse::ok(result)))
|
||||
}
|
||||
|
||||
/// Handler for _youtube.
|
||||
/// Download YouTube video.
|
||||
/// `/download/youtube?url=...`
|
||||
#[utoipa::path(
|
||||
get,
|
||||
path = "/download/youtube",
|
||||
tag = "download",
|
||||
operation_id = "dl_download_youtube",
|
||||
params(DownloadParams),
|
||||
responses(
|
||||
(status = 200, description = "Download result", body = DownloadResponse),
|
||||
(status = 400, description = "Bad URL or platform"),
|
||||
(status = 502, description = "Upstream error"),
|
||||
)
|
||||
)]
|
||||
pub async fn download_youtube(
|
||||
Query(params): Query<DownloadParams>,
|
||||
) -> Result<Json<DownloadResponse>, AppError> {
|
||||
@@ -83,7 +158,20 @@ pub async fn download_youtube(
|
||||
Ok(Json(DownloadResponse::ok(result)))
|
||||
}
|
||||
|
||||
/// Handler for _youtube_mp3.
|
||||
/// Convert YouTube to MP3.
|
||||
/// `/download/youtube/mp3?url=...`
|
||||
#[utoipa::path(
|
||||
get,
|
||||
path = "/download/youtube/mp3",
|
||||
tag = "download",
|
||||
operation_id = "dl_download_youtube_mp3",
|
||||
params(DownloadParams),
|
||||
responses(
|
||||
(status = 200, description = "Download result", body = DownloadResponse),
|
||||
(status = 400, description = "Bad URL or platform"),
|
||||
(status = 502, description = "Upstream error"),
|
||||
)
|
||||
)]
|
||||
pub async fn download_youtube_mp3(
|
||||
Query(params): Query<DownloadParams>,
|
||||
) -> Result<Json<DownloadResponse>, AppError> {
|
||||
@@ -91,7 +179,20 @@ pub async fn download_youtube_mp3(
|
||||
Ok(Json(DownloadResponse::ok(result)))
|
||||
}
|
||||
|
||||
/// Handler for _spotify.
|
||||
/// Download Spotify track.
|
||||
/// `/download/spotify?url=...`
|
||||
#[utoipa::path(
|
||||
get,
|
||||
path = "/download/spotify",
|
||||
tag = "download",
|
||||
operation_id = "dl_download_spotify",
|
||||
params(DownloadParams),
|
||||
responses(
|
||||
(status = 200, description = "Download result", body = DownloadResponse),
|
||||
(status = 400, description = "Bad URL or platform"),
|
||||
(status = 502, description = "Upstream error"),
|
||||
)
|
||||
)]
|
||||
pub async fn download_spotify(
|
||||
Query(params): Query<DownloadParams>,
|
||||
) -> Result<Json<DownloadResponse>, AppError> {
|
||||
@@ -99,7 +200,20 @@ pub async fn download_spotify(
|
||||
Ok(Json(DownloadResponse::ok(result)))
|
||||
}
|
||||
|
||||
/// Handler for _twitter.
|
||||
/// Download Twitter/X media.
|
||||
/// `/download/twitter?url=...`
|
||||
#[utoipa::path(
|
||||
get,
|
||||
path = "/download/twitter",
|
||||
tag = "download",
|
||||
operation_id = "dl_download_twitter",
|
||||
params(DownloadParams),
|
||||
responses(
|
||||
(status = 200, description = "Download result", body = DownloadResponse),
|
||||
(status = 400, description = "Bad URL or platform"),
|
||||
(status = 502, description = "Upstream error"),
|
||||
)
|
||||
)]
|
||||
pub async fn download_twitter(
|
||||
Query(params): Query<DownloadParams>,
|
||||
) -> Result<Json<DownloadResponse>, AppError> {
|
||||
@@ -107,7 +221,20 @@ pub async fn download_twitter(
|
||||
Ok(Json(DownloadResponse::ok(result)))
|
||||
}
|
||||
|
||||
/// Handler for _pinterest.
|
||||
/// Download Pinterest pin.
|
||||
/// `/download/pinterest?url=...`
|
||||
#[utoipa::path(
|
||||
get,
|
||||
path = "/download/pinterest",
|
||||
tag = "download",
|
||||
operation_id = "dl_download_pinterest",
|
||||
params(DownloadParams),
|
||||
responses(
|
||||
(status = 200, description = "Download result", body = DownloadResponse),
|
||||
(status = 400, description = "Bad URL or platform"),
|
||||
(status = 502, description = "Upstream error"),
|
||||
)
|
||||
)]
|
||||
pub async fn download_pinterest(
|
||||
Query(params): Query<DownloadParams>,
|
||||
) -> Result<Json<DownloadResponse>, AppError> {
|
||||
@@ -115,7 +242,20 @@ pub async fn download_pinterest(
|
||||
Ok(Json(DownloadResponse::ok(result)))
|
||||
}
|
||||
|
||||
/// Handler for _mega.
|
||||
/// Download from Mega.
|
||||
/// `/download/mega?url=...`
|
||||
#[utoipa::path(
|
||||
get,
|
||||
path = "/download/mega",
|
||||
tag = "download",
|
||||
operation_id = "dl_download_mega",
|
||||
params(DownloadParams),
|
||||
responses(
|
||||
(status = 200, description = "Download result", body = DownloadResponse),
|
||||
(status = 400, description = "Bad URL or platform"),
|
||||
(status = 502, description = "Upstream error"),
|
||||
)
|
||||
)]
|
||||
pub async fn download_mega(
|
||||
Query(params): Query<DownloadParams>,
|
||||
) -> Result<Json<DownloadResponse>, AppError> {
|
||||
@@ -123,7 +263,20 @@ pub async fn download_mega(
|
||||
Ok(Json(DownloadResponse::ok(result)))
|
||||
}
|
||||
|
||||
/// Handler for _terabox.
|
||||
/// Download from Terabox.
|
||||
/// `/download/terabox?url=...`
|
||||
#[utoipa::path(
|
||||
get,
|
||||
path = "/download/terabox",
|
||||
tag = "download",
|
||||
operation_id = "dl_download_terabox",
|
||||
params(DownloadParams),
|
||||
responses(
|
||||
(status = 200, description = "Download result", body = DownloadResponse),
|
||||
(status = 400, description = "Bad URL or platform"),
|
||||
(status = 502, description = "Upstream error"),
|
||||
)
|
||||
)]
|
||||
pub async fn download_terabox(
|
||||
Query(params): Query<DownloadParams>,
|
||||
) -> Result<Json<DownloadResponse>, AppError> {
|
||||
@@ -131,7 +284,20 @@ pub async fn download_terabox(
|
||||
Ok(Json(DownloadResponse::ok(result)))
|
||||
}
|
||||
|
||||
/// Handler for _gdrive.
|
||||
/// Download from Google Drive.
|
||||
/// `/download/gdrive?url=...`
|
||||
#[utoipa::path(
|
||||
get,
|
||||
path = "/download/gdrive",
|
||||
tag = "download",
|
||||
operation_id = "dl_download_gdrive",
|
||||
params(DownloadParams),
|
||||
responses(
|
||||
(status = 200, description = "Download result", body = DownloadResponse),
|
||||
(status = 400, description = "Bad URL or platform"),
|
||||
(status = 502, description = "Upstream error"),
|
||||
)
|
||||
)]
|
||||
pub async fn download_gdrive(
|
||||
Query(params): Query<DownloadParams>,
|
||||
) -> Result<Json<DownloadResponse>, AppError> {
|
||||
@@ -139,7 +305,20 @@ pub async fn download_gdrive(
|
||||
Ok(Json(DownloadResponse::ok(result)))
|
||||
}
|
||||
|
||||
/// Handler for _mediafire.
|
||||
/// Download from MediaFire.
|
||||
/// `/download/mediafire?url=...`
|
||||
#[utoipa::path(
|
||||
get,
|
||||
path = "/download/mediafire",
|
||||
tag = "download",
|
||||
operation_id = "dl_download_mediafire",
|
||||
params(DownloadParams),
|
||||
responses(
|
||||
(status = 200, description = "Download result", body = DownloadResponse),
|
||||
(status = 400, description = "Bad URL or platform"),
|
||||
(status = 502, description = "Upstream error"),
|
||||
)
|
||||
)]
|
||||
pub async fn download_mediafire(
|
||||
Query(params): Query<DownloadParams>,
|
||||
) -> Result<Json<DownloadResponse>, AppError> {
|
||||
@@ -147,7 +326,20 @@ pub async fn download_mediafire(
|
||||
Ok(Json(DownloadResponse::ok(result)))
|
||||
}
|
||||
|
||||
/// Handler for _pixeldrain.
|
||||
/// Download from Pixeldrain.
|
||||
/// `/download/pixeldrain?url=...`
|
||||
#[utoipa::path(
|
||||
get,
|
||||
path = "/download/pixeldrain",
|
||||
tag = "download",
|
||||
operation_id = "dl_download_pixeldrain",
|
||||
params(DownloadParams),
|
||||
responses(
|
||||
(status = 200, description = "Download result", body = DownloadResponse),
|
||||
(status = 400, description = "Bad URL or platform"),
|
||||
(status = 502, description = "Upstream error"),
|
||||
)
|
||||
)]
|
||||
pub async fn download_pixeldrain(
|
||||
Query(params): Query<DownloadParams>,
|
||||
) -> Result<Json<DownloadResponse>, AppError> {
|
||||
@@ -155,7 +347,20 @@ pub async fn download_pixeldrain(
|
||||
Ok(Json(DownloadResponse::ok(result)))
|
||||
}
|
||||
|
||||
/// Handler for _threads.
|
||||
/// Download Threads media.
|
||||
/// `/download/threads?url=...`
|
||||
#[utoipa::path(
|
||||
get,
|
||||
path = "/download/threads",
|
||||
tag = "download",
|
||||
operation_id = "dl_download_threads",
|
||||
params(DownloadParams),
|
||||
responses(
|
||||
(status = 200, description = "Download result", body = DownloadResponse),
|
||||
(status = 400, description = "Bad URL or platform"),
|
||||
(status = 502, description = "Upstream error"),
|
||||
)
|
||||
)]
|
||||
pub async fn download_threads(
|
||||
Query(params): Query<DownloadParams>,
|
||||
) -> Result<Json<DownloadResponse>, AppError> {
|
||||
@@ -163,7 +368,20 @@ pub async fn download_threads(
|
||||
Ok(Json(DownloadResponse::ok(result)))
|
||||
}
|
||||
|
||||
/// Handler for _doodstream.
|
||||
/// Download from DoodStream.
|
||||
/// `/download/doodstream?url=...`
|
||||
#[utoipa::path(
|
||||
get,
|
||||
path = "/download/doodstream",
|
||||
tag = "download",
|
||||
operation_id = "dl_download_doodstream",
|
||||
params(DownloadParams),
|
||||
responses(
|
||||
(status = 200, description = "Download result", body = DownloadResponse),
|
||||
(status = 400, description = "Bad URL or platform"),
|
||||
(status = 502, description = "Upstream error"),
|
||||
)
|
||||
)]
|
||||
pub async fn download_doodstream(
|
||||
Query(params): Query<DownloadParams>,
|
||||
) -> Result<Json<DownloadResponse>, AppError> {
|
||||
@@ -171,7 +389,20 @@ pub async fn download_doodstream(
|
||||
Ok(Json(DownloadResponse::ok(result)))
|
||||
}
|
||||
|
||||
/// Handler for _krakenfiles.
|
||||
/// Download from KrakenFiles.
|
||||
/// `/download/krakenfiles?url=...`
|
||||
#[utoipa::path(
|
||||
get,
|
||||
path = "/download/krakenfiles",
|
||||
tag = "download",
|
||||
operation_id = "dl_download_krakenfiles",
|
||||
params(DownloadParams),
|
||||
responses(
|
||||
(status = 200, description = "Download result", body = DownloadResponse),
|
||||
(status = 400, description = "Bad URL or platform"),
|
||||
(status = 502, description = "Upstream error"),
|
||||
)
|
||||
)]
|
||||
pub async fn download_krakenfiles(
|
||||
Query(params): Query<DownloadParams>,
|
||||
) -> Result<Json<DownloadResponse>, AppError> {
|
||||
@@ -179,7 +410,20 @@ pub async fn download_krakenfiles(
|
||||
Ok(Json(DownloadResponse::ok(result)))
|
||||
}
|
||||
|
||||
/// Handler for _danbooru.
|
||||
/// Download from Danbooru.
|
||||
/// `/download/danbooru?url=...`
|
||||
#[utoipa::path(
|
||||
get,
|
||||
path = "/download/danbooru",
|
||||
tag = "download",
|
||||
operation_id = "dl_download_danbooru",
|
||||
params(DownloadParams),
|
||||
responses(
|
||||
(status = 200, description = "Download result", body = DownloadResponse),
|
||||
(status = 400, description = "Bad URL or platform"),
|
||||
(status = 502, description = "Upstream error"),
|
||||
)
|
||||
)]
|
||||
pub async fn download_danbooru(
|
||||
Query(params): Query<DownloadParams>,
|
||||
) -> Result<Json<DownloadResponse>, AppError> {
|
||||
@@ -187,7 +431,20 @@ pub async fn download_danbooru(
|
||||
Ok(Json(DownloadResponse::ok(result)))
|
||||
}
|
||||
|
||||
/// Handler for _soundcloud.
|
||||
/// Download SoundCloud track.
|
||||
/// `/download/soundcloud?url=...`
|
||||
#[utoipa::path(
|
||||
get,
|
||||
path = "/download/soundcloud",
|
||||
tag = "download",
|
||||
operation_id = "dl_download_soundcloud",
|
||||
params(DownloadParams),
|
||||
responses(
|
||||
(status = 200, description = "Download result", body = DownloadResponse),
|
||||
(status = 400, description = "Bad URL or platform"),
|
||||
(status = 502, description = "Upstream error"),
|
||||
)
|
||||
)]
|
||||
pub async fn download_soundcloud(
|
||||
Query(params): Query<DownloadParams>,
|
||||
) -> Result<Json<DownloadResponse>, AppError> {
|
||||
@@ -195,7 +452,20 @@ pub async fn download_soundcloud(
|
||||
Ok(Json(DownloadResponse::ok(result)))
|
||||
}
|
||||
|
||||
/// Handler for _dailymotion.
|
||||
/// Download Dailymotion video.
|
||||
/// `/download/dailymotion?url=...`
|
||||
#[utoipa::path(
|
||||
get,
|
||||
path = "/download/dailymotion",
|
||||
tag = "download",
|
||||
operation_id = "dl_download_dailymotion",
|
||||
params(DownloadParams),
|
||||
responses(
|
||||
(status = 200, description = "Download result", body = DownloadResponse),
|
||||
(status = 400, description = "Bad URL or platform"),
|
||||
(status = 502, description = "Upstream error"),
|
||||
)
|
||||
)]
|
||||
pub async fn download_dailymotion(
|
||||
Query(params): Query<DownloadParams>,
|
||||
) -> Result<Json<DownloadResponse>, AppError> {
|
||||
@@ -203,7 +473,20 @@ pub async fn download_dailymotion(
|
||||
Ok(Json(DownloadResponse::ok(result)))
|
||||
}
|
||||
|
||||
/// Handler for _reddit.
|
||||
/// Download Reddit media.
|
||||
/// `/download/reddit?url=...`
|
||||
#[utoipa::path(
|
||||
get,
|
||||
path = "/download/reddit",
|
||||
tag = "download",
|
||||
operation_id = "dl_download_reddit",
|
||||
params(DownloadParams),
|
||||
responses(
|
||||
(status = 200, description = "Download result", body = DownloadResponse),
|
||||
(status = 400, description = "Bad URL or platform"),
|
||||
(status = 502, description = "Upstream error"),
|
||||
)
|
||||
)]
|
||||
pub async fn download_reddit(
|
||||
Query(params): Query<DownloadParams>,
|
||||
) -> Result<Json<DownloadResponse>, AppError> {
|
||||
@@ -211,7 +494,20 @@ pub async fn download_reddit(
|
||||
Ok(Json(DownloadResponse::ok(result)))
|
||||
}
|
||||
|
||||
/// Handler for _streamable.
|
||||
/// Download Streamable video.
|
||||
/// `/download/streamable?url=...`
|
||||
#[utoipa::path(
|
||||
get,
|
||||
path = "/download/streamable",
|
||||
tag = "download",
|
||||
operation_id = "dl_download_streamable",
|
||||
params(DownloadParams),
|
||||
responses(
|
||||
(status = 200, description = "Download result", body = DownloadResponse),
|
||||
(status = 400, description = "Bad URL or platform"),
|
||||
(status = 502, description = "Upstream error"),
|
||||
)
|
||||
)]
|
||||
pub async fn download_streamable(
|
||||
Query(params): Query<DownloadParams>,
|
||||
) -> Result<Json<DownloadResponse>, AppError> {
|
||||
@@ -219,7 +515,20 @@ pub async fn download_streamable(
|
||||
Ok(Json(DownloadResponse::ok(result)))
|
||||
}
|
||||
|
||||
/// Handler for _videy.
|
||||
/// Download Videy video.
|
||||
/// `/download/videy?url=...`
|
||||
#[utoipa::path(
|
||||
get,
|
||||
path = "/download/videy",
|
||||
tag = "download",
|
||||
operation_id = "dl_download_videy",
|
||||
params(DownloadParams),
|
||||
responses(
|
||||
(status = 200, description = "Download result", body = DownloadResponse),
|
||||
(status = 400, description = "Bad URL or platform"),
|
||||
(status = 502, description = "Upstream error"),
|
||||
)
|
||||
)]
|
||||
pub async fn download_videy(
|
||||
Query(params): Query<DownloadParams>,
|
||||
) -> Result<Json<DownloadResponse>, AppError> {
|
||||
@@ -227,7 +536,20 @@ pub async fn download_videy(
|
||||
Ok(Json(DownloadResponse::ok(result)))
|
||||
}
|
||||
|
||||
/// Handler for _bilibili.
|
||||
/// Download Bilibili video.
|
||||
/// `/download/bilibili?url=...`
|
||||
#[utoipa::path(
|
||||
get,
|
||||
path = "/download/bilibili",
|
||||
tag = "download",
|
||||
operation_id = "dl_download_bilibili",
|
||||
params(DownloadParams),
|
||||
responses(
|
||||
(status = 200, description = "Download result", body = DownloadResponse),
|
||||
(status = 400, description = "Bad URL or platform"),
|
||||
(status = 502, description = "Upstream error"),
|
||||
)
|
||||
)]
|
||||
pub async fn download_bilibili(
|
||||
Query(params): Query<DownloadParams>,
|
||||
) -> Result<Json<DownloadResponse>, AppError> {
|
||||
|
||||
@@ -7,35 +7,62 @@ use axum::extract::Query;
|
||||
use axum::http::{header, StatusCode};
|
||||
use axum::response::Response;
|
||||
use serde::Deserialize;
|
||||
use utoipa::IntoParams;
|
||||
|
||||
use crate::application::image as use_cases;
|
||||
use crate::presentation::error::AppError;
|
||||
|
||||
#[derive(Deserialize)]
|
||||
#[derive(Deserialize, IntoParams)]
|
||||
pub struct BratParams {
|
||||
pub text: Option<String>,
|
||||
}
|
||||
|
||||
/// GET /image/brat?text=Hello
|
||||
#[utoipa::path(
|
||||
get,
|
||||
path = "/image/brat",
|
||||
tag = "image",
|
||||
operation_id = "image_brat",
|
||||
params(BratParams),
|
||||
responses(
|
||||
(status = 200, description = "Static brat image (PNG)", body = Vec<u8>),
|
||||
(status = 400, description = "Missing text"),
|
||||
(status = 502, description = "Upstream error"),
|
||||
)
|
||||
)]
|
||||
pub async fn brat_handler(Query(params): Query<BratParams>) -> Result<Response, AppError> {
|
||||
let text = params
|
||||
.text
|
||||
.ok_or_else(|| AppError::BadRequest("text parameter is required".into()))?;
|
||||
let bytes = use_cases::brat(&text).await?;
|
||||
Ok(Response::builder()
|
||||
Response::builder()
|
||||
.status(StatusCode::OK)
|
||||
.header(header::CONTENT_TYPE, "image/png")
|
||||
.body(Body::from(bytes))
|
||||
.expect("valid response"))
|
||||
.map_err(|e| AppError::Internal(format!("build response: {e}")))
|
||||
}
|
||||
|
||||
/// GET /image/brat/animated?text=Hello
|
||||
#[utoipa::path(
|
||||
get,
|
||||
path = "/image/brat/animated",
|
||||
tag = "image",
|
||||
operation_id = "image_brat_animated",
|
||||
params(BratParams),
|
||||
responses(
|
||||
(status = 200, description = "Animated brat image (GIF)", body = Vec<u8>),
|
||||
(status = 400, description = "Missing text"),
|
||||
(status = 502, description = "Upstream error"),
|
||||
)
|
||||
)]
|
||||
pub async fn brat_animated_handler(Query(params): Query<BratParams>) -> Result<Response, AppError> {
|
||||
let text = params
|
||||
.text
|
||||
.ok_or_else(|| AppError::BadRequest("text parameter is required".into()))?;
|
||||
let bytes = use_cases::brat_animated(&text).await?;
|
||||
Ok(Response::builder()
|
||||
Response::builder()
|
||||
.status(StatusCode::OK)
|
||||
.header(header::CONTENT_TYPE, "image/gif")
|
||||
.body(Body::from(bytes))
|
||||
.expect("valid response"))
|
||||
.map_err(|e| AppError::Internal(format!("build response: {e}")))
|
||||
}
|
||||
|
||||
@@ -7,12 +7,13 @@ use axum::http::StatusCode;
|
||||
use axum::Json;
|
||||
use serde::Deserialize;
|
||||
use serde_json::Value;
|
||||
use utoipa::IntoParams;
|
||||
|
||||
use crate::application::misc as use_cases;
|
||||
use crate::presentation::error::AppError;
|
||||
|
||||
/// Query params for currency converter.
|
||||
#[derive(Deserialize)]
|
||||
#[derive(Deserialize, IntoParams)]
|
||||
pub struct CurrencyParams {
|
||||
pub amount: Option<f64>,
|
||||
pub from: Option<String>,
|
||||
@@ -20,6 +21,17 @@ pub struct CurrencyParams {
|
||||
}
|
||||
|
||||
/// GET /misc/currency-converter?amount=100&from=USD&to=IDR
|
||||
#[utoipa::path(
|
||||
get,
|
||||
path = "/misc/currency-converter",
|
||||
tag = "misc",
|
||||
operation_id = "currency_converter",
|
||||
params(CurrencyParams),
|
||||
responses(
|
||||
(status = 200, description = "Currency conversion result", body = Value),
|
||||
(status = 502, description = "Upstream error"),
|
||||
)
|
||||
)]
|
||||
pub async fn currency_converter_handler(
|
||||
Query(params): Query<CurrencyParams>,
|
||||
) -> Result<(StatusCode, Json<Value>), AppError> {
|
||||
@@ -32,18 +44,48 @@ pub async fn currency_converter_handler(
|
||||
}
|
||||
|
||||
/// GET /misc/harga-emas
|
||||
#[utoipa::path(
|
||||
get,
|
||||
path = "/misc/harga-emas",
|
||||
tag = "misc",
|
||||
operation_id = "harga_emas",
|
||||
responses(
|
||||
(status = 200, description = "Gold prices (Antam)", body = Value),
|
||||
(status = 502, description = "Upstream error"),
|
||||
)
|
||||
)]
|
||||
pub async fn harga_emas_handler() -> Result<(StatusCode, Json<Value>), AppError> {
|
||||
let result = use_cases::harga_emas().await?;
|
||||
Ok((StatusCode::OK, Json(result)))
|
||||
}
|
||||
|
||||
/// GET /misc/kurs-bca
|
||||
#[utoipa::path(
|
||||
get,
|
||||
path = "/misc/kurs-bca",
|
||||
tag = "misc",
|
||||
operation_id = "kurs_bca",
|
||||
responses(
|
||||
(status = 200, description = "BCA exchange rates", body = Value),
|
||||
(status = 502, description = "Upstream error"),
|
||||
)
|
||||
)]
|
||||
pub async fn kurs_bca_handler() -> Result<(StatusCode, Json<Value>), AppError> {
|
||||
let result = use_cases::kurs_bca().await?;
|
||||
Ok((StatusCode::OK, Json(result)))
|
||||
}
|
||||
|
||||
/// GET /misc/server-info
|
||||
#[utoipa::path(
|
||||
get,
|
||||
path = "/misc/server-info",
|
||||
tag = "misc",
|
||||
operation_id = "server_info",
|
||||
responses(
|
||||
(status = 200, description = "Server info", body = Value),
|
||||
(status = 502, description = "Upstream error"),
|
||||
)
|
||||
)]
|
||||
pub async fn server_info_handler() -> Result<(StatusCode, Json<Value>), AppError> {
|
||||
let result = use_cases::server_info().await?;
|
||||
Ok((StatusCode::OK, Json(result)))
|
||||
|
||||
@@ -19,17 +19,41 @@ pub struct SearchParams {
|
||||
pub city: Option<String>,
|
||||
}
|
||||
|
||||
fn require<'a>(v: Option<&'a String>, name: &str) -> Result<String, AppError> {
|
||||
fn require(v: Option<&String>, name: &str) -> Result<String, AppError> {
|
||||
v.cloned()
|
||||
.ok_or_else(|| AppError::BadRequest(format!("Missing '{}' parameter", name)))
|
||||
}
|
||||
|
||||
/// GET /search/bmkg
|
||||
#[utoipa::path(
|
||||
get,
|
||||
path = "/search/bmkg",
|
||||
tag = "search",
|
||||
operation_id = "search_bmkg_handler",
|
||||
responses(
|
||||
(status = 200, description = "OK", body = serde_json::Value),
|
||||
(status = 400, description = "Bad Request"),
|
||||
(status = 502, description = "Upstream error"),
|
||||
)
|
||||
)]
|
||||
|
||||
pub async fn bmkg_handler() -> Result<(StatusCode, Json<Value>), AppError> {
|
||||
Ok((StatusCode::OK, Json(use_cases::bmkg().await?)))
|
||||
}
|
||||
|
||||
/// GET /search/jadwal-sholat?kota=jakarta
|
||||
#[utoipa::path(
|
||||
get,
|
||||
path = "/search/jadwal-sholat",
|
||||
tag = "search",
|
||||
operation_id = "search_jadwal_sholat_handler",
|
||||
responses(
|
||||
(status = 200, description = "OK", body = serde_json::Value),
|
||||
(status = 400, description = "Bad Request"),
|
||||
(status = 502, description = "Upstream error"),
|
||||
)
|
||||
)]
|
||||
|
||||
pub async fn jadwal_sholat_handler(
|
||||
Query(p): Query<SearchParams>,
|
||||
) -> Result<(StatusCode, Json<Value>), AppError> {
|
||||
@@ -38,6 +62,18 @@ pub async fn jadwal_sholat_handler(
|
||||
}
|
||||
|
||||
/// GET /search/weather?city=jakarta
|
||||
#[utoipa::path(
|
||||
get,
|
||||
path = "/search/weather",
|
||||
tag = "search",
|
||||
operation_id = "search_weather_handler",
|
||||
responses(
|
||||
(status = 200, description = "OK", body = serde_json::Value),
|
||||
(status = 400, description = "Bad Request"),
|
||||
(status = 502, description = "Upstream error"),
|
||||
)
|
||||
)]
|
||||
|
||||
pub async fn weather_handler(
|
||||
Query(p): Query<SearchParams>,
|
||||
) -> Result<(StatusCode, Json<Value>), AppError> {
|
||||
@@ -46,6 +82,18 @@ pub async fn weather_handler(
|
||||
}
|
||||
|
||||
/// GET /search/google?query=rust
|
||||
#[utoipa::path(
|
||||
get,
|
||||
path = "/search/google",
|
||||
tag = "search",
|
||||
operation_id = "search_google_handler",
|
||||
responses(
|
||||
(status = 200, description = "OK", body = serde_json::Value),
|
||||
(status = 400, description = "Bad Request"),
|
||||
(status = 502, description = "Upstream error"),
|
||||
)
|
||||
)]
|
||||
|
||||
pub async fn google_handler(
|
||||
Query(p): Query<SearchParams>,
|
||||
) -> Result<(StatusCode, Json<Value>), AppError> {
|
||||
@@ -54,6 +102,18 @@ pub async fn google_handler(
|
||||
}
|
||||
|
||||
/// GET /search/yt?query=rickroll
|
||||
#[utoipa::path(
|
||||
get,
|
||||
path = "/search/yt",
|
||||
tag = "search",
|
||||
operation_id = "search_yt_handler",
|
||||
responses(
|
||||
(status = 200, description = "OK", body = serde_json::Value),
|
||||
(status = 400, description = "Bad Request"),
|
||||
(status = 502, description = "Upstream error"),
|
||||
)
|
||||
)]
|
||||
|
||||
pub async fn yt_handler(
|
||||
Query(p): Query<SearchParams>,
|
||||
) -> Result<(StatusCode, Json<Value>), AppError> {
|
||||
|
||||
@@ -7,12 +7,13 @@ use axum::http::StatusCode;
|
||||
use axum::Json;
|
||||
use serde::Deserialize;
|
||||
use serde_json::Value;
|
||||
use utoipa::IntoParams;
|
||||
|
||||
use crate::application::stalk as use_cases;
|
||||
use crate::presentation::error::AppError;
|
||||
|
||||
/// Query params for stalk endpoints.
|
||||
#[derive(Deserialize)]
|
||||
#[derive(Deserialize, IntoParams)]
|
||||
pub struct StalkParams {
|
||||
pub username: Option<String>,
|
||||
pub user_id: Option<String>,
|
||||
@@ -22,6 +23,18 @@ pub struct StalkParams {
|
||||
}
|
||||
|
||||
/// GET /stalk/github?username=asepharyana
|
||||
#[utoipa::path(
|
||||
get,
|
||||
path = "/stalk/github",
|
||||
tag = "stalk",
|
||||
operation_id = "stalk_github",
|
||||
params(StalkParams),
|
||||
responses(
|
||||
(status = 200, description = "GitHub user profile", body = Value),
|
||||
(status = 400, description = "Missing username"),
|
||||
(status = 502, description = "Upstream error"),
|
||||
)
|
||||
)]
|
||||
pub async fn github_handler(
|
||||
Query(params): Query<StalkParams>,
|
||||
) -> Result<(StatusCode, Json<Value>), AppError> {
|
||||
@@ -33,6 +46,18 @@ pub async fn github_handler(
|
||||
}
|
||||
|
||||
/// GET /stalk/youtube?username=<handle>
|
||||
#[utoipa::path(
|
||||
get,
|
||||
path = "/stalk/youtube",
|
||||
tag = "stalk",
|
||||
operation_id = "stalk_youtube",
|
||||
params(StalkParams),
|
||||
responses(
|
||||
(status = 200, description = "YouTube channel profile", body = Value),
|
||||
(status = 400, description = "Missing username"),
|
||||
(status = 502, description = "Upstream error"),
|
||||
)
|
||||
)]
|
||||
pub async fn youtube_handler(
|
||||
Query(params): Query<StalkParams>,
|
||||
) -> Result<(StatusCode, Json<Value>), AppError> {
|
||||
@@ -44,6 +69,18 @@ pub async fn youtube_handler(
|
||||
}
|
||||
|
||||
/// GET /stalk/mobile-legends?user_id=...&zone_id=... (aliases: userid, zoneid)
|
||||
#[utoipa::path(
|
||||
get,
|
||||
path = "/stalk/mobile-legends",
|
||||
tag = "stalk",
|
||||
operation_id = "stalk_mobile_legends",
|
||||
params(StalkParams),
|
||||
responses(
|
||||
(status = 200, description = "Mobile Legends player info", body = Value),
|
||||
(status = 400, description = "Missing user_id/zone_id"),
|
||||
(status = 502, description = "Upstream error"),
|
||||
)
|
||||
)]
|
||||
pub async fn mobile_legends_handler(
|
||||
Query(params): Query<StalkParams>,
|
||||
) -> Result<(StatusCode, Json<Value>), AppError> {
|
||||
@@ -60,6 +97,18 @@ pub async fn mobile_legends_handler(
|
||||
}
|
||||
|
||||
/// GET /stalk/free-fire?user_id=... (alias: userid)
|
||||
#[utoipa::path(
|
||||
get,
|
||||
path = "/stalk/free-fire",
|
||||
tag = "stalk",
|
||||
operation_id = "stalk_free_fire",
|
||||
params(StalkParams),
|
||||
responses(
|
||||
(status = 200, description = "Free Fire player info", body = Value),
|
||||
(status = 400, description = "Missing user_id"),
|
||||
(status = 502, description = "Upstream error"),
|
||||
)
|
||||
)]
|
||||
pub async fn free_fire_handler(
|
||||
Query(params): Query<StalkParams>,
|
||||
) -> Result<(StatusCode, Json<Value>), AppError> {
|
||||
@@ -72,6 +121,18 @@ pub async fn free_fire_handler(
|
||||
}
|
||||
|
||||
/// GET /stalk/genshin-impact?user_id=... (alias: userid)
|
||||
#[utoipa::path(
|
||||
get,
|
||||
path = "/stalk/genshin-impact",
|
||||
tag = "stalk",
|
||||
operation_id = "stalk_genshin",
|
||||
params(StalkParams),
|
||||
responses(
|
||||
(status = 200, description = "Genshin Impact profile", body = Value),
|
||||
(status = 400, description = "Missing user_id"),
|
||||
(status = 502, description = "Upstream error"),
|
||||
)
|
||||
)]
|
||||
pub async fn genshin_handler(
|
||||
Query(params): Query<StalkParams>,
|
||||
) -> Result<(StatusCode, Json<Value>), AppError> {
|
||||
@@ -84,6 +145,18 @@ pub async fn genshin_handler(
|
||||
}
|
||||
|
||||
/// GET /stalk/twitter?username=...
|
||||
#[utoipa::path(
|
||||
get,
|
||||
path = "/stalk/twitter",
|
||||
tag = "stalk",
|
||||
operation_id = "stalk_twitter",
|
||||
params(StalkParams),
|
||||
responses(
|
||||
(status = 200, description = "Twitter/X user profile", body = Value),
|
||||
(status = 400, description = "Missing username"),
|
||||
(status = 502, description = "Upstream error"),
|
||||
)
|
||||
)]
|
||||
pub async fn twitter_handler(
|
||||
Query(params): Query<StalkParams>,
|
||||
) -> Result<(StatusCode, Json<Value>), AppError> {
|
||||
@@ -95,6 +168,18 @@ pub async fn twitter_handler(
|
||||
}
|
||||
|
||||
/// GET /stalk/tiktok?username=...
|
||||
#[utoipa::path(
|
||||
get,
|
||||
path = "/stalk/tiktok",
|
||||
tag = "stalk",
|
||||
operation_id = "stalk_tiktok",
|
||||
params(StalkParams),
|
||||
responses(
|
||||
(status = 200, description = "TikTok user profile", body = Value),
|
||||
(status = 400, description = "Missing username"),
|
||||
(status = 502, description = "Upstream error"),
|
||||
)
|
||||
)]
|
||||
pub async fn tiktok_handler(
|
||||
Query(params): Query<StalkParams>,
|
||||
) -> Result<(StatusCode, Json<Value>), AppError> {
|
||||
@@ -106,6 +191,18 @@ pub async fn tiktok_handler(
|
||||
}
|
||||
|
||||
/// GET /stalk/instagram?username=...
|
||||
#[utoipa::path(
|
||||
get,
|
||||
path = "/stalk/instagram",
|
||||
tag = "stalk",
|
||||
operation_id = "stalk_instagram",
|
||||
params(StalkParams),
|
||||
responses(
|
||||
(status = 200, description = "Instagram user profile", body = Value),
|
||||
(status = 400, description = "Missing username"),
|
||||
(status = 502, description = "Upstream error"),
|
||||
)
|
||||
)]
|
||||
pub async fn instagram_handler(
|
||||
Query(params): Query<StalkParams>,
|
||||
) -> Result<(StatusCode, Json<Value>), AppError> {
|
||||
|
||||
@@ -1,35 +1,49 @@
|
||||
//! Axum handlers for tools.
|
||||
//! Axum handlers for tool endpoints.
|
||||
|
||||
use axum::extract::Query;
|
||||
use axum::http::StatusCode;
|
||||
use axum::Json;
|
||||
use serde::Deserialize;
|
||||
use serde_json::Value;
|
||||
use utoipa::IntoParams;
|
||||
|
||||
use crate::application::tools as use_cases;
|
||||
use crate::presentation::error::AppError;
|
||||
|
||||
#[derive(Deserialize)]
|
||||
#[derive(Deserialize, IntoParams)]
|
||||
pub struct DomainParams {
|
||||
pub domain: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
#[derive(Deserialize, IntoParams)]
|
||||
pub struct IpParams {
|
||||
pub ip: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
#[derive(Deserialize, IntoParams)]
|
||||
pub struct UrlParams {
|
||||
pub url: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
#[derive(Deserialize, IntoParams)]
|
||||
pub struct ResiParams {
|
||||
pub resi: Option<String>,
|
||||
pub ekspedisi: Option<String>,
|
||||
}
|
||||
|
||||
/// GET /tool/whois?domain=example.com
|
||||
#[utoipa::path(
|
||||
get,
|
||||
path = "/tool/whois",
|
||||
tag = "tool",
|
||||
operation_id = "tool_whois",
|
||||
params(DomainParams),
|
||||
responses(
|
||||
(status = 200, description = "WHOIS info", body = Value),
|
||||
(status = 400, description = "Missing domain"),
|
||||
(status = 502, description = "Upstream error"),
|
||||
)
|
||||
)]
|
||||
pub async fn whois_handler(
|
||||
Query(params): Query<DomainParams>,
|
||||
) -> Result<(StatusCode, Json<Value>), AppError> {
|
||||
@@ -39,6 +53,19 @@ pub async fn whois_handler(
|
||||
Ok((StatusCode::OK, Json(use_cases::whois(&domain).await?)))
|
||||
}
|
||||
|
||||
/// GET /tool/ip-location?ip=1.1.1.1
|
||||
#[utoipa::path(
|
||||
get,
|
||||
path = "/tool/ip-location",
|
||||
tag = "tool",
|
||||
operation_id = "tool_ip_location",
|
||||
params(IpParams),
|
||||
responses(
|
||||
(status = 200, description = "IP geolocation", body = Value),
|
||||
(status = 400, description = "Missing IP"),
|
||||
(status = 502, description = "Upstream error"),
|
||||
)
|
||||
)]
|
||||
pub async fn ip_location_handler(
|
||||
Query(params): Query<IpParams>,
|
||||
) -> Result<(StatusCode, Json<Value>), AppError> {
|
||||
@@ -48,6 +75,19 @@ pub async fn ip_location_handler(
|
||||
Ok((StatusCode::OK, Json(use_cases::ip_location(&ip).await?)))
|
||||
}
|
||||
|
||||
/// GET /tool/tinyurl?url=https://example.com
|
||||
#[utoipa::path(
|
||||
get,
|
||||
path = "/tool/tinyurl",
|
||||
tag = "tool",
|
||||
operation_id = "tool_tinyurl",
|
||||
params(UrlParams),
|
||||
responses(
|
||||
(status = 200, description = "Shortened URL", body = Value),
|
||||
(status = 400, description = "Missing URL"),
|
||||
(status = 502, description = "Upstream error"),
|
||||
)
|
||||
)]
|
||||
pub async fn tinyurl_handler(
|
||||
Query(params): Query<UrlParams>,
|
||||
) -> Result<(StatusCode, Json<Value>), AppError> {
|
||||
@@ -57,6 +97,19 @@ pub async fn tinyurl_handler(
|
||||
Ok((StatusCode::OK, Json(use_cases::tinyurl(&url).await?)))
|
||||
}
|
||||
|
||||
/// GET /tool/check-hosting?domain=example.com
|
||||
#[utoipa::path(
|
||||
get,
|
||||
path = "/tool/check-hosting",
|
||||
tag = "tool",
|
||||
operation_id = "tool_check_hosting",
|
||||
params(DomainParams),
|
||||
responses(
|
||||
(status = 200, description = "Hosting info", body = Value),
|
||||
(status = 400, description = "Missing domain"),
|
||||
(status = 502, description = "Upstream error"),
|
||||
)
|
||||
)]
|
||||
pub async fn check_hosting_handler(
|
||||
Query(params): Query<DomainParams>,
|
||||
) -> Result<(StatusCode, Json<Value>), AppError> {
|
||||
@@ -69,10 +122,34 @@ pub async fn check_hosting_handler(
|
||||
))
|
||||
}
|
||||
|
||||
/// GET /tool/hargapangan
|
||||
#[utoipa::path(
|
||||
get,
|
||||
path = "/tool/hargapangan",
|
||||
tag = "tool",
|
||||
operation_id = "tool_hargapangan",
|
||||
responses(
|
||||
(status = 200, description = "Food prices", body = Value),
|
||||
(status = 502, description = "Upstream error"),
|
||||
)
|
||||
)]
|
||||
pub async fn hargapangan_handler() -> Result<(StatusCode, Json<Value>), AppError> {
|
||||
Ok((StatusCode::OK, Json(use_cases::hargapangan().await?)))
|
||||
}
|
||||
|
||||
/// GET /tool/cek-resi?resi=...&ekspedisi=...
|
||||
#[utoipa::path(
|
||||
get,
|
||||
path = "/tool/cek-resi",
|
||||
tag = "tool",
|
||||
operation_id = "tool_cek_resi",
|
||||
params(ResiParams),
|
||||
responses(
|
||||
(status = 200, description = "Shipment tracking", body = Value),
|
||||
(status = 400, description = "Missing resi"),
|
||||
(status = 502, description = "Upstream error"),
|
||||
)
|
||||
)]
|
||||
pub async fn cek_resi_handler(
|
||||
Query(params): Query<ResiParams>,
|
||||
) -> Result<(StatusCode, Json<Value>), AppError> {
|
||||
|
||||
@@ -14,6 +14,18 @@ use crate::application::uploader as use_cases;
|
||||
use crate::presentation::error::AppError;
|
||||
|
||||
/// POST /uploader/ryzencdn — accept a multipart file upload.
|
||||
#[utoipa::path(
|
||||
post,
|
||||
path = "/uploader/ryzencdn",
|
||||
tag = "uploader",
|
||||
operation_id = "uploader_ryzencdn_handler",
|
||||
responses(
|
||||
(status = 200, description = "OK", body = serde_json::Value),
|
||||
(status = 400, description = "Bad Request"),
|
||||
(status = 502, description = "Upstream error"),
|
||||
)
|
||||
)]
|
||||
|
||||
pub async fn ryzencdn_handler(mut multipart: Multipart) -> Result<Json<Value>, AppError> {
|
||||
// Read the first file field.
|
||||
let mut file_name = String::from("");
|
||||
@@ -47,12 +59,24 @@ pub async fn ryzencdn_handler(mut multipart: Multipart) -> Result<Json<Value>, A
|
||||
}
|
||||
|
||||
/// GET /uploader/file/:name — serve an uploaded file.
|
||||
#[utoipa::path(
|
||||
get,
|
||||
path = "/uploader/file/:name",
|
||||
tag = "uploader",
|
||||
operation_id = "uploader_serve_file_handler",
|
||||
responses(
|
||||
(status = 200, description = "OK", body = serde_json::Value),
|
||||
(status = 400, description = "Bad Request"),
|
||||
(status = 502, description = "Upstream error"),
|
||||
)
|
||||
)]
|
||||
|
||||
pub async fn serve_file_handler(Path(name): Path<String>) -> Result<Response<Body>, AppError> {
|
||||
let (bytes, mime) = use_cases::serve(&name)?;
|
||||
Ok(Response::builder()
|
||||
Response::builder()
|
||||
.status(StatusCode::OK)
|
||||
.header(header::CONTENT_TYPE, mime)
|
||||
.header(header::CACHE_CONTROL, "public, max-age=86400")
|
||||
.body(Body::from(bytes))
|
||||
.unwrap())
|
||||
.map_err(|e| AppError::Internal(format!("build response: {e}")))
|
||||
}
|
||||
|
||||
@@ -19,12 +19,24 @@ pub struct WeebsParams {
|
||||
pub tag: Option<String>,
|
||||
}
|
||||
|
||||
fn require<'a>(v: Option<&'a String>, name: &str) -> Result<String, AppError> {
|
||||
fn require(v: Option<&String>, name: &str) -> Result<String, AppError> {
|
||||
v.cloned()
|
||||
.ok_or_else(|| AppError::BadRequest(format!("Missing '{}' parameter", name)))
|
||||
}
|
||||
|
||||
/// GET /weebs/anime-info?query=naruto
|
||||
#[utoipa::path(
|
||||
get,
|
||||
path = "/weebs/anime-info",
|
||||
tag = "weebs",
|
||||
operation_id = "weebs_anime_info_handler",
|
||||
responses(
|
||||
(status = 200, description = "OK", body = serde_json::Value),
|
||||
(status = 400, description = "Bad Request"),
|
||||
(status = 502, description = "Upstream error"),
|
||||
)
|
||||
)]
|
||||
|
||||
pub async fn anime_info_handler(
|
||||
Query(p): Query<WeebsParams>,
|
||||
) -> Result<(StatusCode, Json<Value>), AppError> {
|
||||
@@ -33,6 +45,18 @@ pub async fn anime_info_handler(
|
||||
}
|
||||
|
||||
/// GET /weebs/manga-info?query=one%20piece
|
||||
#[utoipa::path(
|
||||
get,
|
||||
path = "/weebs/manga-info",
|
||||
tag = "weebs",
|
||||
operation_id = "weebs_manga_info_handler",
|
||||
responses(
|
||||
(status = 200, description = "OK", body = serde_json::Value),
|
||||
(status = 400, description = "Bad Request"),
|
||||
(status = 502, description = "Upstream error"),
|
||||
)
|
||||
)]
|
||||
|
||||
pub async fn manga_info_handler(
|
||||
Query(p): Query<WeebsParams>,
|
||||
) -> Result<(StatusCode, Json<Value>), AppError> {
|
||||
@@ -41,6 +65,18 @@ pub async fn manga_info_handler(
|
||||
}
|
||||
|
||||
/// GET /weebs/whatanime?url=<image-url>
|
||||
#[utoipa::path(
|
||||
get,
|
||||
path = "/weebs/whatanime",
|
||||
tag = "weebs",
|
||||
operation_id = "weebs_whatanime_handler",
|
||||
responses(
|
||||
(status = 200, description = "OK", body = serde_json::Value),
|
||||
(status = 400, description = "Bad Request"),
|
||||
(status = 502, description = "Upstream error"),
|
||||
)
|
||||
)]
|
||||
|
||||
pub async fn whatanime_handler(
|
||||
Query(p): Query<WeebsParams>,
|
||||
) -> Result<(StatusCode, Json<Value>), AppError> {
|
||||
@@ -49,6 +85,18 @@ pub async fn whatanime_handler(
|
||||
}
|
||||
|
||||
/// GET /weebs/sfw-waifu?tag=waifu
|
||||
#[utoipa::path(
|
||||
get,
|
||||
path = "/weebs/sfw-waifu",
|
||||
tag = "weebs",
|
||||
operation_id = "weebs_sfw_waifu_handler",
|
||||
responses(
|
||||
(status = 200, description = "OK", body = serde_json::Value),
|
||||
(status = 400, description = "Bad Request"),
|
||||
(status = 502, description = "Upstream error"),
|
||||
)
|
||||
)]
|
||||
|
||||
pub async fn sfw_waifu_handler(
|
||||
Query(p): Query<WeebsParams>,
|
||||
) -> Result<(StatusCode, Json<Value>), AppError> {
|
||||
@@ -57,6 +105,18 @@ pub async fn sfw_waifu_handler(
|
||||
}
|
||||
|
||||
/// GET /weebs/nsfw-waifu?tag=waifu
|
||||
#[utoipa::path(
|
||||
get,
|
||||
path = "/weebs/nsfw-waifu",
|
||||
tag = "weebs",
|
||||
operation_id = "weebs_nsfw_waifu_handler",
|
||||
responses(
|
||||
(status = 200, description = "OK", body = serde_json::Value),
|
||||
(status = 400, description = "Bad Request"),
|
||||
(status = 502, description = "Upstream error"),
|
||||
)
|
||||
)]
|
||||
|
||||
pub async fn nsfw_waifu_handler(
|
||||
Query(p): Query<WeebsParams>,
|
||||
) -> Result<(StatusCode, Json<Value>), AppError> {
|
||||
|
||||
Reference in New Issue
Block a user