From 1e6c3da58bb6a411b490ce5bd6de6a414c4ae16d Mon Sep 17 00:00:00 2001 From: Lol3rrr Date: Mon, 16 Sep 2024 23:20:55 +0200 Subject: [PATCH] Fix some formatting and improve structure --- backend/src/analysis.rs | 6 +- backend/src/api.rs | 60 +++++--- backend/src/api/demos.rs | 240 ++++++++++++++++++----------- backend/src/diesel_sessionstore.rs | 74 ++++++--- backend/src/lib.rs | 119 +++++++------- backend/src/main.rs | 6 +- backend/src/schema.rs | 8 +- backend/src/steam_api.rs | 47 ++++++ frontend/src/demo.rs | 24 +-- frontend/src/lib.rs | 21 ++- frontend/src/main.rs | 47 +++--- frontend/src/navbar.rs | 20 ++- 12 files changed, 423 insertions(+), 249 deletions(-) create mode 100644 backend/src/steam_api.rs diff --git a/backend/src/analysis.rs b/backend/src/analysis.rs index b815661..be8c451 100644 --- a/backend/src/analysis.rs +++ b/backend/src/analysis.rs @@ -10,17 +10,15 @@ pub struct AnalysisInput { #[derive(Debug)] pub struct BaseInfo { pub map: String, - pub players: Vec<()> + pub players: Vec<()>, } #[tracing::instrument(skip(input))] pub fn analyse_base(input: AnalysisInput) -> BaseInfo { tracing::info!("Performing Base analysis"); - let file = std::fs::File::open(&input.path).unwrap(); let mmap = unsafe { memmap2::MmapOptions::new().map(&file).unwrap() }; - let tmp = csdemo::Container::parse(&mmap).unwrap(); let output = csdemo::parser::parse(csdemo::FrameIterator::parse(tmp.inner)).unwrap(); @@ -85,6 +83,6 @@ pub fn analyse_base(input: AnalysisInput) -> BaseInfo { BaseInfo { map, - players: Vec::new() + players: Vec::new(), } } diff --git a/backend/src/api.rs b/backend/src/api.rs index 777b4e1..2685b6e 100644 --- a/backend/src/api.rs +++ b/backend/src/api.rs @@ -2,14 +2,14 @@ pub mod demos; pub mod steam { use axum::extract::State; - use serde::Deserialize; - use std::{sync::Arc, collections::HashMap}; use diesel::prelude::*; use diesel_async::RunQueryDsl; + use serde::Deserialize; + use std::{collections::HashMap, sync::Arc}; #[derive(Debug, Deserialize)] struct ProfileInfoResponse { - players: Vec + players: Vec, } #[derive(Debug, Deserialize)] struct ProfileInfo { @@ -23,11 +23,15 @@ pub mod steam { axum::Router::new() .route("/login", axum::routing::get(steam_login)) .route("/callback", axum::routing::get(steam_callback)) - .with_state(Arc::new(steam_openid::SteamOpenId::new(url, callback_path).unwrap())) + .with_state(Arc::new( + steam_openid::SteamOpenId::new(url, callback_path).unwrap(), + )) } #[tracing::instrument(skip(openid))] - async fn steam_login(State(openid): State>) -> Result { + async fn steam_login( + State(openid): State>, + ) -> Result { let url = openid.get_redirect_url(); Ok(axum::response::Redirect::to(url)) @@ -52,7 +56,13 @@ pub mod steam { })?; let steam_client = crate::steam_api::Client::new(std::env::var("STEAM_API_KEY").unwrap()); - let profile_response_data: ProfileInfoResponse = match steam_client.get("http://api.steampowered.com/ISteamUser/GetPlayerSummaries/v2/", &[("steamids", &format!("{}", id))]).await { + let profile_response_data: ProfileInfoResponse = match steam_client + .get( + "http://api.steampowered.com/ISteamUser/GetPlayerSummaries/v2/", + &[("steamids", &format!("{}", id))], + ) + .await + { Ok(r) => r, Err(e) => { tracing::error!("Getting Steam Profile Info: {:?}", e); @@ -62,10 +72,14 @@ pub mod steam { let mut db_con = crate::db_connection().await; for player in profile_response_data.players { - let query = diesel::dsl::insert_into(crate::schema::users::dsl::users).values(crate::models::User { - steamid: player.steamid, - name: player.personaname.clone(), - }).on_conflict(crate::schema::users::dsl::steamid).do_update().set((crate::schema::users::dsl::name.eq(player.personaname))); + let query = diesel::dsl::insert_into(crate::schema::users::dsl::users) + .values(crate::models::User { + steamid: player.steamid, + name: player.personaname.clone(), + }) + .on_conflict(crate::schema::users::dsl::steamid) + .do_update() + .set((crate::schema::users::dsl::name.eq(player.personaname))); tracing::debug!("Running Query: {:?}", query); if let Err(e) = query.execute(&mut db_con).await { @@ -88,12 +102,13 @@ pub mod user { use diesel_async::RunQueryDsl; pub fn router() -> axum::Router { - axum::Router::new() - .route("/status", axum::routing::get(status)) + axum::Router::new().route("/status", axum::routing::get(status)) } #[tracing::instrument(skip(session))] - async fn status(session: crate::UserSession) -> Result, reqwest::StatusCode> { + async fn status( + session: crate::UserSession, + ) -> Result, reqwest::StatusCode> { let steam_id = match session.data().steam_id { Some(s) => s, None => { @@ -105,16 +120,20 @@ pub mod user { let mut db_con = crate::db_connection().await; - let query = crate::schema::users::dsl::users.filter(crate::schema::users::dsl::steamid.eq(format!("{}", steam_id))); + let query = crate::schema::users::dsl::users + .filter(crate::schema::users::dsl::steamid.eq(format!("{}", steam_id))); - let mut result = query.load::(&mut db_con).await.unwrap(); + let mut result = query + .load::(&mut db_con) + .await + .unwrap(); if result.len() != 1 { tracing::error!("Unexpected query result: {:?}", result); return Err(axum::http::StatusCode::INTERNAL_SERVER_ERROR); } let user_entry = result.pop().unwrap(); - + Ok(axum::Json(common::UserStatus { name: user_entry.name, steamid: user_entry.steamid, @@ -122,9 +141,14 @@ pub mod user { } } -pub fn router(base_analysis: tokio::sync::mpsc::UnboundedSender) -> axum::Router { +pub fn router( + base_analysis: tokio::sync::mpsc::UnboundedSender, +) -> axum::Router { axum::Router::new() - .nest("/steam/", steam::router("http://192.168.0.156:3000", "/api/steam/callback")) + .nest( + "/steam/", + steam::router("http://192.168.0.156:3000", "/api/steam/callback"), + ) .nest("/demos/", demos::router("uploads/", base_analysis)) .nest("/user/", user::router()) } diff --git a/backend/src/api/demos.rs b/backend/src/api/demos.rs index 7ee4903..e53aedf 100644 --- a/backend/src/api/demos.rs +++ b/backend/src/api/demos.rs @@ -1,94 +1,148 @@ use crate::UserSession; - use diesel_async::RunQueryDsl; - use diesel::prelude::*; - use axum::extract::{State, Path}; - use std::sync::Arc; +use axum::extract::{Path, State}; +use diesel::prelude::*; +use diesel_async::RunQueryDsl; +use std::sync::Arc; - struct DemoState { - upload_folder: std::path::PathBuf, - base_analysis: tokio::sync::mpsc::UnboundedSender +struct DemoState { + upload_folder: std::path::PathBuf, + base_analysis: tokio::sync::mpsc::UnboundedSender, +} + +pub fn router

( + upload_folder: P, + base_analysis: tokio::sync::mpsc::UnboundedSender, +) -> axum::Router +where + P: Into, +{ + axum::Router::new() + .route("/list", axum::routing::get(list)) + .route( + "/upload", + axum::routing::post(upload) + .layer(axum::extract::DefaultBodyLimit::max(500 * 1024 * 1024)), + ) + .route("/:id/info", axum::routing::get(info)) + .route("/:id/reanalyse", axum::routing::get(analyise)) + .with_state(Arc::new(DemoState { + upload_folder: upload_folder.into(), + base_analysis, + })) +} + +#[tracing::instrument(skip(session))] +async fn list( + session: UserSession, +) -> Result>, axum::http::StatusCode> { + let steam_id = session + .data() + .steam_id + .ok_or_else(|| axum::http::StatusCode::UNAUTHORIZED)?; + tracing::info!("SteamID: {:?}", steam_id); + + let query = crate::schema::demos::dsl::demos + .inner_join(crate::schema::demo_info::dsl::demo_info) + .select(( + crate::models::Demo::as_select(), + crate::models::DemoInfo::as_select(), + )) + .filter(crate::schema::demos::dsl::steam_id.eq(steam_id.to_string())); + let results: Vec<(crate::models::Demo, crate::models::DemoInfo)> = + query.load(&mut crate::db_connection().await).await.unwrap(); + + Ok(axum::response::Json( + results + .into_iter() + .map(|(demo, info)| common::BaseDemoInfo { + id: demo.demo_id, + map: info.map, + }) + .collect::>(), + )) +} + +#[tracing::instrument(skip(state, session))] +async fn upload( + State(state): State>, + session: crate::UserSession, + form: axum::extract::Multipart, +) -> Result { + let steam_id = session + .data() + .steam_id + .ok_or_else(|| (axum::http::StatusCode::UNAUTHORIZED, "Not logged in"))?; + + tracing::info!("Upload for Session: {:?}", steam_id); + + let file_content = crate::get_demo_from_upload("demo", form).await.unwrap(); + + let user_folder = std::path::Path::new(&state.upload_folder).join(format!("{}/", steam_id)); + if !tokio::fs::try_exists(&user_folder).await.unwrap_or(false) { + tokio::fs::create_dir_all(&user_folder).await.unwrap(); } - pub fn router

(upload_folder: P, base_analysis: tokio::sync::mpsc::UnboundedSender) -> axum::Router where P: Into { - axum::Router::new() - .route("/list", axum::routing::get(list)) - .route("/upload", axum::routing::post(upload).layer(axum::extract::DefaultBodyLimit::max(500*1024*1024))) - .route("/:id/info", axum::routing::get(info)) - .route("/:id/reanalyse", axum::routing::get(analyise)) - .with_state(Arc::new(DemoState { - upload_folder: upload_folder.into(), - base_analysis, - })) - } + let timestamp_secs = std::time::SystemTime::now() + .duration_since(std::time::SystemTime::UNIX_EPOCH) + .unwrap() + .as_secs(); + let demo_id = timestamp_secs as i64; + let demo_file_path = user_folder.join(format!("{}.dem", timestamp_secs)); - #[tracing::instrument(skip(session))] - async fn list(session: UserSession) -> Result>, axum::http::StatusCode> { - let steam_id = session.data().steam_id.ok_or_else(|| axum::http::StatusCode::UNAUTHORIZED)?; - tracing::info!("SteamID: {:?}", steam_id); - - let query = crate::schema::demos::dsl::demos.inner_join(crate::schema::demo_info::dsl::demo_info).select((crate::models::Demo::as_select(), crate::models::DemoInfo::as_select())).filter(crate::schema::demos::dsl::steam_id.eq(steam_id.to_string())); - let results: Vec<(crate::models::Demo, crate::models::DemoInfo)> = query.load(&mut crate::db_connection().await).await.unwrap(); - - Ok(axum::response::Json(results.into_iter().map(|(demo, info)| common::BaseDemoInfo { - id: demo.demo_id, - map: info.map, - }).collect::>())) - } - - #[tracing::instrument(skip(state, session))] - async fn upload(State(state): State>, session: crate::UserSession, form: axum::extract::Multipart) -> Result { - let steam_id = session.data().steam_id.ok_or_else(|| (axum::http::StatusCode::UNAUTHORIZED, "Not logged in"))?; - - tracing::info!("Upload for Session: {:?}", steam_id); - - let file_content = crate::get_demo_from_upload("demo", form).await.unwrap(); - - let user_folder = std::path::Path::new(&state.upload_folder).join(format!("{}/", steam_id)); - if !tokio::fs::try_exists(&user_folder).await.unwrap_or(false) { - tokio::fs::create_dir_all(&user_folder).await.unwrap(); - } - - let timestamp_secs = std::time::SystemTime::now().duration_since(std::time::SystemTime::UNIX_EPOCH).unwrap().as_secs(); - let demo_id = timestamp_secs as i64; - let demo_file_path = user_folder.join(format!("{}.dem", timestamp_secs)); - - tokio::fs::write(&demo_file_path, file_content).await.unwrap(); - - let mut db_con = crate::db_connection().await; - - let query = diesel::dsl::insert_into(crate::schema::demos::dsl::demos).values(crate::models::Demo { - demo_id, - steam_id: steam_id.to_string(), - }); - query.execute(&mut db_con).await.unwrap(); - - state.base_analysis.send(crate::analysis::AnalysisInput { - steamid: steam_id.to_string(), - demoid: demo_id, - path: demo_file_path, - }); - let processing_query = diesel::dsl::insert_into(crate::schema::processing_status::dsl::processing_status).values(crate::models::ProcessingStatus { - demo_id, - info: 0, - }); - processing_query.execute(&mut db_con).await.unwrap(); - - Ok(axum::response::Redirect::to("/")) - } - - #[tracing::instrument(skip(state, session))] - async fn analyise(State(state): State>, session: crate::UserSession, Path(demo_id): Path) -> Result<(), (axum::http::StatusCode, &'static str)> { - let steam_id = session.data().steam_id.ok_or_else(|| (axum::http::StatusCode::UNAUTHORIZED, "Not logged in"))?; - - tracing::info!("Upload for Session: {:?}", steam_id); + tokio::fs::write(&demo_file_path, file_content) + .await + .unwrap(); let mut db_con = crate::db_connection().await; - let query = crate::schema::demos::dsl::demos.filter(crate::schema::demos::dsl::steam_id.eq(steam_id.to_string())).filter(crate::schema::demos::dsl::demo_id.eq(demo_id)); - let result: Vec<_> = query.load::(&mut db_con).await.unwrap(); + let query = + diesel::dsl::insert_into(crate::schema::demos::dsl::demos).values(crate::models::Demo { + demo_id, + steam_id: steam_id.to_string(), + }); + query.execute(&mut db_con).await.unwrap(); + + state.base_analysis.send(crate::analysis::AnalysisInput { + steamid: steam_id.to_string(), + demoid: demo_id, + path: demo_file_path, + }); + let processing_query = + diesel::dsl::insert_into(crate::schema::processing_status::dsl::processing_status) + .values(crate::models::ProcessingStatus { demo_id, info: 0 }); + processing_query.execute(&mut db_con).await.unwrap(); + + Ok(axum::response::Redirect::to("/")) +} + +#[tracing::instrument(skip(state, session))] +async fn analyise( + State(state): State>, + session: crate::UserSession, + Path(demo_id): Path, +) -> Result<(), (axum::http::StatusCode, &'static str)> { + let steam_id = session + .data() + .steam_id + .ok_or_else(|| (axum::http::StatusCode::UNAUTHORIZED, "Not logged in"))?; + + tracing::info!("Upload for Session: {:?}", steam_id); + + let mut db_con = crate::db_connection().await; + + let query = crate::schema::demos::dsl::demos + .filter(crate::schema::demos::dsl::steam_id.eq(steam_id.to_string())) + .filter(crate::schema::demos::dsl::demo_id.eq(demo_id)); + let result: Vec<_> = query + .load::(&mut db_con) + .await + .unwrap(); if result.len() != 1 { - return Err((axum::http::StatusCode::BAD_REQUEST, "Expected exactly 1 demo to match")); + return Err(( + axum::http::StatusCode::BAD_REQUEST, + "Expected exactly 1 demo to match", + )); } let user_folder = std::path::Path::new(&state.upload_folder).join(format!("{}/", steam_id)); @@ -99,24 +153,30 @@ use crate::UserSession; }); Ok(()) - } +} - #[tracing::instrument(skip(session))] - async fn info(session: UserSession, Path(demo_id): Path) -> Result, axum::http::StatusCode> { - tracing::info!("Get info for Demo: {:?}", demo_id); +#[tracing::instrument(skip(session))] +async fn info( + session: UserSession, + Path(demo_id): Path, +) -> Result, axum::http::StatusCode> { + tracing::info!("Get info for Demo: {:?}", demo_id); - let query = crate::schema::demo_info::dsl::demo_info.select(crate::models::DemoInfo::as_select()).filter(crate::schema::demo_info::dsl::demo_id.eq(demo_id)); - let mut results: Vec = query.load(&mut crate::db_connection().await).await.unwrap(); + let query = crate::schema::demo_info::dsl::demo_info + .select(crate::models::DemoInfo::as_select()) + .filter(crate::schema::demo_info::dsl::demo_id.eq(demo_id)); + let mut results: Vec = + query.load(&mut crate::db_connection().await).await.unwrap(); - if results.len() != 1 { + if results.len() != 1 { tracing::error!("Expected only 1 match but got {} matches", results.len()); return Err(axum::http::StatusCode::INTERNAL_SERVER_ERROR); } let result = results.pop().unwrap(); - Ok(axum::Json(common::DemoInfo { + Ok(axum::Json(common::DemoInfo { id: result.demo_id, map: result.map, })) - } +} diff --git a/backend/src/diesel_sessionstore.rs b/backend/src/diesel_sessionstore.rs index cdd9977..ab57f86 100644 --- a/backend/src/diesel_sessionstore.rs +++ b/backend/src/diesel_sessionstore.rs @@ -6,21 +6,25 @@ use diesel_async::RunQueryDsl; #[derive(Debug, Clone)] pub struct DieselStore {} -static EXPIRY_FORMAT: std::sync::LazyLock<&[time::format_description::BorrowedFormatItem<'static>]> = std::sync::LazyLock::new(|| { - time::macros::format_description!( +static EXPIRY_FORMAT: std::sync::LazyLock< + &[time::format_description::BorrowedFormatItem<'static>], +> = std::sync::LazyLock::new(|| { + time::macros::format_description!( "[year]-[month]-[day] [hour]:[minute]:[second] [offset_hour sign:mandatory]:[offset_minute]:[offset_second]" ) - }); +}); impl DieselStore { - pub fn new() -> Self { Self {} } fn id_to_bytes(&self, val: i128) -> Vec { let id_bytes = val.to_be_bytes(); - vec![i64::from_be_bytes((id_bytes[0..8]).try_into().unwrap()), i64::from_be_bytes((id_bytes[8..16]).try_into().unwrap())] + vec![ + i64::from_be_bytes((id_bytes[0..8]).try_into().unwrap()), + i64::from_be_bytes((id_bytes[8..16]).try_into().unwrap()), + ] } fn bytes_to_id(&self, val: Vec) -> i128 { assert_eq!(2, val.len()); @@ -28,7 +32,10 @@ impl DieselStore { let fb = val[0].to_be_bytes(); let sb = val[1].to_be_bytes(); - i128::from_be_bytes([fb[0], fb[1], fb[2], fb[3], fb[4], fb[5], fb[6], fb[7], sb[0], sb[1], sb[2], sb[3], sb[4], sb[5], sb[6], sb[7]]) + i128::from_be_bytes([ + fb[0], fb[1], fb[2], fb[3], fb[4], fb[5], fb[6], fb[7], sb[0], sb[1], sb[2], sb[3], + sb[4], sb[5], sb[6], sb[7], + ]) } fn expiry_to_string(&self, expiry_date: &time::OffsetDateTime) -> String { @@ -41,15 +48,22 @@ impl DieselStore { #[async_trait::async_trait] impl tower_sessions::SessionStore for DieselStore { - async fn save(&self,session_record: &tower_sessions::session::Record) -> tower_sessions::session_store::Result<()> { + async fn save( + &self, + session_record: &tower_sessions::session::Record, + ) -> tower_sessions::session_store::Result<()> { let db_id = session_record.id.0.to_string(); let data = serde_json::to_value(&session_record.data).unwrap(); let expiry_date = self.expiry_to_string(&session_record.expiry_date); - let steamid = session_record.data.get(crate::UserSession::KEY).map(|e| serde_json::from_value::(e.clone()).ok()).flatten().map(|d| { - d.steam_id.map(|s| s.to_string()) - }).flatten(); + let steamid = session_record + .data + .get(crate::UserSession::KEY) + .map(|e| serde_json::from_value::(e.clone()).ok()) + .flatten() + .map(|d| d.steam_id.map(|s| s.to_string())) + .flatten(); let query = diesel::dsl::insert_into(crate::schema::sessions::dsl::sessions) .values(crate::models::Session { @@ -59,7 +73,10 @@ impl tower_sessions::SessionStore for DieselStore { }) .on_conflict(crate::schema::sessions::dsl::id) .do_update() - .set((crate::schema::sessions::dsl::steamid.eq(steamid), crate::schema::sessions::dsl::expiry_date.eq(expiry_date))); + .set(( + crate::schema::sessions::dsl::steamid.eq(steamid), + crate::schema::sessions::dsl::expiry_date.eq(expiry_date), + )); let mut connection = crate::db_connection().await; @@ -68,10 +85,14 @@ impl tower_sessions::SessionStore for DieselStore { Ok(()) } - async fn load(&self,session_id: &tower_sessions::session::Id) -> tower_sessions::session_store::Result> { + async fn load( + &self, + session_id: &tower_sessions::session::Id, + ) -> tower_sessions::session_store::Result> { let db_id = session_id.0.to_string(); - let query = crate::schema::sessions::dsl::sessions.filter(crate::schema::sessions::dsl::id.eq(db_id)); + let query = crate::schema::sessions::dsl::sessions + .filter(crate::schema::sessions::dsl::id.eq(db_id)); let mut connection = crate::db_connection().await; @@ -79,7 +100,9 @@ impl tower_sessions::SessionStore for DieselStore { if result.len() > 1 { tracing::error!("Found more than 1 result"); - return Err(tower_sessions::session_store::Error::Backend("Found more than 1 result".to_string())); + return Err(tower_sessions::session_store::Error::Backend( + "Found more than 1 result".to_string(), + )); } if result.is_empty() { @@ -90,9 +113,13 @@ impl tower_sessions::SessionStore for DieselStore { let data = { let mut tmp = HashMap::::new(); - tmp.insert(crate::UserSession::KEY.to_string(), serde_json::to_value(&crate::UserSessionData { - steam_id: result.steamid.map(|s| s.parse().ok()).flatten(), - }).unwrap()); + tmp.insert( + crate::UserSession::KEY.to_string(), + serde_json::to_value(&crate::UserSessionData { + steam_id: result.steamid.map(|s| s.parse().ok()).flatten(), + }) + .unwrap(), + ); tmp }; @@ -103,13 +130,20 @@ impl tower_sessions::SessionStore for DieselStore { })) } - async fn delete(&self,session_id: &tower_sessions::session::Id) -> tower_sessions::session_store::Result<()> { + async fn delete( + &self, + session_id: &tower_sessions::session::Id, + ) -> tower_sessions::session_store::Result<()> { let db_id = session_id.0.to_string(); - let query = crate::schema::sessions::dsl::sessions.filter(crate::schema::sessions::dsl::id.eq(db_id)); + let query = crate::schema::sessions::dsl::sessions + .filter(crate::schema::sessions::dsl::id.eq(db_id)); let mut connection = crate::db_connection().await; - diesel::dsl::delete(query).execute(&mut connection).await.unwrap(); + diesel::dsl::delete(query) + .execute(&mut connection) + .await + .unwrap(); Ok(()) } diff --git a/backend/src/lib.rs b/backend/src/lib.rs index 0b73f4b..02c82a7 100644 --- a/backend/src/lib.rs +++ b/backend/src/lib.rs @@ -2,7 +2,7 @@ pub mod models; pub mod schema; mod usersession; -pub use usersession::{UserSessionData, UserSession}; +pub use usersession::{UserSession, UserSessionData}; pub mod diesel_sessionstore; @@ -13,10 +13,15 @@ pub async fn db_connection() -> diesel_async::AsyncPgConnection { let database_url = std::env::var("DATABASE_URL").expect("'DATABASE_URL' must be set"); - diesel_async::AsyncPgConnection::establish(&database_url).await.unwrap_or_else(|e| panic!("Error connecting to {} - {:?}", database_url, e)) + diesel_async::AsyncPgConnection::establish(&database_url) + .await + .unwrap_or_else(|e| panic!("Error connecting to {} - {:?}", database_url, e)) } -pub async fn get_demo_from_upload(name: &str, mut form: axum::extract::Multipart) -> Option { +pub async fn get_demo_from_upload( + name: &str, + mut form: axum::extract::Multipart, +) -> Option { while let Ok(field) = form.next_field().await { let field = match field { Some(f) => f, @@ -36,49 +41,23 @@ pub async fn get_demo_from_upload(name: &str, mut form: axum::extract::Multipart } pub mod api; -pub mod steam_api { - use serde::Deserialize; - - pub struct Client { - http: reqwest::Client, - api_key: String, - } - - #[derive(Debug, Deserialize)] - struct Response { - response: T, - } - - impl Client { - pub fn new(api_key: IS) -> Self where IS: Into { - Self { - http: reqwest::Client::new(), - api_key: api_key.into(), - } - } - - pub async fn get(&self, path: &str, args: &[(&str, &str)]) -> Result where T: serde::de::DeserializeOwned { - let response = self.http.get(path).query(&[("key", &self.api_key)]).query(args).send().await.map_err(|e| ())?; - if !response.status().is_success() { - dbg!(&response); - return Err(()); - } - - response.json::>().await.map(|r| r.response).map_err(|e| ()) - } - } -} +pub mod steam_api; #[tracing::instrument(skip(upload_folder, base_analysis_tx))] -pub async fn run_api(upload_folder: UP, base_analysis_tx: tokio::sync::mpsc::UnboundedSender) where UP: Into { +pub async fn run_api( + upload_folder: UP, + base_analysis_tx: tokio::sync::mpsc::UnboundedSender, +) where + UP: Into, +{ let upload_folder: std::path::PathBuf = upload_folder.into(); let session_store = crate::diesel_sessionstore::DieselStore::new(); let session_layer = tower_sessions::SessionManagerLayer::new(session_store) .with_secure(false) - .with_expiry(tower_sessions::Expiry::OnInactivity( - time::Duration::hours(48), - )); + .with_expiry(tower_sessions::Expiry::OnInactivity(time::Duration::hours( + 48, + ))); if !tokio::fs::try_exists(&upload_folder).await.unwrap_or(false) { tokio::fs::create_dir_all(&upload_folder).await.unwrap(); @@ -87,42 +66,54 @@ pub async fn run_api(upload_folder: UP, base_analysis_tx: tokio::sync::mpsc: let router = axum::Router::new() .nest("/api/", crate::api::router(base_analysis_tx)) .layer(session_layer) - .nest_service("/", tower_http::services::ServeDir::new("../frontend/dist/")); + .nest_service( + "/", + tower_http::services::ServeDir::new("../frontend/dist/"), + ); let listener = tokio::net::TcpListener::bind("0.0.0.0:3000").await.unwrap(); axum::serve(listener, router).await.unwrap(); } #[tracing::instrument] -pub async fn run_analysis(mut base_analysis_rx: tokio::sync::mpsc::UnboundedReceiver) { +pub async fn run_analysis( + mut base_analysis_rx: tokio::sync::mpsc::UnboundedReceiver, +) { use diesel::prelude::*; - use diesel_async::{RunQueryDsl, AsyncConnection}; + use diesel_async::{AsyncConnection, RunQueryDsl}; - while let Some(input) = base_analysis_rx.recv().await { - let demo_id = input.demoid; + while let Some(input) = base_analysis_rx.recv().await { + let demo_id = input.demoid; - let result = tokio::task::spawn_blocking(move || { - crate::analysis::analyse_base(input) - }).await.unwrap(); + let result = tokio::task::spawn_blocking(move || crate::analysis::analyse_base(input)) + .await + .unwrap(); - dbg!(&result); + dbg!(&result); - let mut db_con = crate::db_connection().await; - - let store_info_query = diesel::dsl::insert_into(crate::schema::demo_info::dsl::demo_info).values(crate::models::DemoInfo { - demo_id, - map: result.map, - }); - let update_process_info = diesel::dsl::update(crate::schema::processing_status::dsl::processing_status).set(crate::schema::processing_status::dsl::info.eq(1)).filter(crate::schema::processing_status::dsl::demo_id.eq(demo_id)); + let mut db_con = crate::db_connection().await; - tracing::trace!(?store_info_query, "Store demo info query"); - tracing::trace!(?update_process_info, "Update processing info query"); + let store_info_query = diesel::dsl::insert_into(crate::schema::demo_info::dsl::demo_info) + .values(crate::models::DemoInfo { + demo_id, + map: result.map, + }); + let update_process_info = + diesel::dsl::update(crate::schema::processing_status::dsl::processing_status) + .set(crate::schema::processing_status::dsl::info.eq(1)) + .filter(crate::schema::processing_status::dsl::demo_id.eq(demo_id)); - db_con.transaction::<'_, '_, '_, _, diesel::result::Error, _>(|conn| Box::pin(async move { - store_info_query.execute(conn).await.map(|e| ())?; - update_process_info.execute(conn).await.map(|e| ())?; - Ok(()) - })).await; - - } + tracing::trace!(?store_info_query, "Store demo info query"); + tracing::trace!(?update_process_info, "Update processing info query"); + + db_con + .transaction::<'_, '_, '_, _, diesel::result::Error, _>(|conn| { + Box::pin(async move { + store_info_query.execute(conn).await.map(|e| ())?; + update_process_info.execute(conn).await.map(|e| ())?; + Ok(()) + }) + }) + .await; + } } diff --git a/backend/src/main.rs b/backend/src/main.rs index b15e147..cb55b29 100644 --- a/backend/src/main.rs +++ b/backend/src/main.rs @@ -2,7 +2,8 @@ use tracing_subscriber::prelude::__tracing_subscriber_SubscriberExt; static UPLOAD_FOLDER: &str = "uploads/"; -const MIGRATIONS: diesel_async_migrations::EmbeddedMigrations = diesel_async_migrations::embed_migrations!("../migrations/"); +const MIGRATIONS: diesel_async_migrations::EmbeddedMigrations = + diesel_async_migrations::embed_migrations!("../migrations/"); async fn run_migrations(connection: &mut diesel_async::AsyncPgConnection) { MIGRATIONS.run_pending_migrations(connection).await.unwrap(); @@ -23,7 +24,8 @@ async fn main() { run_migrations(&mut backend::db_connection().await).await; tracing::info!("Completed Migrations"); - let (base_analysis_tx, base_analysis_rx) = tokio::sync::mpsc::unbounded_channel::(); + let (base_analysis_tx, base_analysis_rx) = + tokio::sync::mpsc::unbounded_channel::(); let mut component_set = tokio::task::JoinSet::new(); diff --git a/backend/src/schema.rs b/backend/src/schema.rs index b16afa1..689d5e7 100644 --- a/backend/src/schema.rs +++ b/backend/src/schema.rs @@ -39,10 +39,4 @@ diesel::table! { diesel::joinable!(demo_info -> demos (demo_id)); diesel::joinable!(processing_status -> demos (demo_id)); -diesel::allow_tables_to_appear_in_same_query!( - demo_info, - demos, - processing_status, - sessions, - users, -); +diesel::allow_tables_to_appear_in_same_query!(demo_info, demos, processing_status, sessions, users,); diff --git a/backend/src/steam_api.rs b/backend/src/steam_api.rs new file mode 100644 index 0000000..2f3a9d5 --- /dev/null +++ b/backend/src/steam_api.rs @@ -0,0 +1,47 @@ +use serde::Deserialize; + +pub struct Client { + http: reqwest::Client, + api_key: String, +} + +#[derive(Debug, Deserialize)] +struct Response { + response: T, +} + +impl Client { + pub fn new(api_key: IS) -> Self + where + IS: Into, + { + Self { + http: reqwest::Client::new(), + api_key: api_key.into(), + } + } + + pub async fn get(&self, path: &str, args: &[(&str, &str)]) -> Result + where + T: serde::de::DeserializeOwned, + { + let response = self + .http + .get(path) + .query(&[("key", &self.api_key)]) + .query(args) + .send() + .await + .map_err(|e| ())?; + if !response.status().is_success() { + dbg!(&response); + return Err(()); + } + + response + .json::>() + .await + .map(|r| r.response) + .map_err(|e| ()) + } +} diff --git a/frontend/src/demo.rs b/frontend/src/demo.rs index 04a7138..9487eeb 100644 --- a/frontend/src/demo.rs +++ b/frontend/src/demo.rs @@ -6,16 +6,20 @@ pub fn demo() -> impl leptos::IntoView { let params = leptos_router::use_params_map(); let id = move || params.with(|params| params.get("id").cloned().unwrap_or_default()); - let demo_info = create_resource(|| (), move |_| async move { - let res = reqwasm::http::Request::get(&format!("/api/demos/{}/info", id())).send().await.unwrap(); - res.json::().await.unwrap() - }); + let demo_info = create_resource( + || (), + move |_| async move { + let res = reqwasm::http::Request::get(&format!("/api/demos/{}/info", id())) + .send() + .await + .unwrap(); + res.json::().await.unwrap() + }, + ); - let map = move || { - match demo_info.get() { - Some(v) => v.map.clone(), - None => String::new(), - } + let map = move || match demo_info.get() { + Some(v) => v.map.clone(), + None => String::new(), }; let selected_tab = move || { @@ -24,7 +28,7 @@ pub fn demo() -> impl leptos::IntoView { let trailing = loc_path.split('/').last(); trailing.unwrap_or("/").to_owned() }; - + let style = stylers::style! { "Demo", .analysis_bar { diff --git a/frontend/src/lib.rs b/frontend/src/lib.rs index cfb0948..33cade3 100644 --- a/frontend/src/lib.rs +++ b/frontend/src/lib.rs @@ -25,7 +25,10 @@ pub fn demo_list_entry(demo: common::BaseDemoInfo) -> impl leptos::IntoView { } #[leptos::component] -pub fn upload_demo(shown: ReadSignal, update_shown: WriteSignal) -> impl leptos::IntoView { +pub fn upload_demo( + shown: ReadSignal, + update_shown: WriteSignal, +) -> impl leptos::IntoView { use leptos_router::Form; let style = stylers::style! { @@ -69,11 +72,17 @@ pub fn upload_demo(shown: ReadSignal, update_shown: WriteSigna #[leptos::component] pub fn homepage() -> impl leptos::IntoView { - let demo_data = create_resource(|| (), |_| async move { - let res = reqwasm::http::Request::get("/api/demos/list").send().await.unwrap(); - let demos: Vec = res.json().await.unwrap(); - demos - }); + let demo_data = create_resource( + || (), + |_| async move { + let res = reqwasm::http::Request::get("/api/demos/list") + .send() + .await + .unwrap(); + let demos: Vec = res.json().await.unwrap(); + demos + }, + ); view! {

diff --git a/frontend/src/main.rs b/frontend/src/main.rs index 807ab7a..a1b2ef9 100644 --- a/frontend/src/main.rs +++ b/frontend/src/main.rs @@ -1,39 +1,42 @@ use leptos::*; use leptos_router::*; -use frontend::{UploadDemo, TopBar, Homepage, Demo}; +use frontend::{Demo, Homepage, TopBar, UploadDemo}; async fn load_demos() -> Vec { - let res = reqwasm::http::Request::get("/api/demos/list").send().await.unwrap(); + let res = reqwasm::http::Request::get("/api/demos/list") + .send() + .await + .unwrap(); let demos: Vec = res.json().await.unwrap(); demos } fn main() { - let async_data = create_resource(|| (), |_| async move { - load_demos().await - }); + let async_data = create_resource(|| (), |_| async move { load_demos().await }); let (upload_demo_read, upload_demo_write) = create_signal(frontend::DemoUploadStatus::Hidden); - mount_to_body(move || view! { - - -
- + mount_to_body(move || { + view! { + + +
+ - - - - - - - - -
-
+ + + + + + + + +
+
+ } }) } diff --git a/frontend/src/navbar.rs b/frontend/src/navbar.rs index 54ff517..e1698bd 100644 --- a/frontend/src/navbar.rs +++ b/frontend/src/navbar.rs @@ -5,12 +5,19 @@ use crate::DemoUploadStatus; #[leptos::component] fn steam_login(height: &'static str, width: &'static str) -> impl leptos::IntoView { - let user_status = create_resource(|| (), |_| async move { - let res = reqwasm::http::Request::get("/api/user/status").send().await.unwrap(); - res.json::().await.map_err(|e| ()) - }); + let user_status = create_resource( + || (), + |_| async move { + let res = reqwasm::http::Request::get("/api/user/status") + .send() + .await + .unwrap(); + res.json::().await.map_err(|e| ()) + }, + ); - let tmp = move || match user_status.get() { + let tmp = move || { + match user_status.get() { Some(Ok(user)) => view! {

{user.name}

@@ -21,6 +28,7 @@ fn steam_login(height: &'static str, width: &'static str) -> impl leptos::IntoVi Steam Login }.into_view(), + } }; view! { @@ -66,7 +74,7 @@ pub fn top_bar(update_demo_visible: WriteSignal) -> impl lepto - +