manta_server/server/handlers/
image.rs1use axum::{Json, extract::Query, http::StatusCode, response::IntoResponse};
12
13use super::{
14 ErrorResponse, RequestCtx, SiteHeader, parse_iso_datetime, to_handler_error,
15};
16use crate::server::audit::AuditNote;
17use crate::service;
18
19pub use manta_shared::types::api::queries::{DeleteImagesQuery, ImageQuery};
24
25#[utoipa::path(get, path = "/images", tag = "images",
27 params(ImageQuery, SiteHeader),
28 security(("bearerAuth" = [])),
29 responses(
30 (status = 200, description = "List of images", body = Vec<serde_json::Value>),
31 (status = 400, description = "Malformed since/until, or since after until", body = ErrorResponse),
32 (status = 401, description = "Unauthorized", body = ErrorResponse),
33 (status = 500, description = "Internal error", body = ErrorResponse),
34 )
35)]
36#[tracing::instrument(skip_all)]
37pub async fn get_images(
38 ctx: RequestCtx,
39 Query(q): Query<ImageQuery>,
40) -> Result<impl IntoResponse, (StatusCode, Json<ErrorResponse>)> {
41 let infra = ctx.infra();
42
43 let since = q
44 .since
45 .as_deref()
46 .map(|s| parse_iso_datetime("since", s))
47 .transpose()?;
48 let until = q
49 .until
50 .as_deref()
51 .map(|s| parse_iso_datetime("until", s))
52 .transpose()?;
53
54 let params = service::image::GetImagesParams {
55 id: q.id,
56 pattern: q.pattern,
57 since,
58 until,
59 limit: q.limit,
60 };
61
62 let images = service::image::get_images(&infra, &ctx.token, ¶ms)
63 .await
64 .map_err(to_handler_error)?;
65
66 Ok(Json(images))
67}
68
69#[utoipa::path(delete, path = "/images", tag = "images",
75 params(DeleteImagesQuery, SiteHeader),
76 security(("bearerAuth" = [])),
77 responses(
78 (status = 200, description = "Images deleted or validation result", body = serde_json::Value),
80 (status = 400, description = "Bad request", body = ErrorResponse),
81 (status = 401, description = "Unauthorized", body = ErrorResponse),
82 (status = 500, description = "Internal error", body = ErrorResponse),
83 )
84)]
85#[tracing::instrument(skip_all)]
86pub async fn delete_images(
87 ctx: RequestCtx,
88 note: AuditNote,
89 Query(q): Query<DeleteImagesQuery>,
90) -> Result<impl IntoResponse, (StatusCode, Json<ErrorResponse>)> {
91 note
92 .action("image.delete")
93 .targets(
94 "image",
95 q.ids.split(',').map(str::trim).filter(|s| !s.is_empty()),
96 )
97 .dry_run(q.dry_run);
98
99 tracing::info!("delete_images ids={} dry_run={}", q.ids, q.dry_run);
100 let infra = ctx.infra();
101
102 let id_strings: Vec<String> =
103 q.ids.split(',').map(|s| s.trim().to_string()).collect();
104 let id_refs: Vec<&str> =
105 id_strings.iter().map(std::string::String::as_str).collect();
106
107 if q.dry_run {
108 service::image::validate_image_deletion(&infra, &ctx.token, &id_refs, None)
109 .await
110 .map_err(to_handler_error)?;
111 return Ok((
112 StatusCode::OK,
113 Json(serde_json::json!({ "validated_ids": id_strings })),
114 ));
115 }
116
117 let deleted =
118 service::image::delete_images(&infra, &ctx.token, &id_refs, None)
119 .await
120 .map_err(to_handler_error)?;
121
122 Ok((
123 StatusCode::OK,
124 Json(serde_json::json!({ "deleted": deleted })),
125 ))
126}