diff --git a/frontend/rust-lib/flowy-ai-pub/src/lib.rs b/frontend/rust-lib/flowy-ai-pub/src/lib.rs index 9a7423ec3f..df7dc957e2 100644 --- a/frontend/rust-lib/flowy-ai-pub/src/lib.rs +++ b/frontend/rust-lib/flowy-ai-pub/src/lib.rs @@ -1,2 +1,3 @@ pub mod cloud; pub mod persistence; +pub mod user_service; diff --git a/frontend/rust-lib/flowy-ai-pub/src/user_service.rs b/frontend/rust-lib/flowy-ai-pub/src/user_service.rs new file mode 100644 index 0000000000..e227c977fe --- /dev/null +++ b/frontend/rust-lib/flowy-ai-pub/src/user_service.rs @@ -0,0 +1,14 @@ +use flowy_error::{FlowyError, FlowyResult}; +use flowy_sqlite::DBConnection; +use lib_infra::async_trait::async_trait; +use std::path::PathBuf; +use uuid::Uuid; + +#[async_trait] +pub trait AIUserService: Send + Sync + 'static { + fn user_id(&self) -> Result; + async fn is_local_model(&self) -> FlowyResult; + fn workspace_id(&self) -> Result; + fn sqlite_connection(&self, uid: i64) -> Result; + fn application_root_dir(&self) -> Result; +} diff --git a/frontend/rust-lib/flowy-ai/src/ai_manager.rs b/frontend/rust-lib/flowy-ai/src/ai_manager.rs index 9055341b99..8a2ddeead5 100644 --- a/frontend/rust-lib/flowy-ai/src/ai_manager.rs +++ b/frontend/rust-lib/flowy-ai/src/ai_manager.rs @@ -14,7 +14,6 @@ use flowy_ai_pub::cloud::{ }; use flowy_error::{FlowyError, FlowyResult}; use flowy_sqlite::kv::KVStorePreferences; -use flowy_sqlite::DBConnection; use crate::notification::{chat_notification_builder, ChatNotification}; use crate::util::ai_available_models_key; @@ -22,6 +21,7 @@ use collab_integrate::persistence::collab_metadata_sql::{ batch_insert_collab_metadata, batch_select_collab_metadata, AFCollabMetadata, }; use flowy_ai_pub::cloud::ai_dto::AvailableModel; +use flowy_ai_pub::user_service::AIUserService; use flowy_storage_pub::storage::StorageService; use lib_infra::async_trait::async_trait; use lib_infra::util::timestamp; @@ -33,15 +33,6 @@ use tokio::sync::RwLock; use tracing::{error, info, instrument, trace}; use uuid::Uuid; -#[async_trait] -pub trait AIUserService: Send + Sync + 'static { - fn user_id(&self) -> Result; - async fn is_local_model(&self) -> FlowyResult; - fn workspace_id(&self) -> Result; - fn sqlite_connection(&self, uid: i64) -> Result; - fn application_root_dir(&self) -> Result; -} - /// AIExternalService is an interface for external services that AI plugin can interact with. #[async_trait] pub trait AIExternalService: Send + Sync + 'static { @@ -450,13 +441,9 @@ impl AIManager { pub async fn get_available_models(&self, source: String) -> FlowyResult { let is_local_mode = self.user_service.is_local_model().await?; if is_local_mode { - let mut selected_model = AIModel::default(); - let mut models = vec![]; - if let Some(local_model) = self.local_ai.get_plugin_chat_model() { - let model = AIModel::local(local_model, "".to_string()); - selected_model = model.clone(); - models.push(model); - } + let setting = self.local_ai.get_local_ai_setting(); + let selected_model = AIModel::local(setting.chat_model_name, "".to_string()); + let models = vec![selected_model.clone()]; Ok(AvailableModelsPB { models: models.into_iter().map(|m| m.into()).collect(), diff --git a/frontend/rust-lib/flowy-ai/src/chat.rs b/frontend/rust-lib/flowy-ai/src/chat.rs index 3180227ed0..052599ef48 100644 --- a/frontend/rust-lib/flowy-ai/src/chat.rs +++ b/frontend/rust-lib/flowy-ai/src/chat.rs @@ -1,4 +1,3 @@ -use crate::ai_manager::AIUserService; use crate::entities::{ ChatMessageErrorPB, ChatMessageListPB, ChatMessagePB, PredefinedFormatPB, RepeatedRelatedQuestionPB, StreamMessageParams, @@ -14,6 +13,7 @@ use flowy_ai_pub::persistence::{ select_answer_where_match_reply_message_id, select_chat_messages, upsert_chat_messages, ChatMessageTable, }; +use flowy_ai_pub::user_service::AIUserService; use flowy_error::{ErrorCode, FlowyError, FlowyResult}; use flowy_sqlite::DBConnection; use futures::{SinkExt, StreamExt}; diff --git a/frontend/rust-lib/flowy-ai/src/completion.rs b/frontend/rust-lib/flowy-ai/src/completion.rs index 31acde4ae7..ffdccd0680 100644 --- a/frontend/rust-lib/flowy-ai/src/completion.rs +++ b/frontend/rust-lib/flowy-ai/src/completion.rs @@ -1,4 +1,3 @@ -use crate::ai_manager::AIUserService; use crate::entities::{CompleteTextPB, CompleteTextTaskPB, CompletionTypePB}; use allo_isolate::Isolate; use std::str::FromStr; @@ -14,6 +13,7 @@ use futures::{SinkExt, StreamExt}; use lib_infra::isolate_stream::IsolateSink; use crate::stream_message::StreamMessage; +use flowy_ai_pub::user_service::AIUserService; use std::sync::{Arc, Weak}; use tokio::select; use tracing::{error, info}; diff --git a/frontend/rust-lib/flowy-ai/src/local_ai/controller.rs b/frontend/rust-lib/flowy-ai/src/local_ai/controller.rs index b9dc7a73c1..43dd7ce9b2 100644 --- a/frontend/rust-lib/flowy-ai/src/local_ai/controller.rs +++ b/frontend/rust-lib/flowy-ai/src/local_ai/controller.rs @@ -1,4 +1,3 @@ -use crate::ai_manager::AIUserService; use crate::entities::{LocalAIPB, RunningStatePB}; use crate::local_ai::resource::{LLMResourceService, LocalAIResourceController}; use crate::notification::{ @@ -17,6 +16,7 @@ use af_local_ai::ollama_plugin::OllamaAIPlugin; use af_plugin::core::path::is_plugin_ready; use af_plugin::core::plugin::RunningState; use arc_swap::ArcSwapOption; +use flowy_ai_pub::user_service::AIUserService; use futures_util::SinkExt; use lib_infra::util::get_operating_system; use serde::{Deserialize, Serialize}; diff --git a/frontend/rust-lib/flowy-ai/src/local_ai/resource.rs b/frontend/rust-lib/flowy-ai/src/local_ai/resource.rs index 6251ef8de5..36a56e171d 100644 --- a/frontend/rust-lib/flowy-ai/src/local_ai/resource.rs +++ b/frontend/rust-lib/flowy-ai/src/local_ai/resource.rs @@ -1,4 +1,3 @@ -use crate::ai_manager::AIUserService; use crate::local_ai::controller::LocalAISetting; use flowy_error::{ErrorCode, FlowyError, FlowyResult}; use lib_infra::async_trait::async_trait; @@ -11,6 +10,7 @@ use crate::notification::{ }; use af_local_ai::ollama_plugin::OllamaPluginConfig; use af_plugin::core::path::{is_plugin_ready, ollama_plugin_path}; +use flowy_ai_pub::user_service::AIUserService; use lib_infra::util::{get_operating_system, OperatingSystem}; use reqwest::Client; use serde::Deserialize; diff --git a/frontend/rust-lib/flowy-ai/src/middleware/chat_service_mw.rs b/frontend/rust-lib/flowy-ai/src/middleware/chat_service_mw.rs index 22a2bec674..74f5d5560b 100644 --- a/frontend/rust-lib/flowy-ai/src/middleware/chat_service_mw.rs +++ b/frontend/rust-lib/flowy-ai/src/middleware/chat_service_mw.rs @@ -1,4 +1,3 @@ -use crate::ai_manager::AIUserService; use crate::entities::{ChatStatePB, ModelTypePB}; use crate::local_ai::controller::LocalAIController; use crate::notification::{ @@ -19,6 +18,7 @@ use futures::{stream, StreamExt, TryStreamExt}; use lib_infra::async_trait::async_trait; use crate::local_ai::stream_util::QuestionStream; +use flowy_ai_pub::user_service::AIUserService; use flowy_storage_pub::storage::StorageService; use serde_json::{json, Value}; use std::path::Path; diff --git a/frontend/rust-lib/flowy-ai/src/offline/offline_message_sync.rs b/frontend/rust-lib/flowy-ai/src/offline/offline_message_sync.rs index 8d7e8d2e42..55daf6b77f 100644 --- a/frontend/rust-lib/flowy-ai/src/offline/offline_message_sync.rs +++ b/frontend/rust-lib/flowy-ai/src/offline/offline_message_sync.rs @@ -1,4 +1,3 @@ -use crate::ai_manager::AIUserService; use flowy_ai_pub::cloud::{ AIModel, ChatCloudService, ChatMessage, ChatMessageType, ChatSettings, CompleteTextParams, MessageCursor, ModelList, RepeatedChatMessage, RepeatedRelatedQuestion, ResponseFormat, @@ -8,6 +7,7 @@ use flowy_ai_pub::persistence::{ update_chat_is_sync, update_chat_message_is_sync, upsert_chat, upsert_chat_messages, ChatMessageTable, ChatTable, }; +use flowy_ai_pub::user_service::AIUserService; use flowy_error::FlowyError; use lib_infra::async_trait::async_trait; use serde_json::Value; diff --git a/frontend/rust-lib/flowy-core/src/deps_resolve/chat_deps.rs b/frontend/rust-lib/flowy-core/src/deps_resolve/chat_deps.rs index c8c93a7f4c..a7d2bc15c1 100644 --- a/frontend/rust-lib/flowy-core/src/deps_resolve/chat_deps.rs +++ b/frontend/rust-lib/flowy-core/src/deps_resolve/chat_deps.rs @@ -5,9 +5,10 @@ use collab::preclude::{Collab, StateVector}; use collab::util::is_change_since_sv; use collab_entity::CollabType; use collab_integrate::persistence::collab_metadata_sql::AFCollabMetadata; -use flowy_ai::ai_manager::{AIExternalService, AIManager, AIUserService}; +use flowy_ai::ai_manager::{AIExternalService, AIManager}; use flowy_ai::local_ai::controller::LocalAIController; use flowy_ai_pub::cloud::ChatCloudService; +use flowy_ai_pub::user_service::AIUserService; use flowy_error::{FlowyError, FlowyResult}; use flowy_folder::ViewLayout; use flowy_folder_pub::cloud::{FolderCloudService, FullSyncCollabParams}; @@ -153,7 +154,7 @@ impl AIExternalService for ChatQueryServiceImpl { } } -struct ChatUserServiceImpl(Weak); +pub struct ChatUserServiceImpl(Weak); impl ChatUserServiceImpl { fn upgrade_user(&self) -> Result, FlowyError> { let user = self diff --git a/frontend/rust-lib/flowy-core/src/server_layer.rs b/frontend/rust-lib/flowy-core/src/server_layer.rs index b666ab4749..6e5d35d726 100644 --- a/frontend/rust-lib/flowy-core/src/server_layer.rs +++ b/frontend/rust-lib/flowy-core/src/server_layer.rs @@ -5,10 +5,8 @@ use dashmap::mapref::one::Ref; use dashmap::DashMap; use flowy_ai::local_ai::controller::LocalAIController; use flowy_error::{FlowyError, FlowyResult}; -use flowy_server::af_cloud::{ - define::{AIUserServiceImpl, LoggedUser}, - AppFlowyCloudServer, -}; +use flowy_server::af_cloud::define::AIUserServiceImpl; +use flowy_server::af_cloud::{define::LoggedUser, AppFlowyCloudServer}; use flowy_server::local_server::LocalServer; use flowy_server::{AppFlowyEncryption, AppFlowyServer, EncryptionImpl}; use flowy_server_pub::AuthenticatorType; @@ -117,12 +115,14 @@ impl ServerProvider { .cloud_config .clone() .ok_or_else(|| FlowyError::internal().with_context("Missing cloud config"))?; + let ai_user_service = Arc::new(AIUserServiceImpl(Arc::downgrade(&self.logged_user))); Arc::new(AppFlowyCloudServer::new( cfg, self.user_enable_sync.load(Ordering::Acquire), self.config.device_id.clone(), self.config.app_version.clone(), Arc::downgrade(&self.logged_user), + ai_user_service, )) }, }; diff --git a/frontend/rust-lib/flowy-server/src/af_cloud/define.rs b/frontend/rust-lib/flowy-server/src/af_cloud/define.rs index 65808e5b6b..31114629ac 100644 --- a/frontend/rust-lib/flowy-server/src/af_cloud/define.rs +++ b/frontend/rust-lib/flowy-server/src/af_cloud/define.rs @@ -1,5 +1,5 @@ use collab_plugins::CollabKVDB; -use flowy_ai::ai_manager::AIUserService; +use flowy_ai_pub::user_service::AIUserService; use flowy_error::{FlowyError, FlowyResult}; use flowy_sqlite::DBConnection; use lib_infra::async_trait::async_trait; @@ -28,6 +28,7 @@ pub trait LoggedUser: Send + Sync { fn application_root_dir(&self) -> Result; } +// pub struct AIUserServiceImpl(pub Weak); impl AIUserServiceImpl { diff --git a/frontend/rust-lib/flowy-server/src/af_cloud/server.rs b/frontend/rust-lib/flowy-server/src/af_cloud/server.rs index 66abb32031..500c78c930 100644 --- a/frontend/rust-lib/flowy-server/src/af_cloud/server.rs +++ b/frontend/rust-lib/flowy-server/src/af_cloud/server.rs @@ -2,7 +2,7 @@ use std::sync::atomic::{AtomicBool, Ordering}; use std::sync::{Arc, Weak}; use std::time::Duration; -use crate::af_cloud::define::{AIUserServiceImpl, LoggedUser}; +use crate::af_cloud::define::LoggedUser; use anyhow::Error; use arc_swap::ArcSwap; use client_api::collab_sync::ServerCollabMessage; @@ -28,7 +28,9 @@ use crate::af_cloud::impls::{ AFCloudDatabaseCloudServiceImpl, AFCloudDocumentCloudServiceImpl, AFCloudFileStorageServiceImpl, AFCloudFolderCloudServiceImpl, AFCloudUserAuthServiceImpl, CloudChatServiceImpl, }; +use crate::AppFlowyServer; use flowy_ai::offline::offline_message_sync::AutoSyncChatService; +use flowy_ai_pub::user_service::AIUserService; use rand::Rng; use semver::Version; use tokio::select; @@ -39,8 +41,6 @@ use tokio_util::sync::CancellationToken; use tracing::{error, info, warn}; use uuid::Uuid; -use crate::AppFlowyServer; - use super::impls::AFCloudSearchCloudServiceImpl; pub(crate) type AFCloudClient = Client; @@ -54,6 +54,7 @@ pub struct AppFlowyCloudServer { pub device_id: String, ws_client: Arc, logged_user: Weak, + ai_user_service: Arc, } impl AppFlowyCloudServer { @@ -63,6 +64,7 @@ impl AppFlowyCloudServer { mut device_id: String, client_version: Version, logged_user: Weak, + ai_user_service: Arc, ) -> Self { // The device id can't be empty, so we generate a new one if it is. if device_id.is_empty() { @@ -101,6 +103,7 @@ impl AppFlowyCloudServer { device_id, ws_client, logged_user, + ai_user_service, } } @@ -222,7 +225,7 @@ impl AppFlowyServer for AppFlowyCloudServer { Arc::new(CloudChatServiceImpl { inner: self.get_server_impl(), }), - Arc::new(AIUserServiceImpl(self.logged_user.clone())), + self.ai_user_service.clone(), )) } diff --git a/frontend/rust-lib/flowy-server/tests/af_cloud_test/mod.rs b/frontend/rust-lib/flowy-server/tests/af_cloud_test/mod.rs deleted file mode 100644 index 94ad2e2e1d..0000000000 --- a/frontend/rust-lib/flowy-server/tests/af_cloud_test/mod.rs +++ /dev/null @@ -1,2 +0,0 @@ -mod user_test; -mod util; diff --git a/frontend/rust-lib/flowy-server/tests/af_cloud_test/user_test.rs b/frontend/rust-lib/flowy-server/tests/af_cloud_test/user_test.rs deleted file mode 100644 index a14d8eaf25..0000000000 --- a/frontend/rust-lib/flowy-server/tests/af_cloud_test/user_test.rs +++ /dev/null @@ -1,21 +0,0 @@ -use flowy_server::AppFlowyServer; -use flowy_user_pub::entities::AuthResponse; -use lib_infra::box_any::BoxAny; - -use crate::af_cloud_test::util::{ - af_cloud_server, af_cloud_sign_up_param, generate_test_email, get_af_cloud_config, -}; - -#[tokio::test] -async fn sign_up_test() { - if let Some(config) = get_af_cloud_config() { - let server = af_cloud_server(config.clone()); - let user_service = server.user_service(); - let email = generate_test_email(); - let params = af_cloud_sign_up_param(&email, &config).await; - let resp: AuthResponse = user_service.sign_up(BoxAny::new(params)).await.unwrap(); - assert_eq!(resp.email.unwrap(), email); - assert!(resp.is_new_user); - assert_eq!(resp.user_workspaces.len(), 1); - } -} diff --git a/frontend/rust-lib/flowy-server/tests/af_cloud_test/util.rs b/frontend/rust-lib/flowy-server/tests/af_cloud_test/util.rs deleted file mode 100644 index 7e38f423cc..0000000000 --- a/frontend/rust-lib/flowy-server/tests/af_cloud_test/util.rs +++ /dev/null @@ -1,119 +0,0 @@ -use client_api::ClientConfiguration; -use collab_plugins::CollabKVDB; -use flowy_error::{FlowyError, FlowyResult}; -use semver::Version; -use std::collections::HashMap; -use std::path::PathBuf; -use std::sync::{Arc, Weak}; -use uuid::Uuid; - -use crate::setup_log; -use flowy_server::af_cloud::define::LoggedUser; -use flowy_server::af_cloud::AppFlowyCloudServer; -use flowy_server_pub::af_cloud_config::AFCloudConfiguration; -use flowy_sqlite::DBConnection; -use lib_infra::async_trait::async_trait; - -/// To run the test, create a .env.ci file in the 'flowy-server' directory and set the following environment variables: -/// -/// - `APPFLOWY_CLOUD_BASE_URL=http://localhost:8000` -/// - `APPFLOWY_CLOUD_WS_BASE_URL=ws://localhost:8000/ws` -/// - `APPFLOWY_CLOUD_GOTRUE_URL=http://localhost:9998` -/// -/// - `GOTRUE_ADMIN_EMAIL=admin@example.com` -/// - `GOTRUE_ADMIN_PASSWORD=password` -pub fn get_af_cloud_config() -> Option { - dotenv::from_filename("./.env.ci").ok()?; - setup_log(); - AFCloudConfiguration::from_env().ok() -} - -pub fn af_cloud_server(config: AFCloudConfiguration) -> Arc { - let fake_device_id = uuid::Uuid::new_v4().to_string(); - let logged_user = Arc::new(FakeServerUserImpl) as Arc; - Arc::new(AppFlowyCloudServer::new( - config, - true, - fake_device_id, - Version::new(0, 5, 8), - // do nothing, just for test - Arc::downgrade(&logged_user), - )) -} - -struct FakeServerUserImpl; - -#[async_trait] -impl LoggedUser for FakeServerUserImpl { - fn workspace_id(&self) -> FlowyResult { - todo!() - } - - fn user_id(&self) -> FlowyResult { - todo!() - } - - async fn is_local_mode(&self) -> FlowyResult { - Ok(true) - } - - fn get_sqlite_db(&self, _uid: i64) -> Result { - todo!() - } - - fn get_collab_db(&self, _uid: i64) -> Result, FlowyError> { - todo!() - } - - fn application_root_dir(&self) -> Result { - todo!() - } -} - -pub async fn generate_sign_in_url(user_email: &str, config: &AFCloudConfiguration) -> String { - let client = client_api::Client::new( - &config.base_url, - &config.ws_base_url, - &config.gotrue_url, - "fake_device_id", - ClientConfiguration::default(), - "test", - ); - let admin_email = std::env::var("GOTRUE_ADMIN_EMAIL").unwrap(); - let admin_password = std::env::var("GOTRUE_ADMIN_PASSWORD").unwrap(); - let admin_client = client_api::Client::new( - client.base_url(), - client.ws_addr(), - client.gotrue_url(), - "fake_device_id", - ClientConfiguration::default(), - &client.client_version.to_string(), - ); - admin_client - .sign_in_password(&admin_email, &admin_password) - .await - .unwrap(); - - let action_link = admin_client - .generate_sign_in_action_link(user_email) - .await - .unwrap(); - client.extract_sign_in_url(&action_link).await.unwrap() -} - -pub async fn af_cloud_sign_up_param( - email: &str, - config: &AFCloudConfiguration, -) -> HashMap { - let mut params = HashMap::new(); - params.insert( - "sign_in_url".to_string(), - generate_sign_in_url(email, config).await, - ); - params.insert("device_id".to_string(), Uuid::new_v4().to_string()); - params -} - -pub fn generate_test_email() -> String { - format!("{}@test.com", Uuid::new_v4()) -} diff --git a/frontend/rust-lib/flowy-server/tests/logo.png b/frontend/rust-lib/flowy-server/tests/logo.png deleted file mode 100644 index d6f09e3e2e..0000000000 Binary files a/frontend/rust-lib/flowy-server/tests/logo.png and /dev/null differ diff --git a/frontend/rust-lib/flowy-server/tests/main.rs b/frontend/rust-lib/flowy-server/tests/main.rs deleted file mode 100644 index fb12ed51b3..0000000000 --- a/frontend/rust-lib/flowy-server/tests/main.rs +++ /dev/null @@ -1,24 +0,0 @@ -use std::sync::Once; - -use tracing_subscriber::fmt::Subscriber; -use tracing_subscriber::util::SubscriberInitExt; -use tracing_subscriber::EnvFilter; - -mod af_cloud_test; -// mod supabase_test; - -pub fn setup_log() { - static START: Once = Once::new(); - START.call_once(|| { - let level = "trace"; - let mut filters = vec![]; - filters.push(format!("flowy_server={}", level)); - std::env::set_var("RUST_LOG", filters.join(",")); - - let subscriber = Subscriber::builder() - .with_env_filter(EnvFilter::from_default_env()) - .with_ansi(true) - .finish(); - subscriber.try_init().unwrap(); - }); -} diff --git a/frontend/rust-lib/flowy-server/tests/supabase_test/database_test.rs b/frontend/rust-lib/flowy-server/tests/supabase_test/database_test.rs deleted file mode 100644 index 841c76b443..0000000000 --- a/frontend/rust-lib/flowy-server/tests/supabase_test/database_test.rs +++ /dev/null @@ -1,63 +0,0 @@ -use collab::core::collab::DataSource; -use collab_entity::{CollabObject, CollabType}; -use uuid::Uuid; - -use flowy_user_pub::entities::AuthResponse; -use lib_infra::box_any::BoxAny; - -use crate::supabase_test::util::{ - collab_service, database_service, get_supabase_ci_config, third_party_sign_up_param, - user_auth_service, -}; - -#[tokio::test] -async fn supabase_create_database_test() { - if get_supabase_ci_config().is_none() { - return; - } - - let user_service = user_auth_service(); - let uuid = Uuid::new_v4().to_string(); - let params = third_party_sign_up_param(uuid); - let user: AuthResponse = user_service.sign_up(BoxAny::new(params)).await.unwrap(); - - let collab_service = collab_service(); - let database_service = database_service(); - - let mut row_ids = vec![]; - for _i in 0..3 { - let row_id = uuid::Uuid::new_v4().to_string(); - row_ids.push(row_id.clone()); - let collab_object = CollabObject::new( - user.user_id, - row_id, - CollabType::DatabaseRow, - user.latest_workspace.id.clone(), - "fake_device_id".to_string(), - ); - collab_service - .send_update(&collab_object, 0, vec![1, 2, 3]) - .await - .unwrap(); - collab_service - .send_update(&collab_object, 0, vec![4, 5, 6]) - .await - .unwrap(); - } - - let updates_by_oid = database_service - .batch_get_database_object_doc_state(row_ids, CollabType::DatabaseRow, "fake_workspace_id") - .await - .unwrap(); - - assert_eq!(updates_by_oid.len(), 3); - for (_, source) in updates_by_oid { - match source { - DataSource::Disk => panic!("should not be from disk"), - DataSource::DocStateV1(doc_state) => { - assert_eq!(doc_state.len(), 2); - }, - DataSource::DocStateV2(_) => {}, - } - } -} diff --git a/frontend/rust-lib/flowy-server/tests/supabase_test/file_test.rs b/frontend/rust-lib/flowy-server/tests/supabase_test/file_test.rs deleted file mode 100644 index 4377ce8e68..0000000000 --- a/frontend/rust-lib/flowy-server/tests/supabase_test/file_test.rs +++ /dev/null @@ -1,78 +0,0 @@ -// use url::Url; -// use uuid::Uuid; -// -// use flowy_storage::StorageObject; -// -// use crate::supabase_test::util::{file_storage_service, get_supabase_ci_config}; -// -// #[tokio::test] -// async fn supabase_get_object_test() { -// if get_supabase_ci_config().is_none() { -// return; -// } -// -// let service = file_storage_service(); -// let file_name = format!("test-{}.txt", Uuid::new_v4()); -// let object = StorageObject::from_file("1", &file_name, "tests/test.txt"); -// -// // Upload a file -// let url = service -// .create_object(object) -// .await -// .unwrap() -// .parse::() -// .unwrap(); -// -// // The url would be something like: -// // https://acfrqdbdtbsceyjbxsfc.supabase.co/storage/v1/object/data/test-1693472809.txt -// let name = url.path_segments().unwrap().last().unwrap(); -// assert_eq!(name, &file_name); -// -// // Download the file -// let bytes = service.get_object(url.to_string()).await.unwrap(); -// let s = String::from_utf8(bytes.to_vec()).unwrap(); -// assert_eq!(s, "hello world"); -// } -// -// #[tokio::test] -// async fn supabase_upload_image_test() { -// if get_supabase_ci_config().is_none() { -// return; -// } -// -// let service = file_storage_service(); -// let file_name = format!("image-{}.png", Uuid::new_v4()); -// let object = StorageObject::from_file("1", &file_name, "tests/logo.png"); -// -// // Upload a file -// let url = service -// .create_object(object) -// .await -// .unwrap() -// .parse::() -// .unwrap(); -// -// // Download object by url -// let bytes = service.get_object(url.to_string()).await.unwrap(); -// assert_eq!(bytes.len(), 15694); -// } -// -// #[tokio::test] -// async fn supabase_delete_object_test() { -// if get_supabase_ci_config().is_none() { -// return; -// } -// -// let service = file_storage_service(); -// let file_name = format!("test-{}.txt", Uuid::new_v4()); -// let object = StorageObject::from_file("1", &file_name, "tests/test.txt"); -// let url = service.create_object(object).await.unwrap(); -// -// let result = service.get_object(url.clone()).await; -// assert!(result.is_ok()); -// -// let _ = service.delete_object(url.clone()).await; -// -// let result = service.get_object(url.clone()).await; -// assert!(result.is_err()); -// } diff --git a/frontend/rust-lib/flowy-server/tests/supabase_test/folder_test.rs b/frontend/rust-lib/flowy-server/tests/supabase_test/folder_test.rs deleted file mode 100644 index a9037caa6c..0000000000 --- a/frontend/rust-lib/flowy-server/tests/supabase_test/folder_test.rs +++ /dev/null @@ -1,316 +0,0 @@ -use assert_json_diff::assert_json_eq; -use collab_entity::{CollabObject, CollabType}; -use serde_json::json; -use uuid::Uuid; -use yrs::types::ToJson; -use yrs::updates::decoder::Decode; -use yrs::{merge_updates_v1, Array, Doc, Map, MapPrelim, ReadTxn, StateVector, Transact, Update}; - -use flowy_user_pub::entities::AuthResponse; -use lib_infra::box_any::BoxAny; - -use crate::supabase_test::util::{ - collab_service, folder_service, get_supabase_ci_config, third_party_sign_up_param, - user_auth_service, -}; - -#[tokio::test] -async fn supabase_create_workspace_test() { - if get_supabase_ci_config().is_none() { - return; - } - - let service = folder_service(); - // will replace the uid with the real uid - let workspace = service.create_workspace(1, "test").await.unwrap(); - dbg!(workspace); -} - -#[tokio::test] -async fn supabase_get_folder_test() { - if get_supabase_ci_config().is_none() { - return; - } - - let folder_service = folder_service(); - let user_service = user_auth_service(); - let collab_service = collab_service(); - let uuid = Uuid::new_v4().to_string(); - let params = third_party_sign_up_param(uuid); - let user: AuthResponse = user_service.sign_up(BoxAny::new(params)).await.unwrap(); - - let collab_object = CollabObject::new( - user.user_id, - user.latest_workspace.id.clone(), - CollabType::Folder, - user.latest_workspace.id.clone(), - "fake_device_id".to_string(), - ); - - let doc = Doc::with_client_id(1); - let map = { doc.get_or_insert_map("map") }; - { - let mut txn = doc.transact_mut(); - map.insert(&mut txn, "1", "a"); - collab_service - .send_update(&collab_object, 0, txn.encode_update_v1()) - .await - .unwrap(); - }; - - { - let mut txn = doc.transact_mut(); - map.insert(&mut txn, "2", "b"); - collab_service - .send_update(&collab_object, 1, txn.encode_update_v1()) - .await - .unwrap(); - }; - - // let updates = collab_service.get_all_updates(&collab_object).await.unwrap(); - let updates = folder_service - .get_folder_doc_state( - &user.latest_workspace.id, - user.user_id, - CollabType::Folder, - &user.latest_workspace.id, - ) - .await - .unwrap(); - assert_eq!(updates.len(), 2); - - for _ in 0..5 { - collab_service - .send_init_sync(&collab_object, 3, vec![]) - .await - .unwrap(); - } - let updates = folder_service - .get_folder_doc_state( - &user.latest_workspace.id, - user.user_id, - CollabType::Folder, - &user.latest_workspace.id, - ) - .await - .unwrap(); - - // Other the init sync, try to get the updates from the server. - let expected_update = doc - .transact_mut() - .encode_state_as_update_v1(&StateVector::default()); - - // check the update is the same as local document update. - assert_eq!(updates, expected_update); -} - -/// This async test function checks the behavior of updates duplication in Supabase. -/// It creates a new user and simulates two updates to the user's workspace with different values. -/// Then, it merges these updates and sends an initial synchronization request to test duplication handling. -/// Finally, it asserts that the duplicated updates don't affect the overall data consistency in Supabase. -#[tokio::test] -async fn supabase_duplicate_updates_test() { - if get_supabase_ci_config().is_none() { - return; - } - - let folder_service = folder_service(); - let user_service = user_auth_service(); - let collab_service = collab_service(); - let uuid = Uuid::new_v4().to_string(); - let params = third_party_sign_up_param(uuid); - let user: AuthResponse = user_service.sign_up(BoxAny::new(params)).await.unwrap(); - - let collab_object = CollabObject::new( - user.user_id, - user.latest_workspace.id.clone(), - CollabType::Folder, - user.latest_workspace.id.clone(), - "fake_device_id".to_string(), - ); - let doc = Doc::with_client_id(1); - let map = { doc.get_or_insert_map("map") }; - let mut duplicated_updates = vec![]; - { - let mut txn = doc.transact_mut(); - map.insert(&mut txn, "1", "a"); - let update = txn.encode_update_v1(); - duplicated_updates.push(update.clone()); - collab_service - .send_update(&collab_object, 0, update) - .await - .unwrap(); - }; - { - let mut txn = doc.transact_mut(); - map.insert(&mut txn, "2", "b"); - let update = txn.encode_update_v1(); - duplicated_updates.push(update.clone()); - collab_service - .send_update(&collab_object, 1, update) - .await - .unwrap(); - }; - // send init sync - collab_service - .send_init_sync(&collab_object, 3, vec![]) - .await - .unwrap(); - let first_init_sync_update = folder_service - .get_folder_doc_state( - &user.latest_workspace.id, - user.user_id, - CollabType::Folder, - &user.latest_workspace.id, - ) - .await - .unwrap(); - - // simulate the duplicated updates. - let merged_update = merge_updates_v1( - &duplicated_updates - .iter() - .map(|update| update.as_ref()) - .collect::>(), - ) - .unwrap(); - collab_service - .send_init_sync(&collab_object, 4, merged_update) - .await - .unwrap(); - let second_init_sync_update = folder_service - .get_folder_doc_state( - &user.latest_workspace.id, - user.user_id, - CollabType::Folder, - &user.latest_workspace.id, - ) - .await - .unwrap(); - - let doc_2 = Doc::new(); - assert_eq!(first_init_sync_update.len(), second_init_sync_update.len()); - let map = { doc_2.get_or_insert_map("map") }; - { - let mut txn = doc_2.transact_mut(); - let update = Update::decode_v1(&second_init_sync_update).unwrap(); - txn.apply_update(update).unwrap(); - } - { - let txn = doc_2.transact(); - let json = map.to_json(&txn); - assert_json_eq!( - json, - json!({ - "1": "a", - "2": "b" - }) - ); - } -} - -/// The state vector of doc; -/// ```json -/// "map": {}, -/// "array": [] -/// ``` -/// The old version of doc: -/// ```json -/// "map": {} -/// ``` -/// -/// Try to apply the updates from doc to old version doc and check the result. -#[tokio::test] -async fn supabase_diff_state_vector_test() { - if get_supabase_ci_config().is_none() { - return; - } - - let folder_service = folder_service(); - let user_service = user_auth_service(); - let collab_service = collab_service(); - let uuid = Uuid::new_v4().to_string(); - let params = third_party_sign_up_param(uuid); - let user: AuthResponse = user_service.sign_up(BoxAny::new(params)).await.unwrap(); - - let collab_object = CollabObject::new( - user.user_id, - user.latest_workspace.id.clone(), - CollabType::Folder, - user.latest_workspace.id.clone(), - "fake_device_id".to_string(), - ); - let doc = Doc::with_client_id(1); - let map = { doc.get_or_insert_map("map") }; - let array = { doc.get_or_insert_array("array") }; - - { - let mut txn = doc.transact_mut(); - map.insert(&mut txn, "1", "a"); - map.insert(&mut txn, "inner_map", MapPrelim::::new()); - - array.push_back(&mut txn, "element 1"); - let update = txn.encode_update_v1(); - collab_service - .send_update(&collab_object, 0, update) - .await - .unwrap(); - }; - { - let mut txn = doc.transact_mut(); - map.insert(&mut txn, "2", "b"); - array.push_back(&mut txn, "element 2"); - let update = txn.encode_update_v1(); - collab_service - .send_update(&collab_object, 1, update) - .await - .unwrap(); - }; - - // restore the doc with given updates. - let old_version_doc = Doc::new(); - let map = { old_version_doc.get_or_insert_map("map") }; - let doc_state = folder_service - .get_folder_doc_state( - &user.latest_workspace.id, - user.user_id, - CollabType::Folder, - &user.latest_workspace.id, - ) - .await - .unwrap(); - { - let mut txn = old_version_doc.transact_mut(); - let update = Update::decode_v1(&doc_state).unwrap(); - txn.apply_update(update).unwrap(); - } - let txn = old_version_doc.transact(); - let json = map.to_json(&txn); - assert_json_eq!( - json, - json!({ - "1": "a", - "2": "b", - "inner_map": {} - }) - ); -} - -// #[tokio::test] -// async fn print_folder_object_test() { -// if get_supabase_dev_config().is_none() { -// return; -// } -// let secret = Some("43bSxEPHeNkk5ZxxEYOfAjjd7sK2DJ$vVnxwuNc5ru0iKFvhs8wLg==".to_string()); -// print_encryption_folder("f8b14b84-e8ec-4cf4-a318-c1e008ecfdfa", secret).await; -// } -// -// #[tokio::test] -// async fn print_folder_snapshot_object_test() { -// if get_supabase_dev_config().is_none() { -// return; -// } -// let secret = Some("NTXRXrDSybqFEm32jwMBDzbxvCtgjU$8np3TGywbBdJAzHtu1QIyQ==".to_string()); -// // let secret = None; -// print_encryption_folder_snapshot("12533251-bdd4-41f4-995f-ff12fceeaa42", secret).await; -// } diff --git a/frontend/rust-lib/flowy-server/tests/supabase_test/mod.rs b/frontend/rust-lib/flowy-server/tests/supabase_test/mod.rs deleted file mode 100644 index ab82d37866..0000000000 --- a/frontend/rust-lib/flowy-server/tests/supabase_test/mod.rs +++ /dev/null @@ -1,5 +0,0 @@ -mod database_test; -mod file_test; -mod folder_test; -mod user_test; -mod util; diff --git a/frontend/rust-lib/flowy-server/tests/supabase_test/user_test.rs b/frontend/rust-lib/flowy-server/tests/supabase_test/user_test.rs deleted file mode 100644 index 13df930601..0000000000 --- a/frontend/rust-lib/flowy-server/tests/supabase_test/user_test.rs +++ /dev/null @@ -1,141 +0,0 @@ -use uuid::Uuid; - -use flowy_encrypt::{encrypt_text, generate_encryption_secret}; -use flowy_error::FlowyError; -use flowy_user_pub::entities::*; -use lib_infra::box_any::BoxAny; - -use crate::supabase_test::util::{ - get_supabase_ci_config, third_party_sign_up_param, user_auth_service, -}; - -// ‼️‼️‼️ Warning: this test will create a table in the database -#[tokio::test] -async fn supabase_user_sign_up_test() { - if get_supabase_ci_config().is_none() { - return; - } - let user_service = user_auth_service(); - let uuid = Uuid::new_v4().to_string(); - let params = third_party_sign_up_param(uuid); - let user: AuthResponse = user_service.sign_up(BoxAny::new(params)).await.unwrap(); - assert!(!user.latest_workspace.id.is_empty()); - assert!(!user.user_workspaces.is_empty()); - assert!(!user.latest_workspace.database_indexer_id.is_empty()); -} - -#[tokio::test] -async fn supabase_user_sign_up_with_existing_uuid_test() { - if get_supabase_ci_config().is_none() { - return; - } - let user_service = user_auth_service(); - let uuid = Uuid::new_v4().to_string(); - let params = third_party_sign_up_param(uuid); - let _user: AuthResponse = user_service - .sign_up(BoxAny::new(params.clone())) - .await - .unwrap(); - let user: AuthResponse = user_service.sign_up(BoxAny::new(params)).await.unwrap(); - assert!(!user.latest_workspace.id.is_empty()); - assert!(!user.latest_workspace.database_indexer_id.is_empty()); - assert!(!user.user_workspaces.is_empty()); -} - -#[tokio::test] -async fn supabase_update_user_profile_test() { - if get_supabase_ci_config().is_none() { - return; - } - let user_service = user_auth_service(); - let uuid = Uuid::new_v4().to_string(); - let params = third_party_sign_up_param(uuid); - let user: AuthResponse = user_service - .sign_up(BoxAny::new(params.clone())) - .await - .unwrap(); - - let params = UpdateUserProfileParams::new(user.user_id) - .with_name("123") - .with_email(format!("{}@test.com", Uuid::new_v4())); - - user_service - .update_user(UserCredentials::from_uid(user.user_id), params) - .await - .unwrap(); - - let user_profile = user_service - .get_user_profile(UserCredentials::from_uid(user.user_id)) - .await - .unwrap(); - - assert_eq!(user_profile.name, "123"); -} - -#[tokio::test] -async fn supabase_get_user_profile_test() { - if get_supabase_ci_config().is_none() { - return; - } - let user_service = user_auth_service(); - let uuid = Uuid::new_v4().to_string(); - let params = third_party_sign_up_param(uuid); - let user: AuthResponse = user_service - .sign_up(BoxAny::new(params.clone())) - .await - .unwrap(); - - let credential = UserCredentials::from_uid(user.user_id); - user_service - .get_user_profile(credential.clone()) - .await - .unwrap(); -} - -#[tokio::test] -async fn supabase_get_not_exist_user_profile_test() { - if get_supabase_ci_config().is_none() { - return; - } - - let user_service = user_auth_service(); - let result: FlowyError = user_service - .get_user_profile(UserCredentials::from_uid(i64::MAX)) - .await - .unwrap_err(); - // user not found - assert!(result.is_record_not_found()); -} - -#[tokio::test] -async fn user_encryption_sign_test() { - if get_supabase_ci_config().is_none() { - return; - } - let user_service = user_auth_service(); - let uuid = Uuid::new_v4().to_string(); - let params = third_party_sign_up_param(uuid); - let user: AuthResponse = user_service.sign_up(BoxAny::new(params)).await.unwrap(); - - // generate encryption sign - let secret = generate_encryption_secret(); - let sign = encrypt_text(user.user_id.to_string(), &secret).unwrap(); - - user_service - .update_user( - UserCredentials::from_uid(user.user_id), - UpdateUserProfileParams::new(user.user_id) - .with_encryption_type(EncryptionType::SelfEncryption(sign.clone())), - ) - .await - .unwrap(); - - let user_profile: UserProfile = user_service - .get_user_profile(UserCredentials::from_uid(user.user_id)) - .await - .unwrap(); - assert_eq!( - user_profile.encryption_type, - EncryptionType::SelfEncryption(sign) - ); -} diff --git a/frontend/rust-lib/flowy-server/tests/supabase_test/util.rs b/frontend/rust-lib/flowy-server/tests/supabase_test/util.rs deleted file mode 100644 index 7fba91fe9a..0000000000 --- a/frontend/rust-lib/flowy-server/tests/supabase_test/util.rs +++ /dev/null @@ -1,162 +0,0 @@ -use std::collections::HashMap; -use std::sync::Arc; - -use collab::core::collab::{DataSource, MutexCollab}; -use collab::core::origin::CollabOrigin; -use collab::preclude::Collab; -use collab_plugins::cloud_storage::RemoteCollabStorage; -use uuid::Uuid; - -use flowy_database_pub::cloud::DatabaseCloudService; -use flowy_error::FlowyError; -use flowy_folder_pub::cloud::{Folder, FolderCloudService}; -use flowy_server::supabase::api::{ - RESTfulPostgresServer, SupabaseCollabStorageImpl, SupabaseDatabaseServiceImpl, - SupabaseFolderServiceImpl, SupabaseServerServiceImpl, SupabaseUserServiceImpl, -}; -use flowy_server::supabase::define::{USER_DEVICE_ID, USER_EMAIL, USER_UUID}; -use flowy_server::{AppFlowyEncryption, EncryptionImpl}; -use flowy_server_pub::supabase_config::SupabaseConfiguration; -use flowy_user_pub::cloud::UserCloudService; -use lib_infra::future::FutureResult; - -use crate::setup_log; - -pub fn get_supabase_ci_config() -> Option { - dotenv::from_filename("./.env.ci").ok()?; - setup_log(); - SupabaseConfiguration::from_env().ok() -} - -#[allow(dead_code)] -pub fn get_supabase_dev_config() -> Option { - dotenv::from_filename("./.env.dev").ok()?; - setup_log(); - SupabaseConfiguration::from_env().ok() -} - -pub fn collab_service() -> Arc { - let (server, encryption_impl) = supabase_server_service(None); - Arc::new(SupabaseCollabStorageImpl::new( - server, - None, - Arc::downgrade(&encryption_impl), - )) -} - -pub fn database_service() -> Arc { - let (server, _encryption_impl) = supabase_server_service(None); - Arc::new(SupabaseDatabaseServiceImpl::new(server)) -} - -pub fn user_auth_service() -> Arc { - let (server, _encryption_impl) = supabase_server_service(None); - Arc::new(SupabaseUserServiceImpl::new(server, vec![], None)) -} - -pub fn folder_service() -> Arc { - let (server, _encryption_impl) = supabase_server_service(None); - Arc::new(SupabaseFolderServiceImpl::new(server)) -} - -#[allow(dead_code)] -pub fn file_storage_service() -> Arc { - let encryption_impl: Arc = Arc::new(EncryptionImpl::new(None)); - let config = SupabaseConfiguration::from_env().unwrap(); - Arc::new( - SupabaseFileStorage::new( - &config, - Arc::downgrade(&encryption_impl), - Arc::new(TestFileStoragePlan), - ) - .unwrap(), - ) -} - -#[allow(dead_code)] -pub fn encryption_folder_service( - secret: Option, -) -> (Arc, Arc) { - let (server, encryption_impl) = supabase_server_service(secret); - let service = Arc::new(SupabaseFolderServiceImpl::new(server)); - (service, encryption_impl) -} - -#[allow(dead_code)] -pub fn encryption_collab_service( - secret: Option, -) -> (Arc, Arc) { - let (server, encryption_impl) = supabase_server_service(secret); - let service = Arc::new(SupabaseCollabStorageImpl::new( - server, - None, - Arc::downgrade(&encryption_impl), - )); - (service, encryption_impl) -} - -#[allow(dead_code)] -pub async fn print_encryption_folder( - uid: &i64, - folder_id: &str, - encryption_secret: Option, -) { - let (cloud_service, _encryption) = encryption_folder_service(encryption_secret); - let folder_data = cloud_service.get_folder_data(folder_id, uid).await.unwrap(); - let json = serde_json::to_value(folder_data).unwrap(); - println!("{}", serde_json::to_string_pretty(&json).unwrap()); -} - -#[allow(dead_code)] -pub async fn print_encryption_folder_snapshot( - uid: &i64, - folder_id: &str, - encryption_secret: Option, -) { - let (cloud_service, _encryption) = encryption_collab_service(encryption_secret); - let snapshot = cloud_service - .get_snapshots(folder_id, 1) - .await - .pop() - .unwrap(); - let collab = Arc::new(MutexCollab::new( - Collab::new_with_source( - CollabOrigin::Empty, - folder_id, - DataSource::DocStateV1(snapshot.blob), - vec![], - false, - ) - .unwrap(), - )); - let folder_data = Folder::open(uid, collab, None) - .unwrap() - .get_folder_data(folder_id) - .unwrap(); - let json = serde_json::to_value(folder_data).unwrap(); - println!("{}", serde_json::to_string_pretty(&json).unwrap()); -} - -pub fn supabase_server_service( - encryption_secret: Option, -) -> (SupabaseServerServiceImpl, Arc) { - let config = SupabaseConfiguration::from_env().unwrap(); - let encryption_impl: Arc = - Arc::new(EncryptionImpl::new(encryption_secret)); - let encryption = Arc::downgrade(&encryption_impl); - let server = Arc::new(RESTfulPostgresServer::new(config, encryption)); - (SupabaseServerServiceImpl::new(server), encryption_impl) -} - -pub fn third_party_sign_up_param(uuid: String) -> HashMap { - let mut params = HashMap::new(); - params.insert(USER_UUID.to_string(), uuid); - params.insert( - USER_EMAIL.to_string(), - format!("{}@test.com", Uuid::new_v4()), - ); - params.insert(USER_DEVICE_ID.to_string(), Uuid::new_v4().to_string()); - params -} - -pub struct TestFileStoragePlan; diff --git a/frontend/rust-lib/flowy-server/tests/test.txt b/frontend/rust-lib/flowy-server/tests/test.txt deleted file mode 100644 index 95d09f2b10..0000000000 --- a/frontend/rust-lib/flowy-server/tests/test.txt +++ /dev/null @@ -1 +0,0 @@ -hello world \ No newline at end of file diff --git a/frontend/rust-lib/flowy-user-pub/src/sql/user_sql.rs b/frontend/rust-lib/flowy-user-pub/src/sql/user_sql.rs index e8d186c484..a78172aa9f 100644 --- a/frontend/rust-lib/flowy-user-pub/src/sql/user_sql.rs +++ b/frontend/rust-lib/flowy-user-pub/src/sql/user_sql.rs @@ -6,7 +6,7 @@ use crate::sql::{ use flowy_error::{FlowyError, FlowyResult}; use flowy_sqlite::schema::user_table; use flowy_sqlite::{prelude::*, DBConnection, ExpressionMethods, RunQueryDsl}; -use tracing::{trace, warn}; +use tracing::trace; /// The order of the fields in the struct must be the same as the order of the fields in the table. /// Check out the [schema.rs] for table schema. @@ -157,26 +157,12 @@ pub fn select_user_profile( Ok(user) } -pub fn select_workspace_auth_type( +pub fn select_user_auth_type( uid: i64, - workspace_id: &str, conn: &mut SqliteConnection, ) -> Result { - match select_user_workspace(workspace_id, conn) { - Ok(workspace) => Ok(AuthType::from(workspace.workspace_type)), - Err(err) => { - if err.is_record_not_found() { - let row = select_user_table_row(uid, conn)?; - warn!( - "user user auth type:{} as workspace auth type", - row.auth_type - ); - Ok(AuthType::from(row.auth_type)) - } else { - Err(err) - } - }, - } + let row = select_user_table_row(uid, conn)?; + Ok(AuthType::from(row.auth_type)) } pub fn upsert_user(user: UserTable, mut conn: DBConnection) -> FlowyResult<()> { diff --git a/frontend/rust-lib/flowy-user/src/migrations/anon_user_workspace.rs b/frontend/rust-lib/flowy-user/src/migrations/anon_user_workspace.rs index 7c806d3aaf..1b1c3f890f 100644 --- a/frontend/rust-lib/flowy-user/src/migrations/anon_user_workspace.rs +++ b/frontend/rust-lib/flowy-user/src/migrations/anon_user_workspace.rs @@ -1,7 +1,7 @@ use diesel::SqliteConnection; use semver::Version; use std::sync::Arc; -use tracing::{info, instrument}; +use tracing::instrument; use collab_integrate::CollabKVDB; use flowy_error::FlowyResult; @@ -9,7 +9,7 @@ use flowy_user_pub::entities::AuthType; use crate::migrations::migration::UserDataMigration; use flowy_user_pub::session::Session; -use flowy_user_pub::sql::{select_user_workspace, upsert_user_workspace}; +use flowy_user_pub::sql::upsert_user_workspace; pub struct AnonUserWorkspaceTableMigration; @@ -34,23 +34,15 @@ impl UserDataMigration for AnonUserWorkspaceTableMigration { &self, session: &Session, _collab_db: &Arc, - auth_type: &AuthType, + user_auth_type: &AuthType, db: &mut SqliteConnection, ) -> FlowyResult<()> { // For historical reason, anon user doesn't have a workspace in user_workspace_table. // So we need to create a new entry for the anon user in the user_workspace_table. - if matches!(auth_type, AuthType::Local) { - let user_workspace = &session.user_workspace; - let result = select_user_workspace(&user_workspace.id, db); - if let Err(e) = result { - if e.is_record_not_found() { - info!( - "Anon user workspace not found in the database, creating a new entry for user_id: {}", - session.user_id - ); - upsert_user_workspace(session.user_id, *auth_type, user_workspace.clone(), db)?; - } - } + if matches!(user_auth_type, AuthType::Local) { + let mut user_workspace = session.user_workspace.clone(); + user_workspace.workspace_type = AuthType::Local; + upsert_user_workspace(session.user_id, *user_auth_type, user_workspace, db)?; } Ok(()) diff --git a/frontend/rust-lib/flowy-user/src/migrations/doc_key_with_workspace.rs b/frontend/rust-lib/flowy-user/src/migrations/doc_key_with_workspace.rs index 84acc0b56a..735d8f1f49 100644 --- a/frontend/rust-lib/flowy-user/src/migrations/doc_key_with_workspace.rs +++ b/frontend/rust-lib/flowy-user/src/migrations/doc_key_with_workspace.rs @@ -40,7 +40,7 @@ impl UserDataMigration for CollabDocKeyWithWorkspaceIdMigration { &self, session: &Session, collab_db: &Arc, - _authenticator: &AuthType, + _user_auth_type: &AuthType, _db: &mut SqliteConnection, ) -> FlowyResult<()> { trace!( diff --git a/frontend/rust-lib/flowy-user/src/migrations/document_empty_content.rs b/frontend/rust-lib/flowy-user/src/migrations/document_empty_content.rs index 2e4581f7ec..996386cb5e 100644 --- a/frontend/rust-lib/flowy-user/src/migrations/document_empty_content.rs +++ b/frontend/rust-lib/flowy-user/src/migrations/document_empty_content.rs @@ -42,13 +42,13 @@ impl UserDataMigration for HistoricalEmptyDocumentMigration { &self, session: &Session, collab_db: &Arc, - authenticator: &AuthType, + user_auth_type: &AuthType, _db: &mut SqliteConnection, ) -> FlowyResult<()> { // - The `empty document` struct has already undergone refactoring prior to the launch of the AppFlowy cloud version. // - Consequently, if a user is utilizing the AppFlowy cloud version, there is no need to perform any migration for the `empty document` struct. // - This migration step is only necessary for users who are transitioning from a local version of AppFlowy to the cloud version. - if !matches!(authenticator, AuthType::Local) { + if !matches!(user_auth_type, AuthType::Local) { return Ok(()); } collab_db.with_write_txn(|write_txn| { diff --git a/frontend/rust-lib/flowy-user/src/migrations/migration.rs b/frontend/rust-lib/flowy-user/src/migrations/migration.rs index 0f5c2c2624..1cf8d6a943 100644 --- a/frontend/rust-lib/flowy-user/src/migrations/migration.rs +++ b/frontend/rust-lib/flowy-user/src/migrations/migration.rs @@ -54,7 +54,7 @@ impl UserLocalDataMigration { pub fn run( self, migrations: Vec>, - auth_type: &AuthType, + user_auth_type: &AuthType, app_version: &Version, ) -> FlowyResult> { let mut applied_migrations = vec![]; @@ -75,7 +75,7 @@ impl UserLocalDataMigration { let migration_name = migration.name().to_string(); if !duplicated_names.contains(&migration_name) { - migration.run(&self.session, &self.collab_db, auth_type, &mut conn)?; + migration.run(&self.session, &self.collab_db, user_auth_type, &mut conn)?; applied_migrations.push(migration.name().to_string()); save_migration_record(&mut conn, &migration_name); duplicated_names.push(migration_name); @@ -98,7 +98,7 @@ pub trait UserDataMigration { &self, user: &Session, collab_db: &Arc, - authenticator: &AuthType, + user_auth_type: &AuthType, db: &mut SqliteConnection, ) -> FlowyResult<()>; } diff --git a/frontend/rust-lib/flowy-user/src/migrations/workspace_and_favorite_v1.rs b/frontend/rust-lib/flowy-user/src/migrations/workspace_and_favorite_v1.rs index 5f14051e26..d3cea0e976 100644 --- a/frontend/rust-lib/flowy-user/src/migrations/workspace_and_favorite_v1.rs +++ b/frontend/rust-lib/flowy-user/src/migrations/workspace_and_favorite_v1.rs @@ -40,7 +40,7 @@ impl UserDataMigration for FavoriteV1AndWorkspaceArrayMigration { &self, session: &Session, collab_db: &Arc, - _authenticator: &AuthType, + _user_auth_type: &AuthType, _db: &mut SqliteConnection, ) -> FlowyResult<()> { collab_db.with_write_txn(|write_txn| { diff --git a/frontend/rust-lib/flowy-user/src/migrations/workspace_trash_v1.rs b/frontend/rust-lib/flowy-user/src/migrations/workspace_trash_v1.rs index b5eeead8c6..ee9156199e 100644 --- a/frontend/rust-lib/flowy-user/src/migrations/workspace_trash_v1.rs +++ b/frontend/rust-lib/flowy-user/src/migrations/workspace_trash_v1.rs @@ -38,7 +38,7 @@ impl UserDataMigration for WorkspaceTrashMapToSectionMigration { &self, session: &Session, collab_db: &Arc, - _authenticator: &AuthType, + _user_auth_type: &AuthType, _db: &mut SqliteConnection, ) -> FlowyResult<()> { collab_db.with_write_txn(|write_txn| { diff --git a/frontend/rust-lib/flowy-user/src/services/authenticate_user.rs b/frontend/rust-lib/flowy-user/src/services/authenticate_user.rs index 84c1e9afe9..418f0638d3 100644 --- a/frontend/rust-lib/flowy-user/src/services/authenticate_user.rs +++ b/frontend/rust-lib/flowy-user/src/services/authenticate_user.rs @@ -10,7 +10,7 @@ use collab_plugins::local_storage::kv::KVTransactionDB; use flowy_error::{internal_error, ErrorCode, FlowyError, FlowyResult}; use flowy_sqlite::kv::KVStorePreferences; use flowy_sqlite::DBConnection; -use flowy_user_pub::entities::UserWorkspace; +use flowy_user_pub::entities::{AuthType, UserWorkspace}; use flowy_user_pub::session::Session; use std::path::PathBuf; use std::str::FromStr; @@ -48,14 +48,11 @@ impl AuthenticateUser { } pub async fn is_local_mode(&self) -> FlowyResult { - let uid = self.user_id()?; - if let Ok(anon_user) = self.get_anon_user().await { - if anon_user == uid { - return Ok(true); - } - } - - Ok(false) + let session = self.get_session()?; + Ok(matches!( + session.user_workspace.workspace_type, + AuthType::Local + )) } pub fn device_id(&self) -> FlowyResult { @@ -150,28 +147,24 @@ impl AuthenticateUser { match self .store_preferences - .get_object::>(&self.user_config.session_cache_key) + .get_object::(&self.user_config.session_cache_key) { None => Err(FlowyError::new( ErrorCode::RecordNotFound, - "User is not logged in", + "Can't find user session. Please login again", )), - Some(session) => { + Some(mut session) => { + // Set the workspace type to local if the user is anon. + if let Some(anon_session) = self.store_preferences.get_object::(ANON_USER) { + if session.user_id == anon_session.user_id { + session.user_workspace.workspace_type = AuthType::Local; + } + } + + let session = Arc::new(session); self.session.store(Some(session.clone())); Ok(session) }, } } - - async fn get_anon_user(&self) -> FlowyResult { - let anon_session = self - .store_preferences - .get_object::(ANON_USER) - .ok_or(FlowyError::new( - ErrorCode::RecordNotFound, - "Anon user not found", - ))?; - - Ok(anon_session.user_id) - } } diff --git a/frontend/rust-lib/flowy-user/src/services/data_import/appflowy_data_import.rs b/frontend/rust-lib/flowy-user/src/services/data_import/appflowy_data_import.rs index 20b0c26368..90113b8062 100644 --- a/frontend/rust-lib/flowy-user/src/services/data_import/appflowy_data_import.rs +++ b/frontend/rust-lib/flowy-user/src/services/data_import/appflowy_data_import.rs @@ -3,7 +3,7 @@ use crate::migrations::session_migration::migrate_session_with_user_uuid; use crate::services::data_import::importer::load_collab_by_object_ids; use crate::services::db::UserDBPath; use crate::services::entities::UserPaths; -use crate::user_manager::run_collab_data_migration; +use crate::user_manager::run_data_migration; use anyhow::anyhow; use collab::core::collab::DataSource; use collab::core::origin::CollabOrigin; @@ -36,7 +36,7 @@ use std::collections::{HashMap, HashSet}; use collab_document::blocks::TextDelta; use collab_document::document::Document; -use flowy_user_pub::sql::{select_user_profile, select_workspace_auth_type}; +use flowy_user_pub::sql::{select_user_auth_type, select_user_profile}; use semver::Version; use serde_json::json; use std::ops::{Deref, DerefMut}; @@ -103,23 +103,17 @@ pub(crate) fn prepare_import( ); let mut conn = imported_sqlite_db.get_connection()?; - let imported_workspace_auth_type = select_user_profile( + let imported_user_auth_type = select_user_profile( imported_session.user_id, &imported_session.user_workspace.id, &mut conn, ) - .map(|v| v.workspace_auth_type) - .or_else(|_| { - select_workspace_auth_type( - imported_session.user_id, - &imported_session.user_workspace.id, - &mut conn, - ) - })?; + .map(|v| v.auth_type) + .or_else(|_| select_user_auth_type(imported_session.user_id, &mut conn))?; - run_collab_data_migration( + run_data_migration( &imported_session, - &imported_workspace_auth_type, + &imported_user_auth_type, imported_collab_db.clone(), imported_sqlite_db.get_pool(), other_store_preferences.clone(), diff --git a/frontend/rust-lib/flowy-user/src/user_manager/manager.rs b/frontend/rust-lib/flowy-user/src/user_manager/manager.rs index 9cefddf44b..2118362a8b 100644 --- a/frontend/rust-lib/flowy-user/src/user_manager/manager.rs +++ b/frontend/rust-lib/flowy-user/src/user_manager/manager.rs @@ -235,9 +235,9 @@ impl UserManager { self.authenticate_user.database.get_pool(session.user_id), ) { (Ok(collab_db), Ok(sqlite_pool)) => { - run_collab_data_migration( + run_data_migration( &session, - &auth_type, + &user.auth_type, collab_db, sqlite_pool, self.store_preferences.clone(), @@ -844,9 +844,9 @@ fn mark_all_migrations_as_applied(sqlite_pool: &Arc) { } } -pub(crate) fn run_collab_data_migration( +pub(crate) fn run_data_migration( session: &Session, - auth_type: &AuthType, + user_auth_type: &AuthType, collab_db: Arc, sqlite_pool: Arc, kv: Arc, @@ -855,7 +855,7 @@ pub(crate) fn run_collab_data_migration( let migrations = collab_migration_list(); match UserLocalDataMigration::new(session.clone(), collab_db, sqlite_pool, kv).run( migrations, - auth_type, + user_auth_type, app_version, ) { Ok(applied_migrations) => { diff --git a/frontend/rust-lib/flowy-user/src/user_manager/manager_history_user.rs b/frontend/rust-lib/flowy-user/src/user_manager/manager_history_user.rs index a7191f0509..188cc3c5ac 100644 --- a/frontend/rust-lib/flowy-user/src/user_manager/manager_history_user.rs +++ b/frontend/rust-lib/flowy-user/src/user_manager/manager_history_user.rs @@ -53,6 +53,18 @@ impl UserManager { Ok(UserProfilePB::from(profile)) } + pub fn get_anon_user_id(&self) -> FlowyResult { + let anon_session = self + .store_preferences + .get_object::(ANON_USER) + .ok_or(FlowyError::new( + ErrorCode::RecordNotFound, + "Anon user not found", + ))?; + + Ok(anon_session.user_id) + } + /// Opens a historical user's session based on their user ID, device ID, and authentication type. /// /// This function facilitates the re-opening of a user's session from historical tracking.