feat: Implement user management with a new add_user binary and refactor state handling into a dedicated store module.

This commit is contained in:
code002lover 2025-12-04 00:17:28 +01:00
parent 6bdfb49d59
commit f49900adad
5 changed files with 120 additions and 54 deletions

View File

@ -2,6 +2,8 @@
name = "backend"
version = "0.1.0"
edition = "2024"
default-run = "backend"
[dependencies]
prost = "0.14.1"

View File

@ -0,0 +1,55 @@
use backend::items::Person;
use backend::store::{self, User, save_state};
use std::io::{self, Write};
fn main() {
println!("Add User Tool");
println!("-------------");
print!("Username: ");
io::stdout().flush().unwrap();
let mut username = String::new();
io::stdin().read_line(&mut username).unwrap();
let username = username.trim().to_string();
if username.is_empty() {
println!("Username cannot be empty.");
return;
}
print!("Password: ");
io::stdout().flush().unwrap();
let mut password = String::new();
io::stdin().read_line(&mut password).unwrap();
let password = password.trim().to_string();
if password.is_empty() {
println!("Password cannot be empty.");
return;
}
let (games, mut users) = store::load_state().unwrap_or_else(|| {
println!("No existing state found. Creating new state.");
(Vec::new(), Vec::new())
});
if users.iter().any(|u| u.person.name == username) {
println!("User '{}' already exists.", username);
return;
}
let password_hash = bcrypt::hash(&password, bcrypt::DEFAULT_COST).unwrap();
let new_user = User {
person: Person {
name: username.clone(),
opinion: Vec::new(),
},
password_hash,
};
users.push(new_user);
save_state(&games, &users);
println!("User '{}' added successfully.", username);
}

12
backend/src/lib.rs Normal file
View File

@ -0,0 +1,12 @@
#[macro_use]
extern crate rocket;
pub mod items {
include!(concat!(env!("OUT_DIR"), "/items.rs"));
}
pub mod auth;
pub mod proto_utils;
pub mod store;
pub use store::User;

View File

@ -1,63 +1,14 @@
use rocket::fs::FileServer;
use serde::{Deserialize, Serialize};
use std::fs::File;
use std::io::BufReader;
use std::sync::Mutex;
use crate::items::Game;
use backend::auth;
use backend::items::{self, Game};
use backend::proto_utils;
use backend::store::{self, User, save_state};
#[macro_use]
extern crate rocket;
pub mod items {
include!(concat!(env!("OUT_DIR"), "/items.rs"));
}
mod auth;
mod proto_utils;
#[derive(Clone, Serialize, Deserialize)]
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
}
}
#[derive(Serialize, Deserialize)]
struct PersistentState {
games: Vec<Game>,
users: Vec<User>,
}
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<Game>, Vec<User>)> {
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("/<name>")]
fn get_user(
_token: auth::Token,
@ -178,7 +129,7 @@ async fn index_fallback() -> Option<rocket::fs::NamedFile> {
#[rocket::main]
async fn main() -> Result<(), std::io::Error> {
let (game_list, user_list) = load_state().unwrap_or_else(|| {
let (game_list, user_list) = store::load_state().unwrap_or_else(|| {
let mut game_list: Vec<Game> = Vec::new();
let mut user_list: Vec<User> = Vec::new();

46
backend/src/store.rs Normal file
View File

@ -0,0 +1,46 @@
use crate::items::{Game, Person};
use serde::{Deserialize, Serialize};
use std::fs::File;
use std::io::BufReader;
#[derive(Clone, Serialize, Deserialize)]
pub struct User {
pub person: Person,
pub password_hash: String,
}
impl std::ops::Deref for User {
type Target = Person;
fn deref(&self) -> &Self::Target {
&self.person
}
}
#[derive(Serialize, Deserialize)]
pub struct PersistentState {
pub games: Vec<Game>,
pub users: Vec<User>,
}
pub const STATE_FILE: &str = "state.json";
pub 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);
}
}
pub fn load_state() -> Option<(Vec<Game>, Vec<User>)> {
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
}