Skip to main content

rbp_server/
lib.rs

1//! Unified Backend Server
2//!
3//! Combines analysis API routes and live game hosting routes
4//! into a single actix-web server.
5//!
6//! ## Submodules
7//!
8//! - [`analysis`] — Training result analysis and query interface
9//! - [`hosting`] — WebSocket game hosting infrastructure
10
11pub mod analysis;
12pub mod hosting;
13
14// Re-export main types (not handlers or Client, which conflict between modules)
15pub use analysis::API;
16pub use analysis::CLI;
17pub use analysis::Query;
18pub use hosting::Casino;
19pub use hosting::RoomHandle;
20
21use actix_cors::Cors;
22use actix_web::App;
23use actix_web::HttpResponse;
24use actix_web::HttpServer;
25use actix_web::Responder;
26use actix_web::middleware::Logger;
27use actix_web::web;
28use std::sync::Arc;
29use tokio_postgres::Client;
30
31async fn health(client: web::Data<Arc<Client>>) -> impl Responder {
32    match client
33        .execute("SELECT 1", &[])
34        .await
35        .inspect_err(|e| log::error!("health check failed: {}", e))
36    {
37        Ok(_) => HttpResponse::Ok().body("ok"),
38        Err(_) => HttpResponse::ServiceUnavailable().body("database unavailable"),
39    }
40}
41
42#[rustfmt::skip]
43pub async fn run() -> Result<(), std::io::Error> {
44    let client = rbp_database::db().await;
45    let api = web::Data::new(analysis::API::new(client.clone()));
46    let crypto = web::Data::new(rbp_auth::Crypto::from_env());
47    let casino = web::Data::new(hosting::Casino::new(client.clone()));
48    let client = web::Data::new(client);
49    log::info!("starting unified server");
50    HttpServer::new(move || {
51        App::new()
52            .wrap(Logger::new("%r %s %Ts"))
53            .wrap(
54                Cors::default()
55                    .allow_any_origin()
56                    .allow_any_method()
57                    .allow_any_header(),
58            )
59            .app_data(api.clone())
60            .app_data(casino.clone())
61            .app_data(crypto.clone())
62            .app_data(client.clone())
63            .route("/health", web::get().to(health))
64            .service(
65                web::scope("/auth")
66                    .route("/register", web::post().to(rbp_auth::register))
67                    .route("/logout", web::post().to(rbp_auth::logout))
68                    .route("/login", web::post().to(rbp_auth::login))
69                    .route("/me", web::get().to(rbp_auth::me)),
70            )
71            .service(
72                web::scope("/room")
73                    .route("/start", web::post().to(hosting::handlers::start))
74                    .route("/enter/{room_id}", web::get().to(hosting::handlers::enter))
75                    .route("/leave/{room_id}", web::post().to(hosting::handlers::leave)),
76            )
77            .service(
78                web::scope("/api")
79                    .route("/replace-obs", web::post().to(analysis::handlers::replace_obs))
80                    .route("/nbr-any-abs", web::post().to(analysis::handlers::nbr_any_wrt_abs))
81                    .route("/nbr-obs-abs", web::post().to(analysis::handlers::nbr_obs_wrt_abs))
82                    .route("/nbr-abs-abs", web::post().to(analysis::handlers::nbr_abs_wrt_abs))
83                    .route("/nbr-kfn-abs", web::post().to(analysis::handlers::kfn_wrt_abs))
84                    .route("/nbr-knn-abs", web::post().to(analysis::handlers::knn_wrt_abs))
85                    .route("/nbr-kgn-abs", web::post().to(analysis::handlers::kgn_wrt_abs))
86                    .route("/exp-wrt-str", web::post().to(analysis::handlers::exp_wrt_str))
87                    .route("/exp-wrt-abs", web::post().to(analysis::handlers::exp_wrt_abs))
88                    .route("/exp-wrt-obs", web::post().to(analysis::handlers::exp_wrt_obs))
89                    .route("/hst-wrt-abs", web::post().to(analysis::handlers::hst_wrt_abs))
90                    .route("/hst-wrt-obs", web::post().to(analysis::handlers::hst_wrt_obs))
91                    .route("/blueprint", web::post().to(analysis::handlers::blueprint)),
92            )
93    })
94    .workers(6)
95    .bind(std::env::var("BIND_ADDR").expect("BIND_ADDR must be set"))?
96    .run()
97    .await
98}