From 4e2990e5996e4050c45504ac510201848e8781a3 Mon Sep 17 00:00:00 2001 From: Nathan Date: Mon, 21 Apr 2025 22:02:06 +0800 Subject: [PATCH] chore: remove local model --- frontend/rust-lib/flowy-ai-pub/src/lib.rs | 1 + .../rust-lib/flowy-ai-pub/src/user_service.rs | 14 + frontend/rust-lib/flowy-ai/src/ai_manager.rs | 21 +- frontend/rust-lib/flowy-ai/src/chat.rs | 2 +- frontend/rust-lib/flowy-ai/src/completion.rs | 2 +- .../flowy-ai/src/local_ai/controller.rs | 2 +- .../flowy-ai/src/local_ai/resource.rs | 2 +- .../src/middleware/chat_service_mw.rs | 2 +- .../src/offline/offline_message_sync.rs | 2 +- .../flowy-core/src/deps_resolve/chat_deps.rs | 5 +- .../rust-lib/flowy-core/src/server_layer.rs | 8 +- .../flowy-server/src/af_cloud/define.rs | 3 +- .../flowy-server/src/af_cloud/server.rs | 11 +- .../flowy-server/tests/af_cloud_test/mod.rs | 2 - .../tests/af_cloud_test/user_test.rs | 21 -- .../flowy-server/tests/af_cloud_test/util.rs | 119 ------- frontend/rust-lib/flowy-server/tests/logo.png | Bin 15694 -> 0 bytes frontend/rust-lib/flowy-server/tests/main.rs | 24 -- .../tests/supabase_test/database_test.rs | 63 ---- .../tests/supabase_test/file_test.rs | 78 ----- .../tests/supabase_test/folder_test.rs | 316 ------------------ .../flowy-server/tests/supabase_test/mod.rs | 5 - .../tests/supabase_test/user_test.rs | 141 -------- .../flowy-server/tests/supabase_test/util.rs | 162 --------- frontend/rust-lib/flowy-server/tests/test.txt | 1 - .../flowy-user-pub/src/sql/user_sql.rs | 22 +- .../src/migrations/anon_user_workspace.rs | 22 +- .../src/migrations/doc_key_with_workspace.rs | 2 +- .../src/migrations/document_empty_content.rs | 4 +- .../flowy-user/src/migrations/migration.rs | 6 +- .../migrations/workspace_and_favorite_v1.rs | 2 +- .../src/migrations/workspace_trash_v1.rs | 2 +- .../src/services/authenticate_user.rs | 41 +-- .../data_import/appflowy_data_import.rs | 20 +- .../flowy-user/src/user_manager/manager.rs | 10 +- .../src/user_manager/manager_history_user.rs | 12 + 36 files changed, 101 insertions(+), 1049 deletions(-) create mode 100644 frontend/rust-lib/flowy-ai-pub/src/user_service.rs delete mode 100644 frontend/rust-lib/flowy-server/tests/af_cloud_test/mod.rs delete mode 100644 frontend/rust-lib/flowy-server/tests/af_cloud_test/user_test.rs delete mode 100644 frontend/rust-lib/flowy-server/tests/af_cloud_test/util.rs delete mode 100644 frontend/rust-lib/flowy-server/tests/logo.png delete mode 100644 frontend/rust-lib/flowy-server/tests/main.rs delete mode 100644 frontend/rust-lib/flowy-server/tests/supabase_test/database_test.rs delete mode 100644 frontend/rust-lib/flowy-server/tests/supabase_test/file_test.rs delete mode 100644 frontend/rust-lib/flowy-server/tests/supabase_test/folder_test.rs delete mode 100644 frontend/rust-lib/flowy-server/tests/supabase_test/mod.rs delete mode 100644 frontend/rust-lib/flowy-server/tests/supabase_test/user_test.rs delete mode 100644 frontend/rust-lib/flowy-server/tests/supabase_test/util.rs delete mode 100644 frontend/rust-lib/flowy-server/tests/test.txt 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 d6f09e3e2e0cef43e926fbec6b7b343069bb6111..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 15694 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 o`&$41Bk(oo|8x^?;S+IqL7fKk14AULy% 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.