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! {
+
+
+
+ }
+}
+
+#[leptos::component]
+pub fn upload_demo() -> impl leptos::IntoView {
+ use leptos_router::Form;
+
+ view! {
+
+ }
+}
+
+#[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,
+
+ }
+}
+
+#[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,
- } }
-
-
+
+
+
+
+
+
+
+
+
})
}
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,
}
}