Skip to main content

freshblu_server/
lib.rs

1pub mod bus;
2pub mod config;
3pub mod handlers;
4pub mod hub;
5pub mod local_bus;
6pub mod metrics;
7pub mod mqtt;
8pub mod nats_bus;
9pub mod presence;
10pub mod ws;
11
12use axum::{
13    http::StatusCode,
14    response::{IntoResponse, Response},
15    routing::{delete, get, post, put},
16    Json, Router,
17};
18use freshblu_core::error::FreshBluError;
19use freshblu_store::DynStore;
20use serde_json::json;
21use tower_http::{cors::CorsLayer, trace::TraceLayer};
22
23pub use bus::DynBus;
24pub use config::ServerConfig;
25pub use hub::MessageHub;
26
27/// Newtype wrapper to implement IntoResponse for FreshBluError (orphan rule)
28pub struct ApiError(pub FreshBluError);
29
30impl From<FreshBluError> for ApiError {
31    fn from(e: FreshBluError) -> Self {
32        ApiError(e)
33    }
34}
35
36impl IntoResponse for ApiError {
37    fn into_response(self) -> Response {
38        let status = StatusCode::from_u16(self.0.http_status())
39            .unwrap_or(StatusCode::INTERNAL_SERVER_ERROR);
40        let body = json!({ "error": self.0.to_string() });
41        (status, Json(body)).into_response()
42    }
43}
44
45/// Shared application state
46#[derive(Clone)]
47pub struct AppState {
48    pub store: DynStore,
49    pub bus: DynBus,
50    pub config: ServerConfig,
51}
52
53pub fn build_router(state: AppState) -> Router {
54    let app = Router::new()
55        // Status
56        .route("/status", get(handlers::status::status))
57        // Authentication
58        .route("/authenticate", post(handlers::auth::authenticate))
59        // Device registration / auth
60        .route("/devices", post(handlers::devices::register))
61        .route("/devices/search", post(handlers::devices::search))
62        .route("/devices/:uuid", get(handlers::devices::get_device))
63        .route("/devices/:uuid", put(handlers::devices::update_device))
64        .route("/devices/:uuid", delete(handlers::devices::unregister))
65        // v2 aliases
66        .route("/v2/devices", post(handlers::devices::register))
67        .route("/v2/devices/:uuid", get(handlers::devices::get_device))
68        .route("/v2/devices/:uuid", put(handlers::devices::update_device))
69        .route("/v2/devices/:uuid", delete(handlers::devices::unregister))
70        .route("/v2/devices/search", post(handlers::devices::search))
71        // v3 aliases
72        .route("/v3/devices/:uuid", get(handlers::devices::get_device))
73        // Whoami
74        .route("/whoami", get(handlers::devices::whoami))
75        .route("/v2/whoami", get(handlers::devices::whoami))
76        // Messaging
77        .route("/messages", post(handlers::messages::send_message))
78        .route("/v2/messages", post(handlers::messages::send_message))
79        // My devices
80        .route("/mydevices", get(handlers::devices::my_devices))
81        // Subscriptions
82        .route(
83            "/devices/:uuid/subscriptions",
84            post(handlers::subscriptions::create_subscription),
85        )
86        .route(
87            "/devices/:uuid/subscriptions",
88            get(handlers::subscriptions::list_subscriptions),
89        )
90        .route(
91            "/devices/:uuid/subscriptions/:emitter_uuid/:sub_type",
92            delete(handlers::subscriptions::delete_subscription),
93        )
94        // Token management
95        .route(
96            "/devices/:uuid/tokens",
97            post(handlers::tokens::generate_token),
98        )
99        .route(
100            "/devices/:uuid/tokens/:token",
101            delete(handlers::tokens::revoke_token),
102        )
103        // WebSocket
104        .route("/ws", get(ws::ws_handler))
105        .route("/socket.io", get(ws::ws_handler))
106        // Metrics
107        .route("/metrics", get(metrics::metrics_handler))
108        // Middleware
109        .layer(CorsLayer::permissive())
110        .layer(TraceLayer::new_for_http())
111        .with_state(state);
112
113    app
114}