From 04e21708b28233d752a2a38851e3f2db1d5b24c5 Mon Sep 17 00:00:00 2001 From: code002lover Date: Thu, 4 Dec 2025 15:15:47 +0100 Subject: [PATCH] feat: Add game thumbnail fetching from external APIs, migrate --- Cargo.lock | 691 +++++++++++++++++++++++++++++++++++- backend/Cargo.toml | 1 + backend/src/auth.rs | 24 +- backend/src/main.rs | 123 +++++-- frontend/src/GameFilter.tsx | 12 +- frontend/src/GameImage.tsx | 44 +++ state.json | 46 +-- 7 files changed, 882 insertions(+), 59 deletions(-) create mode 100644 frontend/src/GameImage.tsx diff --git a/Cargo.lock b/Cargo.lock index 5838ea3..d596d8b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -110,6 +110,7 @@ dependencies = [ "prost", "prost-build", "prost-types", + "reqwest", "rocket", "rocket_prost_responder_derive", "serde", @@ -162,6 +163,12 @@ dependencies = [ "virtue", ] +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + [[package]] name = "bitflags" version = "2.10.0" @@ -250,6 +257,22 @@ dependencies = [ "version_check", ] +[[package]] +name = "core-foundation" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91e195e091a93c46f7102ec7818a2aa394e1e1771c3ab4825963fa03e45afb8f" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "core-foundation-sys" +version = "0.8.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" + [[package]] name = "crossbeam-utils" version = "0.8.21" @@ -312,13 +335,24 @@ version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b035a542cf7abf01f2e3c4d5a7acbaebfefe120ae4efc7bde3df98186e4b8af7" dependencies = [ - "bitflags", + "bitflags 2.10.0", "proc-macro2", "proc-macro2-diagnostics", "quote", "syn", ] +[[package]] +name = "displaydoc" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "dunce" version = "1.0.5" @@ -400,6 +434,30 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "77ce24cb58228fbb8aa041425bb1050850ac19177686ea6e0f41a70416f56fdb" +[[package]] +name = "foreign-types" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" +dependencies = [ + "foreign-types-shared", +] + +[[package]] +name = "foreign-types-shared" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" + +[[package]] +name = "form_urlencoded" +version = "1.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb4cb245038516f5f85277875cdaa4f7d2c9a0fa0468de06ed190163b1581fcf" +dependencies = [ + "percent-encoding", +] + [[package]] name = "fs_extra" version = "1.3.0" @@ -634,6 +692,19 @@ dependencies = [ "http", ] +[[package]] +name = "http-body-util" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b021d93e26becf5dc7e1b75b1bed1fd93124b374ceb73f43d4d4eafec896a64a" +dependencies = [ + "bytes", + "futures-core", + "http", + "http-body", + "pin-project-lite", +] + [[package]] name = "httparse" version = "1.10.1" @@ -666,6 +737,39 @@ dependencies = [ "pin-utils", "smallvec", "tokio", + "want", +] + +[[package]] +name = "hyper-rustls" +version = "0.27.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3c93eb611681b207e1fe55d5a71ecf91572ec8a6705cdb6857f7d8d5242cf58" +dependencies = [ + "http", + "hyper", + "hyper-util", + "rustls", + "rustls-pki-types", + "tokio", + "tokio-rustls", + "tower-service", +] + +[[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", + "hyper-util", + "native-tls", + "tokio", + "tokio-native-tls", + "tower-service", ] [[package]] @@ -674,13 +778,126 @@ version = "0.1.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "52e9a2a24dc5c6821e71a7030e1e14b7b632acac55c40e9d2e082c621261bb56" dependencies = [ + "base64", "bytes", + "futures-channel", "futures-core", + "futures-util", "http", "http-body", "hyper", + "ipnet", + "libc", + "percent-encoding", "pin-project-lite", + "socket2", + "system-configuration", "tokio", + "tower-service", + "tracing", + "windows-registry", +] + +[[package]] +name = "icu_collections" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c6b649701667bbe825c3b7e6388cb521c23d88644678e83c0c4d0a621a34b43" +dependencies = [ + "displaydoc", + "potential_utf", + "yoke", + "zerofrom", + "zerovec", +] + +[[package]] +name = "icu_locale_core" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "edba7861004dd3714265b4db54a3c390e880ab658fec5f7db895fae2046b5bb6" +dependencies = [ + "displaydoc", + "litemap", + "tinystr", + "writeable", + "zerovec", +] + +[[package]] +name = "icu_normalizer" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f6c8828b67bf8908d82127b2054ea1b4427ff0230ee9141c54251934ab1b599" +dependencies = [ + "icu_collections", + "icu_normalizer_data", + "icu_properties", + "icu_provider", + "smallvec", + "zerovec", +] + +[[package]] +name = "icu_normalizer_data" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7aedcccd01fc5fe81e6b489c15b247b8b0690feb23304303a9e560f37efc560a" + +[[package]] +name = "icu_properties" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e93fcd3157766c0c8da2f8cff6ce651a31f0810eaa1c51ec363ef790bbb5fb99" +dependencies = [ + "icu_collections", + "icu_locale_core", + "icu_properties_data", + "icu_provider", + "zerotrie", + "zerovec", +] + +[[package]] +name = "icu_properties_data" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "02845b3647bb045f1100ecd6480ff52f34c35f82d9880e029d329c21d1054899" + +[[package]] +name = "icu_provider" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85962cf0ce02e1e0a629cc34e7ca3e373ce20dda4c4d7294bbd0bf1fdb59e614" +dependencies = [ + "displaydoc", + "icu_locale_core", + "writeable", + "yoke", + "zerofrom", + "zerotrie", + "zerovec", +] + +[[package]] +name = "idna" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b0875f23caa03898994f6ddc501886a45c7d3d62d04d2d90788d47be1b1e4de" +dependencies = [ + "idna_adapter", + "smallvec", + "utf8_iter", +] + +[[package]] +name = "idna_adapter" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3acae9609540aa318d1bc588455225fb2085b9ed0c4f6bd0d9d5bcd86f1a0344" +dependencies = [ + "icu_normalizer", + "icu_properties", ] [[package]] @@ -719,6 +936,22 @@ dependencies = [ "memoffset", ] +[[package]] +name = "ipnet" +version = "2.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "469fb0b9cefa57e3ef31275ee7cacb78f2fdca44e4765491884a2b119d4eb130" + +[[package]] +name = "iri-string" +version = "0.7.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4f867b9d1d896b67beb18518eda36fdb77a32ea590de864f1325b294a6d14397" +dependencies = [ + "memchr", + "serde", +] + [[package]] name = "is-terminal" version = "0.4.17" @@ -789,6 +1022,12 @@ version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "df1d3c3b53da64cf5760482273a98e575c651a67eec7f77df96b5b642de8f039" +[[package]] +name = "litemap" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6373607a59f0be73a39b6fe456b8192fcc3585f602af20751600e974dd455e77" + [[package]] name = "lock_api" version = "0.4.14" @@ -886,6 +1125,23 @@ version = "0.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1d87ecb2933e8aeadb3e3a02b828fed80a7528047e68b4f424523a0981a3a084" +[[package]] +name = "native-tls" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87de3442987e9dbec73158d5c715e7ad9072fda936bb03d19d7fa10e00520f0e" +dependencies = [ + "libc", + "log", + "openssl", + "openssl-probe", + "openssl-sys", + "schannel", + "security-framework", + "security-framework-sys", + "tempfile", +] + [[package]] name = "nu-ansi-term" version = "0.50.3" @@ -946,6 +1202,50 @@ version = "1.21.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" +[[package]] +name = "openssl" +version = "0.10.75" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08838db121398ad17ab8531ce9de97b244589089e290a384c900cb9ff7434328" +dependencies = [ + "bitflags 2.10.0", + "cfg-if", + "foreign-types", + "libc", + "once_cell", + "openssl-macros", + "openssl-sys", +] + +[[package]] +name = "openssl-macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "openssl-probe" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d05e27ee213611ffe7d6348b942e8f942b37114c00cc03cec254295a4a17852e" + +[[package]] +name = "openssl-sys" +version = "0.9.111" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "82cab2d520aa75e3c58898289429321eb788c3106963d0dc886ec7a5f4adc321" +dependencies = [ + "cc", + "libc", + "pkg-config", + "vcpkg", +] + [[package]] name = "parking_lot" version = "0.12.5" @@ -1020,6 +1320,21 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" +[[package]] +name = "pkg-config" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c" + +[[package]] +name = "potential_utf" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b73949432f5e2a09657003c25bca5e19a0e9c84f8058ca374f49e0ebe605af77" +dependencies = [ + "zerovec", +] + [[package]] name = "powerfmt" version = "0.2.0" @@ -1240,7 +1555,7 @@ version = "0.5.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ed2bf2547551a7053d6fdfafda3f938979645c44812fbfcda098faae3f1a362d" dependencies = [ - "bitflags", + "bitflags 2.10.0", ] [[package]] @@ -1298,6 +1613,46 @@ version = "0.8.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7a2d987857b319362043e95f5353c0535c1f58eec5336fdfcf626430af7def58" +[[package]] +name = "reqwest" +version = "0.12.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d0946410b9f7b082a427e4ef5c8ff541a88b357bc6c637c40db3a68ac70a36f" +dependencies = [ + "base64", + "bytes", + "encoding_rs", + "futures-core", + "h2", + "http", + "http-body", + "http-body-util", + "hyper", + "hyper-rustls", + "hyper-tls", + "hyper-util", + "js-sys", + "log", + "mime", + "native-tls", + "percent-encoding", + "pin-project-lite", + "rustls-pki-types", + "serde", + "serde_json", + "serde_urlencoded", + "sync_wrapper", + "tokio", + "tokio-native-tls", + "tower", + "tower-http", + "tower-service", + "url", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", +] + [[package]] name = "ring" version = "0.17.14" @@ -1410,7 +1765,7 @@ version = "1.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cd15f8a2c5551a84d56efdc1cd049089e409ac19a3072d5037a17fd70719ff3e" dependencies = [ - "bitflags", + "bitflags 2.10.0", "errno", "libc", "linux-raw-sys", @@ -1596,6 +1951,15 @@ dependencies = [ "smallvec", ] +[[package]] +name = "schannel" +version = "0.1.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "891d81b926048e76efe18581bf793546b4c0eaf8448d72be8de2bbee5fd166e1" +dependencies = [ + "windows-sys 0.61.2", +] + [[package]] name = "scoped-tls" version = "1.0.1" @@ -1608,6 +1972,29 @@ version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" +[[package]] +name = "security-framework" +version = "2.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "897b2245f0b511c87893af39b033e5ca9cce68824c4d7e7630b5a1d339658d02" +dependencies = [ + "bitflags 2.10.0", + "core-foundation", + "core-foundation-sys", + "libc", + "security-framework-sys", +] + +[[package]] +name = "security-framework-sys" +version = "2.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc1f0cbffaac4852523ce30d8bd3c5cdc873501d96ff467ca09b6767bb8cd5c0" +dependencies = [ + "core-foundation-sys", + "libc", +] + [[package]] name = "serde" version = "1.0.228" @@ -1660,6 +2047,18 @@ dependencies = [ "serde", ] +[[package]] +name = "serde_urlencoded" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd" +dependencies = [ + "form_urlencoded", + "itoa", + "ryu", + "serde", +] + [[package]] name = "sharded-slab" version = "0.1.7" @@ -1727,6 +2126,12 @@ dependencies = [ "memchr", ] +[[package]] +name = "stable_deref_trait" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ce2be8dc25455e1f91df71bfa12ad37d7af1092ae736f3a6cd0e37bc7810596" + [[package]] name = "state" version = "0.6.0" @@ -1753,6 +2158,47 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "sync_wrapper" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0bf256ce5efdfa370213c1dabab5935a12e49f2c58d15e9eac2870d3b4f27263" +dependencies = [ + "futures-core", +] + +[[package]] +name = "synstructure" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "system-configuration" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba3a3adc5c275d719af8cb4272ea1c4a6d668a777f37e115f6d11ddbc1c8e0e7" +dependencies = [ + "bitflags 1.3.2", + "core-foundation", + "system-configuration-sys", +] + +[[package]] +name = "system-configuration-sys" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a75fb188eb626b924683e3b95e3a48e63551fcfb51949de2f06a9d91dbee93c9" +dependencies = [ + "core-foundation-sys", + "libc", +] + [[package]] name = "tempfile" version = "3.23.0" @@ -1806,6 +2252,16 @@ dependencies = [ "time-core", ] +[[package]] +name = "tinystr" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42d3e9c45c09de15d06dd8acf5f4e0e399e85927b7f00711024eb7ae10fa4869" +dependencies = [ + "displaydoc", + "zerovec", +] + [[package]] name = "tinyvec" version = "1.10.0" @@ -1849,6 +2305,16 @@ dependencies = [ "syn", ] +[[package]] +name = "tokio-native-tls" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbae76ab933c85776efabc971569dd6119c580d8f5d448769dec1764bf796ef2" +dependencies = [ + "native-tls", + "tokio", +] + [[package]] name = "tokio-rustls" version = "0.26.4" @@ -1924,6 +2390,51 @@ version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5d99f8c9a7727884afe522e9bd5edbfc91a3312b36a77b5fb8926e4c31a41801" +[[package]] +name = "tower" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d039ad9159c98b70ecfd540b2573b97f7f52c3e8d9f8ad57a24b916a536975f9" +dependencies = [ + "futures-core", + "futures-util", + "pin-project-lite", + "sync_wrapper", + "tokio", + "tower-layer", + "tower-service", +] + +[[package]] +name = "tower-http" +version = "0.6.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9cf146f99d442e8e68e585f5d798ccd3cad9a7835b917e09728880a862706456" +dependencies = [ + "bitflags 2.10.0", + "bytes", + "futures-util", + "http", + "http-body", + "iri-string", + "pin-project-lite", + "tower", + "tower-layer", + "tower-service", +] + +[[package]] +name = "tower-layer" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "121c2a6cda46980bb0fcd1647ffaf6cd3fc79a013de288782836f6df9c48780e" + +[[package]] +name = "tower-service" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8df9b6e13f2d32c91b9bd719c00d1958837bc7dec474d94952798cc8e69eeec3" + [[package]] name = "tracing" version = "0.1.43" @@ -1986,6 +2497,12 @@ dependencies = [ "tracing-log", ] +[[package]] +name = "try-lock" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" + [[package]] name = "typenum" version = "1.19.0" @@ -2041,6 +2558,24 @@ version = "0.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6d49784317cd0d1ee7ec5c716dd598ec5b4483ea832a2dced265471cc0f690ae" +[[package]] +name = "url" +version = "2.5.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08bc136a29a3d1758e07a9cca267be308aeebf5cfd5a10f3f67ab2097683ef5b" +dependencies = [ + "form_urlencoded", + "idna", + "percent-encoding", + "serde", +] + +[[package]] +name = "utf8_iter" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" + [[package]] name = "uuid" version = "1.19.0" @@ -2058,6 +2593,12 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ba73ea9cf16a25df0c8caa16c51acb937d5712a8429db78a3ee29d5dcacd3a65" +[[package]] +name = "vcpkg" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" + [[package]] name = "version_check" version = "0.9.5" @@ -2070,6 +2611,15 @@ version = "0.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "051eb1abcf10076295e815102942cc58f9d5e3b4560e46e53c21e8ff6f3af7b1" +[[package]] +name = "want" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfa7760aed19e106de2c7c0b581b509f2f25d3dacaf737cb82ac61bc6d760b0e" +dependencies = [ + "try-lock", +] + [[package]] name = "wasi" version = "0.9.0+wasi-snapshot-preview1" @@ -2104,6 +2654,19 @@ dependencies = [ "wasm-bindgen-shared", ] +[[package]] +name = "wasm-bindgen-futures" +version = "0.4.56" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "836d9622d604feee9e5de25ac10e3ea5f2d65b41eac0d9ce72eb5deae707ce7c" +dependencies = [ + "cfg-if", + "js-sys", + "once_cell", + "wasm-bindgen", + "web-sys", +] + [[package]] name = "wasm-bindgen-macro" version = "0.2.106" @@ -2136,6 +2699,16 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "web-sys" +version = "0.3.83" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b32828d774c412041098d182a8b38b16ea816958e07cf40eec2bc080ae137ac" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + [[package]] name = "windows" version = "0.48.0" @@ -2151,6 +2724,35 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" +[[package]] +name = "windows-registry" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "02752bf7fbdcce7f2a27a742f798510f3e5ad88dbe84871e5168e2120c3d5720" +dependencies = [ + "windows-link", + "windows-result", + "windows-strings", +] + +[[package]] +name = "windows-result" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7781fa89eaf60850ac3d2da7af8e5242a5ea78d1a11c49bf2910bb5a73853eb5" +dependencies = [ + "windows-link", +] + +[[package]] +name = "windows-strings" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7837d08f69c77cf6b07689544538e017c1bfcf57e34b4c0ff58e6c2cd3b37091" +dependencies = [ + "windows-link", +] + [[package]] name = "windows-sys" version = "0.52.0" @@ -2379,6 +2981,12 @@ version = "0.46.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f17a85883d4e6d00e8a97c586de764dabcc06133f7f1d55dce5cdc070ad7fe59" +[[package]] +name = "writeable" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9edde0db4769d2dc68579893f2306b26c6ecfbe0ef499b013d731b7b9247e0b9" + [[package]] name = "yansi" version = "1.0.1" @@ -2388,6 +2996,29 @@ dependencies = [ "is-terminal", ] +[[package]] +name = "yoke" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72d6e5c6afb84d73944e5cedb052c4680d5657337201555f9f2a16b7406d4954" +dependencies = [ + "stable_deref_trait", + "yoke-derive", + "zerofrom", +] + +[[package]] +name = "yoke-derive" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b659052874eb698efe5b9e8cf382204678a0086ebf46982b79d6ca3182927e5d" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "synstructure", +] + [[package]] name = "zerocopy" version = "0.8.31" @@ -2408,6 +3039,27 @@ dependencies = [ "syn", ] +[[package]] +name = "zerofrom" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50cc42e0333e05660c3587f3bf9d0478688e15d870fab3346451ce7f8c9fbea5" +dependencies = [ + "zerofrom-derive", +] + +[[package]] +name = "zerofrom-derive" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d71e5d6e06ab090c67b5e44993ec16b72dcbaabc526db883a360057678b48502" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "synstructure", +] + [[package]] name = "zeroize" version = "1.8.2" @@ -2427,3 +3079,36 @@ dependencies = [ "quote", "syn", ] + +[[package]] +name = "zerotrie" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a59c17a5562d507e4b54960e8569ebee33bee890c70aa3fe7b97e85a9fd7851" +dependencies = [ + "displaydoc", + "yoke", + "zerofrom", +] + +[[package]] +name = "zerovec" +version = "0.11.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c28719294829477f525be0186d13efa9a3c602f7ec202ca9e353d310fb9a002" +dependencies = [ + "yoke", + "zerofrom", + "zerovec-derive", +] + +[[package]] +name = "zerovec-derive" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eadce39539ca5cb3985590102671f2567e659fca9666581ad3411d59207951f3" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] diff --git a/backend/Cargo.toml b/backend/Cargo.toml index 5e7888c..90a6e5f 100644 --- a/backend/Cargo.toml +++ b/backend/Cargo.toml @@ -16,6 +16,7 @@ bcrypt = "0.17.1" bincode = "2.0.1" serde = { version = "1.0", features = ["derive"] } serde_json = "1.0" +reqwest = { version = "0.12.24", features = ["json"] } [build-dependencies] prost-build = "0.14.1" diff --git a/backend/src/auth.rs b/backend/src/auth.rs index 7ee6957..e75eb9d 100644 --- a/backend/src/auth.rs +++ b/backend/src/auth.rs @@ -1,8 +1,8 @@ use crate::items; use crate::proto_utils::Proto; use rocket::State; +use rocket::futures::lock::Mutex; use std::collections::HashMap; -use std::sync::Mutex; use uuid::Uuid; pub struct AuthState { @@ -18,6 +18,12 @@ impl AuthState { } } +impl Default for AuthState { + fn default() -> Self { + Self::new() + } +} + #[derive(Debug)] #[allow(dead_code)] pub struct Token { @@ -39,7 +45,7 @@ impl<'r> rocket::request::FromRequest<'r> for Token { // Check if token starts with "Bearer " if let Some(token) = token.strip_prefix("Bearer ") { let state = request.guard::<&State>().await.unwrap(); - let tokens = state.tokens.lock().unwrap(); + let tokens = state.tokens.lock().await; if let Some(username) = tokens.get(token) { return rocket::request::Outcome::Success(Token { @@ -57,19 +63,19 @@ impl<'r> rocket::request::FromRequest<'r> for Token { } #[post("/login", data = "")] -pub fn login( +pub async fn login( state: &State, user_list: &State>>, request: Proto, ) -> items::LoginResponse { let req = request.into_inner(); - let users = user_list.lock().unwrap(); + let users = user_list.lock().await; if let Some(user) = users.iter().find(|u| u.person.name == req.username) && bcrypt::verify(&req.password, &user.password_hash).unwrap_or(false) { let token = Uuid::new_v4().to_string(); - let mut tokens = state.tokens.lock().unwrap(); + let mut tokens = state.tokens.lock().await; tokens.insert(token.clone(), req.username); return items::LoginResponse { @@ -87,12 +93,12 @@ pub fn login( } #[post("/logout", data = "")] -pub fn logout( +pub async fn logout( state: &State, request: Proto, ) -> items::LogoutResponse { let req = request.into_inner(); - let mut tokens = state.tokens.lock().unwrap(); + let mut tokens = state.tokens.lock().await; if tokens.remove(&req.token).is_some() { items::LogoutResponse { @@ -108,12 +114,12 @@ pub fn logout( } #[post("/get_auth_status", data = "")] -pub fn get_auth_status( +pub async fn get_auth_status( state: &State, request: Proto, ) -> items::AuthStatusResponse { let req = request.into_inner(); - let tokens = state.tokens.lock().unwrap(); + let tokens = state.tokens.lock().await; if let Some(username) = tokens.get(&req.token) { items::AuthStatusResponse { diff --git a/backend/src/main.rs b/backend/src/main.rs index f856506..17fa79e 100644 --- a/backend/src/main.rs +++ b/backend/src/main.rs @@ -1,5 +1,5 @@ use rocket::fs::FileServer; -use std::sync::Mutex; +use rocket::futures::lock::Mutex; use backend::auth; use backend::items::{self, Game}; @@ -10,12 +10,12 @@ use backend::store::{self, User, save_state}; extern crate rocket; #[get("/")] -fn get_user( +async fn get_user( _token: auth::Token, user_list: &rocket::State>>, name: &str, ) -> Option { - let users = user_list.lock().unwrap(); + let users = user_list.lock().await; users .iter() .find(|user| user.person.name == name) @@ -23,42 +23,45 @@ fn get_user( } #[get("/")] -fn get_users( +async fn get_users( _token: auth::Token, user_list: &rocket::State>>, ) -> items::PersonList { - let users = user_list.lock().unwrap(); + let users = user_list.lock().await; items::PersonList { person: users.iter().map(|u| u.person.clone()).collect(), } } #[get("/game/")] -fn get_game( +async fn get_game( _token: auth::Token, game_list: &rocket::State<Mutex<Vec<Game>>>, title: &str, ) -> Option<items::Game> { - let games = game_list.lock().unwrap(); + let games = game_list.lock().await; games.iter().find(|g| g.title == title).cloned() } #[get("/games")] -fn get_games(_token: auth::Token, game_list: &rocket::State<Mutex<Vec<Game>>>) -> items::GameList { - let games = game_list.lock().unwrap(); +async fn get_games( + _token: auth::Token, + game_list: &rocket::State<Mutex<Vec<Game>>>, +) -> items::GameList { + let games = game_list.lock().await; items::GameList { games: games.clone(), } } #[post("/game", data = "<game>")] -fn add_game( +async fn add_game( _token: auth::Token, game_list: &rocket::State<Mutex<Vec<Game>>>, user_list: &rocket::State<Mutex<Vec<User>>>, game: proto_utils::Proto<items::Game>, ) -> Option<items::Game> { - let mut games = game_list.lock().unwrap(); + let mut games = game_list.lock().await; let game = game.into_inner(); if games.iter().any(|g| g.title == game.title) { @@ -67,21 +70,21 @@ fn add_game( games.push(game.clone()); - let users = user_list.lock().unwrap(); + let users = user_list.lock().await; save_state(&games, &users); Some(game) } #[post("/opinion", data = "<req>")] -fn add_opinion( +async fn add_opinion( token: auth::Token, user_list: &rocket::State<Mutex<Vec<User>>>, game_list: &rocket::State<Mutex<Vec<Game>>>, req: proto_utils::Proto<items::AddOpinionRequest>, ) -> Option<items::Person> { - let mut users = user_list.lock().unwrap(); - let games = game_list.lock().unwrap(); + let mut users = user_list.lock().await; + let games = game_list.lock().await; let mut result = None; // Validate game exists @@ -115,6 +118,68 @@ fn add_opinion( result } +#[get("/game_thumbnail/<title>")] +async fn get_game_thumbnail( + title: &str, + game_list: &rocket::State<Mutex<Vec<Game>>>, +) -> Option<(rocket::http::ContentType, Vec<u8>)> { + let games = game_list.lock().await; + let game = games.iter().find(|g| g.title == title)?; + + let url = match items::Source::try_from(game.source).ok()? { + items::Source::Steam => format!( + "https://cdn.cloudflare.steamstatic.com/steam/apps/{}/header.jpg", + game.remote_id + ), + items::Source::Roblox => { + let universe_id = { + let api_url = format!( + "https://apis.roblox.com/universes/v1/places/{}/universe", + game.remote_id + ); + reqwest::get(&api_url) + .await + .ok()? + .json::<serde_json::Value>() + .await + .ok()? + .get("universeId")? + .as_u64() + .unwrap() + }; + + let api_url = format!( + "https://thumbnails.roblox.com/v1/games/icons?universeIds={}&size=512x512&format=Webp&isCircular=false", + universe_id + ); + match reqwest::get(&api_url).await { + Ok(resp) => { + if let Ok(json) = resp.json::<serde_json::Value>().await { + json["data"][0]["imageUrl"].as_str()?.to_string() + } else { + return None; + } + } + Err(_) => return None, + } + } + }; + + match reqwest::get(&url).await { + Ok(resp) => { + let content_type = resp + .headers() + .get(reqwest::header::CONTENT_TYPE) + .and_then(|v| v.to_str().ok()) + .and_then(rocket::http::ContentType::parse_flexible) + .unwrap_or(rocket::http::ContentType::Binary); + let bytes = resp.bytes().await.ok()?.to_vec(); + Some((content_type, bytes)) + } + Err(_) => None, + } +} + #[get("/<_..>", rank = 20)] async fn index_fallback() -> Option<rocket::fs::NamedFile> { // Try multiple paths for robustness @@ -139,16 +204,31 @@ async fn main() -> Result<(), std::io::Error> { min_players: 1, max_players: 90, price: 0, - remote_id: 0, + remote_id: 6032337657, // Universe ID for Naramo Nuclear Plant V2 + }); + + game_list.push(Game { + title: "Terraria".to_string(), + source: items::Source::Steam.into(), + min_players: 1, + max_players: 8, + price: 999, + remote_id: 105600, // App ID for Terraria }); user_list.push(User { person: items::Person { name: "John".to_string(), - opinion: vec![items::Opinion { - title: "Naramo Nuclear Plant V2".to_string(), - would_play: true, - }], + opinion: vec![ + items::Opinion { + title: "Naramo Nuclear Plant V2".to_string(), + would_play: true, + }, + items::Opinion { + title: "Terraria".to_string(), + would_play: true, + }, + ], }, password_hash: bcrypt::hash("password123", bcrypt::DEFAULT_COST).unwrap(), }); @@ -167,7 +247,8 @@ async fn main() -> Result<(), std::io::Error> { get_game, get_games, add_opinion, - add_game + add_game, + get_game_thumbnail ], ) .mount( diff --git a/frontend/src/GameFilter.tsx b/frontend/src/GameFilter.tsx index eaf8fd1..0f0c2fb 100644 --- a/frontend/src/GameFilter.tsx +++ b/frontend/src/GameFilter.tsx @@ -2,6 +2,7 @@ import { useState, useEffect } from "react"; import { Person, PersonList as PersonListProto } from "../items"; import { apiFetch } from "./api"; import { Link } from "react-router-dom"; +import { GameImage } from "./GameImage"; export function GameFilter() { const [people, setPeople] = useState<Person[]>([]); @@ -37,10 +38,10 @@ export function GameFilter() { selectedPersons.forEach((person) => { person.opinion.forEach((op) => { - if (op.wouldPlay) { - if (!gameToPlayers.has(op.title)) { - gameToPlayers.set(op.title, new Set()); - } + if (!gameToPlayers.has(op.title)) { + gameToPlayers.set(op.title, new Set()); + } + if (!op.wouldPlay) { gameToPlayers.get(op.title)!.add(person.name); } }); @@ -48,7 +49,7 @@ export function GameFilter() { // Filter games where ALL selected people would play const games = Array.from(gameToPlayers.entries()) - .filter(([, players]) => players.size === selectedPeople.size) + .filter(([, players]) => players.size === 0) .map(([game]) => game); setFilteredGames(games); @@ -137,6 +138,7 @@ export function GameFilter() { > ✓ All {selectedPeople.size} selected would play </div> + <GameImage game={game} /> </Link> ))} </ul> diff --git a/frontend/src/GameImage.tsx b/frontend/src/GameImage.tsx new file mode 100644 index 0000000..d53e96b --- /dev/null +++ b/frontend/src/GameImage.tsx @@ -0,0 +1,44 @@ +import { useState } from "react"; + +interface GameImageProps { + game: string; +} + +export function GameImage({ game }: GameImageProps) { + const [error, setError] = useState(false); + + if (error) { + return ( + <div + style={{ + width: "100%", + height: "150px", + background: "#ddd", + display: "flex", + alignItems: "center", + justifyContent: "center", + color: "#666", + borderRadius: "8px", + marginTop: "0.5rem", + }} + > + No Image Available + </div> + ); + } + + return ( + <img + src={`/api/game_thumbnail/${encodeURIComponent(game)}`} + alt={`${game} cover`} + onError={() => setError(true)} + style={{ + width: "100%", + height: "auto", + borderRadius: "8px", + marginTop: "0.5rem", + objectFit: "cover", + }} + /> + ); +} diff --git a/state.json b/state.json index ea96021..6559c1c 100644 --- a/state.json +++ b/state.json @@ -6,65 +6,69 @@ "min_players": 1, "max_players": 90, "price": 0, - "remote_id": 0 + "remote_id": 98626216952426 }, { - "title": "Test2", + "title": " Asylum Life", "source": 1, "min_players": 1, - "max_players": 1, + "max_players": 40, "price": 0, - "remote_id": 0 + "remote_id": 132352755769957 + }, + { + "title": "Factorio", + "source": 0, + "min_players": 1, + "max_players": 1000, + "price": 32, + "remote_id": 427520 } ], "users": [ { "person": { - "name": "John", + "name": "HoherGeist", "opinion": [ { "title": "Naramo Nuclear Plant V2", "would_play": true - }, - { - "title": "Test2", - "would_play": true } ] }, - "password_hash": "$2b$12$DRvTP/ibTWULkuJJr285bumRd7SG3n5bYkDpb09Qpklqf6FeTiHkC" + "password_hash": "$2b$12$v7w0E4NWRvxvfwM4rBmmnuG1MEDldRxA2TKejF8RtRgFvBayNxyoK" }, { "person": { - "name": "John2", + "name": "Code002Lover", "opinion": [ { - "title": "Naramo Nuclear Plant V2", - "would_play": false - }, - { - "title": "Test2", + "title": " Asylum Life", "would_play": true } ] }, - "password_hash": "$2b$12$DRvTP/ibTWULkuJJr285bumRd7SG3n5bYkDpb09Qpklqf6FeTiHkC" + "password_hash": "$2b$12$4EVOAuLrrwFvtS8Z6/7n.ertig.agKLGdWF8flIO/LLB2Ir1A4oqW" }, { "person": { - "name": "John3", + "name": "Cwaeck", "opinion": [ { "title": "Naramo Nuclear Plant V2", + "would_play": true + }, + { + "title": " Asylum Life", "would_play": false }, { - "title": "Test2", + "title": "Factorio", "would_play": true } ] }, - "password_hash": "$2b$12$DRvTP/ibTWULkuJJr285bumRd7SG3n5bYkDpb09Qpklqf6FeTiHkC" + "password_hash": "$2b$12$iezDgiYiMSEceyfDrriqVucQBx7v.U4TdKdc5qjEIJDbqsxiXHTBK" } ] -} +} \ No newline at end of file