use actix_cors::Cors; use actix_files as fs; use actix_web::{web, App, Error, HttpResponse, HttpServer}; use futures::Future; use lootalot_db::{DbApi, Pool, QueryResult}; use std::env; type AppPool = web::Data; /// Wraps call to the DbApi and process its result as a async HttpResponse /// /// Provides a convenient way to call the api inside a route definition. Given a connection pool, /// access to the api is granted in a closure. The closure is called in a blocking way and should /// return a QueryResult. /// If the query succeeds, it's result is returned as JSON data. Otherwise, an InternalServerError /// is returned. /// /// # Usage /// ``` /// (...) /// .route("path/to/", /// move |pool: web::Data| { /// // user data can be processed here /// // ... /// db_call(pool, move |api| { /// // ...do what you want with the api /// } /// } /// ) /// ``` pub fn db_call< J: serde::ser::Serialize + Send + 'static, Q: Fn(DbApi) -> QueryResult + Send + 'static, >( pool: AppPool, query: Q, ) -> impl Future { let conn = pool.get().unwrap(); web::block(move || { let api = DbApi::with_conn(&conn); query(api) }) .then(|res| match res { Ok(players) => HttpResponse::Ok().json(players), Err(e) => { dbg!(&e); HttpResponse::InternalServerError().finish() } }) } pub(crate) fn serve() -> std::io::Result<()> { let www_root: String = env::var("WWW_ROOT").expect("WWW_ROOT must be set"); dbg!(&www_root); let pool = lootalot_db::create_pool(); HttpServer::new(move || { App::new() .data(pool.clone()) .wrap( Cors::new() .allowed_origin("http://localhost:8080") .allowed_methods(vec!["GET", "POST"]) .max_age(3600), ) .service( web::scope("/api") .route( "/players", web::get().to_async(move |pool: AppPool| { db_call(pool, move |api| api.fetch_players()) }), ) .route( "/claims", web::get().to_async(move |pool: AppPool| { db_call(pool, move |api| api.fetch_claims()) }), ) .route( "/{player_id}/update-wealth/{amount}", web::get().to_async(move |pool: AppPool, data: web::Path<(i32, f32)>| { db_call(pool, move |api| api.as_player(data.0).update_wealth(data.1)) }), ) .route( "/{player_id}/loot", web::get().to_async(move |pool: AppPool, player_id: web::Path| { db_call(pool, move |api| api.as_player(*player_id).loot()) }), ) .route( "/{player_id}/claim/{item_id}", web::get().to_async(move |pool: AppPool, data: web::Path<(i32, i32)>| { db_call(pool, move |api| api.as_player(data.0).claim(data.1)) }), ) .route( "/{player_id}/unclaim/{item_id}", web::get().to_async(move |pool: AppPool, data: web::Path<(i32, i32)>| { db_call(pool, move |api| api.as_player(data.0).unclaim(data.1)) }), ) .route( "/admin/resolve-claims", web::get().to_async(move |pool: AppPool| { db_call(pool, move |api| api.as_admin().resolve_claims()) }), ) .route( "/admin/add-player/{name}/{wealth}", web::get().to_async( move |pool: AppPool, data: web::Path<(String, f32)>| { db_call(pool, move |api| { api.as_admin().add_player(&data.0, data.1) }) }, ), ), ) .service(fs::Files::new("/", www_root.clone()).index_file("index.html")) }) .bind("127.0.0.1:8088")? .run() }