use std::sync::Arc;
use axum::extract::Extension;
use axum::http::StatusCode;
use axum::response::IntoResponse;
use axum::routing::get;
use axum::{Json, Router};
use serde::Serialize;
use utoipa::ToSchema;
use crate::instance::Instance;
use crate::RuntimeCore;
pub mod paths {
pub const PREFIX: &str = "/v1/runtime";
pub const STATUS: &str = "/v1/runtime/status";
pub const INSTANCES: &str = "/v1/runtime/instances";
pub fn rel(full: &str) -> &str {
full.strip_prefix(PREFIX).unwrap_or(full)
}
}
pub fn routes(core: Arc<RuntimeCore>) -> Router {
use paths::rel;
Router::new()
.route(rel(paths::STATUS), get(status_handler))
.route(rel(paths::INSTANCES), get(instances_handler))
.layer(Extension(core))
}
#[derive(Debug, Serialize, ToSchema)]
pub struct RuntimeStatus {
pub active: bool,
pub backend: Option<String>,
pub instance_count: usize,
}
#[utoipa::path(get, path = "/status", tag = "runtime",
summary = "Runtime adapter status",
responses((status = 200, body = RuntimeStatus)))]
async fn status_handler(Extension(core): Extension<Arc<RuntimeCore>>) -> impl IntoResponse {
let status = core.status().await;
Json(status)
}
#[utoipa::path(get, path = "/instances", tag = "runtime",
summary = "List runtime-managed instances",
responses((status = 200, body = Vec<Instance>)))]
async fn instances_handler(Extension(core): Extension<Arc<RuntimeCore>>) -> impl IntoResponse {
match core.list_instances().await {
Ok(instances) => (StatusCode::OK, Json(instances)).into_response(),
Err(e) => {
let code = koi_common::error::ErrorCode::from(&e);
let status = StatusCode::from_u16(code.http_status())
.unwrap_or(StatusCode::INTERNAL_SERVER_ERROR);
koi_common::http::error_response_with_status(status, code, e.to_string())
}
}
}
#[derive(utoipa::OpenApi)]
#[openapi(
paths(status_handler, instances_handler),
components(schemas(
RuntimeStatus,
Instance,
crate::instance::PortMapping,
crate::instance::PortProtocol,
crate::instance::InstanceState,
crate::instance::KoiMetadata,
))
)]
pub struct RuntimeApiDoc;