alopex_server/http/
admin.rs1use std::net::SocketAddr;
2use std::sync::Arc;
3
4use axum::extract::{ConnectInfo, Extension};
5use axum::http::StatusCode;
6use axum::middleware;
7use axum::response::{IntoResponse, Response};
8use axum::{Json, Router};
9use serde::Serialize;
10
11use crate::ops::status::StatusReporter;
12use crate::server::ServerState;
13
14#[derive(Serialize)]
15struct StatusResponse {
16 status: &'static str,
17}
18
19pub fn router(state: Arc<ServerState>) -> Router {
20 Router::new()
21 .route("/healthz", axum::routing::get(healthz))
22 .route("/status", axum::routing::get(status))
23 .route("/metrics", axum::routing::get(metrics))
24 .layer(middleware::from_fn(allowlist_middleware))
25 .layer(axum::Extension(state))
26}
27
28async fn healthz() -> impl IntoResponse {
29 StatusCode::OK
30}
31
32async fn status() -> impl IntoResponse {
33 Json(StatusResponse { status: "ok" })
34}
35
36async fn metrics(Extension(state): Extension<Arc<ServerState>>) -> Response {
37 if !state.config.metrics_enabled {
38 return StatusCode::NOT_FOUND.into_response();
39 }
40 let reporter = StatusReporter::new(state.lifecycle_state.clone(), state.recovery_info.clone());
41 reporter.refresh_metrics(&state.metrics);
42 match state.metrics.expose_prometheus() {
43 Ok(body) => (
44 StatusCode::OK,
45 [(
46 axum::http::header::CONTENT_TYPE,
47 "text/plain; version=0.0.4",
48 )],
49 body,
50 )
51 .into_response(),
52 Err(err) => (StatusCode::INTERNAL_SERVER_ERROR, err.to_string()).into_response(),
53 }
54}
55
56async fn allowlist_middleware<B>(
57 Extension(state): Extension<Arc<ServerState>>,
58 req: axum::http::Request<B>,
59 next: middleware::Next<B>,
60) -> Response {
61 if state.config.admin_bind.ip().is_loopback() {
62 return next.run(req).await;
63 }
64 let Some(addr) = req.extensions().get::<ConnectInfo<SocketAddr>>() else {
65 return StatusCode::FORBIDDEN.into_response();
66 };
67 let ip = addr.ip();
68 if ip.is_loopback() || state.config.admin_allowlist.contains(&ip) {
69 next.run(req).await
70 } else {
71 StatusCode::FORBIDDEN.into_response()
72 }
73}