2023-07-14 05:37:13 +00:00
|
|
|
use std::collections::HashMap;
|
|
|
|
use std::str::FromStr;
|
2023-04-28 06:08:53 +00:00
|
|
|
use std::sync::Arc;
|
2023-04-04 00:41:16 +00:00
|
|
|
|
2023-05-15 14:16:05 +00:00
|
|
|
use appflowy_integrate::RocksCollabDB;
|
2023-07-14 05:37:13 +00:00
|
|
|
use collab_folder::core::FolderData;
|
2023-05-15 14:16:05 +00:00
|
|
|
use serde::{Deserialize, Serialize};
|
2023-05-21 10:53:59 +00:00
|
|
|
use serde_repr::*;
|
2023-05-15 14:16:05 +00:00
|
|
|
use tokio::sync::RwLock;
|
2023-07-14 05:37:13 +00:00
|
|
|
use uuid::Uuid;
|
2023-05-15 14:16:05 +00:00
|
|
|
|
2023-07-14 05:37:13 +00:00
|
|
|
use flowy_error::{internal_error, ErrorCode};
|
|
|
|
use flowy_server_config::supabase_config::SupabaseConfiguration;
|
2023-01-31 00:28:31 +00:00
|
|
|
use flowy_sqlite::ConnectionPool;
|
|
|
|
use flowy_sqlite::{
|
2023-02-13 01:29:49 +00:00
|
|
|
kv::KV,
|
|
|
|
query_dsl::*,
|
|
|
|
schema::{user_table, user_table::dsl},
|
2023-07-14 05:37:13 +00:00
|
|
|
DBConnection, ExpressionMethods,
|
2021-07-10 08:27:20 +00:00
|
|
|
};
|
2023-05-21 10:53:59 +00:00
|
|
|
use lib_infra::box_any::BoxAny;
|
2023-05-17 01:49:39 +00:00
|
|
|
|
|
|
|
use crate::entities::{
|
2023-05-21 10:53:59 +00:00
|
|
|
AuthTypePB, SignInResponse, SignUpResponse, UpdateUserProfileParams, UserProfile,
|
2023-02-13 01:29:49 +00:00
|
|
|
};
|
2023-04-28 06:08:53 +00:00
|
|
|
use crate::entities::{UserProfilePB, UserSettingPB};
|
2023-07-05 12:57:09 +00:00
|
|
|
use crate::event_map::{
|
2023-07-14 05:37:13 +00:00
|
|
|
DefaultUserStatusCallback, SignUpContext, UserCloudServiceProvider, UserCredentials,
|
|
|
|
UserStatusCallback,
|
2023-07-05 12:57:09 +00:00
|
|
|
};
|
2023-07-14 05:37:13 +00:00
|
|
|
use crate::services::user_data::UserDataMigration;
|
2023-04-28 06:08:53 +00:00
|
|
|
use crate::{
|
2023-05-17 04:46:48 +00:00
|
|
|
errors::FlowyError,
|
2023-04-28 06:08:53 +00:00
|
|
|
notification::*,
|
|
|
|
services::database::{UserDB, UserTable, UserTableChangeset},
|
|
|
|
};
|
|
|
|
|
2023-07-14 05:37:13 +00:00
|
|
|
pub(crate) const SUPABASE_CONFIG_CACHE_KEY: &str = "supabase_config_cache_key";
|
2021-07-10 08:27:20 +00:00
|
|
|
pub struct UserSessionConfig {
|
2023-02-13 01:29:49 +00:00
|
|
|
root_dir: String,
|
2022-12-20 03:14:42 +00:00
|
|
|
|
2023-02-13 01:29:49 +00:00
|
|
|
/// Used as the key of `Session` when saving session information to KV.
|
|
|
|
session_cache_key: String,
|
2021-07-10 08:27:20 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
impl UserSessionConfig {
|
2023-02-13 01:29:49 +00:00
|
|
|
/// The `root_dir` represents as the root of the user folders. It must be unique for each
|
|
|
|
/// users.
|
|
|
|
pub fn new(name: &str, root_dir: &str) -> Self {
|
|
|
|
let session_cache_key = format!("{}_session_cache", name);
|
|
|
|
Self {
|
|
|
|
root_dir: root_dir.to_owned(),
|
|
|
|
session_cache_key,
|
2021-07-10 08:27:20 +00:00
|
|
|
}
|
2023-02-13 01:29:49 +00:00
|
|
|
}
|
2021-07-10 08:27:20 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
pub struct UserSession {
|
2023-02-13 01:29:49 +00:00
|
|
|
database: UserDB,
|
2023-05-15 14:16:05 +00:00
|
|
|
session_config: UserSessionConfig,
|
2023-05-21 10:53:59 +00:00
|
|
|
cloud_services: Arc<dyn UserCloudServiceProvider>,
|
2023-05-31 09:42:14 +00:00
|
|
|
user_status_callback: RwLock<Arc<dyn UserStatusCallback>>,
|
2021-07-10 08:27:20 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
impl UserSession {
|
2023-05-21 10:53:59 +00:00
|
|
|
pub fn new(
|
|
|
|
session_config: UserSessionConfig,
|
|
|
|
cloud_services: Arc<dyn UserCloudServiceProvider>,
|
|
|
|
) -> Self {
|
2023-05-15 14:16:05 +00:00
|
|
|
let db = UserDB::new(&session_config.root_dir);
|
2023-05-31 09:42:14 +00:00
|
|
|
let user_status_callback: RwLock<Arc<dyn UserStatusCallback>> =
|
|
|
|
RwLock::new(Arc::new(DefaultUserStatusCallback));
|
2023-02-13 01:29:49 +00:00
|
|
|
Self {
|
|
|
|
database: db,
|
2023-05-15 14:16:05 +00:00
|
|
|
session_config,
|
2023-05-21 10:53:59 +00:00
|
|
|
cloud_services,
|
2023-02-13 01:29:49 +00:00
|
|
|
user_status_callback,
|
2021-08-31 15:01:46 +00:00
|
|
|
}
|
2023-02-13 01:29:49 +00:00
|
|
|
}
|
2021-08-31 15:01:46 +00:00
|
|
|
|
2023-02-13 01:29:49 +00:00
|
|
|
pub async fn init<C: UserStatusCallback + 'static>(&self, user_status_callback: C) {
|
|
|
|
if let Ok(session) = self.get_session() {
|
2023-07-14 05:37:13 +00:00
|
|
|
if let Err(e) = user_status_callback
|
|
|
|
.did_init(session.user_id, &session.workspace_id)
|
|
|
|
.await
|
|
|
|
{
|
|
|
|
tracing::error!("Failed to call did_sign_in callback: {:?}", e);
|
|
|
|
}
|
2021-07-11 07:33:19 +00:00
|
|
|
}
|
2023-05-31 09:42:14 +00:00
|
|
|
*self.user_status_callback.write().await = Arc::new(user_status_callback);
|
2023-02-13 01:29:49 +00:00
|
|
|
}
|
|
|
|
|
2023-07-14 05:37:13 +00:00
|
|
|
pub fn db_connection(&self, uid: i64) -> Result<DBConnection, FlowyError> {
|
|
|
|
self.database.get_connection(uid)
|
2023-02-13 01:29:49 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// The caller will be not 'Sync' before of the return value,
|
|
|
|
// PooledConnection<ConnectionManager> is not sync. You can use
|
|
|
|
// db_connection_pool function to require the ConnectionPool that is 'Sync'.
|
|
|
|
//
|
|
|
|
// let pool = self.db_connection_pool()?;
|
|
|
|
// let conn: PooledConnection<ConnectionManager> = pool.get()?;
|
2023-07-14 05:37:13 +00:00
|
|
|
pub fn db_pool(&self, uid: i64) -> Result<Arc<ConnectionPool>, FlowyError> {
|
|
|
|
self.database.get_pool(uid)
|
2023-04-04 00:41:16 +00:00
|
|
|
}
|
|
|
|
|
2023-07-14 05:37:13 +00:00
|
|
|
pub fn get_collab_db(&self, uid: i64) -> Result<Arc<RocksCollabDB>, FlowyError> {
|
|
|
|
self.database.get_collab_db(uid)
|
|
|
|
}
|
|
|
|
|
|
|
|
pub async fn migrate_old_user_data(
|
|
|
|
&self,
|
|
|
|
old_uid: i64,
|
|
|
|
old_workspace_id: &str,
|
|
|
|
new_uid: i64,
|
|
|
|
new_workspace_id: &str,
|
|
|
|
) -> Result<Option<FolderData>, FlowyError> {
|
|
|
|
let old_collab_db = self.database.get_collab_db(old_uid)?;
|
|
|
|
let new_collab_db = self.database.get_collab_db(new_uid)?;
|
|
|
|
let folder_data = UserDataMigration::migration(
|
|
|
|
old_uid,
|
|
|
|
&old_collab_db,
|
|
|
|
old_workspace_id,
|
|
|
|
new_uid,
|
|
|
|
&new_collab_db,
|
|
|
|
new_workspace_id,
|
|
|
|
)?;
|
|
|
|
Ok(folder_data)
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn clear_old_user(&self, old_uid: i64) {
|
|
|
|
let _ = self.database.close(old_uid);
|
2023-02-13 01:29:49 +00:00
|
|
|
}
|
|
|
|
|
2023-05-21 10:53:59 +00:00
|
|
|
#[tracing::instrument(level = "debug", skip(self, params))]
|
|
|
|
pub async fn sign_in(
|
|
|
|
&self,
|
|
|
|
params: BoxAny,
|
2023-07-14 05:37:13 +00:00
|
|
|
auth_type: AuthType,
|
2023-05-21 10:53:59 +00:00
|
|
|
) -> Result<UserProfile, FlowyError> {
|
|
|
|
let resp = self
|
|
|
|
.cloud_services
|
2023-05-31 09:42:14 +00:00
|
|
|
.get_auth_service()?
|
2023-05-21 10:53:59 +00:00
|
|
|
.sign_in(params)
|
|
|
|
.await?;
|
|
|
|
|
|
|
|
let session: Session = resp.clone().into();
|
2023-07-14 05:37:13 +00:00
|
|
|
let uid = session.user_id;
|
2023-05-21 10:53:59 +00:00
|
|
|
self.set_session(Some(session))?;
|
2023-07-14 05:37:13 +00:00
|
|
|
let user_profile: UserProfile = self.save_user(uid, (resp, auth_type).into()).await?.into();
|
|
|
|
if let Err(e) = self
|
2023-05-21 10:53:59 +00:00
|
|
|
.user_status_callback
|
|
|
|
.read()
|
|
|
|
.await
|
|
|
|
.did_sign_in(user_profile.id, &user_profile.workspace_id)
|
2023-07-14 05:37:13 +00:00
|
|
|
.await
|
|
|
|
{
|
|
|
|
tracing::error!("Failed to call did_sign_in callback: {:?}", e);
|
|
|
|
}
|
2023-05-21 10:53:59 +00:00
|
|
|
send_sign_in_notification()
|
|
|
|
.payload::<UserProfilePB>(user_profile.clone().into())
|
|
|
|
.send();
|
|
|
|
|
|
|
|
Ok(user_profile)
|
2023-02-13 01:29:49 +00:00
|
|
|
}
|
|
|
|
|
2023-07-14 05:37:13 +00:00
|
|
|
pub async fn update_auth_type(&self, auth_type: &AuthType) {
|
2023-05-31 09:42:14 +00:00
|
|
|
self
|
|
|
|
.user_status_callback
|
|
|
|
.read()
|
|
|
|
.await
|
|
|
|
.auth_type_did_changed(auth_type.clone());
|
|
|
|
|
2023-05-23 15:55:21 +00:00
|
|
|
self.cloud_services.set_auth_type(auth_type.clone());
|
2023-07-14 05:37:13 +00:00
|
|
|
}
|
2023-05-21 10:53:59 +00:00
|
|
|
|
2023-07-14 05:37:13 +00:00
|
|
|
#[tracing::instrument(level = "debug", skip(self, params))]
|
|
|
|
pub async fn sign_up(
|
|
|
|
&self,
|
|
|
|
auth_type: AuthType,
|
|
|
|
params: BoxAny,
|
|
|
|
) -> Result<UserProfile, FlowyError> {
|
|
|
|
let old_user_profile = {
|
|
|
|
if let Ok(old_session) = self.get_session() {
|
|
|
|
self.get_user_profile(old_session.user_id, false).await.ok()
|
|
|
|
} else {
|
|
|
|
None
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
let auth_service = self.cloud_services.get_auth_service()?;
|
|
|
|
let response: SignUpResponse = auth_service.sign_up(params).await?;
|
|
|
|
let mut sign_up_context = SignUpContext {
|
|
|
|
is_new: response.is_new,
|
|
|
|
local_folder: None,
|
|
|
|
};
|
|
|
|
let session = Session {
|
|
|
|
user_id: response.user_id,
|
|
|
|
workspace_id: response.workspace_id.clone(),
|
|
|
|
};
|
|
|
|
let uid = session.user_id;
|
2023-05-21 10:53:59 +00:00
|
|
|
self.set_session(Some(session))?;
|
2023-07-14 05:37:13 +00:00
|
|
|
let user_table = self
|
|
|
|
.save_user(uid, (response, auth_type.clone()).into())
|
|
|
|
.await?;
|
|
|
|
let new_user_profile: UserProfile = user_table.into();
|
|
|
|
|
|
|
|
// Only migrate the data if the user is login in as a guest and sign up as a new user
|
|
|
|
if sign_up_context.is_new {
|
|
|
|
if let Some(old_user_profile) = old_user_profile {
|
|
|
|
if old_user_profile.auth_type == AuthType::Local && !auth_type.is_local() {
|
|
|
|
tracing::info!(
|
|
|
|
"Migrate old user data from {:?} to {:?}",
|
|
|
|
old_user_profile.id,
|
|
|
|
new_user_profile.id
|
|
|
|
);
|
|
|
|
match self
|
|
|
|
.migrate_old_user_data(
|
|
|
|
old_user_profile.id,
|
|
|
|
&old_user_profile.workspace_id,
|
|
|
|
new_user_profile.id,
|
|
|
|
&new_user_profile.workspace_id,
|
|
|
|
)
|
|
|
|
.await
|
|
|
|
{
|
|
|
|
Ok(folder_data) => sign_up_context.local_folder = folder_data,
|
|
|
|
Err(e) => tracing::error!("{:?}", e),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-05-21 10:53:59 +00:00
|
|
|
let _ = self
|
|
|
|
.user_status_callback
|
|
|
|
.read()
|
|
|
|
.await
|
2023-07-14 05:37:13 +00:00
|
|
|
.did_sign_up(sign_up_context, &new_user_profile)
|
2023-05-21 10:53:59 +00:00
|
|
|
.await;
|
2023-07-14 05:37:13 +00:00
|
|
|
Ok(new_user_profile)
|
2023-02-13 01:29:49 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
#[tracing::instrument(level = "debug", skip(self))]
|
2023-07-14 05:37:13 +00:00
|
|
|
pub async fn sign_out(&self) -> Result<(), FlowyError> {
|
2023-02-13 01:29:49 +00:00
|
|
|
let session = self.get_session()?;
|
2023-07-14 05:37:13 +00:00
|
|
|
self.database.close(session.user_id)?;
|
2023-02-13 01:29:49 +00:00
|
|
|
self.set_session(None)?;
|
2023-05-23 15:55:21 +00:00
|
|
|
|
2023-05-31 09:42:14 +00:00
|
|
|
let server = self.cloud_services.get_auth_service()?;
|
2023-06-09 14:23:07 +00:00
|
|
|
tokio::spawn(async move {
|
2023-07-14 05:37:13 +00:00
|
|
|
match server.sign_out(None).await {
|
2023-05-21 10:53:59 +00:00
|
|
|
Ok(_) => {},
|
|
|
|
Err(e) => tracing::error!("Sign out failed: {:?}", e),
|
|
|
|
}
|
|
|
|
});
|
2023-02-13 01:29:49 +00:00
|
|
|
Ok(())
|
|
|
|
}
|
2021-09-01 08:08:32 +00:00
|
|
|
|
2023-02-13 01:29:49 +00:00
|
|
|
#[tracing::instrument(level = "debug", skip(self))]
|
|
|
|
pub async fn update_user_profile(
|
|
|
|
&self,
|
|
|
|
params: UpdateUserProfileParams,
|
|
|
|
) -> Result<(), FlowyError> {
|
2023-05-21 10:53:59 +00:00
|
|
|
let auth_type = params.auth_type.clone();
|
2023-02-13 01:29:49 +00:00
|
|
|
let session = self.get_session()?;
|
|
|
|
let changeset = UserTableChangeset::new(params.clone());
|
2023-07-14 05:37:13 +00:00
|
|
|
diesel_update_table!(
|
|
|
|
user_table,
|
|
|
|
changeset,
|
|
|
|
&*self.db_connection(session.user_id)?
|
|
|
|
);
|
2023-02-13 01:29:49 +00:00
|
|
|
|
2023-07-14 05:37:13 +00:00
|
|
|
let session = self.get_session()?;
|
|
|
|
let user_profile = self.get_user_profile(session.user_id, false).await?;
|
2023-02-13 01:29:49 +00:00
|
|
|
let profile_pb: UserProfilePB = user_profile.into();
|
2023-05-21 10:53:59 +00:00
|
|
|
send_notification(
|
|
|
|
&session.user_id.to_string(),
|
|
|
|
UserNotification::DidUpdateUserProfile,
|
|
|
|
)
|
|
|
|
.payload(profile_pb)
|
|
|
|
.send();
|
|
|
|
self
|
2023-07-14 05:37:13 +00:00
|
|
|
.update_user(&auth_type, session.user_id, None, params)
|
2023-05-21 10:53:59 +00:00
|
|
|
.await?;
|
2023-02-13 01:29:49 +00:00
|
|
|
Ok(())
|
|
|
|
}
|
2021-12-09 14:28:11 +00:00
|
|
|
|
2023-02-13 01:29:49 +00:00
|
|
|
pub async fn init_user(&self) -> Result<(), FlowyError> {
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
2023-07-14 05:37:13 +00:00
|
|
|
pub async fn check_user(&self) -> Result<(), FlowyError> {
|
|
|
|
let user_id = self.get_session()?.user_id;
|
|
|
|
let credential = UserCredentials::from_uid(user_id);
|
|
|
|
let auth_service = self.cloud_services.get_auth_service()?;
|
|
|
|
auth_service.check_user(credential).await
|
|
|
|
}
|
|
|
|
|
|
|
|
pub async fn check_user_with_uuid(&self, uuid: &Uuid) -> Result<(), FlowyError> {
|
|
|
|
let credential = UserCredentials::from_uuid(uuid.to_string());
|
2023-07-05 12:57:09 +00:00
|
|
|
let auth_service = self.cloud_services.get_auth_service()?;
|
|
|
|
auth_service.check_user(credential).await
|
2023-02-13 01:29:49 +00:00
|
|
|
}
|
|
|
|
|
2023-07-14 05:37:13 +00:00
|
|
|
/// Get the user profile from the database
|
|
|
|
/// If the refresh is true, it will try to get the user profile from the server
|
|
|
|
pub async fn get_user_profile(&self, uid: i64, refresh: bool) -> Result<UserProfile, FlowyError> {
|
|
|
|
let user_id = uid.to_string();
|
2023-02-13 01:29:49 +00:00
|
|
|
let user = dsl::user_table
|
|
|
|
.filter(user_table::id.eq(&user_id))
|
2023-07-14 05:37:13 +00:00
|
|
|
.first::<UserTable>(&*(self.db_connection(uid)?))?;
|
|
|
|
|
|
|
|
if refresh {
|
|
|
|
let weak_auth_service = Arc::downgrade(&self.cloud_services.get_auth_service()?);
|
|
|
|
let weak_pool = Arc::downgrade(&self.database.get_pool(uid)?);
|
|
|
|
tokio::spawn(async move {
|
|
|
|
if let (Some(auth_service), Some(pool)) = (weak_auth_service.upgrade(), weak_pool.upgrade())
|
|
|
|
{
|
|
|
|
if let Ok(Some(user_profile)) = auth_service
|
|
|
|
.get_user_profile(UserCredentials::from_uid(uid))
|
|
|
|
.await
|
|
|
|
{
|
|
|
|
let changeset = UserTableChangeset::from_user_profile(user_profile.clone());
|
|
|
|
if let Ok(conn) = pool.get() {
|
|
|
|
let filter = dsl::user_table.filter(dsl::id.eq(changeset.id.clone()));
|
|
|
|
let _ = diesel::update(filter).set(changeset).execute(&*conn);
|
|
|
|
|
|
|
|
// Send notification to the client
|
|
|
|
let user_profile_pb: UserProfilePB = user_profile.into();
|
|
|
|
send_notification(&uid.to_string(), UserNotification::DidUpdateUserProfile)
|
|
|
|
.payload(user_profile_pb)
|
|
|
|
.send();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
2023-02-13 01:29:49 +00:00
|
|
|
|
|
|
|
Ok(user.into())
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn user_dir(&self) -> Result<String, FlowyError> {
|
|
|
|
let session = self.get_session()?;
|
2023-05-15 14:16:05 +00:00
|
|
|
Ok(format!(
|
|
|
|
"{}/{}",
|
|
|
|
self.session_config.root_dir, session.user_id
|
|
|
|
))
|
2023-02-13 01:29:49 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
pub fn user_setting(&self) -> Result<UserSettingPB, FlowyError> {
|
|
|
|
let user_setting = UserSettingPB {
|
|
|
|
user_folder: self.user_dir()?,
|
|
|
|
};
|
|
|
|
Ok(user_setting)
|
|
|
|
}
|
|
|
|
|
2023-04-04 00:41:16 +00:00
|
|
|
pub fn user_id(&self) -> Result<i64, FlowyError> {
|
2023-02-13 01:29:49 +00:00
|
|
|
Ok(self.get_session()?.user_id)
|
|
|
|
}
|
|
|
|
|
2023-07-14 05:37:13 +00:00
|
|
|
pub fn token(&self) -> Result<Option<String>, FlowyError> {
|
|
|
|
Ok(None)
|
2023-02-13 01:29:49 +00:00
|
|
|
}
|
|
|
|
|
2023-07-14 05:37:13 +00:00
|
|
|
pub fn save_supabase_config(&self, config: SupabaseConfiguration) {
|
|
|
|
self.cloud_services.update_supabase_config(&config);
|
|
|
|
let _ = KV::set_object(SUPABASE_CONFIG_CACHE_KEY, config);
|
2023-02-13 01:29:49 +00:00
|
|
|
}
|
2021-09-01 08:08:32 +00:00
|
|
|
}
|
|
|
|
|
2023-07-14 05:37:13 +00:00
|
|
|
pub fn get_supabase_config() -> Option<SupabaseConfiguration> {
|
|
|
|
KV::get_str(SUPABASE_CONFIG_CACHE_KEY)
|
|
|
|
.and_then(|s| serde_json::from_str(&s).ok())
|
|
|
|
.unwrap_or_else(|| SupabaseConfiguration::from_env().ok())
|
|
|
|
}
|
|
|
|
|
2021-09-01 08:08:32 +00:00
|
|
|
impl UserSession {
|
2023-05-21 10:53:59 +00:00
|
|
|
async fn update_user(
|
2023-02-13 01:29:49 +00:00
|
|
|
&self,
|
2023-05-31 09:42:14 +00:00
|
|
|
_auth_type: &AuthType,
|
2023-05-21 10:53:59 +00:00
|
|
|
uid: i64,
|
2023-07-14 05:37:13 +00:00
|
|
|
token: Option<String>,
|
2023-02-13 01:29:49 +00:00
|
|
|
params: UpdateUserProfileParams,
|
|
|
|
) -> Result<(), FlowyError> {
|
2023-05-31 09:42:14 +00:00
|
|
|
let server = self.cloud_services.get_auth_service()?;
|
2023-02-13 01:29:49 +00:00
|
|
|
let token = token.to_owned();
|
|
|
|
let _ = tokio::spawn(async move {
|
2023-07-05 12:57:09 +00:00
|
|
|
let credentials = UserCredentials::new(token, Some(uid), None);
|
|
|
|
match server.update_user(credentials, params).await {
|
2023-02-13 01:29:49 +00:00
|
|
|
Ok(_) => {},
|
|
|
|
Err(e) => {
|
|
|
|
tracing::error!("update user profile failed: {:?}", e);
|
|
|
|
},
|
|
|
|
}
|
|
|
|
})
|
|
|
|
.await;
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
2023-07-14 05:37:13 +00:00
|
|
|
async fn save_user(&self, uid: i64, user: UserTable) -> Result<UserTable, FlowyError> {
|
|
|
|
let conn = self.db_connection(uid)?;
|
2023-07-05 12:57:09 +00:00
|
|
|
conn.immediate_transaction(|| {
|
|
|
|
// delete old user if exists
|
|
|
|
diesel::delete(dsl::user_table.filter(dsl::id.eq(&user.id))).execute(&*conn)?;
|
|
|
|
|
|
|
|
let _ = diesel::insert_into(user_table::table)
|
|
|
|
.values(user.clone())
|
|
|
|
.execute(&*conn)?;
|
|
|
|
Ok::<(), FlowyError>(())
|
|
|
|
})?;
|
|
|
|
|
2023-02-13 01:29:49 +00:00
|
|
|
Ok(user)
|
|
|
|
}
|
|
|
|
|
|
|
|
fn set_session(&self, session: Option<Session>) -> Result<(), FlowyError> {
|
|
|
|
tracing::debug!("Set user session: {:?}", session);
|
|
|
|
match &session {
|
2023-05-17 04:46:48 +00:00
|
|
|
None => KV::remove(&self.session_config.session_cache_key),
|
|
|
|
Some(session) => {
|
|
|
|
KV::set_object(&self.session_config.session_cache_key, session.clone())
|
|
|
|
.map_err(internal_error)?;
|
|
|
|
},
|
2021-09-01 03:21:42 +00:00
|
|
|
}
|
2023-02-13 01:29:49 +00:00
|
|
|
Ok(())
|
|
|
|
}
|
2021-09-03 08:43:03 +00:00
|
|
|
|
2023-07-14 05:37:13 +00:00
|
|
|
/// Returns the current user session.
|
|
|
|
pub fn get_session(&self) -> Result<Session, FlowyError> {
|
2023-05-17 04:46:48 +00:00
|
|
|
match KV::get_object::<Session>(&self.session_config.session_cache_key) {
|
2023-07-14 05:37:13 +00:00
|
|
|
None => Err(FlowyError::new(
|
|
|
|
ErrorCode::RecordNotFound,
|
|
|
|
"User is not logged in".to_string(),
|
|
|
|
)),
|
2023-05-17 04:46:48 +00:00
|
|
|
Some(session) => Ok(session),
|
2021-07-18 15:56:36 +00:00
|
|
|
}
|
2023-02-13 01:29:49 +00:00
|
|
|
}
|
2021-08-31 15:01:46 +00:00
|
|
|
|
2023-07-14 05:37:13 +00:00
|
|
|
pub fn sign_in_history(&self) -> Vec<UserProfile> {
|
|
|
|
// match self.db_connection(uid) {
|
|
|
|
// Ok(conn) => match dsl::user_table.load::<UserTable>(&*conn) {
|
|
|
|
// Ok(users) => users.into_iter().map(|u| u.into()).collect(),
|
|
|
|
// Err(_) => vec![],
|
|
|
|
// },
|
|
|
|
// Err(e) => {
|
|
|
|
// tracing::error!("get user sign in history failed: {:?}", e);
|
|
|
|
// vec![]
|
|
|
|
// },
|
|
|
|
// }
|
|
|
|
vec![]
|
2023-02-13 01:29:49 +00:00
|
|
|
}
|
2021-07-13 15:08:20 +00:00
|
|
|
}
|
|
|
|
|
2021-09-01 03:21:42 +00:00
|
|
|
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
|
2023-07-14 05:37:13 +00:00
|
|
|
pub struct Session {
|
|
|
|
pub user_id: i64,
|
|
|
|
pub workspace_id: String,
|
2021-09-01 03:21:42 +00:00
|
|
|
}
|
|
|
|
|
2021-12-09 13:39:53 +00:00
|
|
|
impl std::convert::From<SignInResponse> for Session {
|
2023-02-13 01:29:49 +00:00
|
|
|
fn from(resp: SignInResponse) -> Self {
|
|
|
|
Session {
|
|
|
|
user_id: resp.user_id,
|
2023-05-21 10:53:59 +00:00
|
|
|
workspace_id: resp.workspace_id,
|
2021-12-09 13:39:53 +00:00
|
|
|
}
|
2023-02-13 01:29:49 +00:00
|
|
|
}
|
2021-12-09 13:39:53 +00:00
|
|
|
}
|
|
|
|
|
2021-09-01 03:21:42 +00:00
|
|
|
impl std::convert::From<String> for Session {
|
2023-02-13 01:29:49 +00:00
|
|
|
fn from(s: String) -> Self {
|
|
|
|
match serde_json::from_str(&s) {
|
|
|
|
Ok(s) => s,
|
|
|
|
Err(e) => {
|
|
|
|
tracing::error!("Deserialize string to Session failed: {:?}", e);
|
|
|
|
Session::default()
|
|
|
|
},
|
2021-09-01 03:21:42 +00:00
|
|
|
}
|
2023-02-13 01:29:49 +00:00
|
|
|
}
|
2021-09-01 03:21:42 +00:00
|
|
|
}
|
2021-11-27 11:19:41 +00:00
|
|
|
impl std::convert::From<Session> for String {
|
2023-02-13 01:29:49 +00:00
|
|
|
fn from(session: Session) -> Self {
|
|
|
|
match serde_json::to_string(&session) {
|
|
|
|
Ok(s) => s,
|
|
|
|
Err(e) => {
|
|
|
|
tracing::error!("Serialize session to string failed: {:?}", e);
|
|
|
|
"".to_string()
|
|
|
|
},
|
2021-09-01 03:21:42 +00:00
|
|
|
}
|
2023-02-13 01:29:49 +00:00
|
|
|
}
|
2021-09-01 03:21:42 +00:00
|
|
|
}
|
2023-04-04 00:41:16 +00:00
|
|
|
|
2023-05-21 10:53:59 +00:00
|
|
|
#[derive(Debug, Clone, Hash, Serialize_repr, Deserialize_repr, Eq, PartialEq)]
|
|
|
|
#[repr(u8)]
|
|
|
|
pub enum AuthType {
|
|
|
|
/// It's a local server, we do fake sign in default.
|
|
|
|
Local = 0,
|
|
|
|
/// Currently not supported. It will be supported in the future when the
|
|
|
|
/// [AppFlowy-Server](https://github.com/AppFlowy-IO/AppFlowy-Server) ready.
|
|
|
|
SelfHosted = 1,
|
|
|
|
/// It uses Supabase as the backend.
|
|
|
|
Supabase = 2,
|
|
|
|
}
|
|
|
|
|
2023-07-14 05:37:13 +00:00
|
|
|
impl AuthType {
|
|
|
|
pub fn is_local(&self) -> bool {
|
|
|
|
matches!(self, AuthType::Local)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-05-21 10:53:59 +00:00
|
|
|
impl Default for AuthType {
|
|
|
|
fn default() -> Self {
|
|
|
|
Self::Local
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl From<AuthTypePB> for AuthType {
|
|
|
|
fn from(pb: AuthTypePB) -> Self {
|
|
|
|
match pb {
|
|
|
|
AuthTypePB::Supabase => AuthType::Supabase,
|
|
|
|
AuthTypePB::Local => AuthType::Local,
|
|
|
|
AuthTypePB::SelfHosted => AuthType::SelfHosted,
|
|
|
|
}
|
|
|
|
}
|
2023-04-04 00:41:16 +00:00
|
|
|
}
|
2023-07-14 05:37:13 +00:00
|
|
|
|
|
|
|
impl From<AuthType> for AuthTypePB {
|
|
|
|
fn from(auth_type: AuthType) -> Self {
|
|
|
|
match auth_type {
|
|
|
|
AuthType::Supabase => AuthTypePB::Supabase,
|
|
|
|
AuthType::Local => AuthTypePB::Local,
|
|
|
|
AuthType::SelfHosted => AuthTypePB::SelfHosted,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl From<i32> for AuthType {
|
|
|
|
fn from(value: i32) -> Self {
|
|
|
|
match value {
|
|
|
|
0 => AuthType::Local,
|
|
|
|
1 => AuthType::SelfHosted,
|
|
|
|
2 => AuthType::Supabase,
|
|
|
|
_ => AuthType::Local,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub struct ThirdPartyParams {
|
|
|
|
pub uuid: Uuid,
|
|
|
|
pub email: String,
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn uuid_from_box_any(any: BoxAny) -> Result<ThirdPartyParams, FlowyError> {
|
|
|
|
let map: HashMap<String, String> = any.unbox_or_error()?;
|
|
|
|
let uuid = uuid_from_map(&map)?;
|
|
|
|
let email = map.get("email").cloned().unwrap_or_default();
|
|
|
|
Ok(ThirdPartyParams { uuid, email })
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn uuid_from_map(map: &HashMap<String, String>) -> Result<Uuid, FlowyError> {
|
|
|
|
let uuid = map
|
|
|
|
.get("uuid")
|
|
|
|
.ok_or_else(|| FlowyError::new(ErrorCode::MissingAuthField, "Missing uuid field"))?
|
|
|
|
.as_str();
|
|
|
|
Uuid::from_str(uuid).map_err(internal_error)
|
|
|
|
}
|