feat: workspace service in user crate (#4373)

* refactor: user manager

* feat: implement workspace service

* refactor: migrate user data when sign up

* chore: fmt

* chore: enable beta cloud

* chore: update ci

* chore: trim slash
This commit is contained in:
Nathan.fooo
2024-01-12 14:34:59 +08:00
committed by GitHub
parent 690a3746fa
commit 9500abb363
66 changed files with 879 additions and 1079 deletions

View File

@ -7,12 +7,13 @@ use flowy_sqlite::{
prelude::*,
schema::{collab_snapshot, collab_snapshot::dsl},
};
use flowy_user::user_manager::UserManager;
use flowy_user::services::authenticate_user::AuthenticateUser;
use lib_infra::util::timestamp;
use std::sync::Weak;
use tracing::debug;
pub struct SnapshotDBImpl(pub Weak<UserManager>);
pub struct SnapshotDBImpl(pub Weak<AuthenticateUser>);
impl SnapshotPersistence for SnapshotDBImpl {
fn create_snapshot(
@ -24,16 +25,12 @@ impl SnapshotPersistence for SnapshotDBImpl {
) -> Result<(), PersistenceError> {
let collab_type = collab_type.clone();
let object_id = object_id.to_string();
let weak_user_session = self.0.clone();
let weak_user = self.0.clone();
tokio::task::spawn_blocking(move || {
if let Some(pool) = weak_user_session
if let Some(mut conn) = weak_user
.upgrade()
.and_then(|user_session| user_session.db_pool(uid).ok())
.and_then(|authenticate_user| authenticate_user.get_sqlite_connection(uid).ok())
{
let mut conn = pool
.get()
.map_err(|e| PersistenceError::Internal(e.into()))?;
// Save the snapshot data to disk
let result = CollabSnapshotSql::create(
CollabSnapshotRow::new(object_id.clone(), collab_type.to_string(), encoded_v1),

View File

@ -1,25 +1,22 @@
use std::sync::{Arc, Weak};
use tokio::sync::RwLock;
use collab_integrate::collab_builder::AppFlowyCollabBuilder;
use collab_integrate::CollabKVDB;
use flowy_database2::{DatabaseManager, DatabaseUser};
use flowy_database_pub::cloud::DatabaseCloudService;
use flowy_error::FlowyError;
use flowy_user::user_manager::UserManager;
use flowy_user::services::authenticate_user::AuthenticateUser;
use lib_infra::priority_task::TaskDispatcher;
use std::sync::{Arc, Weak};
use tokio::sync::RwLock;
pub struct DatabaseDepsResolver();
impl DatabaseDepsResolver {
pub async fn resolve(
user_manager: Weak<UserManager>,
authenticate_user: Weak<AuthenticateUser>,
task_scheduler: Arc<RwLock<TaskDispatcher>>,
collab_builder: Arc<AppFlowyCollabBuilder>,
cloud_service: Arc<dyn DatabaseCloudService>,
) -> Arc<DatabaseManager> {
let user = Arc::new(DatabaseUserImpl(user_manager));
let user = Arc::new(DatabaseUserImpl(authenticate_user));
Arc::new(DatabaseManager::new(
user,
task_scheduler,
@ -29,7 +26,7 @@ impl DatabaseDepsResolver {
}
}
struct DatabaseUserImpl(Weak<UserManager>);
struct DatabaseUserImpl(Weak<AuthenticateUser>);
impl DatabaseUser for DatabaseUserImpl {
fn user_id(&self) -> Result<i64, FlowyError> {
self
@ -39,14 +36,6 @@ impl DatabaseUser for DatabaseUserImpl {
.user_id()
}
fn token(&self) -> Result<Option<String>, FlowyError> {
self
.0
.upgrade()
.ok_or(FlowyError::internal().with_context("Unexpected error: UserSession is None"))?
.token()
}
fn collab_db(&self, uid: i64) -> Result<Weak<CollabKVDB>, FlowyError> {
self
.0

View File

@ -9,20 +9,20 @@ use flowy_document::manager::{DocumentManager, DocumentSnapshotService, Document
use flowy_document_pub::cloud::DocumentCloudService;
use flowy_error::{FlowyError, FlowyResult};
use flowy_storage::FileStorageService;
use flowy_user::user_manager::UserManager;
use flowy_user::services::authenticate_user::AuthenticateUser;
pub struct DocumentDepsResolver();
impl DocumentDepsResolver {
pub fn resolve(
user_manager: Weak<UserManager>,
authenticate_user: Weak<AuthenticateUser>,
_database_manager: &Arc<DatabaseManager>,
collab_builder: Arc<AppFlowyCollabBuilder>,
cloud_service: Arc<dyn DocumentCloudService>,
storage_service: Weak<dyn FileStorageService>,
) -> Arc<DocumentManager> {
let user_service: Arc<dyn DocumentUserService> =
Arc::new(DocumentUserImpl(user_manager.clone()));
let snapshot_service = Arc::new(DocumentSnapshotImpl(user_manager));
Arc::new(DocumentUserImpl(authenticate_user.clone()));
let snapshot_service = Arc::new(DocumentSnapshotImpl(authenticate_user));
Arc::new(DocumentManager::new(
user_service.clone(),
collab_builder,
@ -33,10 +33,10 @@ impl DocumentDepsResolver {
}
}
struct DocumentSnapshotImpl(Weak<UserManager>);
struct DocumentSnapshotImpl(Weak<AuthenticateUser>);
impl DocumentSnapshotImpl {
pub fn get_user_manager(&self) -> FlowyResult<Arc<UserManager>> {
pub fn get_authenticate_user(&self) -> FlowyResult<Arc<AuthenticateUser>> {
self
.0
.upgrade()
@ -49,9 +49,9 @@ impl DocumentSnapshotService for DocumentSnapshotImpl {
&self,
document_id: &str,
) -> FlowyResult<Vec<DocumentSnapshotMeta>> {
let user_manager = self.get_user_manager()?;
let uid = user_manager.user_id()?;
let mut db = user_manager.db_connection(uid)?;
let authenticate_user = self.get_authenticate_user()?;
let uid = authenticate_user.user_id()?;
let mut db = authenticate_user.get_sqlite_connection(uid)?;
CollabSnapshotSql::get_all_snapshots(document_id, &mut db).map(|rows| {
rows
.into_iter()
@ -65,9 +65,9 @@ impl DocumentSnapshotService for DocumentSnapshotImpl {
}
fn get_document_snapshot(&self, snapshot_id: &str) -> FlowyResult<DocumentSnapshotData> {
let user_manager = self.get_user_manager()?;
let uid = user_manager.user_id()?;
let mut db = user_manager.db_connection(uid)?;
let authenticate_user = self.get_authenticate_user()?;
let uid = authenticate_user.user_id()?;
let mut db = authenticate_user.get_sqlite_connection(uid)?;
CollabSnapshotSql::get_snapshot(snapshot_id, &mut db)
.map(|row| DocumentSnapshotData {
object_id: row.id,
@ -79,7 +79,7 @@ impl DocumentSnapshotService for DocumentSnapshotImpl {
}
}
struct DocumentUserImpl(Weak<UserManager>);
struct DocumentUserImpl(Weak<AuthenticateUser>);
impl DocumentUserService for DocumentUserImpl {
fn user_id(&self) -> Result<i64, FlowyError> {
self
@ -97,14 +97,6 @@ impl DocumentUserService for DocumentUserImpl {
.workspace_id()
}
fn token(&self) -> Result<Option<String>, FlowyError> {
self
.0
.upgrade()
.ok_or(FlowyError::internal().with_context("Unexpected error: UserSession is None"))?
.token()
}
fn collab_db(&self, uid: i64) -> Result<Weak<CollabKVDB>, FlowyError> {
self
.0

View File

@ -1,11 +1,4 @@
use std::collections::HashMap;
use std::convert::TryFrom;
use std::sync::{Arc, Weak};
use bytes::Bytes;
use tokio::sync::RwLock;
use collab_integrate::collab_builder::AppFlowyCollabBuilder;
use collab_integrate::CollabKVDB;
use flowy_database2::entities::DatabaseLayoutPB;
@ -21,11 +14,13 @@ use flowy_folder::manager::{FolderManager, FolderUser};
use flowy_folder::share::ImportType;
use flowy_folder::view_operation::{FolderOperationHandler, FolderOperationHandlers, View};
use flowy_folder::ViewLayout;
use std::collections::HashMap;
use std::convert::TryFrom;
use std::sync::{Arc, Weak};
use tokio::sync::RwLock;
use flowy_folder_pub::entities::ImportData;
use flowy_folder_pub::folder_builder::{ParentChildViews, WorkspaceViewBuilder};
use flowy_user::services::data_import::ImportDataSource;
use flowy_user::user_manager::UserManager;
use flowy_folder_pub::folder_builder::WorkspaceViewBuilder;
use flowy_user::services::authenticate_user::AuthenticateUser;
use crate::integrate::server::ServerProvider;
use lib_dispatch::prelude::ToBytes;
@ -35,15 +30,14 @@ use lib_infra::future::FutureResult;
pub struct FolderDepsResolver();
impl FolderDepsResolver {
pub async fn resolve(
user_manager: Weak<UserManager>,
authenticate_user: Weak<AuthenticateUser>,
document_manager: &Arc<DocumentManager>,
database_manager: &Arc<DatabaseManager>,
collab_builder: Arc<AppFlowyCollabBuilder>,
server_provider: Arc<ServerProvider>,
) -> Arc<FolderManager> {
let user: Arc<dyn FolderUser> = Arc::new(FolderUserImpl {
user_manager: user_manager.clone(),
database_manager: Arc::downgrade(database_manager),
authenticate_user: authenticate_user.clone(),
});
let handlers = folder_operation_handlers(document_manager.clone(), database_manager.clone());
@ -77,67 +71,26 @@ fn folder_operation_handlers(
}
struct FolderUserImpl {
user_manager: Weak<UserManager>,
database_manager: Weak<DatabaseManager>,
authenticate_user: Weak<AuthenticateUser>,
}
#[async_trait]
impl FolderUser for FolderUserImpl {
fn user_id(&self) -> Result<i64, FlowyError> {
self
.user_manager
.authenticate_user
.upgrade()
.ok_or(FlowyError::internal().with_context("Unexpected error: UserSession is None"))?
.user_id()
}
fn token(&self) -> Result<Option<String>, FlowyError> {
self
.user_manager
.upgrade()
.ok_or(FlowyError::internal().with_context("Unexpected error: UserSession is None"))?
.token()
}
fn collab_db(&self, uid: i64) -> Result<Weak<CollabKVDB>, FlowyError> {
self
.user_manager
.authenticate_user
.upgrade()
.ok_or(FlowyError::internal().with_context("Unexpected error: UserSession is None"))?
.get_collab_db(uid)
}
async fn import_appflowy_data_folder(
&self,
path: &str,
container_name: Option<String>,
) -> Result<Vec<ParentChildViews>, FlowyError> {
match (self.user_manager.upgrade(), self.database_manager.upgrade()) {
(Some(user_manager), Some(data_manager)) => {
let source = ImportDataSource::AppFlowyDataFolder {
path: path.to_string(),
container_name,
};
let import_data = user_manager.import_data_from_source(source).await?;
match import_data {
ImportData::AppFlowyDataFolder {
views,
database_view_ids_by_database_id,
row_object_ids: _,
database_object_ids: _,
document_object_ids: _,
} => {
let _uid = self.user_id()?;
data_manager
.track_database(database_view_ids_by_database_id)
.await?;
Ok(views)
},
}
},
_ => Err(FlowyError::internal().with_context("Unexpected error: UserSession is None")),
}
}
}
struct DocumentFolderOperation(Arc<DocumentManager>);

View File

@ -2,6 +2,7 @@ pub use collab_deps::*;
pub use database_deps::*;
pub use document_deps::*;
pub use folder_deps::*;
pub use user_deps::*;
mod collab_deps;
mod document_deps;

View File

@ -1 +1,62 @@
use crate::integrate::server::ServerProvider;
use collab_integrate::collab_builder::AppFlowyCollabBuilder;
use flowy_database2::DatabaseManager;
use flowy_error::FlowyResult;
use flowy_folder::manager::FolderManager;
use flowy_folder_pub::folder_builder::ParentChildViews;
use flowy_sqlite::kv::StorePreferences;
use flowy_user::services::authenticate_user::AuthenticateUser;
use flowy_user::user_manager::UserManager;
use flowy_user_pub::workspace_service::UserWorkspaceService;
use lib_infra::async_trait::async_trait;
use std::collections::HashMap;
use std::sync::Arc;
pub struct UserDepsResolver();
impl UserDepsResolver {
pub async fn resolve(
authenticate_user: Arc<AuthenticateUser>,
collab_builder: Arc<AppFlowyCollabBuilder>,
server_provider: Arc<ServerProvider>,
store_preference: Arc<StorePreferences>,
database_manager: Arc<DatabaseManager>,
folder_manager: Arc<FolderManager>,
) -> Arc<UserManager> {
let workspace_service_impl = Arc::new(UserWorkspaceServiceImpl {
database_manager,
folder_manager,
});
UserManager::new(
server_provider,
store_preference,
Arc::downgrade(&collab_builder),
authenticate_user,
workspace_service_impl,
)
}
}
pub struct UserWorkspaceServiceImpl {
pub database_manager: Arc<DatabaseManager>,
pub folder_manager: Arc<FolderManager>,
}
#[async_trait]
impl UserWorkspaceService for UserWorkspaceServiceImpl {
async fn did_import_views(&self, views: Vec<ParentChildViews>) -> FlowyResult<()> {
self.folder_manager.insert_parent_child_views(views).await?;
Ok(())
}
async fn did_import_database_views(
&self,
ids_by_database_id: HashMap<String, Vec<String>>,
) -> FlowyResult<()> {
self
.database_manager
.track_database(ids_by_database_id)
.await?;
Ok(())
}
}

View File

@ -1,7 +1,7 @@
#![allow(unused_doc_comments)]
use std::sync::Arc;
use std::sync::Weak;
use std::time::Duration;
use tokio::sync::RwLock;
@ -13,9 +13,9 @@ use flowy_document::manager::DocumentManager;
use flowy_folder::manager::FolderManager;
use flowy_sqlite::kv::StorePreferences;
use flowy_storage::FileStorageService;
use flowy_user::services::authenticate_user::AuthenticateUser;
use flowy_user::services::entities::UserConfig;
use flowy_user::user_manager::UserManager;
use flowy_user_pub::cloud::UserCloudServiceProvider;
use lib_dispatch::prelude::*;
use lib_dispatch::runtime::AFPluginRuntime;
@ -118,18 +118,23 @@ impl AppFlowyCore {
config.device_id.clone(),
));
let user_manager = init_user_manager(
&config,
&store_preference,
server_provider.clone(),
Arc::downgrade(&collab_builder),
let user_config = UserConfig::new(
&config.name,
&config.storage_path,
&config.application_path,
&config.device_id,
);
let authenticate_user = Arc::new(AuthenticateUser::new(
user_config.clone(),
store_preference.clone(),
));
collab_builder
.set_snapshot_persistence(Arc::new(SnapshotDBImpl(Arc::downgrade(&user_manager))));
.set_snapshot_persistence(Arc::new(SnapshotDBImpl(Arc::downgrade(&authenticate_user))));
let database_manager = DatabaseDepsResolver::resolve(
Arc::downgrade(&user_manager),
Arc::downgrade(&authenticate_user),
task_dispatcher.clone(),
collab_builder.clone(),
server_provider.clone(),
@ -137,7 +142,7 @@ impl AppFlowyCore {
.await;
let document_manager = DocumentDepsResolver::resolve(
Arc::downgrade(&user_manager),
Arc::downgrade(&authenticate_user),
&database_manager,
collab_builder.clone(),
server_provider.clone(),
@ -145,7 +150,7 @@ impl AppFlowyCore {
);
let folder_manager = FolderDepsResolver::resolve(
Arc::downgrade(&user_manager),
Arc::downgrade(&authenticate_user),
&document_manager,
&database_manager,
collab_builder.clone(),
@ -153,6 +158,16 @@ impl AppFlowyCore {
)
.await;
let user_manager = UserDepsResolver::resolve(
authenticate_user,
collab_builder.clone(),
server_provider.clone(),
store_preference.clone(),
database_manager.clone(),
folder_manager.clone(),
)
.await;
(
user_manager,
folder_manager,
@ -216,26 +231,6 @@ impl AppFlowyCore {
}
}
fn init_user_manager(
config: &AppFlowyCoreConfig,
storage_preference: &Arc<StorePreferences>,
user_cloud_service_provider: Arc<dyn UserCloudServiceProvider>,
collab_builder: Weak<AppFlowyCollabBuilder>,
) -> Arc<UserManager> {
let user_config = UserConfig::new(
&config.name,
&config.storage_path,
&config.application_path,
&config.device_id,
);
UserManager::new(
user_config,
user_cloud_service_provider,
storage_preference.clone(),
collab_builder,
)
}
impl From<Server> for CollabPluginProviderType {
fn from(server_type: Server) -> Self {
match server_type {