|
@@ -9,8 +9,7 @@ use serde_json::Value;
|
|
|
use tokio::sync::{Mutex, RwLock};
|
|
|
use uuid::Uuid;
|
|
|
|
|
|
-use flowy_error::{internal_error, ErrorCode};
|
|
|
-use flowy_server_config::supabase_config::SupabaseConfiguration;
|
|
|
+use flowy_error::{internal_error, ErrorCode, FlowyResult};
|
|
|
use flowy_sqlite::kv::StorePreferences;
|
|
|
use flowy_sqlite::schema::user_table;
|
|
|
use flowy_sqlite::ConnectionPool;
|
|
@@ -25,16 +24,15 @@ use crate::event_map::{
|
|
|
use crate::migrations::historical_document::HistoricalEmptyDocumentMigration;
|
|
|
use crate::migrations::local_user_to_cloud::migration_user_to_cloud;
|
|
|
use crate::migrations::migration::UserLocalDataMigration;
|
|
|
-use crate::migrations::UserMigrationContext;
|
|
|
+use crate::migrations::MigrationUser;
|
|
|
+use crate::services::cloud_config::remove_cloud_config;
|
|
|
use crate::services::database::UserDB;
|
|
|
-use crate::services::entities::Session;
|
|
|
+use crate::services::entities::{ResumableSignUp, Session};
|
|
|
use crate::services::user_awareness::UserAwarenessDataSource;
|
|
|
use crate::services::user_sql::{UserTable, UserTableChangeset};
|
|
|
use crate::services::user_workspace::save_user_workspaces;
|
|
|
use crate::{errors::FlowyError, notification::*};
|
|
|
|
|
|
-const SUPABASE_CONFIG_CACHE_KEY: &str = "af_supabase_config";
|
|
|
-
|
|
|
pub struct UserSessionConfig {
|
|
|
root_dir: String,
|
|
|
|
|
@@ -62,6 +60,7 @@ pub struct UserManager {
|
|
|
pub(crate) user_awareness: Arc<Mutex<Option<MutexUserAwareness>>>,
|
|
|
pub(crate) user_status_callback: RwLock<Arc<dyn UserStatusCallback>>,
|
|
|
pub(crate) collab_builder: Weak<AppFlowyCollabBuilder>,
|
|
|
+ resumable_sign_up: Mutex<Option<ResumableSignUp>>,
|
|
|
}
|
|
|
|
|
|
impl UserManager {
|
|
@@ -82,6 +81,7 @@ impl UserManager {
|
|
|
user_awareness: Arc::new(Default::default()),
|
|
|
user_status_callback,
|
|
|
collab_builder,
|
|
|
+ resumable_sign_up: Default::default(),
|
|
|
}
|
|
|
}
|
|
|
|
|
@@ -160,29 +160,18 @@ impl UserManager {
|
|
|
params: BoxAny,
|
|
|
auth_type: AuthType,
|
|
|
) -> Result<UserProfile, FlowyError> {
|
|
|
+ self.update_auth_type(&auth_type).await;
|
|
|
let response: SignInResponse = self
|
|
|
.cloud_services
|
|
|
.get_user_service()?
|
|
|
.sign_in(params)
|
|
|
.await?;
|
|
|
- let session: Session = response.clone().into();
|
|
|
- let uid = session.user_id;
|
|
|
- let device_id = session.device_id.clone();
|
|
|
+ let session = Session::from(&response);
|
|
|
self.set_collab_config(&session);
|
|
|
- self.set_current_session(Some(session.clone()))?;
|
|
|
- self.log_historical_user(
|
|
|
- uid,
|
|
|
- &response.device_id,
|
|
|
- response.name.clone(),
|
|
|
- &auth_type,
|
|
|
- self.user_dir(uid),
|
|
|
- );
|
|
|
- let user_workspace = response.latest_workspace.clone();
|
|
|
- save_user_workspaces(uid, self.db_pool(uid)?, &response.user_workspaces)?;
|
|
|
- let user_profile: UserProfile = self
|
|
|
- .save_user(uid, (response, auth_type).into())
|
|
|
- .await?
|
|
|
- .into();
|
|
|
+
|
|
|
+ let latest_workspace = response.latest_workspace.clone();
|
|
|
+ let user_profile = UserProfile::from((&response, &auth_type));
|
|
|
+ self.save_auth_data(&response, &auth_type, &session).await?;
|
|
|
let _ = self
|
|
|
.initialize_user_awareness(&session, UserAwarenessDataSource::Remote)
|
|
|
.await;
|
|
@@ -191,25 +180,23 @@ impl UserManager {
|
|
|
.user_status_callback
|
|
|
.read()
|
|
|
.await
|
|
|
- .did_sign_in(user_profile.id, &user_workspace, &device_id)
|
|
|
+ .did_sign_in(user_profile.uid, &latest_workspace, &session.device_id)
|
|
|
.await
|
|
|
{
|
|
|
tracing::error!("Failed to call did_sign_in callback: {:?}", e);
|
|
|
}
|
|
|
-
|
|
|
send_sign_in_notification()
|
|
|
.payload::<UserProfilePB>(user_profile.clone().into())
|
|
|
.send();
|
|
|
Ok(user_profile)
|
|
|
}
|
|
|
|
|
|
- pub async fn update_auth_type(&self, auth_type: &AuthType) {
|
|
|
+ pub(crate) async fn update_auth_type(&self, auth_type: &AuthType) {
|
|
|
self
|
|
|
.user_status_callback
|
|
|
.read()
|
|
|
.await
|
|
|
.auth_type_did_changed(auth_type.clone());
|
|
|
-
|
|
|
self.cloud_services.set_auth_type(auth_type.clone());
|
|
|
}
|
|
|
|
|
@@ -220,94 +207,117 @@ impl UserManager {
|
|
|
/// and saving workspace information. If a user is signing up with a new profile and previously had guest data,
|
|
|
/// this function may migrate that data over to the new account.
|
|
|
///
|
|
|
- #[tracing::instrument(level = "debug", skip(self, params))]
|
|
|
+ #[tracing::instrument(level = "info", skip(self, params))]
|
|
|
pub async fn sign_up(
|
|
|
&self,
|
|
|
auth_type: AuthType,
|
|
|
params: BoxAny,
|
|
|
) -> Result<UserProfile, FlowyError> {
|
|
|
- let old_user = {
|
|
|
- if let Ok(old_session) = self.get_session() {
|
|
|
- self
|
|
|
- .get_user_profile(old_session.user_id, false)
|
|
|
- .await
|
|
|
- .ok()
|
|
|
- .map(|user_profile| UserMigrationContext {
|
|
|
- user_profile,
|
|
|
- session: old_session,
|
|
|
- })
|
|
|
- } else {
|
|
|
- None
|
|
|
- }
|
|
|
- };
|
|
|
+ remove_cloud_config(&self.store_preferences);
|
|
|
+ self.update_auth_type(&auth_type).await;
|
|
|
|
|
|
+ let migration_user = self.get_migration_user(&auth_type).await;
|
|
|
let auth_service = self.cloud_services.get_user_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 user_profile = UserProfile::from((&response, &auth_type));
|
|
|
+ if user_profile.encryption_type.is_need_encrypt_secret() {
|
|
|
+ self
|
|
|
+ .resumable_sign_up
|
|
|
+ .lock()
|
|
|
+ .await
|
|
|
+ .replace(ResumableSignUp {
|
|
|
+ user_profile: user_profile.clone(),
|
|
|
+ migration_user,
|
|
|
+ response,
|
|
|
+ auth_type,
|
|
|
+ });
|
|
|
+ } else {
|
|
|
+ self
|
|
|
+ .continue_sign_up(&user_profile, migration_user, response, &auth_type)
|
|
|
+ .await?;
|
|
|
+ }
|
|
|
+ Ok(user_profile)
|
|
|
+ }
|
|
|
+
|
|
|
+ #[tracing::instrument(level = "info", skip(self))]
|
|
|
+ pub async fn resume_sign_up(&self) -> Result<(), FlowyError> {
|
|
|
+ let ResumableSignUp {
|
|
|
+ user_profile,
|
|
|
+ migration_user,
|
|
|
+ response,
|
|
|
+ auth_type,
|
|
|
+ } = self
|
|
|
+ .resumable_sign_up
|
|
|
+ .lock()
|
|
|
+ .await
|
|
|
+ .clone()
|
|
|
+ .ok_or(FlowyError::new(
|
|
|
+ ErrorCode::Internal,
|
|
|
+ "No resumable sign up data",
|
|
|
+ ))?;
|
|
|
+ self
|
|
|
+ .continue_sign_up(&user_profile, migration_user, response, &auth_type)
|
|
|
+ .await?;
|
|
|
+ Ok(())
|
|
|
+ }
|
|
|
+
|
|
|
+ #[tracing::instrument(level = "info", skip_all, err)]
|
|
|
+ async fn continue_sign_up(
|
|
|
+ &self,
|
|
|
+ user_profile: &UserProfile,
|
|
|
+ migration_user: Option<MigrationUser>,
|
|
|
+ response: SignUpResponse,
|
|
|
+ auth_type: &AuthType,
|
|
|
+ ) -> FlowyResult<()> {
|
|
|
let new_session = Session::from(&response);
|
|
|
- self.set_current_session(Some(new_session.clone()))?;
|
|
|
self.set_collab_config(&new_session);
|
|
|
- let uid = response.user_id;
|
|
|
- self.log_historical_user(
|
|
|
- uid,
|
|
|
- &response.device_id,
|
|
|
- response.name.clone(),
|
|
|
- &auth_type,
|
|
|
- self.user_dir(uid),
|
|
|
- );
|
|
|
- save_user_workspaces(uid, self.db_pool(uid)?, &response.user_workspaces)?;
|
|
|
- let new_user_profile: UserProfile = self
|
|
|
- .save_user(uid, (response, auth_type.clone()).into())
|
|
|
- .await?
|
|
|
- .into();
|
|
|
- let user_awareness_source = if sign_up_context.is_new {
|
|
|
+
|
|
|
+ let user_awareness_source = if response.is_new_user {
|
|
|
UserAwarenessDataSource::Local
|
|
|
} else {
|
|
|
UserAwarenessDataSource::Remote
|
|
|
};
|
|
|
- // Only migrate the data if the user is login in as a guest and sign up as a new user if the current
|
|
|
- // auth type is not [AuthType::Local].
|
|
|
- if sign_up_context.is_new {
|
|
|
- if let Some(old_user) = old_user {
|
|
|
- if old_user.user_profile.auth_type == AuthType::Local && !auth_type.is_local() {
|
|
|
- let new_user = UserMigrationContext {
|
|
|
- user_profile: new_user_profile.clone(),
|
|
|
- session: new_session.clone(),
|
|
|
- };
|
|
|
- tracing::info!(
|
|
|
- "Migrate old user data from {:?} to {:?}",
|
|
|
- old_user.user_profile.id,
|
|
|
- new_user.user_profile.id
|
|
|
- );
|
|
|
- match self.migrate_local_user_to_cloud(&old_user, &new_user).await {
|
|
|
- Ok(folder_data) => sign_up_context.local_folder = folder_data,
|
|
|
- Err(e) => tracing::error!("{:?}", e),
|
|
|
- }
|
|
|
- // close the old user db
|
|
|
- let _ = self.database.close(old_user.session.user_id);
|
|
|
+ let mut sign_up_context = SignUpContext {
|
|
|
+ is_new: response.is_new_user,
|
|
|
+ local_folder: None,
|
|
|
+ };
|
|
|
+ if response.is_new_user {
|
|
|
+ if let Some(old_user) = migration_user {
|
|
|
+ let new_user = MigrationUser {
|
|
|
+ user_profile: user_profile.clone(),
|
|
|
+ session: new_session.clone(),
|
|
|
+ };
|
|
|
+ tracing::info!(
|
|
|
+ "Migrate old user data from {:?} to {:?}",
|
|
|
+ old_user.user_profile.uid,
|
|
|
+ new_user.user_profile.uid
|
|
|
+ );
|
|
|
+ match self.migrate_local_user_to_cloud(&old_user, &new_user).await {
|
|
|
+ Ok(folder_data) => sign_up_context.local_folder = folder_data,
|
|
|
+ Err(e) => tracing::error!("{:?}", e),
|
|
|
}
|
|
|
+ let _ = self.database.close(old_user.session.user_id);
|
|
|
}
|
|
|
}
|
|
|
-
|
|
|
self
|
|
|
.initialize_user_awareness(&new_session, user_awareness_source)
|
|
|
.await;
|
|
|
|
|
|
- let _ = self
|
|
|
+ self
|
|
|
.user_status_callback
|
|
|
.read()
|
|
|
.await
|
|
|
.did_sign_up(
|
|
|
sign_up_context,
|
|
|
- &new_user_profile,
|
|
|
+ user_profile,
|
|
|
&new_session.user_workspace,
|
|
|
&new_session.device_id,
|
|
|
)
|
|
|
- .await;
|
|
|
- Ok(new_user_profile)
|
|
|
+ .await?;
|
|
|
+ self
|
|
|
+ .save_auth_data(&response, auth_type, &new_session)
|
|
|
+ .await?;
|
|
|
+ Ok(())
|
|
|
}
|
|
|
|
|
|
#[tracing::instrument(level = "info", skip(self))]
|
|
@@ -315,6 +325,7 @@ impl UserManager {
|
|
|
let session = self.get_session()?;
|
|
|
self.database.close(session.user_id)?;
|
|
|
self.set_current_session(None)?;
|
|
|
+ remove_cloud_config(&self.store_preferences);
|
|
|
|
|
|
let server = self.cloud_services.get_user_service()?;
|
|
|
tokio::spawn(async move {
|
|
@@ -337,7 +348,8 @@ impl UserManager {
|
|
|
&self,
|
|
|
params: UpdateUserProfileParams,
|
|
|
) -> Result<(), FlowyError> {
|
|
|
- let auth_type = params.auth_type.clone();
|
|
|
+ let old_user_profile = self.get_user_profile(params.uid, false).await?;
|
|
|
+ let auth_type = old_user_profile.auth_type.clone();
|
|
|
let session = self.get_session()?;
|
|
|
let changeset = UserTableChangeset::new(params.clone());
|
|
|
diesel_update_table!(
|
|
@@ -347,13 +359,12 @@ impl UserManager {
|
|
|
);
|
|
|
|
|
|
let session = self.get_session()?;
|
|
|
- let user_profile = self.get_user_profile(session.user_id, false).await?;
|
|
|
- let profile_pb: UserProfilePB = user_profile.into();
|
|
|
+ let new_user_profile = self.get_user_profile(session.user_id, false).await?;
|
|
|
send_notification(
|
|
|
&session.user_id.to_string(),
|
|
|
UserNotification::DidUpdateUserProfile,
|
|
|
)
|
|
|
- .payload(profile_pb)
|
|
|
+ .payload(UserProfilePB::from(new_user_profile))
|
|
|
.send();
|
|
|
self
|
|
|
.update_user(&auth_type, session.user_id, None, params)
|
|
@@ -441,13 +452,6 @@ impl UserManager {
|
|
|
Ok(None)
|
|
|
}
|
|
|
|
|
|
- pub fn save_supabase_config(&self, config: SupabaseConfiguration) {
|
|
|
- self.cloud_services.set_supabase_config(&config);
|
|
|
- let _ = self
|
|
|
- .store_preferences
|
|
|
- .set_object(SUPABASE_CONFIG_CACHE_KEY, config);
|
|
|
- }
|
|
|
-
|
|
|
async fn update_user(
|
|
|
&self,
|
|
|
_auth_type: &AuthType,
|
|
@@ -466,7 +470,7 @@ impl UserManager {
|
|
|
Ok(())
|
|
|
}
|
|
|
|
|
|
- async fn save_user(&self, uid: i64, user: UserTable) -> Result<UserTable, FlowyError> {
|
|
|
+ async fn save_user(&self, uid: i64, user: UserTable) -> Result<(), FlowyError> {
|
|
|
let conn = self.db_connection(uid)?;
|
|
|
conn.immediate_transaction(|| {
|
|
|
// delete old user if exists
|
|
@@ -474,12 +478,12 @@ impl UserManager {
|
|
|
.execute(&*conn)?;
|
|
|
|
|
|
let _ = diesel::insert_into(user_table::table)
|
|
|
- .values(user.clone())
|
|
|
+ .values(user)
|
|
|
.execute(&*conn)?;
|
|
|
Ok::<(), FlowyError>(())
|
|
|
})?;
|
|
|
|
|
|
- Ok(user)
|
|
|
+ Ok(())
|
|
|
}
|
|
|
|
|
|
pub(crate) fn set_current_session(&self, session: Option<Session>) -> Result<(), FlowyError> {
|
|
@@ -520,6 +524,29 @@ impl UserManager {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+ async fn save_auth_data(
|
|
|
+ &self,
|
|
|
+ response: &impl UserAuthResponse,
|
|
|
+ auth_type: &AuthType,
|
|
|
+ session: &Session,
|
|
|
+ ) -> Result<(), FlowyError> {
|
|
|
+ let user_profile = UserProfile::from((response, auth_type));
|
|
|
+ let uid = user_profile.uid;
|
|
|
+ self.add_historical_user(
|
|
|
+ uid,
|
|
|
+ response.device_id(),
|
|
|
+ response.user_name().to_string(),
|
|
|
+ auth_type,
|
|
|
+ self.user_dir(uid),
|
|
|
+ );
|
|
|
+ save_user_workspaces(uid, self.db_pool(uid)?, response.user_workspaces())?;
|
|
|
+ self
|
|
|
+ .save_user(uid, (user_profile, auth_type.clone()).into())
|
|
|
+ .await?;
|
|
|
+ self.set_current_session(Some(session.clone()))?;
|
|
|
+ Ok(())
|
|
|
+ }
|
|
|
+
|
|
|
fn set_collab_config(&self, session: &Session) {
|
|
|
let collab_builder = self.collab_builder.upgrade().unwrap();
|
|
|
collab_builder.set_sync_device(session.device_id.clone());
|
|
@@ -529,21 +556,18 @@ impl UserManager {
|
|
|
|
|
|
async fn migrate_local_user_to_cloud(
|
|
|
&self,
|
|
|
- old_user: &UserMigrationContext,
|
|
|
- new_user: &UserMigrationContext,
|
|
|
+ old_user: &MigrationUser,
|
|
|
+ new_user: &MigrationUser,
|
|
|
) -> Result<Option<FolderData>, FlowyError> {
|
|
|
let old_collab_db = self.database.get_collab_db(old_user.session.user_id)?;
|
|
|
let new_collab_db = self.database.get_collab_db(new_user.session.user_id)?;
|
|
|
let folder_data = migration_user_to_cloud(old_user, &old_collab_db, new_user, &new_collab_db)?;
|
|
|
+ // Save the old user workspace setting.
|
|
|
+ save_user_workspaces(
|
|
|
+ old_user.session.user_id,
|
|
|
+ self.database.get_pool(old_user.session.user_id)?,
|
|
|
+ &[old_user.session.user_workspace.clone()],
|
|
|
+ )?;
|
|
|
Ok(folder_data)
|
|
|
}
|
|
|
}
|
|
|
-
|
|
|
-pub fn get_supabase_config(
|
|
|
- store_preference: &Arc<StorePreferences>,
|
|
|
-) -> Option<SupabaseConfiguration> {
|
|
|
- store_preference
|
|
|
- .get_str(SUPABASE_CONFIG_CACHE_KEY)
|
|
|
- .and_then(|s| serde_json::from_str(&s).ok())
|
|
|
- .unwrap_or_else(|| SupabaseConfiguration::from_env().ok())
|
|
|
-}
|