2023-07-29 01:46:24 +00:00
|
|
|
use std::sync::{Arc, Weak};
|
2023-04-04 00:41:16 +00:00
|
|
|
|
2023-05-15 14:16:05 +00:00
|
|
|
use appflowy_integrate::RocksCollabDB;
|
2023-07-14 05:37:13 +00:00
|
|
|
use collab_folder::core::FolderData;
|
2023-05-15 14:16:05 +00:00
|
|
|
use serde::{Deserialize, Serialize};
|
|
|
|
use tokio::sync::RwLock;
|
2023-07-14 05:37:13 +00:00
|
|
|
use uuid::Uuid;
|
2023-05-15 14:16:05 +00:00
|
|
|
|
2023-07-29 01:46:24 +00:00
|
|
|
use flowy_error::{internal_error, ErrorCode, FlowyResult};
|
2023-07-14 05:37:13 +00:00
|
|
|
use flowy_server_config::supabase_config::SupabaseConfiguration;
|
2023-08-06 03:51:03 +00:00
|
|
|
use flowy_sqlite::kv::StorePreferences;
|
2023-07-29 01:46:24 +00:00
|
|
|
use flowy_sqlite::schema::{user_table, user_workspace_table};
|
2023-01-31 00:28:31 +00:00
|
|
|
use flowy_sqlite::ConnectionPool;
|
2023-08-06 03:51:03 +00:00
|
|
|
use flowy_sqlite::{query_dsl::*, DBConnection, ExpressionMethods};
|
2023-07-29 01:46:24 +00:00
|
|
|
use flowy_user_deps::entities::*;
|
2023-05-21 10:53:59 +00:00
|
|
|
use lib_infra::box_any::BoxAny;
|
2023-07-29 01:46:24 +00:00
|
|
|
use lib_infra::util::timestamp;
|
2023-05-17 01:49:39 +00:00
|
|
|
|
2023-07-29 01:46:24 +00:00
|
|
|
use crate::entities::{AuthTypePB, RepeatedUserWorkspacePB};
|
2023-04-28 06:08:53 +00:00
|
|
|
use crate::entities::{UserProfilePB, UserSettingPB};
|
2023-07-05 12:57:09 +00:00
|
|
|
use crate::event_map::{
|
2023-07-29 01:46:24 +00:00
|
|
|
DefaultUserStatusCallback, SignUpContext, UserCloudServiceProvider, UserStatusCallback,
|
2023-04-28 06:08:53 +00:00
|
|
|
};
|
2023-08-03 01:14:52 +00:00
|
|
|
use crate::migrations::historical_document::HistoricalEmptyDocumentMigration;
|
|
|
|
use crate::migrations::local_user_to_cloud::migration_user_to_cloud;
|
|
|
|
use crate::migrations::migration::UserLocalDataMigration;
|
|
|
|
use crate::migrations::UserMigrationContext;
|
2023-07-29 01:46:24 +00:00
|
|
|
use crate::services::database::UserDB;
|
|
|
|
use crate::services::session_serde::Session;
|
|
|
|
use crate::services::user_sql::{UserTable, UserTableChangeset};
|
|
|
|
use crate::services::user_workspace_sql::UserWorkspaceTable;
|
|
|
|
use crate::{errors::FlowyError, notification::*};
|
|
|
|
|
|
|
|
const HISTORICAL_USER: &str = "af_historical_users";
|
|
|
|
const SUPABASE_CONFIG_CACHE_KEY: &str = "af_supabase_config";
|
2023-04-28 06:08:53 +00:00
|
|
|
|
2021-07-10 08:27:20 +00:00
|
|
|
pub struct UserSessionConfig {
|
2023-02-13 01:29:49 +00:00
|
|
|
root_dir: String,
|
2022-12-20 03:14:42 +00:00
|
|
|
|
2023-02-13 01:29:49 +00:00
|
|
|
/// Used as the key of `Session` when saving session information to KV.
|
|
|
|
session_cache_key: String,
|
2021-07-10 08:27:20 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
impl UserSessionConfig {
|
2023-02-13 01:29:49 +00:00
|
|
|
/// The `root_dir` represents as the root of the user folders. It must be unique for each
|
|
|
|
/// users.
|
|
|
|
pub fn new(name: &str, root_dir: &str) -> Self {
|
|
|
|
let session_cache_key = format!("{}_session_cache", name);
|
|
|
|
Self {
|
|
|
|
root_dir: root_dir.to_owned(),
|
|
|
|
session_cache_key,
|
2021-07-10 08:27:20 +00:00
|
|
|
}
|
2023-02-13 01:29:49 +00:00
|
|
|
}
|
2021-07-10 08:27:20 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
pub struct UserSession {
|
2023-02-13 01:29:49 +00:00
|
|
|
database: UserDB,
|
2023-05-15 14:16:05 +00:00
|
|
|
session_config: UserSessionConfig,
|
2023-05-21 10:53:59 +00:00
|
|
|
cloud_services: Arc<dyn UserCloudServiceProvider>,
|
2023-08-06 03:51:03 +00:00
|
|
|
store_preferences: Arc<StorePreferences>,
|
2023-07-29 01:46:24 +00:00
|
|
|
pub(crate) user_status_callback: RwLock<Arc<dyn UserStatusCallback>>,
|
2021-07-10 08:27:20 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
impl UserSession {
|
2023-05-21 10:53:59 +00:00
|
|
|
pub fn new(
|
|
|
|
session_config: UserSessionConfig,
|
|
|
|
cloud_services: Arc<dyn UserCloudServiceProvider>,
|
2023-08-06 03:51:03 +00:00
|
|
|
store_preferences: Arc<StorePreferences>,
|
2023-05-21 10:53:59 +00:00
|
|
|
) -> Self {
|
2023-07-29 01:46:24 +00:00
|
|
|
let database = UserDB::new(&session_config.root_dir);
|
2023-05-31 09:42:14 +00:00
|
|
|
let user_status_callback: RwLock<Arc<dyn UserStatusCallback>> =
|
|
|
|
RwLock::new(Arc::new(DefaultUserStatusCallback));
|
2023-02-13 01:29:49 +00:00
|
|
|
Self {
|
2023-07-29 01:46:24 +00:00
|
|
|
database,
|
2023-05-15 14:16:05 +00:00
|
|
|
session_config,
|
2023-05-21 10:53:59 +00:00
|
|
|
cloud_services,
|
2023-08-06 03:51:03 +00:00
|
|
|
store_preferences,
|
2023-02-13 01:29:49 +00:00
|
|
|
user_status_callback,
|
2021-08-31 15:01:46 +00:00
|
|
|
}
|
2023-02-13 01:29:49 +00:00
|
|
|
}
|
2021-08-31 15:01:46 +00:00
|
|
|
|
2023-08-06 03:51:03 +00:00
|
|
|
pub fn get_store_preferences(&self) -> Weak<StorePreferences> {
|
|
|
|
Arc::downgrade(&self.store_preferences)
|
|
|
|
}
|
|
|
|
|
2023-02-13 01:29:49 +00:00
|
|
|
pub async fn init<C: UserStatusCallback + 'static>(&self, user_status_callback: C) {
|
|
|
|
if let Ok(session) = self.get_session() {
|
2023-08-03 01:14:52 +00:00
|
|
|
match (
|
|
|
|
self.database.get_collab_db(session.user_id),
|
|
|
|
self.database.get_pool(session.user_id),
|
|
|
|
) {
|
|
|
|
(Ok(collab_db), Ok(sqlite_pool)) => {
|
|
|
|
match UserLocalDataMigration::new(session.clone(), collab_db, sqlite_pool)
|
|
|
|
.run(vec![Box::new(HistoricalEmptyDocumentMigration)])
|
|
|
|
{
|
|
|
|
Ok(applied_migrations) => {
|
2023-08-04 11:27:14 +00:00
|
|
|
if !applied_migrations.is_empty() {
|
2023-08-03 01:14:52 +00:00
|
|
|
tracing::info!("Did apply migrations: {:?}", applied_migrations);
|
|
|
|
}
|
|
|
|
},
|
|
|
|
Err(e) => tracing::error!("User data migration failed: {:?}", e),
|
|
|
|
}
|
|
|
|
},
|
|
|
|
_ => tracing::error!("Failed to get collab db or sqlite pool"),
|
|
|
|
}
|
|
|
|
|
2023-07-14 05:37:13 +00:00
|
|
|
if let Err(e) = user_status_callback
|
2023-07-29 01:46:24 +00:00
|
|
|
.did_init(session.user_id, &session.user_workspace)
|
2023-07-14 05:37:13 +00:00
|
|
|
.await
|
|
|
|
{
|
|
|
|
tracing::error!("Failed to call did_sign_in callback: {:?}", e);
|
|
|
|
}
|
2021-07-11 07:33:19 +00:00
|
|
|
}
|
2023-05-31 09:42:14 +00:00
|
|
|
*self.user_status_callback.write().await = Arc::new(user_status_callback);
|
2023-02-13 01:29:49 +00:00
|
|
|
}
|
|
|
|
|
2023-07-14 05:37:13 +00:00
|
|
|
pub fn db_connection(&self, uid: i64) -> Result<DBConnection, FlowyError> {
|
|
|
|
self.database.get_connection(uid)
|
2023-02-13 01:29:49 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// The caller will be not 'Sync' before of the return value,
|
|
|
|
// PooledConnection<ConnectionManager> is not sync. You can use
|
|
|
|
// db_connection_pool function to require the ConnectionPool that is 'Sync'.
|
|
|
|
//
|
|
|
|
// let pool = self.db_connection_pool()?;
|
|
|
|
// let conn: PooledConnection<ConnectionManager> = pool.get()?;
|
2023-07-14 05:37:13 +00:00
|
|
|
pub fn db_pool(&self, uid: i64) -> Result<Arc<ConnectionPool>, FlowyError> {
|
|
|
|
self.database.get_pool(uid)
|
2023-04-04 00:41:16 +00:00
|
|
|
}
|
|
|
|
|
2023-07-29 01:46:24 +00:00
|
|
|
pub fn get_collab_db(&self, uid: i64) -> Result<Weak<RocksCollabDB>, FlowyError> {
|
|
|
|
self
|
|
|
|
.database
|
|
|
|
.get_collab_db(uid)
|
|
|
|
.map(|collab_db| Arc::downgrade(&collab_db))
|
2023-07-14 05:37:13 +00:00
|
|
|
}
|
|
|
|
|
2023-08-03 01:14:52 +00:00
|
|
|
async fn migrate_local_user_to_cloud(
|
2023-07-14 05:37:13 +00:00
|
|
|
&self,
|
2023-07-29 01:46:24 +00:00
|
|
|
old_user: &UserMigrationContext,
|
|
|
|
new_user: &UserMigrationContext,
|
2023-07-14 05:37:13 +00:00
|
|
|
) -> Result<Option<FolderData>, FlowyError> {
|
2023-07-29 01:46:24 +00:00
|
|
|
let old_collab_db = self.database.get_collab_db(old_user.session.user_id)?;
|
|
|
|
let new_collab_db = self.database.get_collab_db(new_user.session.user_id)?;
|
2023-08-03 01:14:52 +00:00
|
|
|
let folder_data = migration_user_to_cloud(old_user, &old_collab_db, new_user, &new_collab_db)?;
|
2023-07-14 05:37:13 +00:00
|
|
|
Ok(folder_data)
|
|
|
|
}
|
|
|
|
|
2023-05-21 10:53:59 +00:00
|
|
|
#[tracing::instrument(level = "debug", skip(self, params))]
|
|
|
|
pub async fn sign_in(
|
|
|
|
&self,
|
|
|
|
params: BoxAny,
|
2023-07-14 05:37:13 +00:00
|
|
|
auth_type: AuthType,
|
2023-05-21 10:53:59 +00:00
|
|
|
) -> Result<UserProfile, FlowyError> {
|
2023-07-29 01:46:24 +00:00
|
|
|
let resp: SignInResponse = self
|
2023-05-21 10:53:59 +00:00
|
|
|
.cloud_services
|
2023-07-29 01:46:24 +00:00
|
|
|
.get_user_service()?
|
2023-05-21 10:53:59 +00:00
|
|
|
.sign_in(params)
|
|
|
|
.await?;
|
|
|
|
|
|
|
|
let session: Session = resp.clone().into();
|
2023-07-14 05:37:13 +00:00
|
|
|
let uid = session.user_id;
|
2023-05-21 10:53:59 +00:00
|
|
|
self.set_session(Some(session))?;
|
2023-07-29 01:46:24 +00:00
|
|
|
self.log_user(uid, self.user_dir(uid));
|
|
|
|
|
|
|
|
let user_workspace = resp.latest_workspace.clone();
|
|
|
|
save_user_workspaces(
|
|
|
|
self.db_pool(uid)?,
|
|
|
|
resp
|
|
|
|
.user_workspaces
|
|
|
|
.iter()
|
|
|
|
.map(|user_workspace| UserWorkspaceTable::from((uid, user_workspace)))
|
|
|
|
.collect(),
|
|
|
|
)?;
|
2023-07-14 05:37:13 +00:00
|
|
|
let user_profile: UserProfile = self.save_user(uid, (resp, auth_type).into()).await?.into();
|
|
|
|
if let Err(e) = self
|
2023-05-21 10:53:59 +00:00
|
|
|
.user_status_callback
|
|
|
|
.read()
|
|
|
|
.await
|
2023-07-29 01:46:24 +00:00
|
|
|
.did_sign_in(user_profile.id, &user_workspace)
|
2023-07-14 05:37:13 +00:00
|
|
|
.await
|
|
|
|
{
|
|
|
|
tracing::error!("Failed to call did_sign_in callback: {:?}", e);
|
|
|
|
}
|
2023-05-21 10:53:59 +00:00
|
|
|
send_sign_in_notification()
|
|
|
|
.payload::<UserProfilePB>(user_profile.clone().into())
|
|
|
|
.send();
|
|
|
|
|
|
|
|
Ok(user_profile)
|
2023-02-13 01:29:49 +00:00
|
|
|
}
|
|
|
|
|
2023-07-14 05:37:13 +00:00
|
|
|
pub async fn update_auth_type(&self, auth_type: &AuthType) {
|
2023-05-31 09:42:14 +00:00
|
|
|
self
|
|
|
|
.user_status_callback
|
|
|
|
.read()
|
|
|
|
.await
|
|
|
|
.auth_type_did_changed(auth_type.clone());
|
|
|
|
|
2023-05-23 15:55:21 +00:00
|
|
|
self.cloud_services.set_auth_type(auth_type.clone());
|
2023-07-14 05:37:13 +00:00
|
|
|
}
|
2023-05-21 10:53:59 +00:00
|
|
|
|
2023-07-14 05:37:13 +00:00
|
|
|
#[tracing::instrument(level = "debug", skip(self, params))]
|
|
|
|
pub async fn sign_up(
|
|
|
|
&self,
|
|
|
|
auth_type: AuthType,
|
|
|
|
params: BoxAny,
|
|
|
|
) -> Result<UserProfile, FlowyError> {
|
2023-07-29 01:46:24 +00:00
|
|
|
let old_user = {
|
2023-07-14 05:37:13 +00:00
|
|
|
if let Ok(old_session) = self.get_session() {
|
2023-07-29 01:46:24 +00:00
|
|
|
self
|
|
|
|
.get_user_profile(old_session.user_id, false)
|
|
|
|
.await
|
|
|
|
.ok()
|
|
|
|
.map(|user_profile| UserMigrationContext {
|
|
|
|
user_profile,
|
|
|
|
session: old_session,
|
|
|
|
})
|
2023-07-14 05:37:13 +00:00
|
|
|
} else {
|
|
|
|
None
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2023-07-29 01:46:24 +00:00
|
|
|
let auth_service = self.cloud_services.get_user_service()?;
|
2023-07-14 05:37:13 +00:00
|
|
|
let response: SignUpResponse = auth_service.sign_up(params).await?;
|
|
|
|
let mut sign_up_context = SignUpContext {
|
|
|
|
is_new: response.is_new,
|
|
|
|
local_folder: None,
|
|
|
|
};
|
2023-07-29 01:46:24 +00:00
|
|
|
let new_session = Session {
|
2023-07-14 05:37:13 +00:00
|
|
|
user_id: response.user_id,
|
2023-07-29 01:46:24 +00:00
|
|
|
user_workspace: response.latest_workspace.clone(),
|
2023-07-14 05:37:13 +00:00
|
|
|
};
|
2023-07-29 01:46:24 +00:00
|
|
|
let uid = new_session.user_id;
|
|
|
|
self.set_session(Some(new_session.clone()))?;
|
|
|
|
self.log_user(uid, self.user_dir(uid));
|
|
|
|
save_user_workspaces(
|
|
|
|
self.db_pool(uid)?,
|
|
|
|
response
|
|
|
|
.user_workspaces
|
|
|
|
.iter()
|
|
|
|
.map(|user_workspace| UserWorkspaceTable::from((uid, user_workspace)))
|
|
|
|
.collect(),
|
|
|
|
)?;
|
2023-07-14 05:37:13 +00:00
|
|
|
let user_table = self
|
|
|
|
.save_user(uid, (response, auth_type.clone()).into())
|
|
|
|
.await?;
|
|
|
|
let new_user_profile: UserProfile = user_table.into();
|
|
|
|
|
2023-07-29 01:46:24 +00:00
|
|
|
// Only migrate the data if the user is login in as a guest and sign up as a new user if the current
|
|
|
|
// auth type is not [AuthType::Local].
|
2023-07-14 05:37:13 +00:00
|
|
|
if sign_up_context.is_new {
|
2023-07-29 01:46:24 +00:00
|
|
|
if let Some(old_user) = old_user {
|
|
|
|
if old_user.user_profile.auth_type == AuthType::Local && !auth_type.is_local() {
|
|
|
|
let new_user = UserMigrationContext {
|
|
|
|
user_profile: new_user_profile.clone(),
|
|
|
|
session: new_session.clone(),
|
|
|
|
};
|
|
|
|
|
2023-07-14 05:37:13 +00:00
|
|
|
tracing::info!(
|
|
|
|
"Migrate old user data from {:?} to {:?}",
|
2023-07-29 01:46:24 +00:00
|
|
|
old_user.user_profile.id,
|
|
|
|
new_user.user_profile.id
|
2023-07-14 05:37:13 +00:00
|
|
|
);
|
2023-08-03 01:14:52 +00:00
|
|
|
match self.migrate_local_user_to_cloud(&old_user, &new_user).await {
|
2023-07-14 05:37:13 +00:00
|
|
|
Ok(folder_data) => sign_up_context.local_folder = folder_data,
|
|
|
|
Err(e) => tracing::error!("{:?}", e),
|
|
|
|
}
|
2023-07-29 01:46:24 +00:00
|
|
|
|
|
|
|
// close the old user db
|
|
|
|
let _ = self.database.close(old_user.session.user_id);
|
2023-07-14 05:37:13 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-05-21 10:53:59 +00:00
|
|
|
let _ = self
|
|
|
|
.user_status_callback
|
|
|
|
.read()
|
|
|
|
.await
|
2023-07-29 01:46:24 +00:00
|
|
|
.did_sign_up(
|
|
|
|
sign_up_context,
|
|
|
|
&new_user_profile,
|
|
|
|
&new_session.user_workspace,
|
|
|
|
)
|
2023-05-21 10:53:59 +00:00
|
|
|
.await;
|
2023-07-14 05:37:13 +00:00
|
|
|
Ok(new_user_profile)
|
2023-02-13 01:29:49 +00:00
|
|
|
}
|
|
|
|
|
2023-07-29 01:46:24 +00:00
|
|
|
#[tracing::instrument(level = "info", skip(self))]
|
2023-07-14 05:37:13 +00:00
|
|
|
pub async fn sign_out(&self) -> Result<(), FlowyError> {
|
2023-02-13 01:29:49 +00:00
|
|
|
let session = self.get_session()?;
|
2023-07-14 05:37:13 +00:00
|
|
|
self.database.close(session.user_id)?;
|
2023-02-13 01:29:49 +00:00
|
|
|
self.set_session(None)?;
|
2023-05-23 15:55:21 +00:00
|
|
|
|
2023-07-29 01:46:24 +00:00
|
|
|
let server = self.cloud_services.get_user_service()?;
|
2023-06-09 14:23:07 +00:00
|
|
|
tokio::spawn(async move {
|
2023-07-14 05:37:13 +00:00
|
|
|
match server.sign_out(None).await {
|
2023-05-21 10:53:59 +00:00
|
|
|
Ok(_) => {},
|
|
|
|
Err(e) => tracing::error!("Sign out failed: {:?}", e),
|
|
|
|
}
|
|
|
|
});
|
2023-02-13 01:29:49 +00:00
|
|
|
Ok(())
|
|
|
|
}
|
2021-09-01 08:08:32 +00:00
|
|
|
|
2023-02-13 01:29:49 +00:00
|
|
|
#[tracing::instrument(level = "debug", skip(self))]
|
|
|
|
pub async fn update_user_profile(
|
|
|
|
&self,
|
|
|
|
params: UpdateUserProfileParams,
|
|
|
|
) -> Result<(), FlowyError> {
|
2023-05-21 10:53:59 +00:00
|
|
|
let auth_type = params.auth_type.clone();
|
2023-02-13 01:29:49 +00:00
|
|
|
let session = self.get_session()?;
|
|
|
|
let changeset = UserTableChangeset::new(params.clone());
|
2023-07-14 05:37:13 +00:00
|
|
|
diesel_update_table!(
|
|
|
|
user_table,
|
|
|
|
changeset,
|
|
|
|
&*self.db_connection(session.user_id)?
|
|
|
|
);
|
2023-02-13 01:29:49 +00:00
|
|
|
|
2023-07-14 05:37:13 +00:00
|
|
|
let session = self.get_session()?;
|
|
|
|
let user_profile = self.get_user_profile(session.user_id, false).await?;
|
2023-02-13 01:29:49 +00:00
|
|
|
let profile_pb: UserProfilePB = user_profile.into();
|
2023-05-21 10:53:59 +00:00
|
|
|
send_notification(
|
|
|
|
&session.user_id.to_string(),
|
|
|
|
UserNotification::DidUpdateUserProfile,
|
|
|
|
)
|
|
|
|
.payload(profile_pb)
|
|
|
|
.send();
|
|
|
|
self
|
2023-07-14 05:37:13 +00:00
|
|
|
.update_user(&auth_type, session.user_id, None, params)
|
2023-05-21 10:53:59 +00:00
|
|
|
.await?;
|
2023-02-13 01:29:49 +00:00
|
|
|
Ok(())
|
|
|
|
}
|
2021-12-09 14:28:11 +00:00
|
|
|
|
2023-02-13 01:29:49 +00:00
|
|
|
pub async fn init_user(&self) -> Result<(), FlowyError> {
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
2023-07-14 05:37:13 +00:00
|
|
|
pub async fn check_user(&self) -> Result<(), FlowyError> {
|
|
|
|
let user_id = self.get_session()?.user_id;
|
|
|
|
let credential = UserCredentials::from_uid(user_id);
|
2023-07-29 01:46:24 +00:00
|
|
|
let auth_service = self.cloud_services.get_user_service()?;
|
|
|
|
auth_service.check_user(credential).await?;
|
|
|
|
Ok(())
|
2023-07-14 05:37:13 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
pub async fn check_user_with_uuid(&self, uuid: &Uuid) -> Result<(), FlowyError> {
|
|
|
|
let credential = UserCredentials::from_uuid(uuid.to_string());
|
2023-07-29 01:46:24 +00:00
|
|
|
let auth_service = self.cloud_services.get_user_service()?;
|
|
|
|
auth_service.check_user(credential).await?;
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
|
|
|
pub async fn open_workspace(&self, workspace_id: &str) -> FlowyResult<()> {
|
|
|
|
let uid = self.user_id()?;
|
|
|
|
if let Some(user_workspace) = self.get_user_workspace(uid, workspace_id) {
|
|
|
|
if let Err(err) = self
|
|
|
|
.user_status_callback
|
|
|
|
.read()
|
|
|
|
.await
|
|
|
|
.open_workspace(uid, &user_workspace)
|
|
|
|
.await
|
|
|
|
{
|
|
|
|
tracing::error!("Open workspace failed: {:?}", err);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
|
|
|
pub async fn add_user_to_workspace(
|
|
|
|
&self,
|
|
|
|
user_email: String,
|
|
|
|
to_workspace_id: String,
|
|
|
|
) -> FlowyResult<()> {
|
|
|
|
self
|
|
|
|
.cloud_services
|
|
|
|
.get_user_service()?
|
|
|
|
.add_workspace_member(user_email, to_workspace_id)
|
|
|
|
.await?;
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
|
|
|
pub async fn remove_user_to_workspace(
|
|
|
|
&self,
|
|
|
|
user_email: String,
|
|
|
|
from_workspace_id: String,
|
|
|
|
) -> FlowyResult<()> {
|
|
|
|
self
|
|
|
|
.cloud_services
|
|
|
|
.get_user_service()?
|
|
|
|
.remove_workspace_member(user_email, from_workspace_id)
|
|
|
|
.await?;
|
|
|
|
Ok(())
|
2023-02-13 01:29:49 +00:00
|
|
|
}
|
|
|
|
|
2023-07-14 05:37:13 +00:00
|
|
|
/// Get the user profile from the database
|
|
|
|
/// If the refresh is true, it will try to get the user profile from the server
|
|
|
|
pub async fn get_user_profile(&self, uid: i64, refresh: bool) -> Result<UserProfile, FlowyError> {
|
|
|
|
let user_id = uid.to_string();
|
2023-07-29 01:46:24 +00:00
|
|
|
let user = user_table::dsl::user_table
|
2023-02-13 01:29:49 +00:00
|
|
|
.filter(user_table::id.eq(&user_id))
|
2023-07-14 05:37:13 +00:00
|
|
|
.first::<UserTable>(&*(self.db_connection(uid)?))?;
|
|
|
|
|
|
|
|
if refresh {
|
2023-07-29 01:46:24 +00:00
|
|
|
let weak_auth_service = Arc::downgrade(&self.cloud_services.get_user_service()?);
|
2023-07-14 05:37:13 +00:00
|
|
|
let weak_pool = Arc::downgrade(&self.database.get_pool(uid)?);
|
|
|
|
tokio::spawn(async move {
|
|
|
|
if let (Some(auth_service), Some(pool)) = (weak_auth_service.upgrade(), weak_pool.upgrade())
|
|
|
|
{
|
|
|
|
if let Ok(Some(user_profile)) = auth_service
|
|
|
|
.get_user_profile(UserCredentials::from_uid(uid))
|
|
|
|
.await
|
|
|
|
{
|
|
|
|
let changeset = UserTableChangeset::from_user_profile(user_profile.clone());
|
|
|
|
if let Ok(conn) = pool.get() {
|
2023-07-29 01:46:24 +00:00
|
|
|
let filter =
|
|
|
|
user_table::dsl::user_table.filter(user_table::dsl::id.eq(changeset.id.clone()));
|
2023-07-14 05:37:13 +00:00
|
|
|
let _ = diesel::update(filter).set(changeset).execute(&*conn);
|
|
|
|
|
|
|
|
// Send notification to the client
|
|
|
|
let user_profile_pb: UserProfilePB = user_profile.into();
|
|
|
|
send_notification(&uid.to_string(), UserNotification::DidUpdateUserProfile)
|
|
|
|
.payload(user_profile_pb)
|
|
|
|
.send();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
2023-02-13 01:29:49 +00:00
|
|
|
|
|
|
|
Ok(user.into())
|
|
|
|
}
|
|
|
|
|
2023-07-29 01:46:24 +00:00
|
|
|
pub fn user_dir(&self, uid: i64) -> String {
|
|
|
|
format!("{}/{}", self.session_config.root_dir, uid)
|
2023-02-13 01:29:49 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
pub fn user_setting(&self) -> Result<UserSettingPB, FlowyError> {
|
2023-07-29 01:46:24 +00:00
|
|
|
let session = self.get_session()?;
|
2023-02-13 01:29:49 +00:00
|
|
|
let user_setting = UserSettingPB {
|
2023-07-29 01:46:24 +00:00
|
|
|
user_folder: self.user_dir(session.user_id),
|
2023-02-13 01:29:49 +00:00
|
|
|
};
|
|
|
|
Ok(user_setting)
|
|
|
|
}
|
|
|
|
|
2023-04-04 00:41:16 +00:00
|
|
|
pub fn user_id(&self) -> Result<i64, FlowyError> {
|
2023-02-13 01:29:49 +00:00
|
|
|
Ok(self.get_session()?.user_id)
|
|
|
|
}
|
|
|
|
|
2023-07-14 05:37:13 +00:00
|
|
|
pub fn token(&self) -> Result<Option<String>, FlowyError> {
|
|
|
|
Ok(None)
|
2023-02-13 01:29:49 +00:00
|
|
|
}
|
|
|
|
|
2023-07-14 05:37:13 +00:00
|
|
|
pub fn save_supabase_config(&self, config: SupabaseConfiguration) {
|
|
|
|
self.cloud_services.update_supabase_config(&config);
|
2023-08-06 03:51:03 +00:00
|
|
|
let _ = self
|
|
|
|
.store_preferences
|
|
|
|
.set_object(SUPABASE_CONFIG_CACHE_KEY, config);
|
2023-02-13 01:29:49 +00:00
|
|
|
}
|
2021-09-01 08:08:32 +00:00
|
|
|
|
2023-05-21 10:53:59 +00:00
|
|
|
async fn update_user(
|
2023-02-13 01:29:49 +00:00
|
|
|
&self,
|
2023-05-31 09:42:14 +00:00
|
|
|
_auth_type: &AuthType,
|
2023-05-21 10:53:59 +00:00
|
|
|
uid: i64,
|
2023-07-14 05:37:13 +00:00
|
|
|
token: Option<String>,
|
2023-02-13 01:29:49 +00:00
|
|
|
params: UpdateUserProfileParams,
|
|
|
|
) -> Result<(), FlowyError> {
|
2023-07-29 01:46:24 +00:00
|
|
|
let server = self.cloud_services.get_user_service()?;
|
2023-02-13 01:29:49 +00:00
|
|
|
let token = token.to_owned();
|
2023-07-29 01:46:24 +00:00
|
|
|
tokio::spawn(async move {
|
2023-07-05 12:57:09 +00:00
|
|
|
let credentials = UserCredentials::new(token, Some(uid), None);
|
2023-07-29 01:46:24 +00:00
|
|
|
server.update_user(credentials, params).await
|
2023-02-13 01:29:49 +00:00
|
|
|
})
|
2023-07-29 01:46:24 +00:00
|
|
|
.await
|
|
|
|
.map_err(internal_error)??;
|
2023-02-13 01:29:49 +00:00
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
2023-07-14 05:37:13 +00:00
|
|
|
async fn save_user(&self, uid: i64, user: UserTable) -> Result<UserTable, FlowyError> {
|
|
|
|
let conn = self.db_connection(uid)?;
|
2023-07-05 12:57:09 +00:00
|
|
|
conn.immediate_transaction(|| {
|
|
|
|
// delete old user if exists
|
2023-07-29 01:46:24 +00:00
|
|
|
diesel::delete(user_table::dsl::user_table.filter(user_table::dsl::id.eq(&user.id)))
|
|
|
|
.execute(&*conn)?;
|
2023-07-05 12:57:09 +00:00
|
|
|
|
|
|
|
let _ = diesel::insert_into(user_table::table)
|
|
|
|
.values(user.clone())
|
|
|
|
.execute(&*conn)?;
|
|
|
|
Ok::<(), FlowyError>(())
|
|
|
|
})?;
|
|
|
|
|
2023-02-13 01:29:49 +00:00
|
|
|
Ok(user)
|
|
|
|
}
|
|
|
|
|
2023-07-29 01:46:24 +00:00
|
|
|
pub fn get_user_workspace(&self, uid: i64, workspace_id: &str) -> Option<UserWorkspace> {
|
|
|
|
let conn = self.db_connection(uid).ok()?;
|
|
|
|
let row = user_workspace_table::dsl::user_workspace_table
|
|
|
|
.filter(user_workspace_table::id.eq(workspace_id))
|
|
|
|
.first::<UserWorkspaceTable>(&*conn)
|
|
|
|
.ok()?;
|
|
|
|
Some(UserWorkspace::from(row))
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn get_all_user_workspaces(&self, uid: i64) -> FlowyResult<Vec<UserWorkspace>> {
|
|
|
|
let conn = self.db_connection(uid)?;
|
|
|
|
let rows = user_workspace_table::dsl::user_workspace_table
|
|
|
|
.filter(user_workspace_table::uid.eq(uid))
|
|
|
|
.load::<UserWorkspaceTable>(&*conn)?;
|
|
|
|
|
|
|
|
if let Ok(service) = self.cloud_services.get_user_service() {
|
|
|
|
if let Ok(pool) = self.db_pool(uid) {
|
|
|
|
tokio::spawn(async move {
|
|
|
|
if let Ok(new_user_workspaces) = service.get_user_workspaces(uid).await {
|
|
|
|
let _ = save_user_workspaces(
|
|
|
|
pool,
|
|
|
|
new_user_workspaces
|
|
|
|
.iter()
|
|
|
|
.map(|user_workspace| UserWorkspaceTable::from((uid, user_workspace)))
|
|
|
|
.collect(),
|
|
|
|
);
|
|
|
|
|
|
|
|
let repeated_workspace_pbs = RepeatedUserWorkspacePB::from(new_user_workspaces);
|
|
|
|
send_notification(&uid.to_string(), UserNotification::DidUpdateUserWorkspaces)
|
|
|
|
.payload(repeated_workspace_pbs)
|
|
|
|
.send();
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
Ok(rows.into_iter().map(UserWorkspace::from).collect())
|
|
|
|
}
|
|
|
|
|
|
|
|
pub async fn save_user_workspaces(
|
|
|
|
&self,
|
|
|
|
uid: i64,
|
|
|
|
user_workspaces: Vec<UserWorkspaceTable>,
|
|
|
|
) -> FlowyResult<()> {
|
|
|
|
let conn = self.db_connection(uid)?;
|
|
|
|
conn.immediate_transaction(|| {
|
|
|
|
for user_workspace in user_workspaces {
|
|
|
|
if let Err(err) = diesel::update(
|
|
|
|
user_workspace_table::dsl::user_workspace_table
|
|
|
|
.filter(user_workspace_table::id.eq(user_workspace.id.clone())),
|
|
|
|
)
|
|
|
|
.set((
|
|
|
|
user_workspace_table::name.eq(&user_workspace.name),
|
|
|
|
user_workspace_table::created_at.eq(&user_workspace.created_at),
|
|
|
|
user_workspace_table::database_storage_id.eq(&user_workspace.database_storage_id),
|
|
|
|
))
|
|
|
|
.execute(&*conn)
|
|
|
|
.and_then(|rows| {
|
|
|
|
if rows == 0 {
|
|
|
|
let _ = diesel::insert_into(user_workspace_table::table)
|
|
|
|
.values(user_workspace)
|
|
|
|
.execute(&*conn)?;
|
|
|
|
}
|
|
|
|
Ok(())
|
|
|
|
}) {
|
|
|
|
tracing::error!("Error saving user workspace: {:?}", err);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
Ok::<(), FlowyError>(())
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2023-02-13 01:29:49 +00:00
|
|
|
fn set_session(&self, session: Option<Session>) -> Result<(), FlowyError> {
|
|
|
|
tracing::debug!("Set user session: {:?}", session);
|
|
|
|
match &session {
|
2023-08-06 03:51:03 +00:00
|
|
|
None => self
|
|
|
|
.store_preferences
|
|
|
|
.remove(&self.session_config.session_cache_key),
|
2023-05-17 04:46:48 +00:00
|
|
|
Some(session) => {
|
2023-08-06 03:51:03 +00:00
|
|
|
self
|
|
|
|
.store_preferences
|
|
|
|
.set_object(&self.session_config.session_cache_key, session.clone())
|
2023-05-17 04:46:48 +00:00
|
|
|
.map_err(internal_error)?;
|
|
|
|
},
|
2021-09-01 03:21:42 +00:00
|
|
|
}
|
2023-02-13 01:29:49 +00:00
|
|
|
Ok(())
|
|
|
|
}
|
2021-09-03 08:43:03 +00:00
|
|
|
|
2023-07-29 01:46:24 +00:00
|
|
|
fn log_user(&self, uid: i64, storage_path: String) {
|
2023-08-06 03:51:03 +00:00
|
|
|
let mut logger_users = self
|
|
|
|
.store_preferences
|
|
|
|
.get_object::<HistoricalUsers>(HISTORICAL_USER)
|
|
|
|
.unwrap_or_default();
|
2023-07-29 01:46:24 +00:00
|
|
|
logger_users.add_user(HistoricalUser {
|
|
|
|
user_id: uid,
|
|
|
|
sign_in_timestamp: timestamp(),
|
|
|
|
storage_path,
|
|
|
|
});
|
2023-08-06 03:51:03 +00:00
|
|
|
let _ = self
|
|
|
|
.store_preferences
|
|
|
|
.set_object(HISTORICAL_USER, logger_users);
|
2023-07-29 01:46:24 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
pub fn get_historical_users(&self) -> Vec<HistoricalUser> {
|
2023-08-06 03:51:03 +00:00
|
|
|
self
|
|
|
|
.store_preferences
|
|
|
|
.get_object::<HistoricalUsers>(HISTORICAL_USER)
|
2023-07-29 01:46:24 +00:00
|
|
|
.unwrap_or_default()
|
|
|
|
.users
|
|
|
|
}
|
|
|
|
|
2023-07-14 05:37:13 +00:00
|
|
|
/// Returns the current user session.
|
|
|
|
pub fn get_session(&self) -> Result<Session, FlowyError> {
|
2023-08-06 03:51:03 +00:00
|
|
|
match self
|
|
|
|
.store_preferences
|
|
|
|
.get_object::<Session>(&self.session_config.session_cache_key)
|
|
|
|
{
|
2023-07-14 05:37:13 +00:00
|
|
|
None => Err(FlowyError::new(
|
|
|
|
ErrorCode::RecordNotFound,
|
2023-08-05 07:02:05 +00:00
|
|
|
"User is not logged in",
|
2023-07-14 05:37:13 +00:00
|
|
|
)),
|
2023-05-17 04:46:48 +00:00
|
|
|
Some(session) => Ok(session),
|
2021-07-18 15:56:36 +00:00
|
|
|
}
|
2023-02-13 01:29:49 +00:00
|
|
|
}
|
2021-07-13 15:08:20 +00:00
|
|
|
}
|
|
|
|
|
2023-08-06 03:51:03 +00:00
|
|
|
pub fn get_supabase_config(
|
|
|
|
store_preference: &Arc<StorePreferences>,
|
|
|
|
) -> Option<SupabaseConfiguration> {
|
|
|
|
store_preference
|
|
|
|
.get_str(SUPABASE_CONFIG_CACHE_KEY)
|
2023-07-29 01:46:24 +00:00
|
|
|
.and_then(|s| serde_json::from_str(&s).ok())
|
|
|
|
.unwrap_or_else(|| SupabaseConfiguration::from_env().ok())
|
2021-12-09 13:39:53 +00:00
|
|
|
}
|
|
|
|
|
2023-07-29 01:46:24 +00:00
|
|
|
pub fn save_user_workspaces(
|
|
|
|
pool: Arc<ConnectionPool>,
|
|
|
|
user_workspaces: Vec<UserWorkspaceTable>,
|
|
|
|
) -> FlowyResult<()> {
|
|
|
|
let conn = pool.get()?;
|
|
|
|
conn.immediate_transaction(|| {
|
|
|
|
for user_workspace in user_workspaces {
|
|
|
|
if let Err(err) = diesel::update(
|
|
|
|
user_workspace_table::dsl::user_workspace_table
|
|
|
|
.filter(user_workspace_table::id.eq(user_workspace.id.clone())),
|
|
|
|
)
|
|
|
|
.set((
|
|
|
|
user_workspace_table::name.eq(&user_workspace.name),
|
|
|
|
user_workspace_table::created_at.eq(&user_workspace.created_at),
|
|
|
|
user_workspace_table::database_storage_id.eq(&user_workspace.database_storage_id),
|
|
|
|
))
|
|
|
|
.execute(&*conn)
|
|
|
|
.and_then(|rows| {
|
|
|
|
if rows == 0 {
|
|
|
|
let _ = diesel::insert_into(user_workspace_table::table)
|
|
|
|
.values(user_workspace)
|
|
|
|
.execute(&*conn)?;
|
|
|
|
}
|
|
|
|
Ok(())
|
|
|
|
}) {
|
|
|
|
tracing::error!("Error saving user workspace: {:?}", err);
|
|
|
|
}
|
2021-09-01 03:21:42 +00:00
|
|
|
}
|
2023-07-29 01:46:24 +00:00
|
|
|
Ok::<(), FlowyError>(())
|
|
|
|
})
|
2023-05-21 10:53:59 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
impl From<AuthTypePB> for AuthType {
|
|
|
|
fn from(pb: AuthTypePB) -> Self {
|
|
|
|
match pb {
|
|
|
|
AuthTypePB::Supabase => AuthType::Supabase,
|
|
|
|
AuthTypePB::Local => AuthType::Local,
|
|
|
|
AuthTypePB::SelfHosted => AuthType::SelfHosted,
|
|
|
|
}
|
|
|
|
}
|
2023-04-04 00:41:16 +00:00
|
|
|
}
|
2023-07-14 05:37:13 +00:00
|
|
|
|
|
|
|
impl From<AuthType> for AuthTypePB {
|
|
|
|
fn from(auth_type: AuthType) -> Self {
|
|
|
|
match auth_type {
|
|
|
|
AuthType::Supabase => AuthTypePB::Supabase,
|
|
|
|
AuthType::Local => AuthTypePB::Local,
|
|
|
|
AuthType::SelfHosted => AuthTypePB::SelfHosted,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-07-29 01:46:24 +00:00
|
|
|
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
|
|
|
|
pub struct HistoricalUsers {
|
|
|
|
pub(crate) users: Vec<HistoricalUser>,
|
2023-07-14 05:37:13 +00:00
|
|
|
}
|
|
|
|
|
2023-07-29 01:46:24 +00:00
|
|
|
impl HistoricalUsers {
|
|
|
|
pub fn add_user(&mut self, new_user: HistoricalUser) {
|
|
|
|
self.users.retain(|user| user.user_id != new_user.user_id);
|
|
|
|
self.users.push(new_user);
|
|
|
|
}
|
2023-07-14 05:37:13 +00:00
|
|
|
}
|
|
|
|
|
2023-07-29 01:46:24 +00:00
|
|
|
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
|
|
|
|
pub struct HistoricalUser {
|
|
|
|
pub user_id: i64,
|
|
|
|
pub sign_in_timestamp: i64,
|
|
|
|
pub storage_path: String,
|
2023-07-14 05:37:13 +00:00
|
|
|
}
|