posemesh_compute_node/
http.rs1use axum::{
2 http::StatusCode,
3 response::IntoResponse,
4 routing::{get, post},
5 Json, Router,
6};
7use serde::{Deserialize, Serialize};
8use tracing::{info, warn};
9
10use crate::dds::persist;
11
12async fn health() -> impl IntoResponse {
13 StatusCode::OK
14}
15
16#[derive(Debug, Deserialize)]
17struct RegistrationRequest {
18 id: String,
19 secret: String,
20 organization_id: Option<String>,
21 #[serde(rename = "lighthouses_in_domains")]
22 _lighthouses_in_domains: Option<serde_json::Value>,
23 #[serde(rename = "domains")]
24 _domains: Option<serde_json::Value>,
25}
26
27#[derive(Debug, Serialize)]
28struct RegistrationResponse {
29 ok: bool,
30}
31
32#[derive(Debug)]
33enum RegistrationError {
34 Unprocessable(&'static str),
35 Forbidden(&'static str),
36 Conflict(&'static str),
37}
38
39impl IntoResponse for RegistrationError {
40 fn into_response(self) -> axum::response::Response {
41 let (status, msg) = match self {
42 RegistrationError::Unprocessable(msg) => (StatusCode::UNPROCESSABLE_ENTITY, msg),
43 RegistrationError::Forbidden(msg) => (StatusCode::FORBIDDEN, msg),
44 RegistrationError::Conflict(msg) => (StatusCode::CONFLICT, msg),
45 };
46 (status, msg).into_response()
47 }
48}
49
50async fn register(
51 Json(payload): Json<RegistrationRequest>,
52) -> Result<Json<RegistrationResponse>, RegistrationError> {
53 if payload.id.trim().is_empty() {
54 return Err(RegistrationError::Unprocessable("missing id"));
55 }
56 if payload.secret.trim().is_empty() {
57 return Err(RegistrationError::Unprocessable("missing secret"));
58 }
59 if payload.secret.len() > 4096 {
60 return Err(RegistrationError::Forbidden("secret too large"));
61 }
62
63 let secret_len = payload.secret.len();
64 let org = payload.organization_id.as_deref().unwrap_or("");
65 info!(
66 id = %payload.id,
67 org = %org,
68 secret_len,
69 "Received registration callback"
70 );
71
72 persist::write_node_secret(&payload.secret)
73 .map_err(|_| RegistrationError::Conflict("persist failed"))?;
74
75 match persist::read_node_secret() {
76 Ok(Some(_)) => {}
77 Ok(None) => {
78 warn!(
79 id = %payload.id,
80 "persisted secret missing after write"
81 );
82 return Err(RegistrationError::Conflict("persist verify failed"));
83 }
84 Err(_) => {
85 return Err(RegistrationError::Conflict("persist verify failed"));
86 }
87 }
88
89 Ok(Json(RegistrationResponse { ok: true }))
90}
91
92pub fn router() -> Router {
94 Router::new()
95 .route("/health", get(health))
96 .route("/internal/v1/registrations", post(register))
97}