add global RwLock for SQLite

now tests pass
This commit is contained in:
Peter Cai 2020-02-21 09:02:52 +08:00
parent f611c5378d
commit 2893bbd595
No known key found for this signature in database
GPG Key ID: 71F5FB4E4F3FD54F
2 changed files with 48 additions and 15 deletions

View File

@ -14,7 +14,6 @@ extern crate serde;
extern crate crypto;
extern crate scrypt;
#[macro_use]
#[cfg(test)]
extern crate lazy_static;
mod schema;
@ -31,9 +30,33 @@ use rocket::Rocket;
use rocket::config::{Config, Environment, Value};
use std::collections::HashMap;
use std::env;
use std::sync::RwLock;
embed_migrations!();
// We need a global RwLock for SQLite
// This is unfortunate when we still use SQLite
// but should be mostly fine for our purpose
lazy_static! {
pub static ref DB_LOCK: RwLock<()> = RwLock::new(());
}
#[macro_export]
macro_rules! lock_db_write {
() => {
crate::DB_LOCK.write()
.map_err(|_| "Cannot lock database for writing".into())
};
}
#[macro_export]
macro_rules! lock_db_read {
() => {
crate::DB_LOCK.read()
.map_err(|_| "Cannot lock database for reading".into())
};
}
#[database("db")]
pub struct DbConn(SqliteConnection);

View File

@ -1,5 +1,6 @@
use crate::schema::users;
use crate::schema::users::dsl::*;
use crate::{lock_db_write, lock_db_read};
use diesel::prelude::*;
use diesel::sqlite::SqliteConnection;
use serde::Deserialize;
@ -15,6 +16,12 @@ impl UserOpError {
}
}
impl Into<UserOpError> for &str {
fn into(self) -> UserOpError {
UserOpError::new(self)
}
}
// Password should ALWAYS be hashed
#[derive(Debug)]
pub struct Password(String);
@ -104,19 +111,21 @@ impl User {
match Self::find_user_by_email(db, &new_user.email) {
Ok(_) => Err(UserOpError::new("User already registered")),
Err(_) => diesel::insert_into(users::table)
.values(user_hashed)
.execute(db)
.map(|_| ())
.map_err(|_| UserOpError::new("Database error"))
Err(_) => lock_db_write!()
.and_then(|_| diesel::insert_into(users::table)
.values(user_hashed)
.execute(db)
.map(|_| ())
.map_err(|_| UserOpError::new("Database error")))
}
}
pub fn find_user_by_email(db: &SqliteConnection, user_email: &str) -> Result<User, UserOpError> {
let mut results = users.filter(email.eq(user_email))
.limit(1)
.load::<UserQuery>(db)
.map_err(|_| UserOpError::new("Database error"))?;
let mut results = lock_db_read!()
.and_then(|_| users.filter(email.eq(user_email))
.limit(1)
.load::<UserQuery>(db)
.map_err(|_| UserOpError::new("Database error")))?;
if results.is_empty() {
Result::Err(UserOpError::new("No matching user found"))
} else {
@ -157,11 +166,12 @@ impl User {
// Update database
// TODO: Maybe we should revoke all JWTs somehow?
// maybe we can record when the user last changed?
diesel::update(users.find(self.id))
.set(password.eq::<String>(Password::new(new_passwd).into()))
.execute(db)
.map(|_| ())
.map_err(|_| UserOpError::new("Database error"))
lock_db_write!()
.and_then(|_| diesel::update(users.find(self.id))
.set(password.eq::<String>(Password::new(new_passwd).into()))
.execute(db)
.map(|_| ())
.map_err(|_| UserOpError::new("Database error")))
}
}
}