manta_server/server/handlers/
configuration.rs

1//! GET/DELETE /api/v1/configurations.
2
3use axum::{Json, extract::Query, http::StatusCode, response::IntoResponse};
4
5use super::{
6  ErrorResponse, RequestCtx, SiteHeader, parse_iso_datetime, serialize_or_500,
7  to_handler_error,
8};
9use crate::service;
10use manta_shared::types::api::configuration_analysis::ConfigurationAnalysis;
11
12// ---------------------------------------------------------------------------
13// GET /api/v1/configurations
14// ---------------------------------------------------------------------------
15
16pub use manta_shared::types::api::queries::{
17  ConfigurationQuery, DeleteConfigurationsQuery,
18};
19
20/// GET /configurations — list CFS configurations with optional
21/// name/pattern/group filters. Every row carries the full
22/// `CfsConfigurationResponse` plus a `safe_to_delete` verdict
23/// derived from CFS components only (a configuration is unsafe iff
24/// some component lists it as `desired_config`).
25#[utoipa::path(get, path = "/configurations", tag = "configurations",
26  params(ConfigurationQuery, SiteHeader),
27  security(("bearerAuth" = [])),
28  responses(
29    (status = 200, description = "Configurations with components-only safe_to_delete verdict",
30     body = Vec<ConfigurationAnalysis>),
31    (status = 401, description = "Unauthorized",           body = ErrorResponse),
32    (status = 500, description = "Internal error",         body = ErrorResponse),
33  )
34)]
35#[tracing::instrument(skip_all)]
36pub async fn get_configurations(
37  ctx: RequestCtx,
38  Query(q): Query<ConfigurationQuery>,
39) -> Result<impl IntoResponse, (StatusCode, Json<ErrorResponse>)> {
40  let infra = ctx.infra();
41
42  let params = service::configuration::GetConfigurationParams {
43    name: q.name,
44    pattern: q.pattern,
45    group_name: q.hsm_group,
46    settings_hsm_group_name: None,
47    since: None,
48    until: None,
49    limit: q.limit,
50  };
51
52  let rows = service::configuration::get_configurations_with_analysis(
53    &infra, &ctx.token, &params,
54  )
55  .await
56  .map_err(to_handler_error)?;
57
58  Ok(Json(rows))
59}
60
61// ---------------------------------------------------------------------------
62// DELETE /api/v1/configurations — with ?pattern=...&since=...&until=...&dry_run=true
63// ---------------------------------------------------------------------------
64
65/// `DELETE /api/v1/configurations` — delete CFS configurations and all derived artifacts.
66#[utoipa::path(delete, path = "/configurations", tag = "configurations",
67  params(DeleteConfigurationsQuery, SiteHeader),
68  security(("bearerAuth" = [])),
69  responses(
70    // dry_run/real result union — kept as Value until the union shape is formalised
71    (status = 200, description = "Configurations deleted or preview", body = serde_json::Value),
72    (status = 400, description = "Bad request",                       body = ErrorResponse),
73    (status = 401, description = "Unauthorized",                      body = ErrorResponse),
74    (status = 500, description = "Internal error",                    body = ErrorResponse),
75  )
76)]
77#[tracing::instrument(skip_all)]
78pub async fn delete_configurations(
79  ctx: RequestCtx,
80  Query(q): Query<DeleteConfigurationsQuery>,
81) -> Result<impl IntoResponse, (StatusCode, Json<ErrorResponse>)> {
82  tracing::info!("delete_configurations dry_run={}", q.dry_run);
83  let infra = ctx.infra();
84
85  let since = q
86    .since
87    .as_deref()
88    .map(|s| parse_iso_datetime("since", s))
89    .transpose()?;
90  let until = q
91    .until
92    .as_deref()
93    .map(|s| parse_iso_datetime("until", s))
94    .transpose()?;
95
96  let candidates = service::configuration::get_deletion_candidates(
97    &infra,
98    &ctx.token,
99    None,
100    q.pattern.as_deref(),
101    since,
102    until,
103  )
104  .await
105  .map_err(to_handler_error)?;
106
107  if q.dry_run {
108    return Ok((StatusCode::OK, Json(serialize_or_500(&candidates)?)));
109  }
110
111  service::configuration::delete_configurations_and_derivatives(
112    &infra,
113    &ctx.token,
114    &candidates,
115  )
116  .await
117  .map_err(to_handler_error)?;
118
119  Ok((
120    StatusCode::OK,
121    Json(serde_json::json!({
122      "deleted_configurations": candidates.configuration_names,
123      "deleted_images": candidates.image_ids,
124    })),
125  ))
126}
127
128// ===========================================================================
129// BATCH A — MEDIUM-COMPLEXITY WRITE ENDPOINTS
130// ===========================================================================