use rocket::fs::FileServer; use std::sync::Mutex; use crate::items::Game; #[macro_use] extern crate rocket; pub mod items { include!(concat!(env!("OUT_DIR"), "/items.rs")); } mod auth; mod proto_utils; #[derive(Clone)] pub struct User { pub person: items::Person, pub password_hash: String, } impl std::ops::Deref for User { type Target = items::Person; fn deref(&self) -> &Self::Target { &self.person } } #[get("/")] fn get_user( _token: auth::Token, user_list: &rocket::State>>, name: String, ) -> Option { let users = user_list.lock().unwrap(); users .iter() .find(|user| user.person.name == name) .map(|u| u.person.clone()) } #[get("/")] fn get_users( _token: auth::Token, user_list: &rocket::State>>, ) -> items::PersonList { let users = user_list.lock().unwrap(); items::PersonList { person: users.iter().map(|u| u.person.clone()).collect(), } } #[get("/game/")] fn get_game( _token: auth::Token, game_list: &rocket::State<Vec<Game>>, title: String, ) -> Option<items::Game> { game_list.iter().find(|g| g.title == title).cloned() } #[post("/opinion", data = "<req>")] fn add_opinion( token: auth::Token, user_list: &rocket::State<Mutex<Vec<User>>>, req: proto_utils::Proto<items::AddOpinionRequest>, ) -> Option<items::Person> { let mut users = user_list.lock().unwrap(); if let Some(user) = users.iter_mut().find(|u| u.person.name == token.username) { let req = req.into_inner(); let opinion = items::Opinion { title: req.game_title.clone(), would_play: req.would_play, }; if let Some(existing) = user .person .opinion .iter_mut() .find(|o| o.title == req.game_title) { existing.would_play = req.would_play; } else { user.person.opinion.push(opinion); } Some(user.person.clone()) } else { None } } #[get("/<_..>", rank = 20)] async fn index_fallback() -> Option<rocket::fs::NamedFile> { // Try multiple paths for robustness let paths = ["../frontend/dist/index.html", "frontend/dist/index.html"]; for path in paths { if let Ok(file) = rocket::fs::NamedFile::open(path).await { return Some(file); } } None } #[launch] fn rocket() -> _ { let mut game_list: Vec<Game> = Vec::new(); let mut user_list: Vec<User> = 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, }); 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(), }); rocket::build() .manage(Mutex::new(user_list)) .manage(auth::AuthState::new()) .manage(game_list) .mount("/api", routes![get_users, get_user, get_game, add_opinion]) .mount( "/auth", routes![auth::login, auth::logout, auth::get_auth_status], ) .mount("/", routes![index_fallback]) .mount("/", FileServer::new("frontend/dist")) }