Sfoglia il codice sorgente

fix: only encrypt if enable (#3236)

* fix: error page display issue

* fix: override document with empty data

* chore: add logs

* fix: encrypt errors

* fix: encrypt errors
Nathan.fooo 1 anno fa
parent
commit
de01bf70cd

+ 3 - 0
frontend/appflowy_flutter/lib/user/application/auth/supabase_auth_service.dart

@@ -107,6 +107,9 @@ class SupabaseAuthService implements AuthService {
     if (!isSupabaseEnabled) {
       return _appFlowyAuthService.signUpWithOAuth(platform: platform);
     }
+    // Before signing in, sign out any existing users. Otherwise, the callback will be triggered even if the user doesn't click the 'Sign In' button on the website
+    await _auth.signOut();
+
     final provider = platform.toProvider();
     final completer = supabaseLoginCompleter(
       onSuccess: (userId, userEmail) async {

+ 4 - 1
frontend/appflowy_flutter/packages/flowy_infra_ui/lib/widget/error_page.dart

@@ -1,4 +1,3 @@
-
 import 'package:flowy_infra_ui/flowy_infra_ui.dart';
 import 'package:flowy_svg/flowy_svg.dart';
 import 'package:flutter/material.dart';
@@ -71,6 +70,8 @@ class FlowyErrorPage extends StatelessWidget {
     return Padding(
       padding: const EdgeInsets.all(8.0),
       child: Column(
+        // mainAxisAlignment: MainAxisAlignment.center,
+        crossAxisAlignment: CrossAxisAlignment.start,
         children: [
           const FlowyText.medium(
             "AppFlowy Error",
@@ -81,12 +82,14 @@ class FlowyErrorPage extends StatelessWidget {
           ),
           FlowyText.semibold(
             message,
+            maxLines: 10,
           ),
           const SizedBox(
             height: _titleToMessagePadding,
           ),
           FlowyText.regular(
             howToFix,
+            maxLines: 10,
           ),
           const SizedBox(
             height: _titleToMessagePadding,

+ 4 - 13
frontend/rust-lib/flowy-core/src/integrate/server.rs

@@ -24,7 +24,7 @@ use flowy_user::event_map::UserCloudServiceProvider;
 use flowy_user::services::database::{
   get_user_profile, get_user_workspace, open_collab_db, open_user_db,
 };
-use flowy_user_deps::cloud::{UserCloudConfig, UserService};
+use flowy_user_deps::cloud::UserService;
 use flowy_user_deps::entities::*;
 use lib_infra::future::FutureResult;
 
@@ -75,25 +75,15 @@ impl AppFlowyServerProvider {
   pub fn new(
     config: AppFlowyCoreConfig,
     provider_type: ServerProviderType,
-    cloud_config: Option<UserCloudConfig>,
     store_preferences: Weak<StorePreferences>,
   ) -> Self {
-    let enable_sync = cloud_config
-      .as_ref()
-      .map(|config| config.enable_sync)
-      .unwrap_or(true);
-    let encryption = EncryptionImpl::new(
-      cloud_config
-        .as_ref()
-        .map(|config| config.encrypt_secret.clone()),
-    );
-
+    let encryption = EncryptionImpl::new(None);
     Self {
       config,
       provider_type: RwLock::new(provider_type),
       device_id: Default::default(),
       providers: RwLock::new(HashMap::new()),
-      enable_sync: RwLock::new(enable_sync),
+      enable_sync: RwLock::new(true),
       encryption: RwLock::new(Arc::new(encryption)),
       store_preferences,
     }
@@ -177,6 +167,7 @@ impl UserCloudServiceProvider for AppFlowyServerProvider {
   }
 
   fn set_encrypt_secret(&self, secret: String) {
+    tracing::info!("🔑Set encrypt secret");
     self.encryption.write().set_secret(secret);
   }
 

+ 13 - 2
frontend/rust-lib/flowy-core/src/lib.rs

@@ -22,7 +22,7 @@ use flowy_sqlite::kv::StorePreferences;
 use flowy_task::{TaskDispatcher, TaskRunner};
 use flowy_user::event_map::{SignUpContext, UserCloudServiceProvider, UserStatusCallback};
 use flowy_user::manager::{UserManager, UserSessionConfig};
-use flowy_user::services::cloud_config::get_cloud_config;
+use flowy_user_deps::cloud::UserCloudConfig;
 use flowy_user_deps::entities::{AuthType, UserProfile, UserWorkspace};
 use lib_dispatch::prelude::*;
 use lib_dispatch::runtime::tokio_default_runtime;
@@ -150,7 +150,6 @@ impl AppFlowyCore {
     let server_provider = Arc::new(AppFlowyServerProvider::new(
       config.clone(),
       provider_type,
-      get_cloud_config(&store_preference),
       Arc::downgrade(&store_preference),
     ));
 
@@ -293,6 +292,7 @@ impl UserStatusCallback for UserStatusCallbackImpl {
   fn did_init(
     &self,
     user_id: i64,
+    cloud_config: &Option<UserCloudConfig>,
     user_workspace: &UserWorkspace,
     _device_id: &str,
   ) -> Fut<FlowyResult<()>> {
@@ -303,6 +303,17 @@ impl UserStatusCallback for UserStatusCallbackImpl {
     let database_manager = self.database_manager.clone();
     let document_manager = self.document_manager.clone();
 
+    if let Some(cloud_config) = cloud_config {
+      self
+        .server_provider
+        .set_enable_sync(cloud_config.enable_sync);
+      if cloud_config.enable_encrypt() {
+        self
+          .server_provider
+          .set_encrypt_secret(cloud_config.encrypt_secret.clone());
+      }
+    }
+
     to_fut(async move {
       collab_builder.initialize(user_workspace.id.clone());
       folder_manager

+ 1 - 1
frontend/rust-lib/flowy-database2/src/manager.rs

@@ -175,7 +175,7 @@ impl DatabaseManager {
     let database = wdb
       .get_database(database_id)
       .await
-      .ok_or_else(FlowyError::record_not_found)?;
+      .ok_or_else(FlowyError::collab_not_sync)?;
 
     let editor = Arc::new(DatabaseEditor::new(database, self.task_scheduler.clone()).await?);
     editors.insert(database_id.to_string(), editor.clone());

+ 4 - 18
frontend/rust-lib/flowy-document2/src/manager.rs

@@ -11,7 +11,7 @@ use collab_document::YrsDocAction;
 use parking_lot::RwLock;
 
 use flowy_document_deps::cloud::DocumentCloudService;
-use flowy_error::{internal_error, ErrorCode, FlowyError, FlowyResult};
+use flowy_error::{internal_error, FlowyError, FlowyResult};
 
 use crate::document::MutexDocument;
 use crate::entities::DocumentSnapshotPB;
@@ -71,6 +71,7 @@ impl DocumentManager {
   }
 
   /// Return the document
+  #[tracing::instrument(level = "debug", skip_all)]
   pub async fn get_document(&self, doc_id: &str) -> FlowyResult<Arc<MutexDocument>> {
     if let Some(doc) = self.documents.read().get(doc_id) {
       return Ok(doc.clone());
@@ -78,16 +79,9 @@ impl DocumentManager {
     let mut updates = vec![];
     if !self.is_doc_exist(doc_id)? {
       // Try to get the document from the cloud service
-      match self.cloud_service.get_document_updates(doc_id).await {
-        Ok(document_updates) => updates = document_updates,
-        Err(e) => {
-          tracing::error!("Get document data failed: {:?}", e);
-          return Err(FlowyError::internal().context("Can't not read the document data"));
-        },
-      }
+      updates = self.cloud_service.get_document_updates(doc_id).await?;
     }
 
-    tracing::debug!("open_document: {:?}", doc_id);
     let uid = self.user.user_id()?;
     let db = self.user.collab_db(uid)?;
     let collab = self
@@ -108,17 +102,9 @@ impl DocumentManager {
     let mut updates = vec![];
     if !self.is_doc_exist(doc_id)? {
       if let Ok(document_updates) = self.cloud_service.get_document_updates(doc_id).await {
-        if document_updates.is_empty() {
-          return Err(FlowyError::new(
-            ErrorCode::UnexpectedEmptyCollabUpdates,
-            "Can't not read the document data",
-          ));
-        }
         updates = document_updates;
       } else {
-        return Err(
-          FlowyError::record_not_found().context(format!("document: {} is not exist", doc_id)),
-        );
+        return Err(FlowyError::collab_not_sync());
       }
     }
     let uid = self.user.user_id()?;

+ 2 - 2
frontend/rust-lib/flowy-error/src/code.rs

@@ -224,8 +224,8 @@ pub enum ErrorCode {
   #[error("Invalid decryption secret")]
   InvalidEncryptSecret = 74,
 
-  #[error("Unexpected empty collab updates")]
-  UnexpectedEmptyCollabUpdates = 75,
+  #[error("It appears that the collaboration object's data has not been fully synchronized")]
+  CollabDataNotSync = 75,
 }
 
 impl ErrorCode {

+ 1 - 0
frontend/rust-lib/flowy-error/src/errors.rs

@@ -88,6 +88,7 @@ impl FlowyError {
     unexpect_calendar_field_type,
     ErrorCode::UnexpectedCalendarFieldType
   );
+  static_flowy_error!(collab_not_sync, ErrorCode::CollabDataNotSync);
 }
 
 impl std::convert::From<ErrorCode> for FlowyError {

+ 2 - 2
frontend/rust-lib/flowy-folder2/src/manager.rs

@@ -158,8 +158,8 @@ impl FolderManager {
         FolderInitializeData::Raw(raw_data) => {
           if raw_data.is_empty() {
             return Err(FlowyError::new(
-              ErrorCode::UnexpectedEmptyCollabUpdates,
-              "Can't fetch the workspace data from server",
+              ErrorCode::CollabDataNotSync,
+              "Can't fetch the workspace from server",
             ));
           }
           let collab = self.collab_for_folder(uid, &workspace_id, collab_db, raw_data)?;

+ 3 - 2
frontend/rust-lib/flowy-server/src/supabase/api/database.rs

@@ -38,9 +38,10 @@ where
       tx.send(
         async move {
           let postgrest = try_get_postgrest?;
-          FetchObjectUpdateAction::new(object_id.to_string(), object_ty, postgrest)
+          let updates = FetchObjectUpdateAction::new(object_id.to_string(), object_ty, postgrest)
             .run_with_fix_interval(5, 10)
-            .await
+            .await?;
+          Ok(updates)
         }
         .await,
       )

+ 6 - 1
frontend/rust-lib/flowy-server/src/supabase/api/document.rs

@@ -6,6 +6,7 @@ use collab_plugins::cloud_storage::CollabType;
 use tokio::sync::oneshot::channel;
 
 use flowy_document_deps::cloud::{DocumentCloudService, DocumentSnapshot};
+use flowy_error::FlowyError;
 use lib_infra::future::FutureResult;
 
 use crate::supabase::api::request::{get_snapshots_from_server, FetchObjectUpdateAction};
@@ -34,7 +35,11 @@ where
         async move {
           let postgrest = try_get_postgrest?;
           let action = FetchObjectUpdateAction::new(document_id, CollabType::Document, postgrest);
-          action.run_with_fix_interval(5, 10).await
+          let updates = action.run_with_fix_interval(5, 10).await?;
+          if updates.is_empty() {
+            return Err(FlowyError::collab_not_sync().into());
+          }
+          Ok(updates)
         }
         .await,
       )

+ 31 - 7
frontend/rust-lib/flowy-server/src/supabase/api/request.rs

@@ -65,9 +65,12 @@ impl Action for FetchObjectUpdateAction {
     Box::pin(async move {
       match weak_postgres.upgrade() {
         None => Ok(vec![]),
-        Some(postgrest) => {
-          let items = get_updates_from_server(&object_id, &object_ty, postgrest).await?;
-          Ok(items.into_iter().map(|item| item.value).collect())
+        Some(postgrest) => match get_updates_from_server(&object_id, &object_ty, postgrest).await {
+          Ok(items) => Ok(items.into_iter().map(|item| item.value).collect()),
+          Err(err) => {
+            tracing::error!("Get {} updates failed with error: {:?}", object_id, err);
+            Err(err)
+          },
         },
       }
     })
@@ -112,7 +115,19 @@ impl Action for BatchFetchObjectUpdateAction {
     Box::pin(async move {
       match weak_postgrest.upgrade() {
         None => Ok(CollabObjectUpdateByOid::default()),
-        Some(server) => batch_get_updates_from_server(object_ids, &object_ty, server).await,
+        Some(server) => {
+          match batch_get_updates_from_server(object_ids.clone(), &object_ty, server).await {
+            Ok(updates_by_oid) => Ok(updates_by_oid),
+            Err(err) => {
+              tracing::error!(
+                "Batch get object with given ids:{:?} failed with error: {:?}",
+                object_ids,
+                err
+              );
+              Err(err)
+            },
+          }
+        },
       }
     })
   }
@@ -349,7 +364,13 @@ fn parser_update_from_json(
     json.get("value").and_then(|value| value.as_str()),
   ) {
     (Some(encrypt), Some(value)) => {
-      SupabaseBinaryColumnDecoder::decode(value, encrypt as i32, encryption_secret).ok()
+      match SupabaseBinaryColumnDecoder::decode(value, encrypt as i32, encryption_secret) {
+        Ok(value) => Some(value),
+        Err(err) => {
+          tracing::error!("Decode value column failed: {:?}", err);
+          None
+        },
+      }
     },
     _ => None,
   };
@@ -371,9 +392,12 @@ fn parser_update_from_json(
     }
     Ok(UpdateItem { key, value })
   } else {
+    let keys = json
+      .as_object()
+      .map(|map| map.iter().map(|(key, _)| key).collect::<Vec<&String>>());
     Err(anyhow::anyhow!(
-      "missing key or value column in json: {:?}",
-      json
+      "missing key or value column. Current keys:: {:?}",
+      keys
     ))
   }
 }

+ 15 - 8
frontend/rust-lib/flowy-server/src/supabase/server.rs

@@ -152,17 +152,24 @@ impl AppFlowyServer for SupabaseServer {
   fn handle_realtime_event(&self, json: Value) {
     match serde_json::from_value::<RealtimeCollabUpdateEvent>(json) {
       Ok(event) => {
-        if let (Some(tx), Some(secret)) = (
-          self.update_tx.read().get(event.payload.oid.as_str()),
-          self
-            .encryption
-            .upgrade()
-            .and_then(|encryption| encryption.get_secret()),
-        ) {
+        if let Some(tx) = self.update_tx.read().get(event.payload.oid.as_str()) {
+          tracing::trace!(
+            "current device: {}, event device: {}",
+            self.did.lock().as_str(),
+            event.payload.did.as_str()
+          );
+
           if self.did.lock().as_str() != event.payload.did.as_str() {
             tracing::trace!("Did receive realtime event: {}", event);
             let value = if event.payload.encrypt == 1 {
-              decrypt_bytes(event.payload.value, &secret).unwrap_or_default()
+              match self
+                .encryption
+                .upgrade()
+                .and_then(|encryption| encryption.get_secret())
+              {
+                None => vec![],
+                Some(secret) => decrypt_bytes(event.payload.value, &secret).unwrap_or_default(),
+              }
             } else {
               event.payload.value
             };

+ 23 - 1
frontend/rust-lib/flowy-user-deps/src/cloud.rs

@@ -1,4 +1,5 @@
 use std::collections::HashMap;
+use std::fmt::{Display, Formatter};
 use std::str::FromStr;
 
 use anyhow::Error;
@@ -18,7 +19,7 @@ use crate::entities::{
 #[derive(Debug, Clone, Serialize, Deserialize)]
 pub struct UserCloudConfig {
   pub enable_sync: bool,
-  pub enable_encrypt: bool,
+  enable_encrypt: bool,
   // The secret used to encrypt the user's data
   pub encrypt_secret: String,
 }
@@ -31,6 +32,27 @@ impl UserCloudConfig {
       encrypt_secret,
     }
   }
+
+  pub fn enable_encrypt(&self) -> bool {
+    self.enable_encrypt
+  }
+
+  pub fn with_enable_encrypt(mut self, enable_encrypt: bool) -> Self {
+    self.enable_encrypt = enable_encrypt;
+    // When the enable_encrypt is true, the encrypt_secret should not be empty
+    debug_assert!(!self.encrypt_secret.is_empty());
+    self
+  }
+}
+
+impl Display for UserCloudConfig {
+  fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
+    write!(
+      f,
+      "enable_sync: {}, enable_encrypt: {}",
+      self.enable_sync, self.enable_encrypt
+    )
+  }
 }
 
 /// Provide the generic interface for the user cloud service

+ 1 - 1
frontend/rust-lib/flowy-user/src/entities/user_setting.rs

@@ -149,7 +149,7 @@ impl From<UserCloudConfig> for UserCloudConfigPB {
   fn from(value: UserCloudConfig) -> Self {
     Self {
       enable_sync: value.enable_sync,
-      enable_encrypt: value.enable_encrypt,
+      enable_encrypt: value.enable_encrypt(),
       encrypt_secret: value.encrypt_secret,
     }
   }

+ 23 - 25
frontend/rust-lib/flowy-user/src/event_handler.rs

@@ -5,6 +5,7 @@ use serde_json::Value;
 
 use flowy_error::{FlowyError, FlowyResult};
 use flowy_sqlite::kv::StorePreferences;
+use flowy_user_deps::cloud::UserCloudConfig;
 use flowy_user_deps::entities::*;
 use lib_dispatch::prelude::*;
 use lib_infra::box_any::BoxAny;
@@ -12,7 +13,9 @@ use lib_infra::box_any::BoxAny;
 use crate::entities::*;
 use crate::manager::UserManager;
 use crate::notification::{send_notification, UserNotification};
-use crate::services::cloud_config::{generate_cloud_config, get_cloud_config, save_cloud_config};
+use crate::services::cloud_config::{
+  get_cloud_config, get_or_create_cloud_config, save_cloud_config,
+};
 
 fn upgrade_manager(manager: AFPluginState<Weak<UserManager>>) -> FlowyResult<Arc<UserManager>> {
   let manager = manager
@@ -186,12 +189,6 @@ pub async fn set_encrypt_secret_handler(
   let manager = upgrade_manager(manager)?;
   let store_preferences = upgrade_store_preferences(store_preferences)?;
   let data = data.into_inner();
-
-  let mut config = get_cloud_config(&store_preferences).unwrap_or_else(|| {
-    tracing::trace!("Generate default cloud config");
-    generate_cloud_config(&store_preferences)
-  });
-
   match data.encryption_type {
     EncryptionTypePB::NoEncryption => {
       tracing::error!("Encryption type is NoEncryption, but set encrypt secret");
@@ -203,8 +200,7 @@ pub async fn set_encrypt_secret_handler(
         &data.encryption_secret,
       )?;
 
-      config.encrypt_secret = data.encryption_secret;
-      config.enable_encrypt = true;
+      let config = UserCloudConfig::new(data.encryption_secret).with_enable_encrypt(true);
       manager
         .set_encrypt_secret(
           data.user_id,
@@ -212,10 +208,10 @@ pub async fn set_encrypt_secret_handler(
           EncryptionType::SelfEncryption(data.encryption_sign),
         )
         .await?;
+      save_cloud_config(data.user_id, &store_preferences, config)?;
     },
   }
 
-  save_cloud_config(data.user_id, &store_preferences, config)?;
   manager.resume_sign_up().await?;
   Ok(())
 }
@@ -250,9 +246,9 @@ pub async fn set_cloud_config_handler(
 ) -> Result<(), FlowyError> {
   let manager = upgrade_manager(manager)?;
   let session = manager.get_session()?;
-  let store_preferences = upgrade_store_preferences(store_preferences)?;
   let update = data.into_inner();
-  let mut config = get_cloud_config(&store_preferences)
+  let store_preferences = upgrade_store_preferences(store_preferences)?;
+  let mut config = get_cloud_config(session.user_id, &store_preferences)
     .ok_or(FlowyError::internal().context("Can't find any cloud config"))?;
 
   if let Some(enable_sync) = update.enable_sync {
@@ -261,20 +257,21 @@ pub async fn set_cloud_config_handler(
   }
 
   if let Some(enable_encrypt) = update.enable_encrypt {
-    config.enable_encrypt = enable_encrypt;
+    debug_assert!(enable_encrypt, "Disable encryption is not supported");
+
     if enable_encrypt {
+      tracing::info!("Enable encryption for user: {}", session.user_id);
+      config = config.with_enable_encrypt(enable_encrypt);
+      let encrypt_secret = config.encrypt_secret.clone();
+
       // The encryption secret is generated when the user first enables encryption and will be
       // used to validate the encryption secret is correct when the user logs in.
-      let encryption_sign =
-        manager.generate_encryption_sign(session.user_id, &config.encrypt_secret)?;
+      let encryption_sign = manager.generate_encryption_sign(session.user_id, &encrypt_secret)?;
       let encryption_type = EncryptionType::SelfEncryption(encryption_sign);
       manager
-        .set_encrypt_secret(
-          session.user_id,
-          config.encrypt_secret.clone(),
-          encryption_type.clone(),
-        )
+        .set_encrypt_secret(session.user_id, encrypt_secret, encryption_type.clone())
         .await?;
+      save_cloud_config(session.user_id, &store_preferences, config.clone())?;
 
       let params =
         UpdateUserProfileParams::new(session.user_id).with_encryption_type(encryption_type);
@@ -282,8 +279,7 @@ pub async fn set_cloud_config_handler(
     }
   }
 
-  let config_pb = UserCloudConfigPB::from(config.clone());
-  save_cloud_config(session.user_id, &store_preferences, config)?;
+  let config_pb = UserCloudConfigPB::from(config);
   send_notification(
     &session.user_id.to_string(),
     UserNotification::DidUpdateCloudConfig,
@@ -295,12 +291,15 @@ pub async fn set_cloud_config_handler(
 
 #[tracing::instrument(level = "debug", skip_all, err)]
 pub async fn get_cloud_config_handler(
+  manager: AFPluginState<Weak<UserManager>>,
   store_preferences: AFPluginState<Weak<StorePreferences>>,
 ) -> DataResult<UserCloudConfigPB, FlowyError> {
+  let manager = upgrade_manager(manager)?;
+  let session = manager.get_session()?;
+
   let store_preferences = upgrade_store_preferences(store_preferences)?;
   // Generate the default config if the config is not exist
-  let config = get_cloud_config(&store_preferences)
-    .unwrap_or_else(|| generate_cloud_config(&store_preferences));
+  let config = get_or_create_cloud_config(session.user_id, &store_preferences);
   data_result_ok(config.into())
 }
 
@@ -389,7 +388,6 @@ pub async fn open_historical_users_handler(
   Ok(())
 }
 
-#[tracing::instrument(level = "debug", skip_all, err)]
 pub async fn push_realtime_event_handler(
   payload: AFPluginData<RealtimePayloadPB>,
   manager: AFPluginState<Weak<UserManager>>,

+ 3 - 1
frontend/rust-lib/flowy-user/src/event_map.rs

@@ -6,7 +6,7 @@ use strum_macros::Display;
 
 use flowy_derive::{Flowy_Event, ProtoBuf_Enum};
 use flowy_error::FlowyResult;
-use flowy_user_deps::cloud::UserService;
+use flowy_user_deps::cloud::{UserCloudConfig, UserService};
 use flowy_user_deps::entities::*;
 use lib_dispatch::prelude::*;
 use lib_infra::future::{to_fut, Fut};
@@ -74,6 +74,7 @@ pub trait UserStatusCallback: Send + Sync + 'static {
   fn did_init(
     &self,
     user_id: i64,
+    cloud_config: &Option<UserCloudConfig>,
     user_workspace: &UserWorkspace,
     device_id: &str,
   ) -> Fut<FlowyResult<()>>;
@@ -145,6 +146,7 @@ impl UserStatusCallback for DefaultUserStatusCallback {
   fn did_init(
     &self,
     _user_id: i64,
+    _cloud_config: &Option<UserCloudConfig>,
     _user_workspace: &UserWorkspace,
     _device_id: &str,
   ) -> Fut<FlowyResult<()>> {

+ 8 - 4
frontend/rust-lib/flowy-user/src/manager.rs

@@ -25,7 +25,7 @@ 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::MigrationUser;
-use crate::services::cloud_config::remove_cloud_config;
+use crate::services::cloud_config::get_cloud_config;
 use crate::services::database::UserDB;
 use crate::services::entities::{ResumableSignUp, Session};
 use crate::services::user_awareness::UserAwarenessDataSource;
@@ -121,8 +121,14 @@ impl UserManager {
       self
         .initialize_user_awareness(&session, UserAwarenessDataSource::Local)
         .await;
+      let cloud_config = get_cloud_config(session.user_id, &self.store_preferences);
       if let Err(e) = user_status_callback
-        .did_init(session.user_id, &session.user_workspace, &session.device_id)
+        .did_init(
+          session.user_id,
+          &cloud_config,
+          &session.user_workspace,
+          &session.device_id,
+        )
         .await
       {
         tracing::error!("Failed to call did_init callback: {:?}", e);
@@ -213,7 +219,6 @@ impl UserManager {
     auth_type: AuthType,
     params: BoxAny,
   ) -> Result<UserProfile, FlowyError> {
-    remove_cloud_config(&self.store_preferences);
     self.update_auth_type(&auth_type).await;
 
     let migration_user = self.get_migration_user(&auth_type).await;
@@ -325,7 +330,6 @@ 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 {

+ 23 - 16
frontend/rust-lib/flowy-user/src/services/cloud_config.rs

@@ -7,41 +7,48 @@ use flowy_user_deps::cloud::UserCloudConfig;
 
 const CLOUD_CONFIG_KEY: &str = "af_user_cloud_config";
 
-pub fn generate_cloud_config(store_preference: &Arc<StorePreferences>) -> UserCloudConfig {
+fn generate_cloud_config(uid: i64, store_preference: &Arc<StorePreferences>) -> UserCloudConfig {
   let config = UserCloudConfig::new(generate_encrypt_secret());
-  let key = cache_key_for_cloud_config();
+  let key = cache_key_for_cloud_config(uid);
   store_preference.set_object(&key, config.clone()).unwrap();
   config
 }
 
-pub fn remove_cloud_config(store_preference: &Arc<StorePreferences>) {
-  let key = cache_key_for_cloud_config();
-  store_preference.remove(&key);
-}
-
 pub fn save_cloud_config(
   uid: i64,
   store_preference: &Arc<StorePreferences>,
   config: UserCloudConfig,
 ) -> FlowyResult<()> {
-  let encrypt_secret = config.encrypt_secret.clone();
-  let key = cache_key_for_cloud_config();
+  tracing::info!("save user:{} cloud config: {}", uid, config);
+  let key = cache_key_for_cloud_config(uid);
   store_preference.set_object(&key, config)?;
-  store_preference.set_object(&format!("{}-encrypt-secret", uid), encrypt_secret)?;
   Ok(())
 }
 
-fn cache_key_for_cloud_config() -> String {
-  CLOUD_CONFIG_KEY.to_string()
+fn cache_key_for_cloud_config(uid: i64) -> String {
+  format!("{}:{}", CLOUD_CONFIG_KEY, uid)
 }
 
-pub fn get_cloud_config(store_preference: &Arc<StorePreferences>) -> Option<UserCloudConfig> {
-  let key = cache_key_for_cloud_config();
+pub fn get_cloud_config(
+  uid: i64,
+  store_preference: &Arc<StorePreferences>,
+) -> Option<UserCloudConfig> {
+  let key = cache_key_for_cloud_config(uid);
   store_preference.get_object::<UserCloudConfig>(&key)
 }
 
-pub fn get_encrypt_secret(store_preference: &Arc<StorePreferences>) -> Option<String> {
-  let key = cache_key_for_cloud_config();
+pub fn get_or_create_cloud_config(
+  uid: i64,
+  store_preferences: &Arc<StorePreferences>,
+) -> UserCloudConfig {
+  let key = cache_key_for_cloud_config(uid);
+  store_preferences
+    .get_object::<UserCloudConfig>(&key)
+    .unwrap_or_else(|| generate_cloud_config(uid, store_preferences))
+}
+
+pub fn get_encrypt_secret(uid: i64, store_preference: &Arc<StorePreferences>) -> Option<String> {
+  let key = cache_key_for_cloud_config(uid);
   store_preference
     .get_object::<UserCloudConfig>(&key)
     .map(|config| config.encrypt_secret)

+ 1 - 1
frontend/rust-lib/flowy-user/src/services/user_encryption.rs

@@ -37,7 +37,7 @@ impl UserManager {
         "Failed to get store preference",
       ))?;
 
-    let encrypt_secret = get_encrypt_secret(&store_preference).ok_or(FlowyError::new(
+    let encrypt_secret = get_encrypt_secret(uid, &store_preference).ok_or(FlowyError::new(
       ErrorCode::Internal,
       "Encrypt secret is not set",
     ))?;