Get and store steam username and display it on the homepage

This commit is contained in:
Lol3rrr
2024-09-10 17:48:06 +02:00
parent d3658be232
commit 9a033b7190
15 changed files with 528 additions and 103 deletions

255
Cargo.lock generated
View File

@@ -54,6 +54,12 @@ dependencies = [
"syn", "syn",
] ]
[[package]]
name = "atomic-waker"
version = "1.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0"
[[package]] [[package]]
name = "attribute-derive" name = "attribute-derive"
version = "0.9.2" version = "0.9.2"
@@ -157,6 +163,7 @@ dependencies = [
"diesel-async", "diesel-async",
"diesel_async_migrations", "diesel_async_migrations",
"futures-util", "futures-util",
"reqwest 0.12.7",
"serde", "serde",
"serde_json", "serde_json",
"steam-openid", "steam-openid",
@@ -850,6 +857,25 @@ dependencies = [
"tracing", "tracing",
] ]
[[package]]
name = "h2"
version = "0.4.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "524e8ac6999421f49a846c2d4411f337e53497d8ec55d67753beffa43c5d9205"
dependencies = [
"atomic-waker",
"bytes",
"fnv",
"futures-core",
"futures-sink",
"http 1.1.0",
"indexmap",
"slab",
"tokio",
"tokio-util",
"tracing",
]
[[package]] [[package]]
name = "half" name = "half"
version = "2.4.1" version = "2.4.1"
@@ -980,7 +1006,7 @@ dependencies = [
"futures-channel", "futures-channel",
"futures-core", "futures-core",
"futures-util", "futures-util",
"h2", "h2 0.3.26",
"http 0.2.12", "http 0.2.12",
"http-body 0.4.6", "http-body 0.4.6",
"httparse", "httparse",
@@ -1003,6 +1029,7 @@ dependencies = [
"bytes", "bytes",
"futures-channel", "futures-channel",
"futures-util", "futures-util",
"h2 0.4.6",
"http 1.1.0", "http 1.1.0",
"http-body 1.0.1", "http-body 1.0.1",
"httparse", "httparse",
@@ -1011,6 +1038,24 @@ dependencies = [
"pin-project-lite", "pin-project-lite",
"smallvec", "smallvec",
"tokio", "tokio",
"want",
]
[[package]]
name = "hyper-rustls"
version = "0.27.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "08afdbb5c31130e3034af566421053ab03787c640246a446327f550d11bcb333"
dependencies = [
"futures-util",
"http 1.1.0",
"hyper 1.4.1",
"hyper-util",
"rustls",
"rustls-pki-types",
"tokio",
"tokio-rustls",
"tower-service",
] ]
[[package]] [[package]]
@@ -1026,6 +1071,22 @@ dependencies = [
"tokio-native-tls", "tokio-native-tls",
] ]
[[package]]
name = "hyper-tls"
version = "0.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "70206fc6890eaca9fde8a0bf71caa2ddfc9fe045ac9e5c70df101a7dbde866e0"
dependencies = [
"bytes",
"http-body-util",
"hyper 1.4.1",
"hyper-util",
"native-tls",
"tokio",
"tokio-native-tls",
"tower-service",
]
[[package]] [[package]]
name = "hyper-util" name = "hyper-util"
version = "0.1.7" version = "0.1.7"
@@ -1033,12 +1094,17 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cde7055719c54e36e95e8719f95883f22072a48ede39db7fc17a4e1d5281e9b9" checksum = "cde7055719c54e36e95e8719f95883f22072a48ede39db7fc17a4e1d5281e9b9"
dependencies = [ dependencies = [
"bytes", "bytes",
"futures-channel",
"futures-util", "futures-util",
"http 1.1.0", "http 1.1.0",
"http-body 1.0.1", "http-body 1.0.1",
"hyper 1.4.1", "hyper 1.4.1",
"pin-project-lite", "pin-project-lite",
"socket2",
"tokio", "tokio",
"tower",
"tower-service",
"tracing",
] ]
[[package]] [[package]]
@@ -1908,11 +1974,11 @@ dependencies = [
"encoding_rs", "encoding_rs",
"futures-core", "futures-core",
"futures-util", "futures-util",
"h2", "h2 0.3.26",
"http 0.2.12", "http 0.2.12",
"http-body 0.4.6", "http-body 0.4.6",
"hyper 0.14.30", "hyper 0.14.30",
"hyper-tls", "hyper-tls 0.5.0",
"ipnet", "ipnet",
"js-sys", "js-sys",
"log", "log",
@@ -1921,12 +1987,12 @@ dependencies = [
"once_cell", "once_cell",
"percent-encoding", "percent-encoding",
"pin-project-lite", "pin-project-lite",
"rustls-pemfile", "rustls-pemfile 1.0.4",
"serde", "serde",
"serde_json", "serde_json",
"serde_urlencoded", "serde_urlencoded",
"sync_wrapper 0.1.2", "sync_wrapper 0.1.2",
"system-configuration", "system-configuration 0.5.1",
"tokio", "tokio",
"tokio-native-tls", "tokio-native-tls",
"tower-service", "tower-service",
@@ -1937,6 +2003,64 @@ dependencies = [
"winreg", "winreg",
] ]
[[package]]
name = "reqwest"
version = "0.12.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f8f4955649ef5c38cc7f9e8aa41761d48fb9677197daea9984dc54f56aad5e63"
dependencies = [
"base64 0.22.1",
"bytes",
"encoding_rs",
"futures-core",
"futures-util",
"h2 0.4.6",
"http 1.1.0",
"http-body 1.0.1",
"http-body-util",
"hyper 1.4.1",
"hyper-rustls",
"hyper-tls 0.6.0",
"hyper-util",
"ipnet",
"js-sys",
"log",
"mime",
"native-tls",
"once_cell",
"percent-encoding",
"pin-project-lite",
"rustls-pemfile 2.1.3",
"serde",
"serde_json",
"serde_urlencoded",
"sync_wrapper 1.0.1",
"system-configuration 0.6.1",
"tokio",
"tokio-native-tls",
"tower-service",
"url",
"wasm-bindgen",
"wasm-bindgen-futures",
"web-sys",
"windows-registry",
]
[[package]]
name = "ring"
version = "0.17.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c17fa4cb658e3583423e915b9f3acc01cceaee1860e33d59ebae66adc3a2dc0d"
dependencies = [
"cc",
"cfg-if",
"getrandom",
"libc",
"spin",
"untrusted",
"windows-sys 0.52.0",
]
[[package]] [[package]]
name = "rstml" name = "rstml"
version = "0.11.2" version = "0.11.2"
@@ -1976,6 +2100,19 @@ dependencies = [
"windows-sys 0.52.0", "windows-sys 0.52.0",
] ]
[[package]]
name = "rustls"
version = "0.23.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f2dabaac7466917e566adb06783a81ca48944c6898a1b08b9374106dd671f4c8"
dependencies = [
"once_cell",
"rustls-pki-types",
"rustls-webpki",
"subtle",
"zeroize",
]
[[package]] [[package]]
name = "rustls-pemfile" name = "rustls-pemfile"
version = "1.0.4" version = "1.0.4"
@@ -1985,6 +2122,33 @@ dependencies = [
"base64 0.21.7", "base64 0.21.7",
] ]
[[package]]
name = "rustls-pemfile"
version = "2.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "196fe16b00e106300d3e45ecfcb764fa292a535d7326a29a5875c579c7417425"
dependencies = [
"base64 0.22.1",
"rustls-pki-types",
]
[[package]]
name = "rustls-pki-types"
version = "1.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fc0a2ce646f8655401bb81e7927b812614bd5d91dbc968696be50603510fcaf0"
[[package]]
name = "rustls-webpki"
version = "0.102.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "64ca1bc8749bd4cf37b5ce386cc146580777b4e8572c7b97baf22c83f444bee9"
dependencies = [
"ring",
"rustls-pki-types",
"untrusted",
]
[[package]] [[package]]
name = "rustversion" name = "rustversion"
version = "1.0.17" version = "1.0.17"
@@ -2308,7 +2472,7 @@ checksum = "b87d7fa6404e4d3227681e436ec9b2255cc91acd93a1b6a7fa00a87484e9c7e0"
dependencies = [ dependencies = [
"lazy_static", "lazy_static",
"regex", "regex",
"reqwest", "reqwest 0.11.27",
"serde", "serde",
"serde_urlencoded", "serde_urlencoded",
"url", "url",
@@ -2383,6 +2547,9 @@ name = "sync_wrapper"
version = "1.0.1" version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a7065abeca94b6a8a577f9bd45aa0867a2238b74e8eb67cf10d492bc39351394" checksum = "a7065abeca94b6a8a577f9bd45aa0867a2238b74e8eb67cf10d492bc39351394"
dependencies = [
"futures-core",
]
[[package]] [[package]]
name = "system-configuration" name = "system-configuration"
@@ -2392,7 +2559,18 @@ checksum = "ba3a3adc5c275d719af8cb4272ea1c4a6d668a777f37e115f6d11ddbc1c8e0e7"
dependencies = [ dependencies = [
"bitflags 1.3.2", "bitflags 1.3.2",
"core-foundation", "core-foundation",
"system-configuration-sys", "system-configuration-sys 0.5.0",
]
[[package]]
name = "system-configuration"
version = "0.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3c879d448e9d986b661742763247d3693ed13609438cf3d006f51f5368a5ba6b"
dependencies = [
"bitflags 2.6.0",
"core-foundation",
"system-configuration-sys 0.6.0",
] ]
[[package]] [[package]]
@@ -2405,6 +2583,16 @@ dependencies = [
"libc", "libc",
] ]
[[package]]
name = "system-configuration-sys"
version = "0.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8e1d1b10ced5ca923a1fcb8d03e96b8d3268065d724548c0211415ff6ac6bac4"
dependencies = [
"core-foundation-sys",
"libc",
]
[[package]] [[package]]
name = "tempfile" name = "tempfile"
version = "3.12.0" version = "3.12.0"
@@ -2557,6 +2745,17 @@ dependencies = [
"whoami", "whoami",
] ]
[[package]]
name = "tokio-rustls"
version = "0.26.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0c7bc40d0e5a97695bb96e27995cd3a08538541b0a846f65bba7a359f36700d4"
dependencies = [
"rustls",
"rustls-pki-types",
"tokio",
]
[[package]] [[package]]
name = "tokio-util" name = "tokio-util"
version = "0.7.12" version = "0.7.12"
@@ -2863,6 +3062,12 @@ version = "0.2.5"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "229730647fbc343e3a80e463c1db7f78f3855d3f3739bee0dda773c9a037c90a" checksum = "229730647fbc343e3a80e463c1db7f78f3855d3f3739bee0dda773c9a037c90a"
[[package]]
name = "untrusted"
version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1"
[[package]] [[package]]
name = "url" name = "url"
version = "2.5.2" version = "2.5.2"
@@ -3072,6 +3277,36 @@ version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
[[package]]
name = "windows-registry"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e400001bb720a623c1c69032f8e3e4cf09984deec740f007dd2b03ec864804b0"
dependencies = [
"windows-result",
"windows-strings",
"windows-targets 0.52.6",
]
[[package]]
name = "windows-result"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1d1043d8214f791817bab27572aaa8af63732e11bf84aa21a45a78d6c317ae0e"
dependencies = [
"windows-targets 0.52.6",
]
[[package]]
name = "windows-strings"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4cd9b125c486025df0eabcb585e62173c6c9eddcec5d117d3b6e8c30e2ee4d10"
dependencies = [
"windows-result",
"windows-targets 0.52.6",
]
[[package]] [[package]]
name = "windows-sys" name = "windows-sys"
version = "0.48.0" version = "0.48.0"
@@ -3271,3 +3506,9 @@ dependencies = [
"quote", "quote",
"syn", "syn",
] ]
[[package]]
name = "zeroize"
version = "1.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ced3678a2879b30306d323f4542626697a464a97c0a07c9aebf7ebca65cd4dde"

View File

@@ -21,4 +21,6 @@ diesel-async = { version = "0.5", features = ["postgres"] }
serde_json = "1.0.128" serde_json = "1.0.128"
diesel_async_migrations = { version = "0.15" } diesel_async_migrations = { version = "0.15" }
reqwest = { version = "0.12", features = ["json"] }
common = { path = "../common/" } common = { path = "../common/" }

View File

@@ -66,7 +66,22 @@ pub mod demos {
pub mod steam { pub mod steam {
use axum::extract::State; use axum::extract::State;
use std::sync::Arc; use serde::Deserialize;
use std::{sync::Arc, collections::HashMap};
use diesel::prelude::*;
use diesel_async::RunQueryDsl;
#[derive(Debug, Deserialize)]
struct ProfileInfoResponse {
players: Vec<ProfileInfo>
}
#[derive(Debug, Deserialize)]
struct ProfileInfo {
pub steamid: String,
pub personaname: String,
#[serde(flatten)]
other: HashMap<String, serde_json::Value>,
}
pub fn router(url: &str, callback_path: &str) -> axum::Router { pub fn router(url: &str, callback_path: &str) -> axum::Router {
axum::Router::new() axum::Router::new()
@@ -98,6 +113,28 @@ pub mod steam {
axum::http::StatusCode::BAD_REQUEST axum::http::StatusCode::BAD_REQUEST
})?; })?;
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 {
Ok(r) => r,
Err(e) => {
tracing::error!("Getting Steam Profile Info: {:?}", e);
return Err(axum::http::StatusCode::INTERNAL_SERVER_ERROR);
}
};
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)));
tracing::debug!("Running Query: {:?}", query);
if let Err(e) = query.execute(&mut db_con).await {
tracing::error!("Inserting/Updating user steam info: {:?}", e);
}
}
session session
.modify_data(|data| { .modify_data(|data| {
data.steam_id = Some(id); data.steam_id = Some(id);
@@ -109,23 +146,46 @@ pub mod steam {
} }
pub mod user { pub mod user {
use diesel::prelude::*;
use diesel_async::RunQueryDsl;
pub fn router() -> axum::Router { pub fn router() -> axum::Router {
axum::Router::new() axum::Router::new()
.route("/status", axum::routing::get(status)) .route("/status", axum::routing::get(status))
} }
async fn status(session: crate::UserSession) -> axum::http::StatusCode { async fn status(session: crate::UserSession) -> Result<axum::response::Json<common::UserStatus>, reqwest::StatusCode> {
if session.data().steam_id.is_some() { let steam_id = match session.data().steam_id {
axum::http::StatusCode::OK Some(s) => s,
} else { None => {
axum::http::StatusCode::UNAUTHORIZED return Err(axum::http::StatusCode::UNAUTHORIZED);
} }
};
tracing::info!("Load user info");
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 mut result = query.load::<crate::models::User>(&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,
}))
} }
} }
pub fn router() -> axum::Router { pub fn router() -> axum::Router {
axum::Router::new() axum::Router::new()
.nest("/steam/", steam::router("http://192.168.0.156:3000", "/api/steam/callback")) .nest("/steam/", steam::router("http://localhost:3000", "/api/steam/callback"))
.nest("/demos/", demos::router("uploads/")) .nest("/demos/", demos::router("uploads/"))
.nest("/user/", user::router()) .nest("/user/", user::router())
} }

View File

@@ -1,3 +1,5 @@
use std::collections::HashMap;
use diesel::prelude::*; use diesel::prelude::*;
use diesel_async::RunQueryDsl; use diesel_async::RunQueryDsl;
@@ -45,15 +47,19 @@ impl tower_sessions::SessionStore for DieselStore {
let data = serde_json::to_value(&session_record.data).unwrap(); let data = serde_json::to_value(&session_record.data).unwrap();
let expiry_date = self.expiry_to_string(&session_record.expiry_date); 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::<crate::UserSessionData>(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) let query = diesel::dsl::insert_into(crate::schema::sessions::dsl::sessions)
.values(crate::models::Session { .values(crate::models::Session {
id: db_id, id: db_id,
data: data.clone(), steamid: steamid.clone(),
expiry_date: expiry_date.clone(), expiry_date: expiry_date.clone(),
}) })
.on_conflict(crate::schema::sessions::dsl::id) .on_conflict(crate::schema::sessions::dsl::id)
.do_update() .do_update()
.set((crate::schema::sessions::dsl::data.eq(data), 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; let mut connection = crate::db_connection().await;
@@ -76,11 +82,23 @@ impl tower_sessions::SessionStore for DieselStore {
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() {
return Ok(None);
}
let result = result.pop().unwrap(); let result = result.pop().unwrap();
let data = {
let mut tmp = HashMap::<String, _>::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
};
Ok(Some(tower_sessions::session::Record { Ok(Some(tower_sessions::session::Record {
id: tower_sessions::session::Id(self.bytes_to_id(result.id)), id: tower_sessions::session::Id(self.bytes_to_id(result.id)),
data: serde_json::from_value(result.data).unwrap(), data: data,
expiry_date: self.string_to_expiry(&result.expiry_date), expiry_date: self.string_to_expiry(&result.expiry_date),
})) }))
} }

View File

@@ -34,3 +34,35 @@ pub async fn get_demo_from_upload(name: &str, mut form: axum::extract::Multipart
} }
pub mod api; pub mod api;
pub mod steam_api {
use serde::Deserialize;
pub struct Client {
http: reqwest::Client,
api_key: String,
}
#[derive(Debug, Deserialize)]
struct Response<T> {
response: T,
}
impl Client {
pub fn new<IS>(api_key: IS) -> Self where IS: Into<String> {
Self {
http: reqwest::Client::new(),
api_key: api_key.into(),
}
}
pub async fn get<T>(&self, path: &str, args: &[(&str, &str)]) -> Result<T, ()> 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::<Response<T>>().await.map(|r| r.response).map_err(|e| ())
}
}
}

View File

@@ -5,7 +5,7 @@ use diesel::prelude::*;
#[diesel(check_for_backend(diesel::pg::Pg))] #[diesel(check_for_backend(diesel::pg::Pg))]
pub struct Session { pub struct Session {
pub id: Vec<i64>, pub id: Vec<i64>,
pub data: serde_json::Value, pub steamid: Option<String>,
pub expiry_date: String, pub expiry_date: String,
} }
@@ -16,3 +16,11 @@ pub struct Demo {
pub steam_id: i64, pub steam_id: i64,
pub demo_id: i64, pub demo_id: i64,
} }
#[derive(Queryable, Selectable, Insertable, Debug)]
#[diesel(table_name = crate::schema::users)]
#[diesel(check_for_backend(diesel::pg::Pg))]
pub struct User {
pub steamid: String,
pub name: String,
}

View File

@@ -1,14 +1,21 @@
diesel::table! { diesel::table! {
sessions (id) { sessions (id) {
id -> Array<BigInt>, id -> Array<BigInt>,
data -> Jsonb, steamid -> Nullable<Text>,
expiry_date -> Text, expiry_date -> Text,
} }
} }
diesel::table! { diesel::table! {
demos (steam_id) { demos (steam_id, demo_id) {
steam_id -> BigInt, steam_id -> BigInt,
demo_id -> BigInt demo_id -> BigInt
} }
} }
diesel::table! {
users (steamid) {
steamid -> Text,
name -> Text
}
}

View File

@@ -15,7 +15,7 @@ pub struct UserSession {
} }
impl UserSession { impl UserSession {
const KEY: &'static str = "user.data"; pub const KEY: &'static str = "user.data";
pub fn data(&self) -> &UserSessionData { pub fn data(&self) -> &UserSessionData {
&self.data &self.data

View File

@@ -2,3 +2,9 @@
pub struct BaseDemoInfo { pub struct BaseDemoInfo {
pub id: i64, pub id: i64,
} }
#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
pub struct UserStatus {
pub name: String,
pub steamid: String,
}

View File

@@ -4,6 +4,15 @@ use leptos_router::A;
mod demo; mod demo;
pub use demo::Demo; pub use demo::Demo;
mod navbar;
pub use navbar::TopBar;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum DemoUploadStatus {
Hidden,
Shown,
}
#[leptos::component] #[leptos::component]
pub fn demo_list_entry(demo: common::BaseDemoInfo) -> impl leptos::IntoView { pub fn demo_list_entry(demo: common::BaseDemoInfo) -> impl leptos::IntoView {
view! { view! {
@@ -14,92 +23,44 @@ pub fn demo_list_entry(demo: common::BaseDemoInfo) -> impl leptos::IntoView {
} }
#[leptos::component] #[leptos::component]
pub fn steam_login(height: &'static str, width: &'static str) -> impl leptos::IntoView { pub fn upload_demo(shown: ReadSignal<DemoUploadStatus>, update_shown: WriteSignal<DemoUploadStatus>) -> impl leptos::IntoView {
let user_status = create_resource(|| (), |_| async move {
let res = reqwasm::http::Request::get("/api/user/status").send().await.unwrap();
res.status() == 200
});
let tmp = move || if user_status.get().unwrap_or(false) {
view! {
<p>Logged in</p>
}.into_any()
} else {
view! {
<a href="/api/steam/login">
<img src="https://community.akamai.steamstatic.com/public/images/signinthroughsteam/sits_01.png" alt="Steam Login" style=format!("height: {height}; width: {width}") />
</a>
}.into_any()
};
view! {
{ tmp }
}
}
#[leptos::component]
pub fn upload_demo() -> impl leptos::IntoView {
use leptos_router::Form; use leptos_router::Form;
view! { let style = stylers::style! {
<div> "UploadDemo",
.container {
position: absolute;
left: 25vw;
top: 15vh;
width: 48vw;
height: 18vh;
padding: 1vh 1vw;
color: #f1f1f1;
background-color: #42424d;
border-radius: 10px;
display: none;
}
.container.shown {
display: block;
}
};
view! {class = style,
<div class="container" class:shown=move || shown() == DemoUploadStatus::Shown>
<h3>Upload a Demo</h3>
<Form action="/api/demos/upload" method="post" enctype="multipart/form-data".to_string()> <Form action="/api/demos/upload" method="post" enctype="multipart/form-data".to_string()>
<p> Select File to upload </p> <p> Select File to upload </p>
<input type="file" name="demo" id="demo"></input> <input type="file" name="demo" id="demo"></input>
<input type="submit" value="Upload Image" name="submit"></input> <input type="submit" value="Upload Image" name="submit"></input>
</Form> </Form>
</div> <button on:click=move |_| update_shown(DemoUploadStatus::Hidden)>
} Close
} </button>
#[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;
display: grid;
grid-template-columns: 15vw auto auto calc(4vh * (180/35) + 20px);
}
.group {
display: block;
width: 30vw;
}
.elem {
display: inline-block;
}
.logo {
color: #d5d5d5;
width: 15vw;
font-size: 24px;
padding: 0px;
margin: 0px;
}
};
view! {class = style,
<div class="bar">
<A href="/">
<p class="logo">Knifer</p>
</A>
<div class="elem">
Upload Demo
</div>
<div class="elem" style="grid-column-start: 4">
<SteamLogin height="4vh" width="auto" />
</div>
</div> </div>
} }
} }
@@ -116,7 +77,6 @@ pub fn homepage() -> impl leptos::IntoView {
<div> <div>
<div> <div>
<h2>Demos</h2> <h2>Demos</h2>
<UploadDemo />
</div> </div>
<ul> <ul>
{ move || demo_data.get().unwrap_or_default().into_iter().map(|demo| crate::DemoListEntry(DemoListEntryProps { { move || demo_data.get().unwrap_or_default().into_iter().map(|demo| crate::DemoListEntry(DemoListEntryProps {

View File

@@ -15,12 +15,16 @@ fn main() {
load_demos().await 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! {
<Router> <Router>
<nav> <nav>
<TopBar /> <TopBar update_demo_visible=upload_demo_write />
</nav> </nav>
<main> <main>
<UploadDemo shown=upload_demo_read update_shown=upload_demo_write />
<Routes> <Routes>
<Route path="/" view=Homepage /> <Route path="/" view=Homepage />
<Route path="/demo/:id" view=Demo /> <Route path="/demo/:id" view=Demo />

80
frontend/src/navbar.rs Normal file
View File

@@ -0,0 +1,80 @@
use leptos::*;
use leptos_router::A;
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::<common::UserStatus>().await.map_err(|e| ())
});
let tmp = move || match user_status.get() {
Some(Ok(user)) =>
view! {
<p>{user.name}</p>
}.into_view(),
_ =>
view! {
<a href="/api/steam/login" rel="external">
<img src="https://community.akamai.steamstatic.com/public/images/signinthroughsteam/sits_01.png" alt="Steam Login" style=format!("height: {height}; width: {width}") />
</a>
}.into_view(),
};
view! {
{ tmp }
}
}
#[leptos::component]
pub fn top_bar(update_demo_visible: WriteSignal<DemoUploadStatus>) -> 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;
display: grid;
grid-template-columns: 15vw auto 10vw calc(4vh * (180/35) + 20px);
}
.elem {
display: inline-block;
margin-top: auto;
margin-bottom: auto;
}
.logo {
color: #d5d5d5;
width: 15vw;
font-size: 24px;
padding: 0px;
margin: 0px;
}
};
view! {class = style,
<div class="bar">
<A href="/">
<p class="logo">Knifer</p>
</A>
<div class="elem" style="grid-column-start: 3">
<button on:click=move |_| update_demo_visible(DemoUploadStatus::Shown)>
Upload Demo
</button>
</div>
<div class="elem" style="grid-column-start: 4">
<SteamLogin height="4vh" width="auto" />
</div>
</div>
}
}

View File

@@ -1,6 +1,6 @@
-- Your SQL goes here -- Your SQL goes here
CREATE TABLE IF NOT EXISTS sessions ( CREATE TABLE IF NOT EXISTS sessions (
id bigint[2] PRIMARY KEY, id bigint[2] PRIMARY KEY,
data jsonb, steamid TEXT,
expiry_date TEXT expiry_date TEXT
) )

View File

@@ -0,0 +1,2 @@
-- This file should undo anything in `up.sql`
DROP TABLE users

View File

@@ -0,0 +1,5 @@
-- Your SQL goes here
CREATE TABLE IF NOT EXISTS users (
steamid TEXT PRIMARY KEY,
name TEXT
)