Skip to main content

manta_server/server/handlers/
template.rs

1//! BOS session-template handlers.
2//!
3//! - `GET  /v2/templates`                       → [`get_templates`] —
4//!   wraps `service::template::get_templates`.
5//! - `POST /v2/templates/{name}/sessions`       → [`post_template_session`] —
6//!   wraps `service::template::create_template_session`; starts a
7//!   BOS session from the named template.
8
9use axum::{
10  Json,
11  extract::{Path, Query},
12  http::StatusCode,
13  response::IntoResponse,
14};
15
16use super::{
17  ErrorResponse, RequestCtx, SiteHeader, serialize_or_500, to_handler_error,
18};
19use crate::server::audit::AuditNote;
20use crate::service;
21
22// ---------------------------------------------------------------------------
23// GET /v2/templates
24// ---------------------------------------------------------------------------
25
26pub use manta_shared::types::api::queries::TemplateQuery;
27
28/// GET /templates — list BOS session templates with optional filters.
29#[utoipa::path(get, path = "/templates", tag = "templates",
30  params(TemplateQuery, SiteHeader),
31  security(("bearerAuth" = [])),
32  responses(
33    // BosSessionTemplate lives in manta-backend-dispatcher (third-party,
34    // no ToSchema) — kept as Value until upstream derives it.
35    (status = 200, description = "List of session templates", body = serde_json::Value),
36    (status = 401, description = "Unauthorized",              body = ErrorResponse),
37    (status = 500, description = "Internal error",            body = ErrorResponse),
38  )
39)]
40#[tracing::instrument(skip_all)]
41pub async fn get_templates(
42  ctx: RequestCtx,
43  Query(q): Query<TemplateQuery>,
44) -> Result<impl IntoResponse, (StatusCode, Json<ErrorResponse>)> {
45  let infra = ctx.infra();
46
47  let params = service::template::GetTemplateParams {
48    name: q.name,
49    group_name: q.hsm_group,
50    settings_group_name: None,
51    limit: q.limit,
52  };
53
54  let templates = service::template::get_templates(&infra, &ctx.token, &params)
55    .await
56    .map_err(to_handler_error)?;
57
58  Ok(Json(templates))
59}
60
61// ---------------------------------------------------------------------------
62// POST /v2/templates/{name}/sessions — Create BOS session from template
63// ---------------------------------------------------------------------------
64
65pub use manta_shared::types::api::template::{
66  BosOperation, PostTemplateSessionRequest,
67};
68
69/// `POST /v2/templates/{name}/sessions` — create a BOS session from a session template.
70#[utoipa::path(post, path = "/templates/{name}/sessions", tag = "templates",
71  params(("name" = String, Path, description = "Template name"), SiteHeader),
72  request_body = PostTemplateSessionRequest,
73  security(("bearerAuth" = [])),
74  responses(
75    // dry_run/real result union — kept as Value until the union shape is formalised
76    (status = 200, description = "Dry run preview",  body = serde_json::Value),
77    // BosSession lives in manta-backend-dispatcher (third-party, no
78    // ToSchema) — kept as Value until upstream derives it.
79    (status = 201, description = "Session created",  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 post_template_session(
87  ctx: RequestCtx,
88  note: AuditNote,
89  Path(name): Path<String>,
90  Json(body): Json<PostTemplateSessionRequest>,
91) -> Result<impl IntoResponse, (StatusCode, Json<ErrorResponse>)> {
92  note
93    .action("template.session.create")
94    .target("template", &name)
95    .target("hosts", &body.limit)
96    .targets("session", body.session_name.as_deref())
97    .detail("operation", body.operation)
98    .detail("include_disabled", body.include_disabled)
99    .dry_run(body.dry_run);
100
101  tracing::info!(
102    "post_template_session template={} op={:?} dry_run={}",
103    name,
104    body.operation,
105    body.dry_run
106  );
107  let infra = ctx.infra();
108
109  let params = service::template::ApplyTemplateParams {
110    bos_session_name: body.session_name,
111    bos_sessiontemplate_name: name,
112    bos_session_operation: body.operation.as_str().to_string(),
113    limit: body.limit,
114    include_disabled: body.include_disabled,
115  };
116
117  let (bos_session, _) =
118    service::template::validate_and_prepare_template_session(
119      &infra, &ctx.token, &params,
120    )
121    .await
122    .map_err(to_handler_error)?;
123
124  if body.dry_run {
125    return Ok((StatusCode::OK, Json(serialize_or_500(&bos_session)?)));
126  }
127
128  let created =
129    service::template::create_bos_session(&infra, &ctx.token, bos_session)
130      .await
131      .map_err(to_handler_error)?;
132  if let Some(session) = &created.name {
133    note.produced("session", session);
134  }
135
136  Ok((StatusCode::CREATED, Json(serialize_or_500(&created)?)))
137}