From 828df3290a74feff8911ffdf09cd19414c9f52dc Mon Sep 17 00:00:00 2001 From: Lol3rrr Date: Sun, 8 Sep 2024 00:41:12 +0200 Subject: [PATCH] Some minor restructuring and improvements --- Cargo.lock | 96 +++++++++++++++++- Cargo.toml | 2 +- backend/Cargo.toml | 2 + backend/src/api.rs | 115 ++++++++++++++++++++++ backend/src/lib.rs | 4 +- backend/src/main.rs | 83 +--------------- backend/src/usersession.rs | 3 - common/Cargo.toml | 7 ++ common/src/lib.rs | 4 + frontend/Cargo.toml | 4 + frontend/Trunk.toml | 4 + frontend/index.html | 4 +- frontend/src/demo.rs | 17 ++++ frontend/src/lib.rs | 107 ++++++++++++++++++++ frontend/src/main.rs | 32 +++--- migrations/2024-09-07-151517_demos/up.sql | 5 +- src/schema.rs | 4 +- 17 files changed, 387 insertions(+), 106 deletions(-) create mode 100644 backend/src/api.rs create mode 100644 common/Cargo.toml create mode 100644 common/src/lib.rs create mode 100644 frontend/Trunk.toml create mode 100644 frontend/src/demo.rs create mode 100644 frontend/src/lib.rs diff --git a/Cargo.lock b/Cargo.lock index dbaeabd..9af6956 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -152,6 +152,7 @@ version = "0.1.0" dependencies = [ "async-trait", "axum", + "common", "diesel", "diesel-async", "diesel_async_migrations", @@ -287,6 +288,13 @@ version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "186dce98367766de751c42c4f03970fc60fc012296e706ccbb9d5df9b6c1e271" +[[package]] +name = "common" +version = "0.1.0" +dependencies = [ + "serde", +] + [[package]] name = "config" version = "0.14.0" @@ -631,8 +639,11 @@ dependencies = [ name = "frontend" version = "0.1.0" dependencies = [ + "common", "leptos", + "leptos_router", "reqwasm", + "stylers", ] [[package]] @@ -1235,6 +1246,32 @@ dependencies = [ "web-sys", ] +[[package]] +name = "leptos_router" +version = "0.6.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5006e35b7c768905286dbea0d3525396cd39d961cb7b9fb664aa00b0c984ae6" +dependencies = [ + "cfg-if", + "gloo-net 0.6.0", + "itertools", + "js-sys", + "lazy_static", + "leptos", + "linear-map", + "once_cell", + "percent-encoding", + "send_wrapper", + "serde", + "serde_json", + "serde_qs 0.13.0", + "thiserror", + "tracing", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", +] + [[package]] name = "leptos_server" version = "0.6.14" @@ -1251,18 +1288,43 @@ dependencies = [ "tracing", ] +[[package]] +name = "levenshtein" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db13adb97ab515a3691f56e4dbab09283d0b86cb45abd991d8634a9d6f501760" + [[package]] name = "libc" version = "0.2.158" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d8adc4bb1803a324070e64a98ae98f38934d91957a99cfb3a43dcbc01bc56439" +[[package]] +name = "linear-map" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfae20f6b19ad527b550c223fddc3077a547fc70cda94b9b566575423fd303ee" +dependencies = [ + "serde", + "serde_test", +] + [[package]] name = "linux-raw-sys" version = "0.4.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89" +[[package]] +name = "litrs" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4ce301924b7887e9d637144fdade93f9dfff9b60981d4ac161db09720d39aa5" +dependencies = [ + "proc-macro2", +] + [[package]] name = "lock_api" version = "0.4.12" @@ -2071,6 +2133,17 @@ dependencies = [ "thiserror", ] +[[package]] +name = "serde_qs" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd34f36fe4c5ba9654417139a9b3a20d2e1de6012ee678ad14d240c22c78d8d6" +dependencies = [ + "percent-encoding", + "serde", + "thiserror", +] + [[package]] name = "serde_spanned" version = "0.6.7" @@ -2080,6 +2153,15 @@ dependencies = [ "serde", ] +[[package]] +name = "serde_test" +version = "1.0.177" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f901ee573cab6b3060453d2d5f0bae4e6d628c23c0a962ff9b5f1d7c8d4f1ed" +dependencies = [ + "serde", +] + [[package]] name = "serde_urlencoded" version = "0.7.1" @@ -2110,7 +2192,7 @@ dependencies = [ "send_wrapper", "serde", "serde_json", - "serde_qs", + "serde_qs 0.12.0", "server_fn_macro_default", "thiserror", "url", @@ -2249,6 +2331,18 @@ version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" +[[package]] +name = "stylers" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "03e306edf4b3cb5cff4b2e21b8895ed9e70fbd1bbb3a8dfb7e4cc245a763a955" +dependencies = [ + "levenshtein", + "litrs", + "proc-macro2", + "quote", +] + [[package]] name = "subtle" version = "2.6.1" diff --git a/Cargo.toml b/Cargo.toml index 2314225..ce18064 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,3 +1,3 @@ [workspace] -members = ["backend", "frontend"] +members = ["backend", "common", "frontend"] resolver = "2" diff --git a/backend/Cargo.toml b/backend/Cargo.toml index 21b1a5d..606ba84 100644 --- a/backend/Cargo.toml +++ b/backend/Cargo.toml @@ -20,3 +20,5 @@ diesel = { version = "2.2", features = ["serde_json"] } diesel-async = { version = "0.5", features = ["postgres"] } serde_json = "1.0.128" diesel_async_migrations = { version = "0.15" } + +common = { path = "../common/" } diff --git a/backend/src/api.rs b/backend/src/api.rs new file mode 100644 index 0000000..477a1a9 --- /dev/null +++ b/backend/src/api.rs @@ -0,0 +1,115 @@ +pub mod demos { + use crate::UserSession; + use diesel_async::RunQueryDsl; + use diesel::prelude::*; + use axum::extract::{State, Path}; + use std::sync::Arc; + + struct DemoState { + upload_folder: std::path::PathBuf, + } + + pub fn router

(upload_folder: P) -> 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)) + .with_state(Arc::new(DemoState { + upload_folder: upload_folder.into(), + })) + } + + 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.filter(crate::schema::demos::dsl::steam_id.eq(steam_id as i64)); + let results: Vec = query.load(&mut crate::db_connection().await).await.unwrap(); + + Ok(axum::response::Json(results.into_iter().map(|demo| common::BaseDemoInfo { + id: demo.demo_id, + }).collect::>())) + } + + 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_file_path = user_folder.join(format!("{}.dem", timestamp_secs)); + + tokio::fs::write(demo_file_path, file_content).await.unwrap(); + + let query = diesel::dsl::insert_into(crate::schema::demos::dsl::demos).values(crate::models::Demo { + demo_id: timestamp_secs as i64, + steam_id: steam_id as i64, + }); + query.execute(&mut crate::db_connection().await).await.unwrap(); + + Ok(axum::response::Redirect::to("/")) + } + + async fn info(session: UserSession, Path(demo_id): Path) -> Result<(), axum::http::StatusCode> { + tracing::info!("Get info for Demo: {:?}", demo_id); + + Ok(()) + } +} + +pub mod steam { + use axum::extract::State; + use std::sync::Arc; + + pub fn router(url: &str, callback_path: &str) -> axum::Router { + 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())) + } + + async fn steam_login(State(openid): State>) -> Result { + let url = openid.get_redirect_url(); + + Ok(axum::response::Redirect::to(url)) + } + + async fn steam_callback( + State(openid): State>, + mut session: crate::UserSession, + request: axum::extract::Request, + ) -> Result { + tracing::info!("Steam Callback"); + + let query = request.uri().query().ok_or_else(|| { + tracing::error!("Missing query in parameters"); + axum::http::StatusCode::BAD_REQUEST + })?; + + let id = openid.verify(query).await.map_err(|e| { + tracing::error!("Verifying OpenID: {:?}", e); + axum::http::StatusCode::BAD_REQUEST + })?; + + session + .modify_data(|data| { + data.steam_id = Some(id); + }) + .await; + + Ok(axum::response::Redirect::to("/")) + } +} + +pub fn router() -> axum::Router { + axum::Router::new() + .nest("/steam/", steam::router("http://192.168.0.156:3000", "/api/steam/callback")) + .nest("/demos/", demos::router("uploads/")) +} diff --git a/backend/src/lib.rs b/backend/src/lib.rs index 6f012c5..eb86c76 100644 --- a/backend/src/lib.rs +++ b/backend/src/lib.rs @@ -1,5 +1,3 @@ -use serde::{Deserialize, Serialize}; - pub mod models; pub mod schema; @@ -34,3 +32,5 @@ pub async fn get_demo_from_upload(name: &str, mut form: axum::extract::Multipart None } + +pub mod api; diff --git a/backend/src/main.rs b/backend/src/main.rs index 90302e2..1993547 100644 --- a/backend/src/main.rs +++ b/backend/src/main.rs @@ -1,11 +1,8 @@ use tracing_subscriber::prelude::__tracing_subscriber_SubscriberExt; use diesel::prelude::*; -use diesel_async::{RunQueryDsl, AsyncConnection, AsyncPgConnection}; +use diesel_async::RunQueryDsl; -static OPENID: std::sync::LazyLock = std::sync::LazyLock::new(|| { - steam_openid::SteamOpenId::new("http://192.168.0.156:3000", "/api/steam/callback").unwrap() -}); static UPLOAD_FOLDER: &str = "uploads/"; const MIGRATIONS: diesel_async_migrations::EmbeddedMigrations = diesel_async_migrations::embed_migrations!("../migrations/"); @@ -33,7 +30,7 @@ async fn main() { let session_layer = tower_sessions::SessionManagerLayer::new(session_store) .with_secure(false) .with_expiry(tower_sessions::Expiry::OnInactivity( - time::Duration::minutes(15), + time::Duration::hours(48), )); if !tokio::fs::try_exists(UPLOAD_FOLDER).await.unwrap_or(false) { @@ -41,14 +38,7 @@ async fn main() { } let router = axum::Router::new() - .nest_service( - "/api/", - axum::Router::new() - .route("/steam/callback", axum::routing::get(steam_callback)) - .route("/steam/login", axum::routing::get(steam_login)) - .route("/demos/upload", axum::routing::post(upload).layer(axum::extract::DefaultBodyLimit::max(1024*1024*500))) - .route("/demos/list", axum::routing::get(demos_list)) - ) + .nest("/api/", backend::api::router()) .layer(session_layer) .nest_service("/", tower_http::services::ServeDir::new("frontend/dist/")); @@ -56,71 +46,6 @@ async fn main() { axum::serve(listener, router).await.unwrap(); } -async fn upload(session: backend::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 = backend::get_demo_from_upload("demo", form).await.unwrap(); - - let user_folder = std::path::Path::new(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_file_path = user_folder.join(format!("{}.dem", timestamp_secs)); - - tokio::fs::write(demo_file_path, file_content).await.unwrap(); - - let query = diesel::dsl::insert_into(backend::schema::demos::dsl::demos).values(backend::models::Demo { - demo_id: timestamp_secs as i64, - steam_id: steam_id as i64, - }); - query.execute(&mut backend::db_connection().await).await.unwrap(); - - Ok(axum::response::Redirect::to("/")) -} - -async fn steam_login() -> Result { - let url = OPENID.get_redirect_url(); - - Ok(axum::response::Redirect::to(url)) -} - -async fn steam_callback( - mut session: backend::UserSession, - request: axum::extract::Request, -) -> Result { - tracing::info!("Steam Callback"); - - let query = request.uri().query().ok_or_else(|| { - tracing::error!("Missing query in parameters"); - axum::http::StatusCode::BAD_REQUEST - })?; - - let id = OPENID.verify(query).await.map_err(|e| { - tracing::error!("Verifying OpenID: {:?}", e); - axum::http::StatusCode::BAD_REQUEST - })?; - - session - .modify_data(|data| { - data.steam_id = Some(id); - }) - .await; - - Ok(axum::response::Redirect::to("/")) -} - -async fn demos_list(session: backend::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 = backend::schema::demos::dsl::demos.filter(backend::schema::demos::dsl::steam_id.eq(steam_id as i64)); - let results: Vec = query.load(&mut backend::db_connection().await).await.unwrap(); - - dbg!(&results); - +async fn demo_info(session: backend::UserSession) -> Result<(), axum::http::StatusCode> { Ok(()) } diff --git a/backend/src/usersession.rs b/backend/src/usersession.rs index cf72207..70acd11 100644 --- a/backend/src/usersession.rs +++ b/backend/src/usersession.rs @@ -1,6 +1,3 @@ -use diesel::prelude::*; -use diesel_async::RunQueryDsl; - #[derive(Debug, Clone, serde::Deserialize, serde::Serialize)] pub struct UserSessionData { pub steam_id: Option, diff --git a/common/Cargo.toml b/common/Cargo.toml new file mode 100644 index 0000000..aa48d9c --- /dev/null +++ b/common/Cargo.toml @@ -0,0 +1,7 @@ +[package] +name = "common" +version = "0.1.0" +edition = "2021" + +[dependencies] +serde = { version = "1", features = ["derive"] } diff --git a/common/src/lib.rs b/common/src/lib.rs new file mode 100644 index 0000000..36fe3f4 --- /dev/null +++ b/common/src/lib.rs @@ -0,0 +1,4 @@ +#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)] +pub struct BaseDemoInfo { + pub id: i64, +} diff --git a/frontend/Cargo.toml b/frontend/Cargo.toml index e24c371..c38f482 100644 --- a/frontend/Cargo.toml +++ b/frontend/Cargo.toml @@ -5,4 +5,8 @@ edition = "2021" [dependencies] leptos = { version = "0.6", features = ["csr", "nightly"] } +leptos_router = { version = "0.6", features = ["csr"] } reqwasm = "0.5.0" +stylers = { version = "0.3" } + +common = { path = "../common/" } diff --git a/frontend/Trunk.toml b/frontend/Trunk.toml new file mode 100644 index 0000000..0d37bd2 --- /dev/null +++ b/frontend/Trunk.toml @@ -0,0 +1,4 @@ +[[hooks]] +stage = "post_build" +command = "sh" +command_arguments = ["-c", "cp ../target/stylers/main.css $TRUNK_STAGING_DIR/"] diff --git a/frontend/index.html b/frontend/index.html index a4acdc4..0adfe97 100644 --- a/frontend/index.html +++ b/frontend/index.html @@ -1,5 +1,7 @@ - + + + diff --git a/frontend/src/demo.rs b/frontend/src/demo.rs new file mode 100644 index 0000000..402c76f --- /dev/null +++ b/frontend/src/demo.rs @@ -0,0 +1,17 @@ +use leptos::*; + +#[leptos::component] +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(); + dbg!(res.text().await); + 0 + }); + + view! { +

Demo - {id}

+ } +} diff --git a/frontend/src/lib.rs b/frontend/src/lib.rs new file mode 100644 index 0000000..b57c0a5 --- /dev/null +++ b/frontend/src/lib.rs @@ -0,0 +1,107 @@ +use leptos::*; +use leptos_router::A; + +mod demo; +pub use demo::Demo; + +#[leptos::component] +pub fn demo_list_entry(demo: common::BaseDemoInfo) -> impl leptos::IntoView { + view! { +
  • + Demo: {demo.id} +
  • + } +} + +#[leptos::component] +pub fn steam_login(height: &'static str, width: &'static str) -> impl leptos::IntoView { + view! { + + Steam Login + + } +} + +#[leptos::component] +pub fn upload_demo() -> impl leptos::IntoView { + use leptos_router::Form; + + view! { +
    +
    +

    Select File to upload

    + + +
    +
    + } +} + +#[leptos::component] +pub fn top_bar() -> impl leptos::IntoView { + let style = stylers::style! { + "TopBar", + .bar { + width: 100%; + height: 4vh; + padding-top: 0.5vh; + padding-bottom: 0.5vh; + + background-color: #28282f; + color: #d5d5d5; + } + + .group { + display: inline-block; + } + + .elem { + display: inline-block; + } + + .logo { + color: #d5d5d5; + } + }; + + view! {class = style, +
    + + + + +
    +
    + Upload Demo +
    + +
    + +
    +
    +
    + } +} + +#[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 + }); + + view! { +
    +
    +

    Demos

    + +
    +
      + { move || demo_data.get().unwrap_or_default().into_iter().map(|demo| crate::DemoListEntry(DemoListEntryProps { + demo + })).collect::>() } +
    +
    + } +} diff --git a/frontend/src/main.rs b/frontend/src/main.rs index 6553d45..635d60e 100644 --- a/frontend/src/main.rs +++ b/frontend/src/main.rs @@ -1,11 +1,13 @@ use leptos::*; -use leptos::prelude::*; +use leptos_router::*; -async fn load_demos() -> usize { +use frontend::{UploadDemo, TopBar, Homepage, Demo}; + +async fn load_demos() -> Vec { let res = reqwasm::http::Request::get("/api/demos/list").send().await.unwrap(); - dbg!(res); + let demos: Vec = res.json().await.unwrap(); - 0 + demos } fn main() { @@ -14,16 +16,16 @@ fn main() { }); mount_to_body(move || view! { -

    "Hello, world!"

    - Steam Login { move || match async_data.get() { - None => 123, - Some(v) => v, - } } - -
    - Select File to upload - - -
    + + +
    + + + + +
    +
    }) } diff --git a/migrations/2024-09-07-151517_demos/up.sql b/migrations/2024-09-07-151517_demos/up.sql index 46fba7f..8ac6e34 100644 --- a/migrations/2024-09-07-151517_demos/up.sql +++ b/migrations/2024-09-07-151517_demos/up.sql @@ -1,5 +1,6 @@ -- Your SQL goes here CREATE TABLE IF NOT EXISTS demos ( - steam_id bigint PRIMARY KEY, - demo_id bigint + steam_id bigint, + demo_id bigint, + PRIMARY KEY(steam_id, demo_id) ) diff --git a/src/schema.rs b/src/schema.rs index 5244f6d..401442f 100644 --- a/src/schema.rs +++ b/src/schema.rs @@ -1,9 +1,9 @@ // @generated automatically by Diesel CLI. diesel::table! { - demos (steam_id) { + demos (steam_id, demo_id) { steam_id -> Int8, - demo_id -> Nullable, + demo_id -> Int8, } }