manta_server/server/handlers/
node.rs

1//! Node CRUD handlers.
2
3use axum::{
4  Json,
5  extract::{Path, Query},
6  http::StatusCode,
7  response::IntoResponse,
8};
9
10use super::{ErrorResponse, RequestCtx, SiteHeader, to_handler_error};
11use crate::service;
12
13// ---------------------------------------------------------------------------
14// GET /api/v1/nodes
15// ---------------------------------------------------------------------------
16
17pub use manta_shared::types::api::queries::NodesQuery;
18
19/// GET /nodes — fetch node details for a given xname expression.
20#[utoipa::path(get, path = "/nodes", tag = "nodes",
21  params(NodesQuery, SiteHeader),
22  security(("bearerAuth" = [])),
23  responses(
24    (status = 200, description = "Node details",  body = Vec<manta_shared::types::dto::NodeDetails>),
25    (status = 400, description = "Bad request",   body = ErrorResponse),
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_nodes(
32  ctx: RequestCtx,
33  Query(q): Query<NodesQuery>,
34) -> Result<impl IntoResponse, (StatusCode, Json<ErrorResponse>)> {
35  let infra = ctx.infra();
36
37  let params = service::node::GetNodesParams {
38    host_expression: q.xname,
39    include_siblings: q.include_siblings.unwrap_or(false),
40    status_filter: q.status,
41  };
42
43  let nodes = service::node::get_nodes(&infra, &ctx.token, &params)
44    .await
45    .map_err(to_handler_error)?;
46
47  Ok(Json(nodes))
48}
49
50// ---------------------------------------------------------------------------
51// DELETE /api/v1/nodes/{id}
52// ---------------------------------------------------------------------------
53
54/// DELETE /nodes/{id} — remove a node from HSM by xname or NID.
55#[utoipa::path(delete, path = "/nodes/{id}", tag = "nodes",
56  params(("id" = String, Path, description = "Node xname or NID"), SiteHeader),
57  security(("bearerAuth" = [])),
58  responses(
59    (status = 204, description = "Node removed"),
60    (status = 401, description = "Unauthorized", body = ErrorResponse),
61    (status = 404, description = "Not found",    body = ErrorResponse),
62    (status = 500, description = "Internal error", body = ErrorResponse),
63  )
64)]
65#[tracing::instrument(skip_all)]
66pub async fn delete_node(
67  ctx: RequestCtx,
68  Path(id): Path<String>,
69) -> Result<impl IntoResponse, (StatusCode, Json<ErrorResponse>)> {
70  tracing::info!("delete_node id={}", id);
71  let infra = ctx.infra();
72
73  service::node::delete_node(&infra, &ctx.token, &id)
74    .await
75    .map_err(to_handler_error)?;
76
77  Ok(StatusCode::NO_CONTENT)
78}
79
80// ---------------------------------------------------------------------------
81// POST /api/v1/nodes
82// ---------------------------------------------------------------------------
83
84pub use manta_shared::types::api::node::AddNodeRequest;
85
86/// POST /nodes — register a new node in HSM and add it to a group.
87#[utoipa::path(post, path = "/nodes", tag = "nodes",
88  params(SiteHeader),
89  request_body = AddNodeRequest,
90  security(("bearerAuth" = [])),
91  responses(
92    (status = 201, description = "Node registered",  body = manta_shared::types::api::responses::AddNodeResponse),
93    (status = 400, description = "Bad request",      body = ErrorResponse),
94    (status = 401, description = "Unauthorized",     body = ErrorResponse),
95    (status = 500, description = "Internal error",   body = ErrorResponse),
96  )
97)]
98#[tracing::instrument(skip_all)]
99pub async fn add_node(
100  ctx: RequestCtx,
101  Json(body): Json<AddNodeRequest>,
102) -> Result<impl IntoResponse, (StatusCode, Json<ErrorResponse>)> {
103  tracing::info!("add_node id={} group={}", body.id, body.group);
104  let infra = ctx.infra();
105
106  service::node::add_node(
107    &infra,
108    &ctx.token,
109    &body.id,
110    &body.group,
111    body.enabled,
112    body.arch,
113    None, // hardware_file_path not applicable via HTTP
114  )
115  .await
116  .map_err(to_handler_error)?;
117
118  Ok((
119    StatusCode::CREATED,
120    Json(serde_json::json!({ "id": body.id })),
121  ))
122}