Skip to content

Commit

Permalink
fix/add a bunch of things and swap to sqlx
Browse files Browse the repository at this point in the history
  • Loading branch information
callmeclover authored Apr 25, 2024
1 parent 26ed985 commit 91d2074
Show file tree
Hide file tree
Showing 3 changed files with 67 additions and 56 deletions.
7 changes: 4 additions & 3 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,13 @@ futures = "0.3.30"
chrono = { version = "0.4.37", features = ["serde", "wasmbind"] }
tower-http = { version="0.5.2", features=["fs", "trace"] }
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"
serde_json = { version = "1.0", features = ["raw_value"] }
lazy_static = "1.4.0"
once_cell = "1.19.0"
ammonia = "4.0.0"
kuchikiki = "0.8.2"
js-sys = "0.3.69"
uuid = { version = "1.8.0", features = ["v4", "fast-rng", "serde"] }
postgres = { version = "0.19.7", features = ["with-uuid-1", "with-serde_json-1"] }

anyhow = "1.0.82"
sqlx = { version = "0.7", features = [ "runtime-tokio", "tls-rustls", "postgres", "uuid", "derive", "json" ] }
validator = { version = "0.16", features = ["derive"] }
87 changes: 54 additions & 33 deletions src/user/auth.rs
Original file line number Diff line number Diff line change
@@ -1,60 +1,81 @@
use std::error::Error;
use postgres::{Client, NoTls};

use anyhow::Result;
use sqlx::{query_as, query, postgres::{PgPoolOptions, PgPool}, types::Json};
use super::model::{Model, User};
use uuid::Uuid;

pub struct DatabaseConnectix {
connection: Client
connection: PgPool
}

impl Default for DatabaseConnectix {
fn default() -> Self {
fn default() -> Result<Self> {
tokio::runtime::Runtime::new().unwrap().block_on(async {
let uri = std::env::var("DB_URL").unwrap();
let mut client = Client::connect(&uri, NoTls).expect("can't connect to database!");
let uri = std::env::var("DB_URL")?;
let pool = PgPoolOptions::new()
.max_connections(5)
.connect(uri).await?;

return Self {
return Ok(Self {
connection: client
};
});
})

}
}

impl DatabaseConnectix {
pub fn new(uri: &str) -> Self {
pub fn new(uri: &str) -> Result<Self> {
tokio::runtime::Runtime::new().unwrap().block_on(async {
let mut client = Client::connect(uri, NoTls).expect("can't connect to database!");
let pool = PgPoolOptions::new()
.max_connections(5)
.connect(uri).await?;

return Self {
connection: client
};
return Ok(Self {
connection: pool
});
})
}

/// Gets a possible user id (if one exists) for a username.
pub fn get_user_id(&self, name: &str) -> Result<i32, Box<dyn Error>> {
match self.connection.query_one("select max(id) from users where name=$1", &[&name]) {
Ok(res) => {
if let Some(id) = res.get(0) {
if id == 9999 { return Err("username is taken".into()); }
Ok(id+1)
} else {
Ok(1)
}
},
Err(err) => {
Err(err)
}
pub fn get_user_id(&mut self, username: &str) -> Result<i32> {
let user: Option<User> = query_as(
"select max(id) from users where username=$1 limit 1;"
)
.bind(username)
.fetch_optional(&mut self.connection)
.await?;

if user.is_none() {
Ok(1)
} else {
if user.id == 9999 { return Err("username is taken"); }
Ok(user.id+1)
}
}

pub fn post_user(&self, user: User) {
let data: Model = user.into();
let Model { id, name, uuid, moderation_stats } = data;
self.connection.execute(
"INSERT INTO users (id, name, uuid, mod) VALUES ($1, $2, $3, $4)",
&[&id, &name, &uuid, &moderation_stats],
).expect("couldn't execute post_user query!");
pub fn post_user(&mut self, username: &str, password: &str) -> Result<()> {
let data: Model = Model {
id: get_user_id(username)?,
uuid: Uuid::new_v4(),
username,
password,
glass: Json(GlassModeration::default())
};
data.validate()?;

let _ = sqlx::query("insert into users (id, uuid, username, password, mod) values ($1, $2, $3, $4, $5)")
.bind(data.id).bind(data.uuid).bind(data.username).bind(data.password).bind(data.glass)
.exectute(&mut self.connection)
.await?;
}

pub fn update_user(&mut self, username: &str, prev_username: &str, prev_id: i32) -> Result<()> {
let id = get_user_id(username)?;

let _ = sqlx::query("update users set username=$1, id=$2 where username=$3 and id=$4")
.bind(username).bind(id).bind(prev_username).bind(prev_id)
.exectute(&mut self.connection)
.await?;
}
}
29 changes: 9 additions & 20 deletions src/user/model.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use std::error::Error;
use rustrict::{Censor, Type};
use serde::{Serialize, Deserialize};
use postgres::types::Json;
use sqlx::{FromRow, types::Json};
use uuid::Uuid;

/// What am I?
Expand All @@ -10,13 +10,11 @@ use uuid::Uuid;
#[derive(Clone)]
pub struct User {
/// Why is the user id (the number after the @) not stored here?
/// Because we can simplify this! Use the method `get_name_split()`.
/// Because we can simplify this! Use the method `get_name_split()` instead.
pub name: String,
pub uuid: Uuid,
pub glass: GlassModeration,
pub sendable_user: SendableUser,
// pub password: String,
// pub email: String
pub sendable_user: SendableUser
}

impl User {
Expand All @@ -39,30 +37,21 @@ impl User {

/// What am I?
/// A struct so that we can save user data in the database.
#[derive(Serialize, Deserialize, Clone)]
#[derive(Serialize, Deserialize, Clone, sqlx::FromRow, Debug, PartialEq, Eq)]
pub struct Model {
pub id: i32,
pub name: String,
pub uuid: Uuid,
//pub password: String,
#[validate(length(min = 3, max = 20))]
pub username: String,
#[validate(length(min = 8, max = 32))]
pub password: String,
//pub email: String,
/// This is just the DB equivalent of `glass`.
/// It's in JSON format.
#[sqlx(rename="mod")]
pub moderation_stats: Json<GlassModeration>
}

impl From<User> for Model {
fn from(item: User) -> Self {
let (name, id) = item.name_split();
Self {
id: id.parse::<i32>().unwrap(),
name: name.to_string(),
uuid: item.uuid,
moderation_stats: Json(item.glass)
}
}
}

/// What am I?
/// A stripped down version of the `User` struct so that you can send something to the other clients.
#[derive(Serialize, Deserialize, Clone)]
Expand Down

0 comments on commit 91d2074

Please sign in to comment.