manta_server/server/handlers/
image.rs

1//! IMS image handlers.
2//!
3//! - `GET    /api/v1/images` → [`get_images`] —
4//!   wraps `service::image::get_images`. Sorts by creation time.
5//! - `DELETE /api/v1/images` → [`delete_images`] —
6//!   wraps `service::image::delete_images`; with `?dry_run=true`
7//!   returns the validation result without deleting.
8
9use axum::{Json, extract::Query, http::StatusCode, response::IntoResponse};
10
11use super::{ErrorResponse, RequestCtx, SiteHeader, to_handler_error};
12use crate::service;
13
14// ---------------------------------------------------------------------------
15// GET /api/v1/images
16// ---------------------------------------------------------------------------
17
18pub use manta_shared::types::api::queries::{DeleteImagesQuery, ImageQuery};
19
20/// GET /images — list IMS images sorted by creation time.
21#[utoipa::path(get, path = "/images", tag = "images",
22  params(ImageQuery, SiteHeader),
23  security(("bearerAuth" = [])),
24  responses(
25    (status = 200, description = "List of images", body = Vec<serde_json::Value>),
26    (status = 401, description = "Unauthorized",   body = ErrorResponse),
27    (status = 500, description = "Internal error", body = ErrorResponse),
28  )
29)]
30#[tracing::instrument(skip_all)]
31pub async fn get_images(
32  ctx: RequestCtx,
33  Query(q): Query<ImageQuery>,
34) -> Result<impl IntoResponse, (StatusCode, Json<ErrorResponse>)> {
35  let infra = ctx.infra();
36
37  let params = service::image::GetImagesParams {
38    id: q.id,
39    pattern: q.pattern,
40    limit: q.limit,
41  };
42
43  let images = service::image::get_images(&infra, &ctx.token, &params)
44    .await
45    .map_err(to_handler_error)?;
46
47  Ok(Json(images))
48}
49
50// ---------------------------------------------------------------------------
51// DELETE /api/v1/images — with ?ids=id1,id2&dry_run=true
52// ---------------------------------------------------------------------------
53
54/// `DELETE /api/v1/images` — delete IMS images by ID; validates only when `dry_run=true`.
55#[utoipa::path(delete, path = "/images", tag = "images",
56  params(DeleteImagesQuery, SiteHeader),
57  security(("bearerAuth" = [])),
58  responses(
59    // dry_run/real result union — kept as Value until the union shape is formalised
60    (status = 200, description = "Images deleted or validation result", body = serde_json::Value),
61    (status = 400, description = "Bad request",                         body = ErrorResponse),
62    (status = 401, description = "Unauthorized",                        body = ErrorResponse),
63    (status = 500, description = "Internal error",                      body = ErrorResponse),
64  )
65)]
66#[tracing::instrument(skip_all)]
67pub async fn delete_images(
68  ctx: RequestCtx,
69  Query(q): Query<DeleteImagesQuery>,
70) -> Result<impl IntoResponse, (StatusCode, Json<ErrorResponse>)> {
71  tracing::info!("delete_images ids={} dry_run={}", q.ids, q.dry_run);
72  let infra = ctx.infra();
73
74  let id_strings: Vec<String> =
75    q.ids.split(',').map(|s| s.trim().to_string()).collect();
76  let id_refs: Vec<&str> =
77    id_strings.iter().map(std::string::String::as_str).collect();
78
79  if q.dry_run {
80    service::image::validate_image_deletion(&infra, &ctx.token, &id_refs, None)
81      .await
82      .map_err(to_handler_error)?;
83    return Ok((
84      StatusCode::OK,
85      Json(serde_json::json!({ "validated_ids": id_strings })),
86    ));
87  }
88
89  let deleted =
90    service::image::delete_images(&infra, &ctx.token, &id_refs, None)
91      .await
92      .map_err(to_handler_error)?;
93
94  Ok((
95    StatusCode::OK,
96    Json(serde_json::json!({ "deleted": deleted })),
97  ))
98}