hanzo-api 1.1.11

Http Api for Hanzo AI platform
use std::sync::Arc;

use utoipa::OpenApi;
use utoipa_swagger_ui::Config;
use warp::{
    filters::path::{FullPath, Tail},
    http::Uri,
    hyper::{Response, StatusCode},
    reject::Rejection,
    reply::Reply,
    Filter,
};

use super::{
    api_v2_handlers_ext_agent_offers::ToolOfferingsApiDoc, api_v2_handlers_general::GeneralApiDoc,
    api_v2_handlers_jobs::JobsApiDoc, api_v2_handlers_mcp_servers::MCPServerApiDoc, api_v2_handlers_tools::ToolsApiDoc,
    api_v2_handlers_vecfs::VecFsApiDoc, api_v2_handlers_wallets::WalletApiDoc,
};

pub fn swagger_ui_routes() -> impl Filter<Extract = impl warp::Reply, Error = warp::Rejection> + Clone {
    let config = Arc::new(Config::new([
        "/v2/openapi/general.json",
        "/v2/openapi/jobs.json",
        "/v2/openapi/vecfs.json",
        "/v2/openapi/wallet.json",
        "/v2/openapi/tools.json",
        "/v2/openapi/ext_agent_offers.json",
    ]));

    let general_schema_route = warp::path!("openapi" / "general.json")
        .and(warp::get())
        .map(|| warp::reply::json(&GeneralApiDoc::openapi()));

    let jobs_schema_route = warp::path!("openapi" / "jobs.json")
        .and(warp::get())
        .map(|| warp::reply::json(&JobsApiDoc::openapi()));

    let vecfs_schema_route = warp::path!("openapi" / "vecfs.json")
        .and(warp::get())
        .map(|| warp::reply::json(&VecFsApiDoc::openapi()));

    let wallet_schema_route = warp::path!("openapi" / "wallet.json")
        .and(warp::get())
        .map(|| warp::reply::json(&WalletApiDoc::openapi()));

    let tools_schema_route = warp::path!("openapi" / "tools.json")
        .and(warp::get())
        .map(|| warp::reply::json(&ToolsApiDoc::openapi()));

    let ext_agent_offers_schema_route = warp::path!("openapi" / "ext_agent_offers.json")
        .and(warp::get())
        .map(|| warp::reply::json(&ToolOfferingsApiDoc::openapi()));

    let swagger_ui = warp::path("swagger-ui")
        .and(warp::get())
        .and(warp::path::full())
        .and(warp::path::tail())
        .and(warp::any().map(move || config.clone()))
        .and_then(serve_swagger);

    let mcp_servers_schema_route = warp::path!("openapi" / "mcp_servers.json")
        .and(warp::get())
        .map(|| warp::reply::json(&MCPServerApiDoc::openapi()));

    general_schema_route
        .or(jobs_schema_route)
        .or(vecfs_schema_route)
        .or(wallet_schema_route)
        .or(tools_schema_route)
        .or(ext_agent_offers_schema_route)
        .or(mcp_servers_schema_route)
        .or(swagger_ui)
}

async fn serve_swagger(
    full_path: FullPath,
    tail: Tail,
    config: Arc<Config<'static>>,
) -> Result<Box<dyn Reply + 'static>, Rejection> {
    if full_path.as_str() == "/v2/swagger-ui" {
        return Ok(Box::new(warp::redirect::found(Uri::from_static("/v2/swagger-ui/"))));
    }

    let path = tail.as_str();
    match utoipa_swagger_ui::serve(path, config) {
        Ok(file) => {
            if let Some(file) = file {
                Ok(Box::new(
                    Response::builder()
                        .header("Content-Type", file.content_type)
                        .body(file.bytes),
                ))
            } else {
                Ok(Box::new(StatusCode::NOT_FOUND))
            }
        }
        Err(error) => Ok(Box::new(
            Response::builder()
                .status(StatusCode::INTERNAL_SERVER_ERROR)
                .body(error.to_string()),
        )),
    }
}