enigma_node_registry/
routes.rs1use std::sync::Arc;
2
3use axum::extract::{Path, State};
4use axum::http::StatusCode;
5use axum::response::{IntoResponse, Response};
6use axum::routing::{get, post};
7use axum::Json;
8use enigma_node_types::{
9 CheckUserResponse, NodesPayload, Presence, RegisterRequest, RegisterResponse, ResolveResponse,
10 SyncRequest, SyncResponse, UserId,
11};
12use serde::{Deserialize, Serialize};
13
14use crate::error::{EnigmaNodeRegistryError, Result};
15use crate::store::Store;
16
17#[derive(Clone)]
18pub struct AppState {
19 pub store: Arc<Store>,
20}
21
22#[derive(Debug, Serialize, Deserialize)]
23#[serde(deny_unknown_fields)]
24struct OkResponse {
25 ok: bool,
26}
27
28#[derive(Debug, Serialize, Deserialize)]
29#[serde(deny_unknown_fields)]
30struct MergedResponse {
31 merged: usize,
32}
33
34#[derive(Debug, Serialize)]
35struct ErrorBody {
36 error: String,
37}
38
39pub fn build_router(state: AppState) -> axum::Router {
40 axum::Router::new()
41 .route("/register", post(register))
42 .route("/resolve/:user_id_hex", get(resolve))
43 .route("/check_user/:user_id_hex", get(check_user))
44 .route("/announce", post(announce))
45 .route("/sync", post(sync))
46 .route("/nodes", get(list_nodes).post(add_nodes))
47 .with_state(state)
48}
49
50async fn register(
51 State(state): State<AppState>,
52 payload: std::result::Result<Json<RegisterRequest>, axum::extract::rejection::JsonRejection>,
53) -> std::result::Result<impl IntoResponse, AppError> {
54 let Json(body) = payload?;
55 state.store.register(body.identity).await?;
56 Ok((StatusCode::OK, Json(RegisterResponse { ok: true })))
57}
58
59async fn resolve(
60 State(state): State<AppState>,
61 Path(user_id_hex): Path<String>,
62) -> std::result::Result<impl IntoResponse, AppError> {
63 let user_id = parse_user_id(&user_id_hex)?;
64 let identity = state.store.resolve(&user_id).await?;
65 Ok((StatusCode::OK, Json(ResolveResponse { identity })))
66}
67
68async fn check_user(
69 State(state): State<AppState>,
70 Path(user_id_hex): Path<String>,
71) -> std::result::Result<impl IntoResponse, AppError> {
72 let user_id = parse_user_id(&user_id_hex)?;
73 let exists = state.store.check_user(&user_id).await?;
74 Ok((StatusCode::OK, Json(CheckUserResponse { exists })))
75}
76
77async fn announce(
78 State(state): State<AppState>,
79 payload: std::result::Result<Json<Presence>, axum::extract::rejection::JsonRejection>,
80) -> std::result::Result<impl IntoResponse, AppError> {
81 let Json(body) = payload?;
82 state.store.announce(body).await?;
83 Ok((StatusCode::OK, Json(OkResponse { ok: true })))
84}
85
86async fn sync(
87 State(state): State<AppState>,
88 payload: std::result::Result<Json<SyncRequest>, axum::extract::rejection::JsonRejection>,
89) -> std::result::Result<impl IntoResponse, AppError> {
90 let Json(body) = payload?;
91 let merged = state.store.sync_identities(body.identities).await?;
92 Ok((StatusCode::OK, Json(SyncResponse { merged })))
93}
94
95async fn list_nodes(
96 State(state): State<AppState>,
97) -> std::result::Result<impl IntoResponse, AppError> {
98 let nodes = state.store.list_nodes().await?;
99 Ok((StatusCode::OK, Json(NodesPayload { nodes })))
100}
101
102async fn add_nodes(
103 State(state): State<AppState>,
104 payload: std::result::Result<Json<NodesPayload>, axum::extract::rejection::JsonRejection>,
105) -> std::result::Result<impl IntoResponse, AppError> {
106 let Json(body) = payload?;
107 let merged = state.store.add_nodes(body.nodes).await?;
108 Ok((StatusCode::OK, Json(MergedResponse { merged })))
109}
110
111fn parse_user_id(user_id_hex: &str) -> Result<UserId> {
112 UserId::from_hex(user_id_hex).map_err(|_| EnigmaNodeRegistryError::InvalidInput("user_id"))
113}
114
115#[derive(Debug)]
116pub struct AppError(pub EnigmaNodeRegistryError);
117
118impl IntoResponse for AppError {
119 fn into_response(self) -> Response {
120 let status = match self.0 {
121 EnigmaNodeRegistryError::InvalidInput(_) => StatusCode::BAD_REQUEST,
122 EnigmaNodeRegistryError::JsonError => StatusCode::BAD_REQUEST,
123 EnigmaNodeRegistryError::Conflict => StatusCode::CONFLICT,
124 EnigmaNodeRegistryError::NotFound => StatusCode::NOT_FOUND,
125 EnigmaNodeRegistryError::Transport => StatusCode::BAD_GATEWAY,
126 EnigmaNodeRegistryError::Internal => StatusCode::INTERNAL_SERVER_ERROR,
127 };
128 let body = Json(ErrorBody {
129 error: self.0.to_string(),
130 });
131 (status, body).into_response()
132 }
133}
134
135impl From<EnigmaNodeRegistryError> for AppError {
136 fn from(err: EnigmaNodeRegistryError) -> Self {
137 AppError(err)
138 }
139}
140
141impl From<axum::extract::rejection::JsonRejection> for AppError {
142 fn from(_: axum::extract::rejection::JsonRejection) -> Self {
143 AppError(EnigmaNodeRegistryError::JsonError)
144 }
145}