111 lines
3.4 KiB
Rust
111 lines
3.4 KiB
Rust
use serde::{Deserialize, Serialize};
|
|
|
|
#[cfg(feature = "server")]
|
|
use crate::util::surrealdb::{thing_to_string, DB};
|
|
#[cfg(feature = "server")]
|
|
use surrealdb::sql::statements::{BeginStatement, CommitStatement};
|
|
|
|
#[derive(Debug, Deserialize, Serialize, PartialEq, Eq, Clone)]
|
|
pub struct User {
|
|
#[cfg_attr(feature = "server", serde(deserialize_with = "thing_to_string"))]
|
|
pub id: String,
|
|
pub email: String,
|
|
password: Option<String>,
|
|
}
|
|
|
|
#[cfg(feature = "server")]
|
|
impl User {
|
|
pub async fn new(
|
|
email: String,
|
|
password: String,
|
|
member_ids: Vec<String>,
|
|
) -> Result<Self, crate::Error> {
|
|
// Create user record
|
|
let mut transaction = DB.query(BeginStatement::default())
|
|
.query("let $user = CREATE ONLY user SET email = $email, password = crypto::argon2::generate($password) RETURN id, email;")
|
|
.bind(("email", email))
|
|
.bind(("password", password));
|
|
|
|
// Link user with members
|
|
for member_id in member_ids {
|
|
transaction = transaction.query(format!("RELATE ONLY (type::thing('user', $user.id))->user_to_member->(type::thing('member', $member_id_{}));", member_id))
|
|
.bind((format!("member_id_{}", member_id), member_id));
|
|
}
|
|
|
|
let mut res = transaction
|
|
.query("RETURN $user")
|
|
.query(CommitStatement::default())
|
|
.await?;
|
|
|
|
let user: Option<Self> = res.take(0)?;
|
|
|
|
match user {
|
|
Some(u) => Ok(u),
|
|
None => Err(crate::Error::NoDocument),
|
|
}
|
|
}
|
|
|
|
pub async fn verify_credentials(
|
|
email: String,
|
|
password: String,
|
|
) -> Result<String, crate::Error> {
|
|
let mut res = DB
|
|
.query("SELECT VALUE id FROM user WHERE email = $email AND crypto::argon2::compare(password, $password)")
|
|
.bind(("email", email)).bind(("password", password))
|
|
.await?;
|
|
|
|
let id: Option<String> = res.take(0)?;
|
|
|
|
match id {
|
|
Some(i) => Ok(i),
|
|
None => Err(crate::Error::NoDocument),
|
|
}
|
|
}
|
|
|
|
pub async fn change_email(&self, new_email: String) -> Result<String, crate::Error> {
|
|
let mut res = DB
|
|
.query("UPDATE type::thing('user', $user_id) SET email = $email RETURN VALUE email")
|
|
.bind(("user_id", self.id.to_string()))
|
|
.bind(("email", new_email))
|
|
.await?;
|
|
|
|
let email: Option<String> = res.take(0)?;
|
|
|
|
match email {
|
|
Some(e) => Ok(e),
|
|
None => Err(crate::Error::NoDocument),
|
|
}
|
|
}
|
|
|
|
pub async fn change_password(
|
|
&self,
|
|
old_password: String,
|
|
new_password: String,
|
|
) -> Result<(), crate::Error> {
|
|
let mut res = DB
|
|
.query(
|
|
"
|
|
LET $user = (SELECT * FROM ONLY type::thing('user', $user_id));
|
|
|
|
IF crypto::argon2::compare($user.password, $old_password) {
|
|
UPDATE type::thing('user', $user_id) SET password = crypto::argon2::generate($new_password)
|
|
} ELSE {
|
|
THROW 'Password dit not match'
|
|
};
|
|
",
|
|
)
|
|
.bind(("user_id", self.id.to_string()))
|
|
.bind(("old_password", old_password))
|
|
.bind(("new_password", new_password))
|
|
.await?;
|
|
|
|
tracing::info!("{res:?}");
|
|
|
|
if res.take_errors().len() != 0 {
|
|
return Err(crate::Error::NoDocument);
|
|
}
|
|
|
|
Ok(())
|
|
}
|
|
}
|