AppFlowy-Cloud/src/biz/user/user_verify.rs

84 lines
2.6 KiB
Rust

use std::ops::DerefMut;
use anyhow::{Context, Result};
use sqlx::types::uuid;
use tracing::{event, instrument, trace};
use app_error::AppError;
use database::user::{create_user, is_user_exist};
use database::workspace::select_workspace;
use database_entity::dto::AFRole;
use workspace_template::document::getting_started::GettingStartedTemplate;
use crate::biz::user::user_init::initialize_workspace_for_user;
use crate::state::AppState;
/// Verify the token from the gotrue server and create the user if it is a new user
/// Return true if the user is a new user
///
#[instrument(skip_all, err)]
pub async fn verify_token(access_token: &str, state: &AppState) -> Result<bool, AppError> {
let user = state.gotrue_client.user_info(access_token).await?;
let user_uuid = uuid::Uuid::parse_str(&user.id)?;
let name = name_from_user_metadata(&user.user_metadata);
// Create new user if it doesn't exist
let mut txn = state
.pg_pool
.begin()
.await
.context("acquire transaction to verify token")?;
let is_new = !is_user_exist(txn.deref_mut(), &user_uuid).await?;
if is_new {
let new_uid = state.id_gen.write().await.next_id();
event!(tracing::Level::INFO, "create new user:{}", new_uid);
let workspace_id =
create_user(txn.deref_mut(), new_uid, &user_uuid, &user.email, &name).await?;
let workspace_row = select_workspace(txn.deref_mut(), &workspace_id).await?;
// It's essential to cache the user's role because subsequent actions will rely on this cached information.
state
.workspace_access_control
.insert_role(&new_uid, &workspace_id, AFRole::Owner)
.await?;
// Need to commit the transaction for the record in `af_user` to be inserted
// so that `initialize_workspace_for_user` will be able to find the user
txn
.commit()
.await
.context("fail to commit transaction to verify token")?;
// Create a workspace with the GetStarted template
let mut txn2 = state.pg_pool.begin().await?;
initialize_workspace_for_user(
new_uid,
&user_uuid,
&workspace_row,
&mut txn2,
vec![GettingStartedTemplate],
&state.collab_access_control_storage,
)
.await?;
txn2
.commit()
.await
.context("fail to commit transaction to initialize workspace")?;
} else {
trace!("user already exists:{},{}", user.id, user.email);
}
Ok(is_new)
}
// Best effort to get user's name after oauth
fn name_from_user_metadata(value: &serde_json::Value) -> String {
value
.get("name")
.or(value.get("full_name"))
.or(value.get("nickname"))
.and_then(serde_json::Value::as_str)
.map(str::to_string)
.unwrap_or_default()
}