mirror of
https://github.com/AppFlowy-IO/AppFlowy.git
synced 2024-08-30 18:12:39 +00:00
feat: calling user event from web (#4535)
* refactor: user manager * refactor: user manager * refactor: session location * refactor: user manager * chore: gen ts files * feat: implement indexeddb persistence * chore: integrate user manager * chore: update * chore: run on web thread * chore: run on web thread * chore: fix test * chore: add test * chore: add test * chore: add user & sign in with password * chore: fix test * chore: update docs * chore: fix warnings * chore: gen files * chore: add user * chore: add files * chore: update config * chore: update scirpt * chore: update scirpt * fix: build * chore: update command * fix: ci * ci: fix * fix: compile * fix: compile * fix: ci * fix: compile * fix: tauri build * chore: fix test * chore: fix test
This commit is contained in:
@ -4,6 +4,9 @@ version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
[lib]
|
||||
crate-type = ["cdylib", "rlib"]
|
||||
|
||||
|
||||
[dependencies]
|
||||
tracing.workspace = true
|
||||
@ -23,7 +26,7 @@ anyhow.workspace = true
|
||||
uuid.workspace = true
|
||||
chrono = { workspace = true, default-features = false, features = ["clock", "serde"] }
|
||||
collab = { version = "0.1.0" }
|
||||
collab-plugins = { version = "0.1.0", features = ["postgres_plugin"] }
|
||||
collab-plugins = { version = "0.1.0"}
|
||||
collab-document = { version = "0.1.0" }
|
||||
collab-entity = { version = "0.1.0" }
|
||||
hex = "0.4.3"
|
||||
@ -53,3 +56,7 @@ yrs = "0.17.1"
|
||||
assert-json-diff = "2.0.2"
|
||||
serde_json.workspace = true
|
||||
client-api = { version = "0.1.0" }
|
||||
|
||||
[features]
|
||||
enable_wasm = ["collab/async-plugin"]
|
||||
enable_supabase = ["collab-plugins/postgres_plugin"]
|
4
frontend/rust-lib/flowy-server/src/af_cloud/define.rs
Normal file
4
frontend/rust-lib/flowy-server/src/af_cloud/define.rs
Normal file
@ -0,0 +1,4 @@
|
||||
pub const USER_SIGN_IN_URL: &str = "sign_in_url";
|
||||
pub const USER_UUID: &str = "uuid";
|
||||
pub const USER_EMAIL: &str = "email";
|
||||
pub const USER_DEVICE_ID: &str = "device_id";
|
@ -4,23 +4,23 @@ use std::sync::Arc;
|
||||
use anyhow::{anyhow, Error};
|
||||
use client_api::entity::workspace_dto::{CreateWorkspaceMember, WorkspaceMemberChangeset};
|
||||
use client_api::entity::{AFRole, AFWorkspace, AuthProvider, CollabParams, CreateCollabParams};
|
||||
use client_api::ClientConfiguration;
|
||||
use client_api::{Client, ClientConfiguration};
|
||||
use collab::core::collab::CollabDocState;
|
||||
use collab_entity::CollabObject;
|
||||
use parking_lot::RwLock;
|
||||
|
||||
use flowy_error::{ErrorCode, FlowyError};
|
||||
use flowy_error::{ErrorCode, FlowyError, FlowyResult};
|
||||
use flowy_user_pub::cloud::{UserCloudService, UserCollabParams, UserUpdate, UserUpdateReceiver};
|
||||
use flowy_user_pub::entities::*;
|
||||
use lib_infra::box_any::BoxAny;
|
||||
use lib_infra::future::FutureResult;
|
||||
|
||||
use crate::af_cloud::define::USER_SIGN_IN_URL;
|
||||
use crate::af_cloud::impls::user::dto::{
|
||||
af_update_from_update_params, from_af_workspace_member, to_af_role, user_profile_from_af_profile,
|
||||
};
|
||||
use crate::af_cloud::impls::user::util::encryption_type_from_profile;
|
||||
use crate::af_cloud::{AFCloudClient, AFServer};
|
||||
use crate::supabase::define::USER_SIGN_IN_URL;
|
||||
|
||||
pub(crate) struct AFCloudUserAuthServiceImpl<T> {
|
||||
server: T,
|
||||
@ -71,32 +71,46 @@ where
|
||||
let try_get_client = self.server.try_get_client();
|
||||
FutureResult::new(async move {
|
||||
let client = try_get_client?;
|
||||
let admin_email = std::env::var("GOTRUE_ADMIN_EMAIL").map_err(|_| {
|
||||
anyhow!(
|
||||
"GOTRUE_ADMIN_EMAIL is not set. Please set it to the admin email for the test server"
|
||||
)
|
||||
})?;
|
||||
let admin_password = std::env::var("GOTRUE_ADMIN_PASSWORD").map_err(|_| {
|
||||
anyhow!(
|
||||
"GOTRUE_ADMIN_PASSWORD is not set. Please set it to the admin password for the test server"
|
||||
)
|
||||
})?;
|
||||
let admin_client = client_api::Client::new(
|
||||
client.base_url(),
|
||||
client.ws_addr(),
|
||||
client.gotrue_url(),
|
||||
ClientConfiguration::default(),
|
||||
);
|
||||
admin_client
|
||||
.sign_in_password(&admin_email, &admin_password)
|
||||
.await?;
|
||||
|
||||
let admin_client = get_admin_client(&client).await?;
|
||||
let action_link = admin_client.generate_sign_in_action_link(&email).await?;
|
||||
let sign_in_url = client.extract_sign_in_url(&action_link).await?;
|
||||
Ok(sign_in_url)
|
||||
})
|
||||
}
|
||||
|
||||
fn create_user(&self, email: &str, password: &str) -> FutureResult<(), FlowyError> {
|
||||
let password = password.to_string();
|
||||
let email = email.to_string();
|
||||
let try_get_client = self.server.try_get_client();
|
||||
FutureResult::new(async move {
|
||||
let client = try_get_client?;
|
||||
let admin_client = get_admin_client(&client).await?;
|
||||
admin_client
|
||||
.create_email_verified_user(&email, &password)
|
||||
.await?;
|
||||
|
||||
Ok(())
|
||||
})
|
||||
}
|
||||
|
||||
fn sign_in_with_password(
|
||||
&self,
|
||||
email: &str,
|
||||
password: &str,
|
||||
) -> FutureResult<UserProfile, FlowyError> {
|
||||
let password = password.to_string();
|
||||
let email = email.to_string();
|
||||
let try_get_client = self.server.try_get_client();
|
||||
FutureResult::new(async move {
|
||||
let client = try_get_client?;
|
||||
client.sign_in_password(&email, &password).await?;
|
||||
let profile = client.get_profile().await?;
|
||||
let token = client.get_token()?;
|
||||
let profile = user_profile_from_af_profile(token, profile)?;
|
||||
Ok(profile)
|
||||
})
|
||||
}
|
||||
|
||||
fn generate_oauth_url_with_provider(&self, provider: &str) -> FutureResult<String, FlowyError> {
|
||||
let provider = AuthProvider::from(provider);
|
||||
let try_get_client = self.server.try_get_client();
|
||||
@ -282,6 +296,23 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
async fn get_admin_client(client: &Arc<AFCloudClient>) -> FlowyResult<Client> {
|
||||
let admin_email =
|
||||
std::env::var("GOTRUE_ADMIN_EMAIL").unwrap_or_else(|_| "admin@example.com".to_string());
|
||||
let admin_password =
|
||||
std::env::var("GOTRUE_ADMIN_PASSWORD").unwrap_or_else(|_| "password".to_string());
|
||||
let admin_client = client_api::Client::new(
|
||||
client.base_url(),
|
||||
client.ws_addr(),
|
||||
client.gotrue_url(),
|
||||
ClientConfiguration::default(),
|
||||
);
|
||||
admin_client
|
||||
.sign_in_password(&admin_email, &admin_password)
|
||||
.await?;
|
||||
Ok(admin_client)
|
||||
}
|
||||
|
||||
pub async fn user_sign_up_request(
|
||||
client: Arc<AFCloudClient>,
|
||||
params: AFCloudOAuthParams,
|
||||
|
@ -1,4 +1,5 @@
|
||||
pub use server::*;
|
||||
|
||||
pub mod define;
|
||||
pub mod impls;
|
||||
mod server;
|
||||
|
@ -5,10 +5,10 @@ use anyhow::Error;
|
||||
use client_api::collab_sync::collab_msg::CollabMessage;
|
||||
use client_api::entity::UserMessage;
|
||||
use client_api::notify::{TokenState, TokenStateReceiver};
|
||||
use client_api::{Client, ClientConfiguration};
|
||||
use client_api::{
|
||||
use client_api::ws::{
|
||||
ConnectState, WSClient, WSClientConfig, WSConnectStateReceiver, WebSocketChannel,
|
||||
};
|
||||
use client_api::{Client, ClientConfiguration};
|
||||
use flowy_storage::ObjectStorageService;
|
||||
use tokio::sync::watch;
|
||||
use tokio_stream::wrappers::WatchStream;
|
||||
@ -137,7 +137,7 @@ impl AppFlowyServer for AppFlowyCloudServer {
|
||||
};
|
||||
let mut user_change = self.ws_client.subscribe_user_changed();
|
||||
let (tx, rx) = tokio::sync::mpsc::channel(1);
|
||||
tokio::spawn(async move {
|
||||
af_spawn(async move {
|
||||
while let Ok(user_message) = user_change.recv().await {
|
||||
if let UserMessage::ProfileChange(change) = user_message {
|
||||
let user_update = UserUpdate {
|
||||
|
@ -5,5 +5,8 @@ pub mod local_server;
|
||||
// mod request;
|
||||
mod response;
|
||||
mod server;
|
||||
|
||||
#[cfg(feature = "enable_supabase")]
|
||||
pub mod supabase;
|
||||
|
||||
pub mod util;
|
||||
|
@ -87,11 +87,28 @@ impl UserCloudService for LocalServerUserAuthServiceImpl {
|
||||
fn generate_sign_in_url_with_email(&self, _email: &str) -> FutureResult<String, FlowyError> {
|
||||
FutureResult::new(async {
|
||||
Err(
|
||||
FlowyError::internal().with_context("Can't generate callback url when using offline mode"),
|
||||
FlowyError::local_version_not_support()
|
||||
.with_context("Not support generate sign in url with email"),
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
fn create_user(&self, _email: &str, _password: &str) -> FutureResult<(), FlowyError> {
|
||||
FutureResult::new(async {
|
||||
Err(FlowyError::local_version_not_support().with_context("Not support create user"))
|
||||
})
|
||||
}
|
||||
|
||||
fn sign_in_with_password(
|
||||
&self,
|
||||
_email: &str,
|
||||
_password: &str,
|
||||
) -> FutureResult<UserProfile, FlowyError> {
|
||||
FutureResult::new(async {
|
||||
Err(FlowyError::local_version_not_support().with_context("Not support"))
|
||||
})
|
||||
}
|
||||
|
||||
fn generate_oauth_url_with_provider(&self, _provider: &str) -> FutureResult<String, FlowyError> {
|
||||
FutureResult::new(async {
|
||||
Err(FlowyError::internal().with_context("Can't oauth url when using offline mode"))
|
||||
|
@ -3,11 +3,11 @@ use std::sync::Arc;
|
||||
|
||||
use anyhow::Error;
|
||||
use client_api::collab_sync::collab_msg::CollabMessage;
|
||||
use client_api::{ConnectState, WSConnectStateReceiver, WebSocketChannel};
|
||||
use collab_entity::CollabObject;
|
||||
use collab_plugins::cloud_storage::RemoteCollabStorage;
|
||||
use client_api::ws::{ConnectState, WSConnectStateReceiver, WebSocketChannel};
|
||||
use parking_lot::RwLock;
|
||||
use tokio_stream::wrappers::WatchStream;
|
||||
#[cfg(feature = "enable_supabase")]
|
||||
use {collab_entity::CollabObject, collab_plugins::cloud_storage::RemoteCollabStorage};
|
||||
|
||||
use flowy_database_pub::cloud::DatabaseCloudService;
|
||||
use flowy_document_pub::cloud::DocumentCloudService;
|
||||
@ -104,6 +104,7 @@ pub trait AppFlowyServer: Send + Sync + 'static {
|
||||
/// # Returns
|
||||
///
|
||||
/// An `Option` that might contain an `Arc` wrapping the `RemoteCollabStorage` interface.
|
||||
#[cfg(feature = "enable_supabase")]
|
||||
fn collab_storage(&self, _collab_object: &CollabObject) -> Option<Arc<dyn RemoteCollabStorage>> {
|
||||
None
|
||||
}
|
||||
|
@ -171,6 +171,22 @@ where
|
||||
})
|
||||
}
|
||||
|
||||
fn create_user(&self, _email: &str, _password: &str) -> FutureResult<(), FlowyError> {
|
||||
FutureResult::new(async {
|
||||
Err(FlowyError::not_support().with_context("Can't create user when using supabase"))
|
||||
})
|
||||
}
|
||||
|
||||
fn sign_in_with_password(
|
||||
&self,
|
||||
_email: &str,
|
||||
_password: &str,
|
||||
) -> FutureResult<UserProfile, FlowyError> {
|
||||
FutureResult::new(async {
|
||||
Err(FlowyError::not_support().with_context("Can't sign in with password when using supabase"))
|
||||
})
|
||||
}
|
||||
|
||||
fn generate_oauth_url_with_provider(&self, _provider: &str) -> FutureResult<String, FlowyError> {
|
||||
FutureResult::new(async {
|
||||
Err(FlowyError::internal().with_context("Can't generate oauth url when using supabase"))
|
||||
|
@ -12,13 +12,14 @@ pub const AF_COLLAB_SNAPSHOT_TABLE: &str = "af_collab_snapshot";
|
||||
|
||||
pub const USER_UUID: &str = "uuid";
|
||||
pub const USER_SIGN_IN_URL: &str = "sign_in_url";
|
||||
pub const USER_EMAIL: &str = "email";
|
||||
pub const USER_DEVICE_ID: &str = "device_id";
|
||||
|
||||
pub const USER_TABLE: &str = "af_user";
|
||||
pub const USER_UID: &str = "uid";
|
||||
pub const OWNER_USER_UID: &str = "owner_uid";
|
||||
pub const USER_EMAIL: &str = "email";
|
||||
pub const USER_TABLE: &str = "af_user";
|
||||
pub const WORKSPACE_TABLE: &str = "af_workspace";
|
||||
pub const USER_PROFILE_VIEW: &str = "af_user_profile_view";
|
||||
pub const USER_DEVICE_ID: &str = "device_id";
|
||||
|
||||
pub(crate) const WORKSPACE_ID: &str = "workspace_id";
|
||||
pub(crate) const WORKSPACE_NAME: &str = "workspace_name";
|
||||
|
@ -2,6 +2,7 @@ use serde::{Deserialize, Deserializer};
|
||||
|
||||
/// Handles the case where the value is null. If the value is null, return the default value of the
|
||||
/// type. Otherwise, deserialize the value.
|
||||
#[allow(dead_code)]
|
||||
pub(crate) fn deserialize_null_or_default<'de, D, T>(deserializer: D) -> Result<T, D::Error>
|
||||
where
|
||||
T: Default + Deserialize<'de>,
|
||||
|
Reference in New Issue
Block a user