diff --git a/Cargo.lock b/Cargo.lock index e98619b..5838ea3 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -112,6 +112,8 @@ dependencies = [ "prost-types", "rocket", "rocket_prost_responder_derive", + "serde", + "serde_json", "uuid", ] @@ -2041,9 +2043,9 @@ checksum = "6d49784317cd0d1ee7ec5c716dd598ec5b4483ea832a2dced265471cc0f690ae" [[package]] name = "uuid" -version = "1.18.1" +version = "1.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2f87b8aa10b915a06587d0dec516c282ff295b475d94abf425d62b57710070a2" +checksum = "e2e054861b4bd027cd373e18e8d8d8e6548085000e41290d95ce0c373a654b4a" dependencies = [ "getrandom 0.3.4", "js-sys", diff --git a/backend/Cargo.toml b/backend/Cargo.toml index 15f612f..5f1fa04 100644 --- a/backend/Cargo.toml +++ b/backend/Cargo.toml @@ -9,9 +9,11 @@ prost-types = "0.14.1" rocket = { git = "https://github.com/rwf2/Rocket", rev = "504efef179622df82ba1dbd37f2e0d9ed2b7c9e4" } bytes = "1" rocket_prost_responder_derive = { path = "rocket_prost_responder_derive" } -uuid = { version = "1.10.0", features = ["v4"] } +uuid = { version = "1.19.0", features = ["v4"] } bcrypt = "0.17.1" bincode = "2.0.1" +serde = { version = "1.0", features = ["derive"] } +serde_json = "1.0" [build-dependencies] prost-build = "0.14.1" diff --git a/backend/build.rs b/backend/build.rs index 113518c..757d864 100644 --- a/backend/build.rs +++ b/backend/build.rs @@ -10,7 +10,7 @@ fn main() -> Result<(), std::io::Error> { cfg.type_attribute( ".", - "#[derive(rocket_prost_responder_derive::RocketResponder)]", + "#[derive(rocket_prost_responder_derive::RocketResponder, serde::Serialize, serde::Deserialize)]", ); cfg.compile_protos(&["../protobuf/items.proto"], &["../protobuf"])?; diff --git a/backend/src/main.rs b/backend/src/main.rs index b100b64..1e641af 100644 --- a/backend/src/main.rs +++ b/backend/src/main.rs @@ -1,5 +1,7 @@ -use bincode::{Decode, Encode}; use rocket::fs::FileServer; +use serde::{Deserialize, Serialize}; +use std::fs::File; +use std::io::BufReader; use std::sync::Mutex; use crate::items::Game; @@ -14,7 +16,7 @@ pub mod items { mod auth; mod proto_utils; -#[derive(Clone)] +#[derive(Clone, Serialize, Deserialize)] pub struct User { pub person: items::Person, pub password_hash: String, @@ -28,6 +30,34 @@ impl std::ops::Deref for User { } } +#[derive(Serialize, Deserialize)] +struct PersistentState { + games: Vec, + users: Vec, +} + +const STATE_FILE: &str = "state.json"; + +fn save_state(games: &[Game], users: &[User]) { + let state = PersistentState { + games: games.to_vec(), + users: users.to_vec(), + }; + if let Ok(file) = File::create(STATE_FILE) { + let _ = serde_json::to_writer_pretty(file, &state); + } +} + +fn load_state() -> Option<(Vec, Vec)> { + if let Ok(file) = File::open(STATE_FILE) { + let reader = BufReader::new(file); + if let Ok(state) = serde_json::from_reader::<_, PersistentState>(reader) { + return Some((state.games, state.users)); + } + } + None +} + #[get("/")] fn get_user( _token: auth::Token, @@ -64,10 +94,14 @@ fn get_game( #[post("/opinion", data = "")] fn add_opinion( token: auth::Token, + user_list: &rocket::State>>, + game_list: &rocket::State>, req: proto_utils::Proto, ) -> Option { let mut users = user_list.lock().unwrap(); + let mut result = None; + if let Some(user) = users.iter_mut().find(|u| u.person.name == token.username) { let req = req.into_inner(); let opinion = items::Opinion { @@ -85,10 +119,13 @@ fn add_opinion( } else { user.person.opinion.push(opinion); } - Some(user.person.clone()) - } else { - None + result = Some(user.person.clone()); } + + if result.is_some() { + save_state(game_list, &users); + } + result } #[get("/<_..>", rank = 20)] @@ -105,28 +142,31 @@ async fn index_fallback() -> Option { #[rocket::main] async fn main() -> Result<(), std::io::Error> { - let mut game_list: Vec = Vec::new(); - let mut user_list: Vec = Vec::new(); + let (game_list, user_list) = load_state().unwrap_or_else(|| { + let mut game_list: Vec = Vec::new(); + let mut user_list: Vec = Vec::new(); - game_list.push(Game { - title: "Naramo Nuclear Plant V2".to_string(), - source: items::Source::Roblox.into(), - multiplayer: true, - min_players: 1, - max_players: 90, - price: 0, - remote_id: 0, - }); + game_list.push(Game { + title: "Naramo Nuclear Plant V2".to_string(), + source: items::Source::Roblox.into(), + multiplayer: true, + min_players: 1, + max_players: 90, + price: 0, + remote_id: 0, + }); - 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, - }], - }, - password_hash: bcrypt::hash("password123", bcrypt::DEFAULT_COST).unwrap(), + 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, + }], + }, + password_hash: bcrypt::hash("password123", bcrypt::DEFAULT_COST).unwrap(), + }); + (game_list, user_list) }); rocket::build() diff --git a/state.json b/state.json new file mode 100644 index 0000000..3b00cfd --- /dev/null +++ b/state.json @@ -0,0 +1,31 @@ +{ + "games": [ + { + "title": "Naramo Nuclear Plant V2", + "source": 1, + "multiplayer": true, + "min_players": 1, + "max_players": 90, + "price": 0, + "remote_id": 0 + } + ], + "users": [ + { + "person": { + "name": "John", + "opinion": [ + { + "title": "Naramo Nuclear Plant V2", + "would_play": true + }, + { + "title": "Test2", + "would_play": true + } + ] + }, + "password_hash": "$2b$12$DRvTP/ibTWULkuJJr285bumRd7SG3n5bYkDpb09Qpklqf6FeTiHkC" + } + ] +} \ No newline at end of file