use std::collections::HashMap; use std::fmt::{Display, Formatter}; use std::str::FromStr; use std::sync::Arc; use anyhow::Error; use collab::core::collab::CollabDocState; use collab_entity::{CollabObject, CollabType}; use serde::{Deserialize, Serialize}; use serde_json::Value; use tokio_stream::wrappers::WatchStream; use uuid::Uuid; use flowy_error::{ErrorCode, FlowyError}; use lib_infra::box_any::BoxAny; use lib_infra::future::FutureResult; use crate::entities::{ AuthResponse, Authenticator, Role, UpdateUserProfileParams, UserCredentials, UserProfile, UserTokenState, UserWorkspace, WorkspaceMember, }; #[derive(Debug, Clone, Serialize, Deserialize)] pub struct UserCloudConfig { pub enable_sync: bool, pub enable_encrypt: bool, // The secret used to encrypt the user's data pub encrypt_secret: String, } impl UserCloudConfig { pub fn new(encrypt_secret: String) -> Self { Self { enable_sync: true, enable_encrypt: false, encrypt_secret, } } pub fn with_enable_encrypt(mut self, enable_encrypt: bool) -> Self { self.enable_encrypt = enable_encrypt; // When the enable_encrypt is true, the encrypt_secret should not be empty debug_assert!(!self.encrypt_secret.is_empty()); self } } impl Display for UserCloudConfig { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { write!( f, "enable_sync: {}, enable_encrypt: {}", self.enable_sync, self.enable_encrypt ) } } /// `UserCloudServiceProvider` defines a set of methods for managing user cloud services, /// including token management, synchronization settings, network reachability, and authentication. /// /// This trait is intended for implementation by providers that offer cloud-based services for users. /// It includes methods for handling authentication tokens, enabling/disabling synchronization, /// setting network reachability, managing encryption secrets, and accessing user-specific cloud services. pub trait UserCloudServiceProvider: Send + Sync + 'static { /// Sets the authentication token for the cloud service. /// /// # Arguments /// * `token`: A string slice representing the authentication token. /// /// # Returns /// A `Result` which is `Ok` if the token is successfully set, or a `FlowyError` otherwise. fn set_token(&self, token: &str) -> Result<(), FlowyError>; /// Subscribes to the state of the authentication token. /// /// # Returns /// An `Option` containing a `WatchStream` if available, or `None` otherwise. /// The stream allows the caller to watch for changes in the token state. fn subscribe_token_state(&self) -> Option>; /// Sets the synchronization state for a user. /// /// # Arguments /// * `uid`: An i64 representing the user ID. /// * `enable_sync`: A boolean indicating whether synchronization should be enabled or disabled. fn set_enable_sync(&self, uid: i64, enable_sync: bool); /// Sets the authentication type for a user. The authentication type is the type when user sign in or sign up. fn set_user_authenticator(&self, authenticator: &Authenticator); /// Sets the authenticator when user sign in or sign up. /// /// # Arguments /// * `authenticator`: An `Authenticator` object. fn set_authenticator(&self, authenticator: Authenticator); /// Sets the network reachability status. /// /// # Arguments /// * `reachable`: A boolean indicating whether the network is reachable. fn set_network_reachable(&self, reachable: bool); /// Sets the encryption secret for secure communication. /// /// # Arguments /// * `secret`: A `String` representing the encryption secret. fn set_encrypt_secret(&self, secret: String); /// Retrieves the current authenticator. /// /// # Returns /// The current `Authenticator` object. fn get_authenticator(&self) -> Authenticator; /// Retrieves the user-specific cloud service. /// /// # Returns /// A `Result` containing an `Arc` if successful, or a `FlowyError` otherwise. fn get_user_service(&self) -> Result, FlowyError>; /// Retrieves the service URL. /// /// # Returns /// A `String` representing the service URL. fn service_url(&self) -> String; } /// Provide the generic interface for the user cloud service /// The user cloud service is responsible for the user authentication and user profile management #[allow(unused_variables)] pub trait UserCloudService: Send + Sync + 'static { /// Sign up a new account. /// The type of the params is defined the this trait's implementation. /// Use the `unbox_or_error` of the [BoxAny] to get the params. fn sign_up(&self, params: BoxAny) -> FutureResult; /// Sign in an account /// The type of the params is defined the this trait's implementation. fn sign_in(&self, params: BoxAny) -> FutureResult; /// Sign out an account fn sign_out(&self, token: Option) -> FutureResult<(), FlowyError>; /// Generate a sign in url for the user with the given email /// Currently, only use the admin client for testing fn generate_sign_in_url_with_email(&self, email: &str) -> FutureResult; /// When the user opens the OAuth URL, it redirects to the corresponding provider's OAuth web page. /// After the user is authenticated, the browser will open a deep link to the AppFlowy app (iOS, macOS, etc.), /// which will call [Client::sign_in_with_url] to sign in. /// /// For example, the OAuth URL on Google looks like `https://appflowy.io/authorize?provider=google`. fn generate_oauth_url_with_provider(&self, provider: &str) -> FutureResult; /// Using the user's token to update the user information fn update_user( &self, credential: UserCredentials, params: UpdateUserProfileParams, ) -> FutureResult<(), FlowyError>; /// Get the user information using the user's token or uid /// return None if the user is not found fn get_user_profile(&self, credential: UserCredentials) -> FutureResult; fn open_workspace(&self, workspace_id: &str) -> FutureResult; /// Return the all the workspaces of the user fn get_all_workspace(&self, uid: i64) -> FutureResult, FlowyError>; fn add_workspace_member( &self, user_email: String, workspace_id: String, ) -> FutureResult<(), Error> { FutureResult::new(async { Ok(()) }) } fn remove_workspace_member( &self, user_email: String, workspace_id: String, ) -> FutureResult<(), Error> { FutureResult::new(async { Ok(()) }) } fn update_workspace_member( &self, user_email: String, workspace_id: String, role: Role, ) -> FutureResult<(), Error> { FutureResult::new(async { Ok(()) }) } fn get_workspace_members( &self, workspace_id: String, ) -> FutureResult, Error> { FutureResult::new(async { Ok(vec![]) }) } fn get_user_awareness_doc_state(&self, uid: i64) -> FutureResult; fn receive_realtime_event(&self, _json: Value) {} fn subscribe_user_update(&self) -> Option { None } fn reset_workspace(&self, collab_object: CollabObject) -> FutureResult<(), Error>; fn create_collab_object( &self, collab_object: &CollabObject, data: Vec, override_if_exist: bool, ) -> FutureResult<(), FlowyError>; fn batch_create_collab_object( &self, workspace_id: &str, objects: Vec, ) -> FutureResult<(), Error>; } pub type UserUpdateReceiver = tokio::sync::mpsc::Receiver; pub type UserUpdateSender = tokio::sync::mpsc::Sender; #[derive(Debug, Clone)] pub struct UserUpdate { pub uid: i64, pub name: Option, pub email: Option, pub encryption_sign: String, } pub fn uuid_from_map(map: &HashMap) -> Result { let uuid = map .get("uuid") .ok_or_else(|| FlowyError::new(ErrorCode::MissingAuthField, "Missing uuid field"))? .as_str(); let uuid = Uuid::from_str(uuid)?; Ok(uuid) } #[derive(Debug)] pub struct UserCollabParams { pub object_id: String, pub encoded_collab_v1: Vec, pub collab_type: CollabType, }