manager.rs 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723
  1. use std::string::ToString;
  2. use std::sync::{Arc, Weak};
  3. use collab_user::core::MutexUserAwareness;
  4. use serde_json::Value;
  5. use tokio::sync::{Mutex, RwLock};
  6. use tokio_stream::StreamExt;
  7. use tracing::{debug, error, event, info, instrument};
  8. use collab_integrate::collab_builder::AppFlowyCollabBuilder;
  9. use collab_integrate::RocksCollabDB;
  10. use flowy_error::{internal_error, ErrorCode, FlowyResult};
  11. use flowy_sqlite::kv::StorePreferences;
  12. use flowy_sqlite::schema::user_table;
  13. use flowy_sqlite::ConnectionPool;
  14. use flowy_sqlite::{query_dsl::*, DBConnection, ExpressionMethods};
  15. use flowy_user_deps::cloud::UserUpdate;
  16. use flowy_user_deps::entities::*;
  17. use lib_infra::box_any::BoxAny;
  18. use crate::entities::{AuthStateChangedPB, AuthStatePB, UserProfilePB, UserSettingPB};
  19. use crate::event_map::{DefaultUserStatusCallback, UserCloudServiceProvider, UserStatusCallback};
  20. use crate::migrations::historical_document::HistoricalEmptyDocumentMigration;
  21. use crate::migrations::migrate_to_new_user::migration_local_user_on_sign_up;
  22. use crate::migrations::migration::UserLocalDataMigration;
  23. use crate::migrations::sync_new_user::sync_user_data_to_cloud;
  24. use crate::migrations::MigrationUser;
  25. use crate::services::cloud_config::get_cloud_config;
  26. use crate::services::collab_interact::{CollabInteract, DefaultCollabInteract};
  27. use crate::services::database::UserDB;
  28. use crate::services::entities::{ResumableSignUp, Session};
  29. use crate::services::user_awareness::UserAwarenessDataSource;
  30. use crate::services::user_sql::{UserTable, UserTableChangeset};
  31. use crate::services::user_workspace::save_user_workspaces;
  32. use crate::{errors::FlowyError, notification::*};
  33. pub struct UserSessionConfig {
  34. root_dir: String,
  35. /// Used as the key of `Session` when saving session information to KV.
  36. session_cache_key: String,
  37. }
  38. impl UserSessionConfig {
  39. /// The `root_dir` represents as the root of the user folders. It must be unique for each
  40. /// users.
  41. pub fn new(name: &str, root_dir: &str) -> Self {
  42. let session_cache_key = format!("{}_session_cache", name);
  43. Self {
  44. root_dir: root_dir.to_owned(),
  45. session_cache_key,
  46. }
  47. }
  48. }
  49. pub struct UserManager {
  50. database: UserDB,
  51. session_config: UserSessionConfig,
  52. pub(crate) cloud_services: Arc<dyn UserCloudServiceProvider>,
  53. pub(crate) store_preferences: Arc<StorePreferences>,
  54. pub(crate) user_awareness: Arc<Mutex<Option<MutexUserAwareness>>>,
  55. pub(crate) user_status_callback: RwLock<Arc<dyn UserStatusCallback>>,
  56. pub(crate) collab_builder: Weak<AppFlowyCollabBuilder>,
  57. pub(crate) collab_interact: RwLock<Arc<dyn CollabInteract>>,
  58. resumable_sign_up: Mutex<Option<ResumableSignUp>>,
  59. current_session: parking_lot::RwLock<Option<Session>>,
  60. }
  61. impl UserManager {
  62. pub fn new(
  63. session_config: UserSessionConfig,
  64. cloud_services: Arc<dyn UserCloudServiceProvider>,
  65. store_preferences: Arc<StorePreferences>,
  66. collab_builder: Weak<AppFlowyCollabBuilder>,
  67. ) -> Arc<Self> {
  68. let database = UserDB::new(&session_config.root_dir);
  69. let user_status_callback: RwLock<Arc<dyn UserStatusCallback>> =
  70. RwLock::new(Arc::new(DefaultUserStatusCallback));
  71. let user_manager = Arc::new(Self {
  72. database,
  73. session_config,
  74. cloud_services,
  75. store_preferences,
  76. user_awareness: Arc::new(Default::default()),
  77. user_status_callback,
  78. collab_builder,
  79. collab_interact: RwLock::new(Arc::new(DefaultCollabInteract)),
  80. resumable_sign_up: Default::default(),
  81. current_session: Default::default(),
  82. });
  83. let weak_user_manager = Arc::downgrade(&user_manager);
  84. if let Ok(user_service) = user_manager.cloud_services.get_user_service() {
  85. if let Some(mut rx) = user_service.subscribe_user_update() {
  86. tokio::spawn(async move {
  87. while let Ok(update) = rx.recv().await {
  88. if let Some(user_manager) = weak_user_manager.upgrade() {
  89. if let Err(err) = user_manager.handler_user_update(update).await {
  90. tracing::error!("handler_user_update failed: {:?}", err);
  91. }
  92. }
  93. }
  94. });
  95. }
  96. }
  97. user_manager
  98. }
  99. pub fn get_store_preferences(&self) -> Weak<StorePreferences> {
  100. Arc::downgrade(&self.store_preferences)
  101. }
  102. /// Initializes the user session, including data migrations and user awareness configuration. This function
  103. /// will be invoked each time the user opens the application.
  104. ///
  105. /// Starts by retrieving the current session. If the session is successfully obtained, it will attempt
  106. /// a local data migration for the user. After ensuring the user's data is migrated and up-to-date,
  107. /// the function will set up the collaboration configuration and initialize the user's awareness. Upon successful
  108. /// completion, a user status callback is invoked to signify that the initialization process is complete.
  109. pub async fn init<C: UserStatusCallback + 'static, I: CollabInteract>(
  110. &self,
  111. user_status_callback: C,
  112. collab_interact: I,
  113. ) -> Result<(), FlowyError> {
  114. if let Ok(session) = self.get_session() {
  115. let user = self.get_user_profile(session.user_id).await?;
  116. if let Err(err) = self.cloud_services.set_token(&user.token) {
  117. error!("Set token failed: {}", err);
  118. }
  119. // Subscribe the token state
  120. let weak_pool = Arc::downgrade(&self.db_pool(user.uid)?);
  121. if let Some(mut token_state_rx) = self.cloud_services.subscribe_token_state() {
  122. tokio::spawn(async move {
  123. while let Some(token_state) = token_state_rx.next().await {
  124. match token_state {
  125. UserTokenState::Refresh { token } => {
  126. if token != user.token {
  127. if let Some(pool) = weak_pool.upgrade() {
  128. // Save the new token
  129. if let Err(err) = save_user_token(user.uid, pool, token) {
  130. error!("Save user token failed: {}", err);
  131. }
  132. }
  133. }
  134. },
  135. UserTokenState::Invalid => {
  136. send_auth_state_notification(AuthStateChangedPB {
  137. state: AuthStatePB::InvalidAuth,
  138. })
  139. .send();
  140. },
  141. }
  142. }
  143. });
  144. }
  145. // Do the user data migration if needed
  146. match (
  147. self.database.get_collab_db(session.user_id),
  148. self.database.get_pool(session.user_id),
  149. ) {
  150. (Ok(collab_db), Ok(sqlite_pool)) => {
  151. match UserLocalDataMigration::new(session.clone(), collab_db, sqlite_pool)
  152. .run(vec![Box::new(HistoricalEmptyDocumentMigration)])
  153. {
  154. Ok(applied_migrations) => {
  155. if !applied_migrations.is_empty() {
  156. info!("Did apply migrations: {:?}", applied_migrations);
  157. }
  158. },
  159. Err(e) => tracing::error!("User data migration failed: {:?}", e),
  160. }
  161. },
  162. _ => tracing::error!("Failed to get collab db or sqlite pool"),
  163. }
  164. self.set_collab_config(&session);
  165. // Init the user awareness
  166. self
  167. .initialize_user_awareness(&session, UserAwarenessDataSource::Local)
  168. .await;
  169. let cloud_config = get_cloud_config(session.user_id, &self.store_preferences);
  170. if let Err(e) = user_status_callback
  171. .did_init(
  172. session.user_id,
  173. &cloud_config,
  174. &session.user_workspace,
  175. &session.device_id,
  176. )
  177. .await
  178. {
  179. tracing::error!("Failed to call did_init callback: {:?}", e);
  180. }
  181. }
  182. *self.user_status_callback.write().await = Arc::new(user_status_callback);
  183. *self.collab_interact.write().await = Arc::new(collab_interact);
  184. Ok(())
  185. }
  186. pub fn db_connection(&self, uid: i64) -> Result<DBConnection, FlowyError> {
  187. self.database.get_connection(uid)
  188. }
  189. pub fn db_pool(&self, uid: i64) -> Result<Arc<ConnectionPool>, FlowyError> {
  190. self.database.get_pool(uid)
  191. }
  192. pub fn get_collab_db(&self, uid: i64) -> Result<Weak<RocksCollabDB>, FlowyError> {
  193. self
  194. .database
  195. .get_collab_db(uid)
  196. .map(|collab_db| Arc::downgrade(&collab_db))
  197. }
  198. /// Performs a user sign-in, initializing user awareness and sending relevant notifications.
  199. ///
  200. /// This asynchronous function interacts with an external user service to authenticate and sign in a user
  201. /// based on provided parameters. Once signed in, it updates the collaboration configuration, logs the user,
  202. /// saves their workspaces, and initializes their user awareness.
  203. ///
  204. /// A sign-in notification is also sent after a successful sign-in.
  205. ///
  206. #[tracing::instrument(level = "debug", skip(self, params))]
  207. pub async fn sign_in(
  208. &self,
  209. params: BoxAny,
  210. auth_type: AuthType,
  211. ) -> Result<UserProfile, FlowyError> {
  212. self.update_auth_type(&auth_type).await;
  213. let response: AuthResponse = self
  214. .cloud_services
  215. .get_user_service()?
  216. .sign_in(params)
  217. .await?;
  218. let session = Session::from(&response);
  219. self.set_collab_config(&session);
  220. let latest_workspace = response.latest_workspace.clone();
  221. let user_profile = UserProfile::from((&response, &auth_type));
  222. self.save_auth_data(&response, &auth_type, &session).await?;
  223. let _ = self
  224. .initialize_user_awareness(&session, UserAwarenessDataSource::Remote)
  225. .await;
  226. if let Err(e) = self
  227. .user_status_callback
  228. .read()
  229. .await
  230. .did_sign_in(user_profile.uid, &latest_workspace, &session.device_id)
  231. .await
  232. {
  233. tracing::error!("Failed to call did_sign_in callback: {:?}", e);
  234. }
  235. send_auth_state_notification(AuthStateChangedPB {
  236. state: AuthStatePB::AuthStateSignIn,
  237. })
  238. .send();
  239. Ok(user_profile)
  240. }
  241. pub(crate) async fn update_auth_type(&self, auth_type: &AuthType) {
  242. self
  243. .user_status_callback
  244. .read()
  245. .await
  246. .auth_type_did_changed(auth_type.clone());
  247. self.cloud_services.set_auth_type(auth_type.clone());
  248. }
  249. /// Manages the user sign-up process, potentially migrating data if necessary.
  250. ///
  251. /// This asynchronous function interacts with an external authentication service to register and sign up a user
  252. /// based on the provided parameters. Following a successful sign-up, it handles configuration updates, logging,
  253. /// and saving workspace information. If a user is signing up with a new profile and previously had guest data,
  254. /// this function may migrate that data over to the new account.
  255. ///
  256. #[tracing::instrument(level = "info", skip(self, params))]
  257. pub async fn sign_up(
  258. &self,
  259. auth_type: AuthType,
  260. params: BoxAny,
  261. ) -> Result<UserProfile, FlowyError> {
  262. self.update_auth_type(&auth_type).await;
  263. let migration_user = self.get_migration_user(&auth_type).await;
  264. let auth_service = self.cloud_services.get_user_service()?;
  265. let response: AuthResponse = auth_service.sign_up(params).await?;
  266. let user_profile = UserProfile::from((&response, &auth_type));
  267. if user_profile.encryption_type.is_need_encrypt_secret() {
  268. self
  269. .resumable_sign_up
  270. .lock()
  271. .await
  272. .replace(ResumableSignUp {
  273. user_profile: user_profile.clone(),
  274. migration_user,
  275. response,
  276. auth_type,
  277. });
  278. } else {
  279. self
  280. .continue_sign_up(&user_profile, migration_user, response, &auth_type)
  281. .await?;
  282. }
  283. Ok(user_profile)
  284. }
  285. #[tracing::instrument(level = "info", skip(self))]
  286. pub async fn resume_sign_up(&self) -> Result<(), FlowyError> {
  287. let ResumableSignUp {
  288. user_profile,
  289. migration_user,
  290. response,
  291. auth_type,
  292. } = self
  293. .resumable_sign_up
  294. .lock()
  295. .await
  296. .clone()
  297. .ok_or(FlowyError::new(
  298. ErrorCode::Internal,
  299. "No resumable sign up data",
  300. ))?;
  301. self
  302. .continue_sign_up(&user_profile, migration_user, response, &auth_type)
  303. .await?;
  304. Ok(())
  305. }
  306. #[tracing::instrument(level = "info", skip_all, err)]
  307. async fn continue_sign_up(
  308. &self,
  309. user_profile: &UserProfile,
  310. migration_user: Option<MigrationUser>,
  311. response: AuthResponse,
  312. auth_type: &AuthType,
  313. ) -> FlowyResult<()> {
  314. let new_session = Session::from(&response);
  315. self.set_collab_config(&new_session);
  316. let user_awareness_source = if response.is_new_user {
  317. UserAwarenessDataSource::Local
  318. } else {
  319. UserAwarenessDataSource::Remote
  320. };
  321. event!(tracing::Level::DEBUG, "Sign up response: {:?}", response);
  322. if response.is_new_user {
  323. if let Some(old_user) = migration_user {
  324. let new_user = MigrationUser {
  325. user_profile: user_profile.clone(),
  326. session: new_session.clone(),
  327. };
  328. event!(
  329. tracing::Level::INFO,
  330. "Migrate old user data from {:?} to {:?}",
  331. old_user.user_profile.uid,
  332. new_user.user_profile.uid
  333. );
  334. self
  335. .migrate_local_user_to_cloud(&old_user, &new_user)
  336. .await?;
  337. let _ = self.database.close(old_user.session.user_id);
  338. }
  339. }
  340. self
  341. .initialize_user_awareness(&new_session, user_awareness_source)
  342. .await;
  343. self
  344. .save_auth_data(&response, auth_type, &new_session)
  345. .await?;
  346. self
  347. .user_status_callback
  348. .read()
  349. .await
  350. .did_sign_up(
  351. response.is_new_user,
  352. user_profile,
  353. &new_session.user_workspace,
  354. &new_session.device_id,
  355. )
  356. .await?;
  357. send_auth_state_notification(AuthStateChangedPB {
  358. state: AuthStatePB::AuthStateSignIn,
  359. })
  360. .send();
  361. Ok(())
  362. }
  363. #[tracing::instrument(level = "info", skip(self))]
  364. pub async fn sign_out(&self) -> Result<(), FlowyError> {
  365. let session = self.get_session()?;
  366. self.database.close(session.user_id)?;
  367. self.set_session(None)?;
  368. let server = self.cloud_services.get_user_service()?;
  369. tokio::spawn(async move {
  370. match server.sign_out(None).await {
  371. Ok(_) => {},
  372. Err(e) => tracing::error!("Sign out failed: {:?}", e),
  373. }
  374. });
  375. Ok(())
  376. }
  377. /// Updates the user's profile with the given parameters.
  378. ///
  379. /// This function modifies the user's profile based on the provided update parameters. After updating, it
  380. /// sends a notification about the change. It's also responsible for handling interactions with the underlying
  381. /// database and updates user profile.
  382. ///
  383. #[tracing::instrument(level = "debug", skip(self))]
  384. pub async fn update_user_profile(
  385. &self,
  386. params: UpdateUserProfileParams,
  387. ) -> Result<(), FlowyError> {
  388. let changeset = UserTableChangeset::new(params.clone());
  389. let session = self.get_session()?;
  390. save_user_profile_change(session.user_id, self.db_pool(session.user_id)?, changeset)?;
  391. self.update_user(session.user_id, None, params).await?;
  392. Ok(())
  393. }
  394. pub async fn init_user(&self) -> Result<(), FlowyError> {
  395. Ok(())
  396. }
  397. pub async fn check_user(&self) -> Result<(), FlowyError> {
  398. let user_id = self.get_session()?.user_id;
  399. let user = self.get_user_profile(user_id).await?;
  400. let credential = UserCredentials::new(Some(user.token), Some(user_id), None);
  401. let auth_service = self.cloud_services.get_user_service()?;
  402. auth_service.check_user(credential).await?;
  403. Ok(())
  404. }
  405. /// Fetches the user profile for the given user ID.
  406. pub async fn get_user_profile(&self, uid: i64) -> Result<UserProfile, FlowyError> {
  407. let user: UserProfile = user_table::dsl::user_table
  408. .filter(user_table::id.eq(&uid.to_string()))
  409. .first::<UserTable>(&*(self.db_connection(uid)?))?
  410. .into();
  411. Ok(user)
  412. }
  413. #[tracing::instrument(level = "info", skip_all)]
  414. pub async fn refresh_user_profile(
  415. &self,
  416. old_user_profile: &UserProfile,
  417. ) -> FlowyResult<UserProfile> {
  418. let uid = old_user_profile.uid;
  419. let new_user_profile: UserProfile = self
  420. .cloud_services
  421. .get_user_service()?
  422. .get_user_profile(UserCredentials::from_uid(uid))
  423. .await?
  424. .ok_or_else(|| FlowyError::new(ErrorCode::RecordNotFound, "User not found"))?;
  425. if !is_user_encryption_sign_valid(old_user_profile, &new_user_profile.encryption_type.sign()) {
  426. return Err(FlowyError::new(
  427. ErrorCode::InvalidEncryptSecret,
  428. "Invalid encryption sign",
  429. ));
  430. }
  431. let changeset = UserTableChangeset::from_user_profile(new_user_profile.clone());
  432. let _ = save_user_profile_change(uid, self.database.get_pool(uid)?, changeset);
  433. Ok(new_user_profile)
  434. }
  435. pub fn user_dir(&self, uid: i64) -> String {
  436. format!("{}/{}", self.session_config.root_dir, uid)
  437. }
  438. pub fn user_setting(&self) -> Result<UserSettingPB, FlowyError> {
  439. let session = self.get_session()?;
  440. let user_setting = UserSettingPB {
  441. user_folder: self.user_dir(session.user_id),
  442. };
  443. Ok(user_setting)
  444. }
  445. pub fn user_id(&self) -> Result<i64, FlowyError> {
  446. Ok(self.get_session()?.user_id)
  447. }
  448. pub fn workspace_id(&self) -> Result<String, FlowyError> {
  449. Ok(self.get_session()?.user_workspace.id)
  450. }
  451. pub fn token(&self) -> Result<Option<String>, FlowyError> {
  452. Ok(None)
  453. }
  454. async fn update_user(
  455. &self,
  456. uid: i64,
  457. token: Option<String>,
  458. params: UpdateUserProfileParams,
  459. ) -> Result<(), FlowyError> {
  460. let server = self.cloud_services.get_user_service()?;
  461. let token = token.to_owned();
  462. tokio::spawn(async move {
  463. let credentials = UserCredentials::new(token, Some(uid), None);
  464. server.update_user(credentials, params).await
  465. })
  466. .await
  467. .map_err(internal_error)??;
  468. Ok(())
  469. }
  470. async fn save_user(&self, uid: i64, user: UserTable) -> Result<(), FlowyError> {
  471. let conn = self.db_connection(uid)?;
  472. conn.immediate_transaction(|| {
  473. // delete old user if exists
  474. diesel::delete(user_table::dsl::user_table.filter(user_table::dsl::id.eq(&user.id)))
  475. .execute(&*conn)?;
  476. let _ = diesel::insert_into(user_table::table)
  477. .values(user)
  478. .execute(&*conn)?;
  479. Ok::<(), FlowyError>(())
  480. })?;
  481. Ok(())
  482. }
  483. pub async fn receive_realtime_event(&self, json: Value) {
  484. if let Ok(user_service) = self.cloud_services.get_user_service() {
  485. user_service.receive_realtime_event(json)
  486. }
  487. }
  488. /// Returns the current user session.
  489. pub fn get_session(&self) -> Result<Session, FlowyError> {
  490. if let Some(session) = (self.current_session.read()).clone() {
  491. return Ok(session);
  492. }
  493. match self
  494. .store_preferences
  495. .get_object::<Session>(&self.session_config.session_cache_key)
  496. {
  497. None => Err(FlowyError::new(
  498. ErrorCode::RecordNotFound,
  499. "User is not logged in",
  500. )),
  501. Some(session) => {
  502. self.current_session.write().replace(session.clone());
  503. Ok(session)
  504. },
  505. }
  506. }
  507. pub(crate) fn set_session(&self, session: Option<Session>) -> Result<(), FlowyError> {
  508. debug!("Set current user: {:?}", session);
  509. match &session {
  510. None => {
  511. self.current_session.write().take();
  512. self
  513. .store_preferences
  514. .remove(&self.session_config.session_cache_key)
  515. },
  516. Some(session) => {
  517. self.current_session.write().replace(session.clone());
  518. self
  519. .store_preferences
  520. .set_object(&self.session_config.session_cache_key, session.clone())
  521. .map_err(internal_error)?;
  522. },
  523. }
  524. Ok(())
  525. }
  526. pub(crate) async fn generate_sign_in_url_with_email(
  527. &self,
  528. auth_type: &AuthType,
  529. email: &str,
  530. ) -> Result<String, FlowyError> {
  531. self.update_auth_type(auth_type).await;
  532. let auth_service = self.cloud_services.get_user_service()?;
  533. let url = auth_service
  534. .generate_sign_in_url_with_email(email)
  535. .await
  536. .map_err(|err| FlowyError::server_error().with_context(err))?;
  537. Ok(url)
  538. }
  539. pub(crate) async fn generate_oauth_url(
  540. &self,
  541. oauth_provider: &str,
  542. ) -> Result<String, FlowyError> {
  543. self.update_auth_type(&AuthType::AFCloud).await;
  544. let auth_service = self.cloud_services.get_user_service()?;
  545. let url = auth_service
  546. .generate_oauth_url_with_provider(oauth_provider)
  547. .await?;
  548. Ok(url)
  549. }
  550. async fn save_auth_data(
  551. &self,
  552. response: &impl UserAuthResponse,
  553. auth_type: &AuthType,
  554. session: &Session,
  555. ) -> Result<(), FlowyError> {
  556. let user_profile = UserProfile::from((response, auth_type));
  557. let uid = user_profile.uid;
  558. self.add_historical_user(
  559. uid,
  560. response.device_id(),
  561. response.user_name().to_string(),
  562. auth_type,
  563. self.user_dir(uid),
  564. );
  565. save_user_workspaces(uid, self.db_pool(uid)?, response.user_workspaces())?;
  566. self
  567. .save_user(uid, (user_profile, auth_type.clone()).into())
  568. .await?;
  569. self.set_session(Some(session.clone()))?;
  570. Ok(())
  571. }
  572. fn set_collab_config(&self, session: &Session) {
  573. let collab_builder = self.collab_builder.upgrade().unwrap();
  574. collab_builder.set_sync_device(session.device_id.clone());
  575. collab_builder.initialize(session.user_workspace.id.clone());
  576. self.cloud_services.set_device_id(&session.device_id);
  577. }
  578. async fn handler_user_update(&self, user_update: UserUpdate) -> FlowyResult<()> {
  579. let session = self.get_session()?;
  580. if session.user_id == user_update.uid {
  581. debug!("Receive user update: {:?}", user_update);
  582. let user_profile = self.get_user_profile(user_update.uid).await?;
  583. if !is_user_encryption_sign_valid(&user_profile, &user_update.encryption_sign) {
  584. return Ok(());
  585. }
  586. // Save the user profile change
  587. save_user_profile_change(
  588. user_update.uid,
  589. self.db_pool(user_update.uid)?,
  590. UserTableChangeset::from(user_update),
  591. )?;
  592. }
  593. Ok(())
  594. }
  595. async fn migrate_local_user_to_cloud(
  596. &self,
  597. old_user: &MigrationUser,
  598. new_user: &MigrationUser,
  599. ) -> Result<(), FlowyError> {
  600. let old_collab_db = self.database.get_collab_db(old_user.session.user_id)?;
  601. let new_collab_db = self.database.get_collab_db(new_user.session.user_id)?;
  602. migration_local_user_on_sign_up(old_user, &old_collab_db, new_user, &new_collab_db)?;
  603. if let Err(err) = sync_user_data_to_cloud(
  604. self.cloud_services.get_user_service()?,
  605. "",
  606. new_user,
  607. &new_collab_db,
  608. )
  609. .await
  610. {
  611. tracing::error!("Sync user data to cloud failed: {:?}", err);
  612. }
  613. // Save the old user workspace setting.
  614. save_user_workspaces(
  615. old_user.session.user_id,
  616. self.database.get_pool(old_user.session.user_id)?,
  617. &[old_user.session.user_workspace.clone()],
  618. )?;
  619. Ok(())
  620. }
  621. }
  622. fn is_user_encryption_sign_valid(user_profile: &UserProfile, encryption_sign: &str) -> bool {
  623. // If the local user profile's encryption sign is not equal to the user update's encryption sign,
  624. // which means the user enable encryption in another device, we should logout the current user.
  625. let is_valid = user_profile.encryption_type.sign() == encryption_sign;
  626. if !is_valid {
  627. send_auth_state_notification(AuthStateChangedPB {
  628. state: AuthStatePB::InvalidAuth,
  629. })
  630. .send();
  631. }
  632. is_valid
  633. }
  634. fn save_user_profile_change(
  635. uid: i64,
  636. pool: Arc<ConnectionPool>,
  637. changeset: UserTableChangeset,
  638. ) -> FlowyResult<()> {
  639. let conn = pool.get()?;
  640. diesel_update_table!(user_table, changeset, &*conn);
  641. let user: UserProfile = user_table::dsl::user_table
  642. .filter(user_table::id.eq(&uid.to_string()))
  643. .first::<UserTable>(&*conn)?
  644. .into();
  645. send_notification(&uid.to_string(), UserNotification::DidUpdateUserProfile)
  646. .payload(UserProfilePB::from(user))
  647. .send();
  648. Ok(())
  649. }
  650. #[instrument(level = "info", skip_all, err)]
  651. fn save_user_token(uid: i64, pool: Arc<ConnectionPool>, token: String) -> FlowyResult<()> {
  652. let params = UpdateUserProfileParams::new(uid).with_token(token);
  653. let changeset = UserTableChangeset::new(params);
  654. save_user_profile_change(uid, pool, changeset)
  655. }