greentic_runner_host/http/
admin.rs

1use axum::Json;
2use axum::extract::State;
3use axum::http::StatusCode;
4use axum::response::IntoResponse;
5use serde_json::json;
6use time::format_description::well_known::Rfc3339;
7
8use crate::http::auth::AdminGuard;
9use crate::runner::ServerState;
10
11pub async fn status(AdminGuard: AdminGuard, State(state): State<ServerState>) -> impl IntoResponse {
12    let snapshot = state.active.snapshot();
13    let tenants = snapshot
14        .iter()
15        .map(|(tenant, runtime)| {
16            let pack = runtime.pack();
17            let metadata = pack.metadata();
18            let overlays = runtime
19                .overlays()
20                .into_iter()
21                .zip(runtime.overlay_digests().into_iter())
22                .map(|(overlay, digest)| {
23                    let meta = overlay.metadata();
24                    json!({
25                        "pack_id": meta.pack_id,
26                        "version": meta.version,
27                        "digest": digest,
28                    })
29                })
30                .collect::<Vec<_>>();
31            json!({
32                "tenant": tenant,
33                "pack_id": metadata.pack_id,
34                "version": metadata.version,
35                "digest": runtime.digest(),
36                "overlays": overlays,
37            })
38        })
39        .collect::<Vec<_>>();
40
41    let health = state.health.snapshot();
42    let last_reload = health.last_reload.and_then(|ts| ts.format(&Rfc3339).ok());
43
44    Json(json!({
45        "tenants": tenants,
46        "active": snapshot.len(),
47        "last_reload": last_reload,
48        "last_error": health.last_error,
49    }))
50}
51
52pub async fn reload(AdminGuard: AdminGuard, State(state): State<ServerState>) -> impl IntoResponse {
53    if let Some(handle) = &state.reload {
54        match handle.trigger().await {
55            Ok(()) => {
56                tracing::info!("pack.reload.requested");
57                (
58                    StatusCode::ACCEPTED,
59                    Json(json!({ "status": "reload requested" })),
60                )
61            }
62            Err(err) => {
63                tracing::warn!(error = %err, "reload trigger failed");
64                (
65                    StatusCode::INTERNAL_SERVER_ERROR,
66                    Json(json!({ "error": err.to_string() })),
67                )
68            }
69        }
70    } else {
71        (
72            StatusCode::NOT_IMPLEMENTED,
73            Json(json!({ "error": "reload handle unavailable" })),
74        )
75    }
76}