diff --git a/frontend/rust-lib/Cargo.lock b/frontend/rust-lib/Cargo.lock index e322f3e42e..11bb4802f8 100644 --- a/frontend/rust-lib/Cargo.lock +++ b/frontend/rust-lib/Cargo.lock @@ -183,7 +183,7 @@ dependencies = [ [[package]] name = "appflowy-cloud-billing-client" version = "0.1.0" -source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud-Billing-Client?rev=196d8ccbc6a63604f1ac56c66e772efd365818aa#196d8ccbc6a63604f1ac56c66e772efd365818aa" +source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud-Billing-Client?rev=c608c4ec97bae19bad212742eeb4e3475845742a#c608c4ec97bae19bad212742eeb4e3475845742a" dependencies = [ "client-api", "reqwest", diff --git a/frontend/rust-lib/Cargo.toml b/frontend/rust-lib/Cargo.toml index 51b9b4538d..875a4859ff 100644 --- a/frontend/rust-lib/Cargo.toml +++ b/frontend/rust-lib/Cargo.toml @@ -100,7 +100,7 @@ yrs = "0.18.7" # scripts/tool/update_client_api_rev.sh new_rev_id # ⚠️⚠️⚠️️ client-api = { git = "https://github.com/AppFlowy-IO/AppFlowy-Cloud", rev = "ef8e6f3" } -appflowy-cloud-billing-client = { git = "https://github.com/AppFlowy-IO/AppFlowy-Cloud-Billing-Client", rev = "196d8ccbc6a63604f1ac56c66e772efd365818aa" } +appflowy-cloud-billing-client = { git = "https://github.com/AppFlowy-IO/AppFlowy-Cloud-Billing-Client", rev = "c608c4ec97bae19bad212742eeb4e3475845742a" } [profile.dev] opt-level = 1 diff --git a/frontend/rust-lib/flowy-server/src/af_cloud/impls/user/cloud_service_impl.rs b/frontend/rust-lib/flowy-server/src/af_cloud/impls/user/cloud_service_impl.rs index 73ac121cea..65639c2110 100644 --- a/frontend/rust-lib/flowy-server/src/af_cloud/impls/user/cloud_service_impl.rs +++ b/frontend/rust-lib/flowy-server/src/af_cloud/impls/user/cloud_service_impl.rs @@ -1,4 +1,6 @@ -use appflowy_cloud_billing_client::entities::{RecurringInterval, SubscriptionPlan}; +use appflowy_cloud_billing_client::entities::{ + RecurringInterval, SubscriptionPlan, WorkspaceSubscriptionStatus, +}; use std::collections::HashMap; use std::sync::Arc; @@ -22,6 +24,7 @@ use flowy_user_pub::cloud::{UserCloudService, UserCollabParams, UserUpdate, User use flowy_user_pub::entities::{ AFCloudOAuthParams, AuthResponse, Role, UpdateUserProfileParams, UserCredentials, UserProfile, UserWorkspace, WorkspaceInvitation, WorkspaceInvitationStatus, WorkspaceMember, + WorkspaceSubscription, }; use lib_infra::box_any::BoxAny; use lib_infra::future::FutureResult; @@ -487,17 +490,31 @@ where let try_get_client = self.server.try_get_client(); let workspace_id = workspace_id.to_string(); FutureResult::new(async move { + let subscription_plan = to_workspace_subscription_plan(workspace_subscription_plan)?; let client = try_get_client?; let payment_link = client .create_subscription( &workspace_id, to_recurring_interval(recurring_interval), - to_workspace_subscription_plan(workspace_subscription_plan), + subscription_plan, &success_url, ) .await?; Ok(payment_link) - // Ok(()) + }) + } + + fn get_workspace_subscriptions(&self) -> FutureResult, FlowyError> { + let try_get_client = self.server.try_get_client(); + FutureResult::new(async move { + let client = try_get_client?; + let workspace_subscriptions = client + .list_subscription() + .await? + .into_iter() + .map(to_workspace_subscription) + .collect(); + Ok(workspace_subscriptions) }) } } @@ -606,9 +623,36 @@ fn to_recurring_interval(r: flowy_user_pub::entities::RecurringInterval) -> Recu fn to_workspace_subscription_plan( s: flowy_user_pub::entities::SubscriptionPlan, -) -> SubscriptionPlan { +) -> Result { match s { - flowy_user_pub::entities::SubscriptionPlan::Pro => SubscriptionPlan::Pro, - flowy_user_pub::entities::SubscriptionPlan::Team => SubscriptionPlan::Team, + flowy_user_pub::entities::SubscriptionPlan::Pro => Ok(SubscriptionPlan::Pro), + flowy_user_pub::entities::SubscriptionPlan::Team => Ok(SubscriptionPlan::Team), + flowy_user_pub::entities::SubscriptionPlan::None => Err(FlowyError::new( + ErrorCode::InvalidParams, + "Invalid subscription plan", + )), + } +} + +fn to_workspace_subscription(s: WorkspaceSubscriptionStatus) -> WorkspaceSubscription { + WorkspaceSubscription { + workspace_id: s.workspace_id, + subscription_plan: match s.workspace_plan { + appflowy_cloud_billing_client::entities::WorkspaceSubscriptionPlan::Pro => { + flowy_user_pub::entities::SubscriptionPlan::Pro + }, + appflowy_cloud_billing_client::entities::WorkspaceSubscriptionPlan::Team => { + flowy_user_pub::entities::SubscriptionPlan::Team + }, + _ => flowy_user_pub::entities::SubscriptionPlan::None, + }, + recurring_interval: match s.recurring_interval { + RecurringInterval::Month => flowy_user_pub::entities::RecurringInterval::Month, + RecurringInterval::Year => flowy_user_pub::entities::RecurringInterval::Year, + }, + is_active: matches!( + s.subscription_status, + appflowy_cloud_billing_client::entities::SubscriptionStatus::Active + ), } } diff --git a/frontend/rust-lib/flowy-user-pub/src/cloud.rs b/frontend/rust-lib/flowy-user-pub/src/cloud.rs index 22741b62d0..adb4df47a9 100644 --- a/frontend/rust-lib/flowy-user-pub/src/cloud.rs +++ b/frontend/rust-lib/flowy-user-pub/src/cloud.rs @@ -15,7 +15,7 @@ use uuid::Uuid; use crate::entities::{ AuthResponse, Authenticator, RecurringInterval, Role, SubscriptionPlan, UpdateUserProfileParams, UserCredentials, UserProfile, UserTokenState, UserWorkspace, WorkspaceInvitation, - WorkspaceInvitationStatus, WorkspaceMember, + WorkspaceInvitationStatus, WorkspaceMember, WorkspaceSubscription, }; #[derive(Debug, Clone, Serialize, Deserialize)] @@ -277,6 +277,10 @@ pub trait UserCloudService: Send + Sync + 'static { ) -> FutureResult { FutureResult::new(async { Err(FlowyError::not_support()) }) } + + fn get_workspace_subscriptions(&self) -> FutureResult, FlowyError> { + FutureResult::new(async { Err(FlowyError::not_support()) }) + } } pub type UserUpdateReceiver = tokio::sync::mpsc::Receiver; diff --git a/frontend/rust-lib/flowy-user-pub/src/entities.rs b/frontend/rust-lib/flowy-user-pub/src/entities.rs index caf0916b26..e63eae7255 100644 --- a/frontend/rust-lib/flowy-user-pub/src/entities.rs +++ b/frontend/rust-lib/flowy-user-pub/src/entities.rs @@ -428,6 +428,14 @@ pub enum RecurringInterval { } pub enum SubscriptionPlan { + None, Pro, Team, } + +pub struct WorkspaceSubscription { + pub workspace_id: String, + pub subscription_plan: SubscriptionPlan, + pub recurring_interval: RecurringInterval, + pub is_active: bool, +} diff --git a/frontend/rust-lib/flowy-user/src/entities/workspace.rs b/frontend/rust-lib/flowy-user/src/entities/workspace.rs index a8233d57a5..0c9ec1a6da 100644 --- a/frontend/rust-lib/flowy-user/src/entities/workspace.rs +++ b/frontend/rust-lib/flowy-user/src/entities/workspace.rs @@ -3,6 +3,7 @@ use validator::Validate; use flowy_derive::{ProtoBuf, ProtoBuf_Enum}; use flowy_user_pub::entities::{ RecurringInterval, Role, SubscriptionPlan, WorkspaceInvitation, WorkspaceMember, + WorkspaceSubscription, }; use lib_infra::validator_fn::required_not_empty_str; @@ -223,19 +224,29 @@ pub enum RecurringIntervalPB { } impl From for RecurringInterval { - fn from(value: RecurringIntervalPB) -> Self { - match value { + fn from(r: RecurringIntervalPB) -> Self { + match r { RecurringIntervalPB::Month => RecurringInterval::Month, RecurringIntervalPB::Year => RecurringInterval::Year, } } } +impl From for RecurringIntervalPB { + fn from(r: RecurringInterval) -> Self { + match r { + RecurringInterval::Month => RecurringIntervalPB::Month, + RecurringInterval::Year => RecurringIntervalPB::Year, + } + } +} + #[derive(ProtoBuf_Enum, Clone, Default, Debug)] pub enum SubscriptionPlanPB { #[default] - Pro = 0, - Team = 1, + None = 0, + Pro = 1, + Team = 2, } impl From for SubscriptionPlan { @@ -243,6 +254,17 @@ impl From for SubscriptionPlan { match value { SubscriptionPlanPB::Pro => SubscriptionPlan::Pro, SubscriptionPlanPB::Team => SubscriptionPlan::Team, + SubscriptionPlanPB::None => SubscriptionPlan::None, + } + } +} + +impl From for SubscriptionPlanPB { + fn from(value: SubscriptionPlan) -> Self { + match value { + SubscriptionPlan::Pro => SubscriptionPlanPB::Pro, + SubscriptionPlan::Team => SubscriptionPlanPB::Team, + SubscriptionPlan::None => SubscriptionPlanPB::None, } } } @@ -252,3 +274,35 @@ pub struct PaymentLinkPB { #[pb(index = 1)] pub payment_link: String, } + +#[derive(Debug, ProtoBuf, Default, Clone)] +pub struct RepeatedWorkspaceSubscriptionPB { + #[pb(index = 1)] + pub items: Vec, +} + +#[derive(Debug, ProtoBuf, Default, Clone)] +pub struct WorkspaceSubscriptionPB { + #[pb(index = 1)] + pub workspace_id: String, + + #[pb(index = 2)] + pub subscription_plan: SubscriptionPlanPB, + + #[pb(index = 3)] + pub recurring_interval: RecurringIntervalPB, + + #[pb(index = 4)] + pub is_active: bool, +} + +impl From for WorkspaceSubscriptionPB { + fn from(s: WorkspaceSubscription) -> Self { + Self { + workspace_id: s.workspace_id, + subscription_plan: s.subscription_plan.into(), + recurring_interval: s.recurring_interval.into(), + is_active: s.is_active, + } + } +} diff --git a/frontend/rust-lib/flowy-user/src/event_handler.rs b/frontend/rust-lib/flowy-user/src/event_handler.rs index 6e26ccbfea..12190cd70c 100644 --- a/frontend/rust-lib/flowy-user/src/event_handler.rs +++ b/frontend/rust-lib/flowy-user/src/event_handler.rs @@ -785,3 +785,17 @@ pub async fn subscribe_workspace_handler( let payment_link = manager.subscribe_workspace(params).await?; data_result_ok(PaymentLinkPB { payment_link }) } + +#[tracing::instrument(level = "debug", skip_all, err)] +pub async fn get_workspace_subscriptions_handler( + manager: AFPluginState>, +) -> DataResult { + let manager = upgrade_manager(manager)?; + let subs = manager + .get_workspace_subscriptions() + .await? + .into_iter() + .map(WorkspaceSubscriptionPB::from) + .collect::>(); + data_result_ok(RepeatedWorkspaceSubscriptionPB { items: subs }) +} diff --git a/frontend/rust-lib/flowy-user/src/event_map.rs b/frontend/rust-lib/flowy-user/src/event_map.rs index 20aaeda0e7..4ff6b1e648 100644 --- a/frontend/rust-lib/flowy-user/src/event_map.rs +++ b/frontend/rust-lib/flowy-user/src/event_map.rs @@ -73,6 +73,7 @@ pub fn init(user_manager: Weak) -> AFPlugin { .event(UserEvent::AcceptWorkspaceInvitation, accept_workspace_invitations_handler) // Billing .event(UserEvent::SubscribeWorkspace, subscribe_workspace_handler) + .event(UserEvent::GetWorkspaceSubscriptions, get_workspace_subscriptions_handler) } #[derive(Clone, Copy, PartialEq, Eq, Debug, Display, Hash, ProtoBuf_Enum, Flowy_Event)] @@ -235,6 +236,9 @@ pub enum UserEvent { #[event(input = "SubscribeWorkspacePB", output = "PaymentLinkPB")] SubscribeWorkspace = 51, + + #[event(output = "RepeatedWorkspaceSubscriptionPB")] + GetWorkspaceSubscriptions = 52, } pub trait UserStatusCallback: Send + Sync + 'static { diff --git a/frontend/rust-lib/flowy-user/src/user_manager/manager_user_workspace.rs b/frontend/rust-lib/flowy-user/src/user_manager/manager_user_workspace.rs index 5ac4ac382c..ecf0e5e825 100644 --- a/frontend/rust-lib/flowy-user/src/user_manager/manager_user_workspace.rs +++ b/frontend/rust-lib/flowy-user/src/user_manager/manager_user_workspace.rs @@ -11,6 +11,7 @@ use flowy_sqlite::schema::user_workspace_table; use flowy_sqlite::{query_dsl::*, DBConnection, ExpressionMethods}; use flowy_user_pub::entities::{ Role, UserWorkspace, WorkspaceInvitation, WorkspaceInvitationStatus, WorkspaceMember, + WorkspaceSubscription, }; use lib_dispatch::prelude::af_spawn; @@ -420,6 +421,16 @@ impl UserManager { Ok(payment_link) } + + #[instrument(level = "info", skip(self), err)] + pub async fn get_workspace_subscriptions(&self) -> FlowyResult> { + let res = self + .cloud_services + .get_user_service()? + .get_workspace_subscriptions() + .await?; + Ok(res) + } } pub fn save_user_workspaces(