lib.rs 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451
  1. #![allow(unused_doc_comments)]
  2. use std::sync::Weak;
  3. use std::time::Duration;
  4. use std::{
  5. fmt,
  6. sync::{
  7. atomic::{AtomicBool, Ordering},
  8. Arc,
  9. },
  10. };
  11. use appflowy_integrate::collab_builder::{AppFlowyCollabBuilder, CollabStorageType};
  12. use tokio::sync::RwLock;
  13. use flowy_database2::DatabaseManager;
  14. use flowy_document2::manager::DocumentManager;
  15. use flowy_error::FlowyResult;
  16. use flowy_folder2::manager::{FolderInitializeData, FolderManager};
  17. use flowy_sqlite::kv::StorePreferences;
  18. use flowy_task::{TaskDispatcher, TaskRunner};
  19. use flowy_user::event_map::{SignUpContext, UserCloudServiceProvider, UserStatusCallback};
  20. use flowy_user::manager::{UserManager, UserSessionConfig};
  21. use flowy_user_deps::cloud::UserCloudConfig;
  22. use flowy_user_deps::entities::{AuthType, UserProfile, UserWorkspace};
  23. use lib_dispatch::prelude::*;
  24. use lib_dispatch::runtime::tokio_default_runtime;
  25. use lib_infra::future::{to_fut, Fut};
  26. use module::make_plugins;
  27. pub use module::*;
  28. use crate::deps_resolve::*;
  29. use crate::integrate::server::{
  30. current_server_provider, AppFlowyServerProvider, ServerProviderType,
  31. };
  32. mod deps_resolve;
  33. mod integrate;
  34. pub mod module;
  35. static INIT_LOG: AtomicBool = AtomicBool::new(false);
  36. /// This name will be used as to identify the current [AppFlowyCore] instance.
  37. /// Don't change this.
  38. pub const DEFAULT_NAME: &str = "appflowy";
  39. #[derive(Clone)]
  40. pub struct AppFlowyCoreConfig {
  41. /// Different `AppFlowyCoreConfig` instance should have different name
  42. name: String,
  43. /// Panics if the `root` path is not existing
  44. pub storage_path: String,
  45. log_filter: String,
  46. }
  47. impl fmt::Debug for AppFlowyCoreConfig {
  48. fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
  49. f.debug_struct("AppFlowyCoreConfig")
  50. .field("storage_path", &self.storage_path)
  51. .finish()
  52. }
  53. }
  54. impl AppFlowyCoreConfig {
  55. pub fn new(root: &str, name: String) -> Self {
  56. AppFlowyCoreConfig {
  57. name,
  58. storage_path: root.to_owned(),
  59. log_filter: create_log_filter("info".to_owned(), vec![]),
  60. }
  61. }
  62. pub fn log_filter(mut self, level: &str, with_crates: Vec<String>) -> Self {
  63. self.log_filter = create_log_filter(level.to_owned(), with_crates);
  64. self
  65. }
  66. }
  67. fn create_log_filter(level: String, with_crates: Vec<String>) -> String {
  68. let level = std::env::var("RUST_LOG").unwrap_or(level);
  69. let mut filters = with_crates
  70. .into_iter()
  71. .map(|crate_name| format!("{}={}", crate_name, level))
  72. .collect::<Vec<String>>();
  73. filters.push(format!("flowy_core={}", level));
  74. filters.push(format!("flowy_folder2={}", level));
  75. filters.push(format!("collab_sync={}", level));
  76. filters.push(format!("collab_folder={}", level));
  77. filters.push(format!("collab_persistence={}", level));
  78. filters.push(format!("collab_database={}", level));
  79. filters.push(format!("collab_plugins={}", level));
  80. filters.push(format!("appflowy_integrate={}", level));
  81. filters.push(format!("collab={}", level));
  82. filters.push(format!("flowy_user={}", level));
  83. filters.push(format!("flowy_document2={}", level));
  84. filters.push(format!("flowy_database2={}", level));
  85. filters.push(format!("flowy_server={}", level));
  86. filters.push(format!("flowy_notification={}", "info"));
  87. filters.push(format!("lib_infra={}", level));
  88. filters.push(format!("flowy_task={}", level));
  89. filters.push(format!("dart_ffi={}", "info"));
  90. filters.push(format!("flowy_sqlite={}", "info"));
  91. filters.push(format!("flowy_net={}", level));
  92. #[cfg(feature = "profiling")]
  93. filters.push(format!("tokio={}", level));
  94. #[cfg(feature = "profiling")]
  95. filters.push(format!("runtime={}", level));
  96. filters.join(",")
  97. }
  98. #[derive(Clone)]
  99. pub struct AppFlowyCore {
  100. #[allow(dead_code)]
  101. pub config: AppFlowyCoreConfig,
  102. pub user_manager: Arc<UserManager>,
  103. pub document_manager: Arc<DocumentManager>,
  104. pub folder_manager: Arc<FolderManager>,
  105. pub database_manager: Arc<DatabaseManager>,
  106. pub event_dispatcher: Arc<AFPluginDispatcher>,
  107. pub server_provider: Arc<AppFlowyServerProvider>,
  108. pub task_dispatcher: Arc<RwLock<TaskDispatcher>>,
  109. pub store_preference: Arc<StorePreferences>,
  110. }
  111. impl AppFlowyCore {
  112. pub fn new(config: AppFlowyCoreConfig) -> Self {
  113. /// The profiling can be used to tracing the performance of the application.
  114. /// Check out the [Link](https://appflowy.gitbook.io/docs/essential-documentation/contribute-to-appflowy/architecture/backend/profiling)
  115. /// for more information.
  116. #[cfg(feature = "profiling")]
  117. console_subscriber::init();
  118. // Init the logger before anything else
  119. init_log(&config);
  120. // Init the key value database
  121. let store_preference = Arc::new(StorePreferences::new(&config.storage_path).unwrap());
  122. tracing::info!("🔥 {:?}", &config);
  123. let runtime = tokio_default_runtime().unwrap();
  124. let task_scheduler = TaskDispatcher::new(Duration::from_secs(2));
  125. let task_dispatcher = Arc::new(RwLock::new(task_scheduler));
  126. runtime.spawn(TaskRunner::run(task_dispatcher.clone()));
  127. let provider_type = current_server_provider(&store_preference);
  128. let server_provider = Arc::new(AppFlowyServerProvider::new(
  129. config.clone(),
  130. provider_type,
  131. Arc::downgrade(&store_preference),
  132. ));
  133. let (
  134. user_manager,
  135. folder_manager,
  136. server_provider,
  137. database_manager,
  138. document_manager,
  139. collab_builder,
  140. ) = runtime.block_on(async {
  141. /// The shared collab builder is used to build the [Collab] instance. The plugins will be loaded
  142. /// on demand based on the [CollabPluginConfig].
  143. let collab_builder = Arc::new(AppFlowyCollabBuilder::new(server_provider.clone()));
  144. let user_manager = mk_user_session(
  145. &config,
  146. &store_preference,
  147. server_provider.clone(),
  148. Arc::downgrade(&collab_builder),
  149. );
  150. collab_builder
  151. .set_snapshot_persistence(Arc::new(SnapshotDBImpl(Arc::downgrade(&user_manager))));
  152. let database_manager = DatabaseDepsResolver::resolve(
  153. Arc::downgrade(&user_manager),
  154. task_dispatcher.clone(),
  155. collab_builder.clone(),
  156. server_provider.clone(),
  157. )
  158. .await;
  159. let document_manager = DocumentDepsResolver::resolve(
  160. Arc::downgrade(&user_manager),
  161. &database_manager,
  162. collab_builder.clone(),
  163. server_provider.clone(),
  164. );
  165. let folder_manager = FolderDepsResolver::resolve(
  166. Arc::downgrade(&user_manager),
  167. &document_manager,
  168. &database_manager,
  169. collab_builder.clone(),
  170. server_provider.clone(),
  171. )
  172. .await;
  173. (
  174. user_manager,
  175. folder_manager,
  176. server_provider,
  177. database_manager,
  178. document_manager,
  179. collab_builder,
  180. )
  181. });
  182. let user_status_listener = UserStatusCallbackImpl {
  183. collab_builder,
  184. folder_manager: folder_manager.clone(),
  185. database_manager: database_manager.clone(),
  186. document_manager: document_manager.clone(),
  187. server_provider: server_provider.clone(),
  188. config: config.clone(),
  189. };
  190. let cloned_user_session = Arc::downgrade(&user_manager);
  191. runtime.block_on(async move {
  192. if let Some(user_session) = cloned_user_session.upgrade() {
  193. user_session.init(user_status_listener).await;
  194. }
  195. });
  196. let event_dispatcher = Arc::new(AFPluginDispatcher::construct(runtime, || {
  197. make_plugins(
  198. Arc::downgrade(&folder_manager),
  199. Arc::downgrade(&database_manager),
  200. Arc::downgrade(&user_manager),
  201. Arc::downgrade(&document_manager),
  202. )
  203. }));
  204. Self {
  205. config,
  206. user_manager,
  207. document_manager,
  208. folder_manager,
  209. database_manager,
  210. event_dispatcher,
  211. server_provider,
  212. task_dispatcher,
  213. store_preference,
  214. }
  215. }
  216. /// Only expose the dispatcher in test
  217. pub fn dispatcher(&self) -> Arc<AFPluginDispatcher> {
  218. self.event_dispatcher.clone()
  219. }
  220. }
  221. fn init_log(config: &AppFlowyCoreConfig) {
  222. if !INIT_LOG.load(Ordering::SeqCst) {
  223. INIT_LOG.store(true, Ordering::SeqCst);
  224. let _ = lib_log::Builder::new("AppFlowy-Client", &config.storage_path)
  225. .env_filter(&config.log_filter)
  226. .build();
  227. }
  228. }
  229. fn mk_user_session(
  230. config: &AppFlowyCoreConfig,
  231. storage_preference: &Arc<StorePreferences>,
  232. user_cloud_service_provider: Arc<dyn UserCloudServiceProvider>,
  233. collab_builder: Weak<AppFlowyCollabBuilder>,
  234. ) -> Arc<UserManager> {
  235. let user_config = UserSessionConfig::new(&config.name, &config.storage_path);
  236. UserManager::new(
  237. user_config,
  238. user_cloud_service_provider,
  239. storage_preference.clone(),
  240. collab_builder,
  241. )
  242. }
  243. struct UserStatusCallbackImpl {
  244. collab_builder: Arc<AppFlowyCollabBuilder>,
  245. folder_manager: Arc<FolderManager>,
  246. database_manager: Arc<DatabaseManager>,
  247. document_manager: Arc<DocumentManager>,
  248. server_provider: Arc<AppFlowyServerProvider>,
  249. #[allow(dead_code)]
  250. config: AppFlowyCoreConfig,
  251. }
  252. impl UserStatusCallback for UserStatusCallbackImpl {
  253. fn auth_type_did_changed(&self, _auth_type: AuthType) {}
  254. fn did_init(
  255. &self,
  256. user_id: i64,
  257. cloud_config: &Option<UserCloudConfig>,
  258. user_workspace: &UserWorkspace,
  259. _device_id: &str,
  260. ) -> Fut<FlowyResult<()>> {
  261. let user_id = user_id.to_owned();
  262. let user_workspace = user_workspace.clone();
  263. let collab_builder = self.collab_builder.clone();
  264. let folder_manager = self.folder_manager.clone();
  265. let database_manager = self.database_manager.clone();
  266. let document_manager = self.document_manager.clone();
  267. if let Some(cloud_config) = cloud_config {
  268. self
  269. .server_provider
  270. .set_enable_sync(cloud_config.enable_sync);
  271. if cloud_config.enable_encrypt() {
  272. self
  273. .server_provider
  274. .set_encrypt_secret(cloud_config.encrypt_secret.clone());
  275. }
  276. }
  277. to_fut(async move {
  278. collab_builder.initialize(user_workspace.id.clone());
  279. folder_manager
  280. .initialize(user_id, &user_workspace.id, FolderInitializeData::Empty)
  281. .await?;
  282. database_manager
  283. .initialize(
  284. user_id,
  285. user_workspace.id.clone(),
  286. user_workspace.database_storage_id,
  287. )
  288. .await?;
  289. document_manager
  290. .initialize(user_id, user_workspace.id)
  291. .await?;
  292. Ok(())
  293. })
  294. }
  295. fn did_sign_in(
  296. &self,
  297. user_id: i64,
  298. user_workspace: &UserWorkspace,
  299. _device_id: &str,
  300. ) -> Fut<FlowyResult<()>> {
  301. let user_id = user_id.to_owned();
  302. let user_workspace = user_workspace.clone();
  303. let folder_manager = self.folder_manager.clone();
  304. let database_manager = self.database_manager.clone();
  305. let document_manager = self.document_manager.clone();
  306. to_fut(async move {
  307. folder_manager
  308. .initialize_with_workspace_id(user_id, &user_workspace.id)
  309. .await?;
  310. database_manager
  311. .initialize(
  312. user_id,
  313. user_workspace.id.clone(),
  314. user_workspace.database_storage_id,
  315. )
  316. .await?;
  317. document_manager
  318. .initialize(user_id, user_workspace.id)
  319. .await?;
  320. Ok(())
  321. })
  322. }
  323. fn did_sign_up(
  324. &self,
  325. context: SignUpContext,
  326. user_profile: &UserProfile,
  327. user_workspace: &UserWorkspace,
  328. _device_id: &str,
  329. ) -> Fut<FlowyResult<()>> {
  330. let user_profile = user_profile.clone();
  331. let folder_manager = self.folder_manager.clone();
  332. let database_manager = self.database_manager.clone();
  333. let user_workspace = user_workspace.clone();
  334. let document_manager = self.document_manager.clone();
  335. to_fut(async move {
  336. folder_manager
  337. .initialize_with_new_user(
  338. user_profile.uid,
  339. &user_profile.token,
  340. context.is_new,
  341. context.local_folder,
  342. &user_workspace.id,
  343. )
  344. .await?;
  345. database_manager
  346. .initialize_with_new_user(
  347. user_profile.uid,
  348. user_workspace.id.clone(),
  349. user_workspace.database_storage_id,
  350. )
  351. .await?;
  352. document_manager
  353. .initialize_with_new_user(user_profile.uid, user_workspace.id)
  354. .await?;
  355. Ok(())
  356. })
  357. }
  358. fn did_expired(&self, _token: &str, user_id: i64) -> Fut<FlowyResult<()>> {
  359. let folder_manager = self.folder_manager.clone();
  360. to_fut(async move {
  361. folder_manager.clear(user_id).await;
  362. Ok(())
  363. })
  364. }
  365. fn open_workspace(&self, user_id: i64, user_workspace: &UserWorkspace) -> Fut<FlowyResult<()>> {
  366. let user_workspace = user_workspace.clone();
  367. let collab_builder = self.collab_builder.clone();
  368. let folder_manager = self.folder_manager.clone();
  369. let database_manager = self.database_manager.clone();
  370. let document_manager = self.document_manager.clone();
  371. to_fut(async move {
  372. collab_builder.initialize(user_workspace.id.clone());
  373. folder_manager
  374. .initialize_with_workspace_id(user_id, &user_workspace.id)
  375. .await?;
  376. database_manager
  377. .initialize(
  378. user_id,
  379. user_workspace.id.clone(),
  380. user_workspace.database_storage_id,
  381. )
  382. .await?;
  383. document_manager
  384. .initialize(user_id, user_workspace.id)
  385. .await?;
  386. Ok(())
  387. })
  388. }
  389. fn did_update_network(&self, reachable: bool) {
  390. self.collab_builder.update_network(reachable);
  391. }
  392. }
  393. impl From<ServerProviderType> for CollabStorageType {
  394. fn from(server_provider: ServerProviderType) -> Self {
  395. match server_provider {
  396. ServerProviderType::Local => CollabStorageType::Local,
  397. ServerProviderType::AppFlowyCloud => CollabStorageType::Local,
  398. ServerProviderType::Supabase => CollabStorageType::Supabase,
  399. }
  400. }
  401. }