use std::future::Future; use futures::{SinkExt, StreamExt}; use result_extended::{error, ok, return_error, ResultExt}; use serde::{Deserialize, Serialize}; use typeshare::typeshare; use warp::{http::Response, http::StatusCode, reply::Reply, ws::Message}; use crate::{ asset_db::AssetId, core::Core, database::{CharacterId, UserId}, types::{AppError, FatalError}, }; /* pub async fn handle_auth( auth_ctx: &AuthDB, auth_token: AuthToken, ) -> Result<http::Response<String>, Error> { match auth_ctx.authenticate(auth_token).await { Ok(Some(session)) => match serde_json::to_string(&session) { Ok(session_token) => Response::builder() .status(StatusCode::OK) .body(session_token), Err(_) => Response::builder() .status(StatusCode::INTERNAL_SERVER_ERROR) .body("".to_owned()), }, Ok(None) => Response::builder() .status(StatusCode::UNAUTHORIZED) .body("".to_owned()), Err(_) => Response::builder() .status(StatusCode::INTERNAL_SERVER_ERROR) .body("".to_owned()), } } */ pub async fn handler<F>(f: F) -> impl Reply where F: Future<Output = ResultExt<Response<Vec<u8>>, AppError, FatalError>>, { match f.await { ResultExt::Ok(response) => response, ResultExt::Err(AppError::NotFound(_)) => Response::builder() .status(StatusCode::NOT_FOUND) .body(vec![]) .unwrap(), ResultExt::Err(err) => { println!("request error: {:?}", err); Response::builder() .status(StatusCode::INTERNAL_SERVER_ERROR) .body(vec![]) .unwrap() } ResultExt::Fatal(err) => { panic!("Shutting down with fatal error: {:?}", err); } } } pub async fn handle_server_status(core: Core) -> impl Reply { handler(async move { let status = return_error!(core.status().await); ok(Response::builder() .header("Access-Control-Allow-Origin", "*") .header("Content-Type", "application/json") .body(serde_json::to_vec(&status).unwrap()) .unwrap()) }) .await } 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) -> impl Reply { 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 } #[derive(Clone, Debug, Deserialize, Serialize)] #[typeshare] pub struct AuthRequest { username: String, password: String, } pub async fn handle_check_password(core: Core, auth_request: AuthRequest) -> impl Reply { handler(async move { let session_id = return_error!( core.auth(&auth_request.username, &auth_request.password) .await ); println!("handle_check_password: {:?}", session_id); ok(Response::builder() .header("Content-Type", "application/json") .body(serde_json::to_vec(&session_id).unwrap()) .unwrap()) }) .await }