use std::sync::Arc;
use hyper::{Response, StatusCode};
use zygo_core::supervisor::{Request as Control, Response as Reply};
use super::reply::{ApiBody, HttpError, json, reply_to_response};
use super::{Api, control};
pub(super) async fn put_deps(
api: &Arc<Api>,
body: &[u8],
tenant: Option<String>,
) -> Result<Response<ApiBody>, HttpError> {
#[derive(serde::Deserialize)]
#[serde(deny_unknown_fields)]
struct PutDepsRequest {
image: String,
files: std::collections::BTreeMap<String, String>,
}
let request: PutDepsRequest = serde_json::from_slice(body).map_err(|e| {
HttpError::new(
StatusCode::BAD_REQUEST,
format!(
"body must be {{\"image\": \"…\", \"files\": {{\"requirements.txt\": \"<base64>\"}}}}: {e}"
),
)
})?;
let reply = control(api, move |c| {
Ok(c.send(&Control::PutDeps {
image: request.image,
files: request.files,
tenant,
})?)
})
.await?;
match reply {
Reply::Dependencies { deps, existed, .. } => {
let status = if existed {
StatusCode::OK
} else {
StatusCode::ACCEPTED
};
Ok(json(status, &deps_json(deps.first(), "")))
}
other => Ok(reply_to_response(other)),
}
}
pub(super) async fn deps(
api: &Arc<Api>,
id: Option<String>,
tenant: Option<String>,
) -> Result<Response<ApiBody>, HttpError> {
let one = id.is_some();
let reply = control(api, move |c| Ok(c.send(&Control::Deps { id, tenant })?)).await?;
match reply {
Reply::Dependencies { deps, log, .. } if one => {
Ok(json(StatusCode::OK, &deps_json(deps.first(), &log)))
}
Reply::Dependencies { deps, .. } => Ok(json(
StatusCode::OK,
&serde_json::json!({
"deps": deps.iter().map(|d| deps_json(Some(d), "")).collect::<Vec<_>>(),
}),
)),
other => Ok(reply_to_response(other)),
}
}
pub(super) async fn delete_deps(
api: &Arc<Api>,
id: String,
) -> Result<Response<ApiBody>, HttpError> {
let reply = control(api, move |c| Ok(c.send(&Control::DeleteDeps { id })?)).await?;
match reply {
Reply::Ok => Ok(json(
StatusCode::OK,
&serde_json::json!({ "deleted": true }),
)),
other => Ok(reply_to_response(other)),
}
}
fn deps_json(status: Option<&zygo_core::deps::Status>, log: &str) -> serde_json::Value {
let Some(status) = status else {
return serde_json::json!({});
};
serde_json::json!({
"id": status.id,
"kind": status.kind.as_str(),
"state": status.state.as_str(),
"image": status.image,
"files": status.files,
"error": status.error,
"started_ms": status.started_ms,
"finished_ms": status.finished_ms,
"tenants": status.tenants,
"log": log,
})
}
pub(super) async fn put_blob(api: &Arc<Api>, body: &[u8]) -> Result<Response<ApiBody>, HttpError> {
if body.is_empty() {
return Err(HttpError::new(
StatusCode::BAD_REQUEST,
"the body is empty; it should be the tar itself",
));
}
let tar = base64::Engine::encode(&base64::engine::general_purpose::STANDARD, body);
let reply = control(api, move |c| Ok(c.send(&Control::PutBlob { tar })?)).await?;
match reply {
Reply::Script {
digest,
size,
existed,
} => Ok(json(
if existed {
StatusCode::OK
} else {
StatusCode::CREATED
},
&serde_json::json!({ "sha256": digest, "size": size, "existed": existed }),
)),
other => Ok(reply_to_response(other)),
}
}
pub(super) async fn get_blob(
api: &Arc<Api>,
digest: String,
) -> Result<Response<ApiBody>, HttpError> {
let reply = control(api, move |c| Ok(c.send(&Control::GetBlob { digest })?)).await?;
Ok(reply_to_response(reply))
}
pub(super) async fn delete_blob(
api: &Arc<Api>,
digest: String,
) -> Result<Response<ApiBody>, HttpError> {
let reply = control(api, move |c| Ok(c.send(&Control::DeleteBlob { digest })?)).await?;
match reply {
Reply::Ok => Ok(json(
StatusCode::OK,
&serde_json::json!({ "deleted": true }),
)),
other => Ok(reply_to_response(other)),
}
}
pub(super) async fn put_script(
api: &Arc<Api>,
body: &[u8],
tenant: Option<String>,
) -> Result<Response<ApiBody>, HttpError> {
let source = std::str::from_utf8(body)
.map_err(|e| {
HttpError::new(
StatusCode::BAD_REQUEST,
format!("a script must be UTF-8 text: {e}"),
)
})?
.to_string();
if source.is_empty() {
return Err(HttpError::new(
StatusCode::BAD_REQUEST,
"the body is empty; it should be the script itself",
));
}
let reply = control(api, move |c| {
Ok(c.send(&Control::PutScript { source, tenant })?)
})
.await?;
match reply {
Reply::Script {
digest,
size,
existed,
} => Ok(json(
if existed {
StatusCode::OK
} else {
StatusCode::CREATED
},
&serde_json::json!({ "sha256": digest, "size": size, "existed": existed }),
)),
other => Ok(reply_to_response(other)),
}
}
pub(super) async fn get_script(
api: &Arc<Api>,
digest: String,
) -> Result<Response<ApiBody>, HttpError> {
let reply = control(api, move |c| Ok(c.send(&Control::GetScript { digest })?)).await?;
match reply {
Reply::Script { digest, size, .. } => Ok(json(
StatusCode::OK,
&serde_json::json!({ "sha256": digest, "size": size }),
)),
other => Ok(reply_to_response(other)),
}
}
pub(super) async fn delete_script(
api: &Arc<Api>,
digest: String,
) -> Result<Response<ApiBody>, HttpError> {
let reply = control(api, move |c| Ok(c.send(&Control::DeleteScript { digest })?)).await?;
match reply {
Reply::Ok => Ok(json(
StatusCode::OK,
&serde_json::json!({ "deleted": true }),
)),
other => Ok(reply_to_response(other)),
}
}