Skip to main content

tuitbot_server/
lib.rs

1//! Tuitbot HTTP API server.
2//!
3//! Exposes `tuitbot-core`'s storage layer as a REST API with read + write
4//! endpoints, local bearer-token auth, and a WebSocket for real-time events.
5
6pub mod auth;
7pub mod error;
8pub mod routes;
9pub mod state;
10pub mod ws;
11
12use std::sync::Arc;
13
14use axum::middleware;
15use axum::routing::{delete, get, post};
16use axum::Router;
17use tower_http::cors::CorsLayer;
18use tower_http::trace::TraceLayer;
19
20use crate::state::AppState;
21
22/// Build the complete axum router with all API routes and middleware.
23pub fn build_router(state: Arc<AppState>) -> Router {
24    let api = Router::new()
25        .route("/health", get(routes::health::health))
26        // Analytics
27        .route("/analytics/summary", get(routes::analytics::summary))
28        .route("/analytics/followers", get(routes::analytics::followers))
29        .route(
30            "/analytics/performance",
31            get(routes::analytics::performance),
32        )
33        .route("/analytics/topics", get(routes::analytics::topics))
34        .route(
35            "/analytics/recent-performance",
36            get(routes::analytics::recent_performance),
37        )
38        // Approval
39        .route("/approval", get(routes::approval::list_pending))
40        .route(
41            "/approval/{id}/approve",
42            post(routes::approval::approve_item),
43        )
44        .route("/approval/{id}/reject", post(routes::approval::reject_item))
45        .route("/approval/approve-all", post(routes::approval::approve_all))
46        // Activity
47        .route("/activity", get(routes::activity::list_activity))
48        .route(
49            "/activity/rate-limits",
50            get(routes::activity::rate_limit_usage),
51        )
52        // Replies
53        .route("/replies", get(routes::replies::list_replies))
54        // Content
55        .route(
56            "/content/tweets",
57            get(routes::content::list_tweets).post(routes::content::compose_tweet),
58        )
59        .route(
60            "/content/threads",
61            get(routes::content::list_threads).post(routes::content::compose_thread),
62        )
63        // Targets
64        .route(
65            "/targets",
66            get(routes::targets::list_targets).post(routes::targets::add_target),
67        )
68        .route(
69            "/targets/{username}",
70            delete(routes::targets::remove_target),
71        )
72        // Settings
73        .route(
74            "/settings",
75            get(routes::settings::get_settings).patch(routes::settings::patch_settings),
76        )
77        // Runtime
78        .route("/runtime/status", get(routes::runtime::status))
79        .route("/runtime/start", post(routes::runtime::start))
80        .route("/runtime/stop", post(routes::runtime::stop))
81        // WebSocket
82        .route("/ws", get(ws::ws_handler))
83        // Auth middleware — applied to all routes; health is exempted internally.
84        .layer(middleware::from_fn_with_state(
85            state.clone(),
86            auth::auth_middleware,
87        ));
88
89    Router::new()
90        .nest("/api", api)
91        .layer(CorsLayer::permissive())
92        .layer(TraceLayer::new_for_http())
93        .with_state(state)
94}