From afb510d92e4d6322b722a1585757909cbc530b8f Mon Sep 17 00:00:00 2001 From: Savanni D'Gerinel Date: Sat, 30 Nov 2024 23:03:52 -0500 Subject: [PATCH] Set up new tables to handle users and roles --- visions/server/migrations/02-users/up.sql | 16 ++++ visions/server/src/database.rs | 109 ++++++++++++++++++++-- 2 files changed, 118 insertions(+), 7 deletions(-) create mode 100644 visions/server/migrations/02-users/up.sql diff --git a/visions/server/migrations/02-users/up.sql b/visions/server/migrations/02-users/up.sql new file mode 100644 index 0000000..e8043e8 --- /dev/null +++ b/visions/server/migrations/02-users/up.sql @@ -0,0 +1,16 @@ +CREATE TABLE users( + uuid TEXT PRIMARY KEY, + name TEXT, + password TEXT, + admin BOOLEAN, + enabled BOOLEAN +); + +CREATE TABLE roles( + user_id TEXT, + game_id TEXT, + role TEXT, + + FOREIGN KEY(user_id) REFERENCES users(uuid), + FOREIGN KEY(game_id) REFERENCES games(uuid) +); diff --git a/visions/server/src/database.rs b/visions/server/src/database.rs index f2a8b7c..df077e0 100644 --- a/visions/server/src/database.rs +++ b/visions/server/src/database.rs @@ -48,12 +48,62 @@ enum DatabaseResponse { Charsheet(Option), } +#[derive(Clone, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)] +pub struct UserId(String); + +impl UserId { + pub fn new() -> Self { + Self(format!("{}", Uuid::new_v4().hyphenated())) + } + + pub fn as_str<'a>(&'a self) -> &'a str { + &self.0 + } +} + +impl From<&str> for UserId { + fn from(s: &str) -> Self { + Self(s.to_owned()) + } +} + +impl From for UserId { + fn from(s: String) -> Self { + Self(s) + } +} + +#[derive(Clone, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)] +pub struct GameId(String); + +impl GameId { + pub fn new() -> Self { + Self(format!("{}", Uuid::new_v4().hyphenated())) + } + + pub fn as_str<'a>(&'a self) -> &'a str { + &self.0 + } +} + +impl From<&str> for GameId { + fn from(s: &str) -> Self { + Self(s.to_owned()) + } +} + +impl From for GameId { + fn from(s: String) -> Self { + Self(s) + } +} + #[derive(Clone, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)] pub struct CharacterId(String); impl CharacterId { pub fn new() -> Self { - CharacterId(format!("{}", Uuid::new_v4().hyphenated())) + Self(format!("{}", Uuid::new_v4().hyphenated())) } pub fn as_str<'a>(&'a self) -> &'a str { @@ -63,20 +113,36 @@ impl CharacterId { impl From<&str> for CharacterId { fn from(s: &str) -> Self { - CharacterId(s.to_owned()) + Self(s.to_owned()) } } impl From for CharacterId { fn from(s: String) -> Self { - CharacterId(s) + Self(s) } } +#[derive(Clone, Debug)] +pub struct UserRow { + id: String, + name: String, + password: String, + admin: bool, + enabled: bool, +} + +#[derive(Clone, Debug)] +pub struct Role { + userid: String, + gameid: String, + role: String, +} + #[derive(Clone, Debug)] pub struct CharsheetRow { id: String, - gametype: String, + game: String, pub data: serde_json::Value, } @@ -93,11 +159,20 @@ fn setup_test_database(conn: &Connection) { let mut gamecount_stmt = conn.prepare("SELECT count(*) FROM games").unwrap(); let mut count = gamecount_stmt.query([]).unwrap(); if count.next().unwrap().unwrap().get::(0) == Ok(0) { + let admin_id = format!("{}", Uuid::new_v4()); + let user_id = format!("{}", Uuid::new_v4()); let game_id = format!("{}", Uuid::new_v4()); let char_id = CharacterId::new(); + let mut user_stmt = conn.prepare("INSERT INTO users VALUES (?, ?, ?, ?, ?)").unwrap(); + user_stmt.execute((admin_id.clone(), "admin", "abcdefg", true, true)).unwrap(); + user_stmt.execute((user_id.clone(), "savanni", "abcdefg", false, true)).unwrap(); + let mut game_stmt = conn.prepare("INSERT INTO games VALUES (?, ?)").unwrap(); - game_stmt.execute((game_id.clone(), "Circle of Bluest Sky")); + game_stmt.execute((game_id.clone(), "Circle of Bluest Sky")).unwrap(); + + let mut role_stmt = conn.prepare("INSERT INTO roles VALUES (?, ?, ?)").unwrap(); + role_stmt.execute((user_id.clone(), game_id.clone(), "gm")).unwrap(); let mut sheet_stmt = conn .prepare("INSERT INTO characters VALUES (?, ?, ?)") @@ -124,17 +199,37 @@ impl DiskDb { Ok(DiskDb { conn }) } + fn user(&self, id: UserId) -> Result, Error> { + let mut stmt = self + .conn + .prepare("SELECT uuid, name, password, admin, enabled WHERE uuid=?") + .unwrap(); + let items: Vec = stmt + .query_map([id.as_str()], |row| Ok(UserRow { + id: row.get(0).unwrap(), + name: row.get(1).unwrap(), + password: row.get(2).unwrap(), + admin: row.get(3).unwrap(), + enabled: row.get(4).unwrap(), + })).unwrap().collect::, rusqlite::Error>>().unwrap(); + match &items[..] { + [] => Ok(None), + [item] => Ok(Some(item.clone())), + _ => unimplemented!(), + } + } + fn charsheet(&self, id: CharacterId) -> Result, Error> { let mut stmt = self .conn - .prepare("SELECT uuid, gametype, data FROM charsheet WHERE uuid=?") + .prepare("SELECT uuid, game, data FROM charsheet WHERE uuid=?") .unwrap(); let items: Vec = stmt .query_map([id.as_str()], |row| { let data: String = row.get(2).unwrap(); Ok(CharsheetRow { id: row.get(0).unwrap(), - gametype: row.get(1).unwrap(), + game: row.get(1).unwrap(), data: serde_json::from_str(&data).unwrap(), }) })