2021-04-09 00:22:17 +00:00
|
|
|
use tokio_postgres::GenericClient;
|
|
|
|
use uuid::Uuid;
|
|
|
|
|
2021-11-12 23:10:20 +00:00
|
|
|
use crate::database::catch_unique_violation;
|
2021-04-09 00:22:17 +00:00
|
|
|
use crate::errors::DatabaseError;
|
2022-11-08 20:33:28 +00:00
|
|
|
use crate::identity::did_pkh::DidPkh;
|
2021-04-09 00:22:17 +00:00
|
|
|
use crate::models::profiles::queries::create_profile;
|
|
|
|
use crate::models::profiles::types::{DbActorProfile, ProfileCreateData};
|
2022-08-15 21:54:46 +00:00
|
|
|
use crate::utils::currencies::Currency;
|
2021-10-05 20:57:24 +00:00
|
|
|
use super::types::{DbUser, User, UserCreateData};
|
2021-10-02 16:32:57 +00:00
|
|
|
use super::utils::generate_invite_code;
|
2021-04-09 00:22:17 +00:00
|
|
|
|
2021-10-02 16:32:57 +00:00
|
|
|
pub async fn create_invite_code(
|
2021-04-09 00:22:17 +00:00
|
|
|
db_client: &impl GenericClient,
|
|
|
|
) -> Result<String, DatabaseError> {
|
2021-10-02 16:32:57 +00:00
|
|
|
let invite_code = generate_invite_code();
|
2021-04-09 00:22:17 +00:00
|
|
|
db_client.execute(
|
|
|
|
"
|
|
|
|
INSERT INTO user_invite_code (code)
|
|
|
|
VALUES ($1)
|
|
|
|
",
|
|
|
|
&[&invite_code],
|
|
|
|
).await?;
|
|
|
|
Ok(invite_code)
|
|
|
|
}
|
|
|
|
|
|
|
|
pub async fn get_invite_codes(
|
|
|
|
db_client: &impl GenericClient,
|
|
|
|
) -> Result<Vec<String>, DatabaseError> {
|
|
|
|
let rows = db_client.query(
|
|
|
|
"
|
|
|
|
SELECT code
|
|
|
|
FROM user_invite_code
|
|
|
|
WHERE used = FALSE
|
|
|
|
",
|
|
|
|
&[],
|
|
|
|
).await?;
|
|
|
|
let codes: Vec<String> = rows.iter()
|
|
|
|
.map(|row| row.try_get("code"))
|
|
|
|
.collect::<Result<_, _>>()?;
|
|
|
|
Ok(codes)
|
|
|
|
}
|
|
|
|
|
|
|
|
pub async fn is_valid_invite_code(
|
|
|
|
db_client: &impl GenericClient,
|
|
|
|
invite_code: &str,
|
|
|
|
) -> Result<bool, DatabaseError> {
|
|
|
|
let maybe_row = db_client.query_opt(
|
|
|
|
"
|
|
|
|
SELECT 1 FROM user_invite_code
|
|
|
|
WHERE code = $1 AND used = FALSE
|
|
|
|
",
|
|
|
|
&[&invite_code],
|
|
|
|
).await?;
|
|
|
|
Ok(maybe_row.is_some())
|
|
|
|
}
|
|
|
|
|
|
|
|
pub async fn create_user(
|
|
|
|
db_client: &mut impl GenericClient,
|
2021-10-05 20:57:24 +00:00
|
|
|
user_data: UserCreateData,
|
2021-04-09 00:22:17 +00:00
|
|
|
) -> Result<User, DatabaseError> {
|
|
|
|
let transaction = db_client.transaction().await?;
|
|
|
|
// Use invite code
|
2021-10-05 20:57:24 +00:00
|
|
|
if let Some(ref invite_code) = user_data.invite_code {
|
2021-04-09 00:22:17 +00:00
|
|
|
let updated_count = transaction.execute(
|
|
|
|
"
|
|
|
|
UPDATE user_invite_code
|
|
|
|
SET used = TRUE
|
|
|
|
WHERE code = $1 AND used = FALSE
|
|
|
|
",
|
|
|
|
&[&invite_code],
|
|
|
|
).await?;
|
|
|
|
if updated_count == 0 {
|
2021-11-13 17:37:31 +00:00
|
|
|
return Err(DatabaseError::NotFound("invite code"));
|
2021-04-09 00:22:17 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
// Create profile
|
|
|
|
let profile_data = ProfileCreateData {
|
2021-10-05 20:57:24 +00:00
|
|
|
username: user_data.username.clone(),
|
2022-10-04 19:18:05 +00:00
|
|
|
hostname: None,
|
2021-04-09 00:22:17 +00:00
|
|
|
display_name: None,
|
|
|
|
bio: None,
|
|
|
|
avatar: None,
|
|
|
|
banner: None,
|
2022-04-26 14:12:26 +00:00
|
|
|
identity_proofs: vec![],
|
2022-07-23 15:47:53 +00:00
|
|
|
payment_options: vec![],
|
2021-09-17 12:36:24 +00:00
|
|
|
extra_fields: vec![],
|
2021-12-31 12:01:08 +00:00
|
|
|
actor_json: None,
|
2021-04-09 00:22:17 +00:00
|
|
|
};
|
2022-01-08 11:20:48 +00:00
|
|
|
let profile = create_profile(&transaction, profile_data).await?;
|
2021-04-09 00:22:17 +00:00
|
|
|
// Create user
|
2021-11-12 23:10:20 +00:00
|
|
|
let row = transaction.query_one(
|
2021-04-09 00:22:17 +00:00
|
|
|
"
|
|
|
|
INSERT INTO user_account (
|
|
|
|
id, wallet_address, password_hash, private_key, invite_code
|
|
|
|
)
|
|
|
|
VALUES ($1, $2, $3, $4, $5)
|
|
|
|
RETURNING user_account
|
|
|
|
",
|
|
|
|
&[
|
|
|
|
&profile.id,
|
2021-10-05 20:57:24 +00:00
|
|
|
&user_data.wallet_address,
|
2022-01-06 19:06:35 +00:00
|
|
|
&user_data.password_hash,
|
|
|
|
&user_data.private_key_pem,
|
2021-10-05 20:57:24 +00:00
|
|
|
&user_data.invite_code,
|
2021-04-09 00:22:17 +00:00
|
|
|
],
|
2021-11-12 23:10:20 +00:00
|
|
|
).await.map_err(catch_unique_violation("user"))?;
|
|
|
|
let db_user: DbUser = row.try_get("user_account")?;
|
2021-11-12 22:05:31 +00:00
|
|
|
let user = User::new(db_user, profile);
|
2021-11-12 23:10:20 +00:00
|
|
|
transaction.commit().await?;
|
|
|
|
Ok(user)
|
2021-04-09 00:22:17 +00:00
|
|
|
}
|
|
|
|
|
2021-12-24 16:12:17 +00:00
|
|
|
pub async fn get_user_by_id(
|
|
|
|
db_client: &impl GenericClient,
|
|
|
|
user_id: &Uuid,
|
|
|
|
) -> Result<User, DatabaseError> {
|
|
|
|
let maybe_row = db_client.query_opt(
|
|
|
|
"
|
|
|
|
SELECT user_account, actor_profile
|
|
|
|
FROM user_account JOIN actor_profile USING (id)
|
|
|
|
WHERE id = $1
|
|
|
|
",
|
|
|
|
&[&user_id],
|
|
|
|
).await?;
|
|
|
|
let row = maybe_row.ok_or(DatabaseError::NotFound("user"))?;
|
|
|
|
let db_user: DbUser = row.try_get("user_account")?;
|
|
|
|
let db_profile: DbActorProfile = row.try_get("actor_profile")?;
|
|
|
|
let user = User::new(db_user, db_profile);
|
|
|
|
Ok(user)
|
|
|
|
}
|
|
|
|
|
|
|
|
pub async fn get_user_by_name(
|
|
|
|
db_client: &impl GenericClient,
|
|
|
|
username: &str,
|
|
|
|
) -> Result<User, DatabaseError> {
|
|
|
|
let maybe_row = db_client.query_opt(
|
|
|
|
"
|
|
|
|
SELECT user_account, actor_profile
|
|
|
|
FROM user_account JOIN actor_profile USING (id)
|
|
|
|
WHERE actor_profile.username = $1
|
|
|
|
",
|
|
|
|
&[&username],
|
|
|
|
).await?;
|
|
|
|
let row = maybe_row.ok_or(DatabaseError::NotFound("user"))?;
|
|
|
|
let db_user: DbUser = row.try_get("user_account")?;
|
|
|
|
let db_profile: DbActorProfile = row.try_get("actor_profile")?;
|
|
|
|
let user = User::new(db_user, db_profile);
|
|
|
|
Ok(user)
|
|
|
|
}
|
|
|
|
|
|
|
|
pub async fn is_registered_user(
|
|
|
|
db_client: &impl GenericClient,
|
|
|
|
username: &str,
|
|
|
|
) -> Result<bool, DatabaseError> {
|
|
|
|
let maybe_row = db_client.query_opt(
|
|
|
|
"
|
|
|
|
SELECT 1 FROM user_account JOIN actor_profile USING (id)
|
|
|
|
WHERE actor_profile.username = $1
|
|
|
|
",
|
|
|
|
&[&username],
|
|
|
|
).await?;
|
|
|
|
Ok(maybe_row.is_some())
|
|
|
|
}
|
|
|
|
|
2022-08-15 21:54:46 +00:00
|
|
|
pub async fn get_user_by_login_address(
|
2021-04-09 00:22:17 +00:00
|
|
|
db_client: &impl GenericClient,
|
|
|
|
wallet_address: &str,
|
|
|
|
) -> Result<User, DatabaseError> {
|
|
|
|
let maybe_row = db_client.query_opt(
|
|
|
|
"
|
|
|
|
SELECT user_account, actor_profile
|
|
|
|
FROM user_account JOIN actor_profile USING (id)
|
|
|
|
WHERE wallet_address = $1
|
|
|
|
",
|
|
|
|
&[&wallet_address],
|
|
|
|
).await?;
|
|
|
|
let row = maybe_row.ok_or(DatabaseError::NotFound("user"))?;
|
|
|
|
let db_user: DbUser = row.try_get("user_account")?;
|
|
|
|
let db_profile: DbActorProfile = row.try_get("actor_profile")?;
|
2021-12-24 16:12:17 +00:00
|
|
|
let user = User::new(db_user, db_profile);
|
2021-04-09 00:22:17 +00:00
|
|
|
Ok(user)
|
|
|
|
}
|
2022-07-07 18:55:47 +00:00
|
|
|
|
2022-08-15 21:54:46 +00:00
|
|
|
pub async fn get_user_by_did(
|
|
|
|
db_client: &impl GenericClient,
|
|
|
|
did: &DidPkh,
|
|
|
|
) -> Result<User, DatabaseError> {
|
|
|
|
// DIDs must be locally unique
|
|
|
|
let maybe_row = db_client.query_opt(
|
|
|
|
"
|
|
|
|
SELECT user_account, actor_profile
|
|
|
|
FROM user_account JOIN actor_profile USING (id)
|
|
|
|
WHERE
|
|
|
|
EXISTS (
|
|
|
|
SELECT 1
|
|
|
|
FROM jsonb_array_elements(actor_profile.identity_proofs) AS proof
|
|
|
|
WHERE proof ->> 'issuer' = $1
|
|
|
|
)
|
|
|
|
",
|
|
|
|
&[&did.to_string()],
|
|
|
|
).await?;
|
|
|
|
let row = maybe_row.ok_or(DatabaseError::NotFound("user"))?;
|
|
|
|
let db_user: DbUser = row.try_get("user_account")?;
|
|
|
|
let db_profile: DbActorProfile = row.try_get("actor_profile")?;
|
|
|
|
let user = User::new(db_user, db_profile);
|
|
|
|
Ok(user)
|
|
|
|
}
|
|
|
|
|
2022-11-02 13:07:18 +00:00
|
|
|
pub async fn get_user_by_public_wallet_address(
|
2022-08-15 21:54:46 +00:00
|
|
|
db_client: &impl GenericClient,
|
|
|
|
currency: &Currency,
|
|
|
|
wallet_address: &str,
|
|
|
|
) -> Result<User, DatabaseError> {
|
|
|
|
let did = DidPkh::from_address(currency, wallet_address);
|
|
|
|
get_user_by_did(db_client, &did).await
|
|
|
|
}
|
|
|
|
|
2022-07-07 18:55:47 +00:00
|
|
|
pub async fn get_user_count(
|
|
|
|
db_client: &impl GenericClient,
|
|
|
|
) -> Result<i64, DatabaseError> {
|
|
|
|
let row = db_client.query_one(
|
|
|
|
"SELECT count(user_account) FROM user_account",
|
|
|
|
&[],
|
|
|
|
).await?;
|
|
|
|
let count = row.try_get("count")?;
|
|
|
|
Ok(count)
|
|
|
|
}
|