mod game_management; mod user_management; use axum::{http::StatusCode, Json}; use futures::Future; pub use game_management::*; use typeshare::typeshare; pub use user_management::*; use result_extended::ResultExt; use serde::{Deserialize, Serialize}; use crate::{ core::Core, types::{AppError, FatalError}, }; #[derive(Clone, Debug, PartialEq, Deserialize, Serialize)] pub struct HealthCheck { pub ok: bool, } pub async fn wrap_handler<F, A, Fut>(f: F) -> (StatusCode, Json<Option<A>>) where F: FnOnce() -> Fut, Fut: Future<Output = ResultExt<A, AppError, FatalError>>, { match f().await { ResultExt::Ok(val) => (StatusCode::OK, Json(Some(val))), ResultExt::Err(AppError::BadRequest) => (StatusCode::BAD_REQUEST, Json(None)), ResultExt::Err(AppError::CouldNotCreateObject) => (StatusCode::BAD_REQUEST, Json(None)), ResultExt::Err(AppError::NotFound(_)) => (StatusCode::NOT_FOUND, Json(None)), ResultExt::Err(AppError::Inaccessible(_)) => (StatusCode::NOT_FOUND, Json(None)), ResultExt::Err(AppError::PermissionDenied) => (StatusCode::FORBIDDEN, Json(None)), ResultExt::Err(AppError::AuthFailed) => (StatusCode::UNAUTHORIZED, Json(None)), ResultExt::Err(AppError::JsonError(_)) => (StatusCode::INTERNAL_SERVER_ERROR, Json(None)), ResultExt::Err(AppError::UnexpectedError(_)) => { (StatusCode::INTERNAL_SERVER_ERROR, Json(None)) } ResultExt::Err(AppError::UsernameUnavailable) => (StatusCode::BAD_REQUEST, Json(None)), ResultExt::Fatal(err) => { panic!("The server encountered a fatal error: {}", err); } } } pub async fn healthcheck(core: Core) -> Vec<u8> { match core.status().await { ResultExt::Ok(s) => serde_json::to_vec(&HealthCheck { ok: s.admin_enabled, }) .unwrap(), ResultExt::Err(_) => serde_json::to_vec(&HealthCheck { ok: false }).unwrap(), ResultExt::Fatal(err) => panic!("{}", err), } } /* pub async fn handle_file(core: Core, asset_id: AssetId) -> impl Reply { handler(async move { let (mime, bytes) = return_error!(core.get_asset(asset_id).await); ok(Response::builder() .header("content-type", mime.to_string()) .body(bytes) .unwrap()) }) .await } pub async fn handle_available_images(core: Core) -> impl Reply { handler(async move { let image_paths: Vec<String> = core .available_images() .await .into_iter() .map(|path| format!("{}", path.as_str())) .collect(); ok(Response::builder() .header("Access-Control-Allow-Origin", "*") .header("Content-Type", "application/json") .body(serde_json::to_vec(&image_paths).unwrap()) .unwrap()) }) .await } #[derive(Deserialize, Serialize)] pub struct RegisterRequest {} #[derive(Deserialize, Serialize)] pub struct RegisterResponse { url: String, } pub async fn handle_register_client(core: Core, _request: RegisterRequest) -> impl Reply { handler(async move { let client_id = core.register_client().await; ok(Response::builder() .header("Access-Control-Allow-Origin", "*") .header("Content-Type", "application/json") .body( serde_json::to_vec(&RegisterResponse { url: format!("ws://127.0.0.1:8001/ws/{}", client_id), }) .unwrap(), ) .unwrap()) }) .await } pub async fn handle_unregister_client(core: Core, client_id: String) -> impl Reply { handler(async move { core.unregister_client(client_id).await; ok(Response::builder() .status(StatusCode::NO_CONTENT) .body(vec![]) .unwrap()) }) .await } pub async fn handle_connect_websocket( core: Core, ws: warp::ws::Ws, client_id: String, ) -> impl Reply { ws.on_upgrade(move |socket| { println!("upgrading websocket"); let core = core.clone(); async move { let (mut ws_sender, _) = socket.split(); let mut receiver = core.connect_client(client_id.clone()).await; tokio::task::spawn(async move { let tabletop = core.tabletop().await; let _ = ws_sender .send(Message::text( serde_json::to_string(&crate::types::Message::UpdateTabletop(tabletop)) .unwrap(), )) .await; while let Some(msg) = receiver.recv().await { println!("Relaying message: {:?}", msg); let _ = ws_sender .send(Message::text(serde_json::to_string(&msg).unwrap())) .await; } println!("process ended for id {}", client_id); }); } }) } pub async fn handle_set_background_image(core: Core, image_name: String) -> impl Reply { handler(async move { let _ = core.set_background_image(AssetId::from(image_name)).await; ok(Response::builder() .header("Access-Control-Allow-Origin", "*") .header("Access-Control-Allow-Methods", "*") .header("Content-Type", "application/json") .body(vec![]) .unwrap()) }) .await } pub async fn handle_get_users(core: Core) -> Response<Vec<u8>> { unimplemented!() /* handler(async move { let users = match core.list_users().await { ResultExt::Ok(users) => users, ResultExt::Err(err) => return ResultExt::Err(err), ResultExt::Fatal(err) => return ResultExt::Fatal(err), }; ok(Response::builder() .header("Access-Control-Allow-Origin", "*") .header("Content-Type", "application/json") .body(serde_json::to_vec(&users).unwrap()) .unwrap()) }) .await */ } pub async fn handle_get_games(core: Core) -> impl Reply { handler(async move { let games = match core.list_games().await { ResultExt::Ok(games) => games, ResultExt::Err(err) => return ResultExt::Err(err), ResultExt::Fatal(err) => return ResultExt::Fatal(err), }; ok(Response::builder() .header("Access-Control-Allow-Origin", "*") .header("Content-Type", "application/json") .body(serde_json::to_vec(&games).unwrap()) .unwrap()) }) .await } pub async fn handle_get_charsheet(core: Core, charid: String) -> impl Reply { handler(async move { let sheet = match core.get_charsheet(CharacterId::from(charid)).await { ResultExt::Ok(sheet) => sheet, ResultExt::Err(err) => return ResultExt::Err(err), ResultExt::Fatal(err) => return ResultExt::Fatal(err), }; match sheet { Some(sheet) => ok(Response::builder() .header("Access-Control-Allow-Origin", "*") .header("Content-Type", "application/json") .body(serde_json::to_vec(&sheet).unwrap()) .unwrap()), None => ok(Response::builder() .status(StatusCode::NOT_FOUND) .body(vec![]) .unwrap()), } }) .await } pub async fn handle_set_admin_password(core: Core, password: String) -> impl Reply { handler(async move { let status = return_error!(core.status().await); if status.admin_enabled { return error(AppError::PermissionDenied); } core.set_password(UserId::from("admin"), password).await; ok(Response::builder() .header("Access-Control-Allow-Origin", "*") .header("Access-Control-Allow-Methods", "*") .header("Content-Type", "application/json") .body(vec![]) .unwrap()) }) .await } */