diff --git a/admin_frontend/src/ext/api.rs b/admin_frontend/src/ext/api.rs index 4fdd6d4d..c4c1d883 100644 --- a/admin_frontend/src/ext/api.rs +++ b/admin_frontend/src/ext/api.rs @@ -238,6 +238,26 @@ pub async fn invite_user_to_workspace( check_response(resp).await } +pub async fn leave_workspace( + access_token: &str, + workspace_id: &str, + appflowy_cloud_base_url: &str, +) -> Result<(), Error> { + let http_client = reqwest::Client::new(); + let url = format!( + "{}/api/workspace/{}/leave", + appflowy_cloud_base_url, workspace_id + ); + let resp = http_client + .post(url) + .header("Authorization", format!("Bearer {}", access_token)) + .json(&()) + .send() + .await?; + + check_response(resp).await +} + pub async fn accept_workspace_invitation( access_token: &str, invite_id: &str, diff --git a/admin_frontend/src/templates.rs b/admin_frontend/src/templates.rs index f5885ea5..14ebecf3 100644 --- a/admin_frontend/src/templates.rs +++ b/admin_frontend/src/templates.rs @@ -64,6 +64,12 @@ pub struct Invite { pub pending_workspace_invitations: Vec, } +#[derive(Template)] +#[template(path = "components/shared_workspaces.html")] +pub struct SharedWorkspaces { + pub shared_workspaces: Vec, +} + #[derive(Template)] #[template(path = "components/admin_navigate.html")] pub struct AdminNavigate; diff --git a/admin_frontend/src/web_api.rs b/admin_frontend/src/web_api.rs index 09274fa9..3eed0e94 100644 --- a/admin_frontend/src/web_api.rs +++ b/admin_frontend/src/web_api.rs @@ -1,5 +1,7 @@ use crate::error::WebApiError; -use crate::ext::api::{accept_workspace_invitation, invite_user_to_workspace, verify_token_cloud}; +use crate::ext::api::{ + accept_workspace_invitation, invite_user_to_workspace, leave_workspace, verify_token_cloud, +}; use crate::models::{ WebApiAdminCreateUserRequest, WebApiChangePasswordRequest, WebApiCreateSSOProviderRequest, WebApiInviteUserRequest, WebApiPutUserRequest, @@ -34,6 +36,7 @@ pub fn router() -> Router { .route("/oauth_login/:provider", post(post_oauth_login_handler)) .route("/invite", post(invite_handler)) .route("/workspace/:workspace_id/invite", post(workspace_invite_handler)) + .route("/workspace/:workspace_id/leave", post(leave_workspace_handler)) .route("/invite/:invite_id/accept", post(invite_accept_handler)) .route("/open_app", post(open_app_handler)) @@ -148,11 +151,26 @@ pub async fn workspace_invite_handler( Ok(WebApiResponse::<()>::from_str("Invitation sent".into())) } +pub async fn leave_workspace_handler( + State(state): State, + session: UserSession, + Path(workspace_id): Path, +) -> Result, WebApiError<'static>> { + leave_workspace( + &session.token.access_token, + &workspace_id, + &state.appflowy_cloud_url, + ) + .await?; + + Ok(WebApiResponse::<()>::from_str("Left workspace".into())) +} + pub async fn invite_accept_handler( State(state): State, session: UserSession, Path(invite_id): Path, -) -> Result, WebApiError<'static>> { +) -> Result> { accept_workspace_invitation( &session.token.access_token, &invite_id, @@ -160,9 +178,7 @@ pub async fn invite_accept_handler( ) .await?; - Ok(WebApiResponse::<()>::from_str( - "Invitation accepted.\nPlease refresh page to see changes".into(), - )) + Ok(htmx_trigger("workspaceInvitationAccepted")) } pub async fn change_password_handler( @@ -300,6 +316,12 @@ pub async fn login_refresh_handler( )) .await?; + verify_token_cloud( + token.access_token.as_str(), + state.appflowy_cloud_url.as_str(), + ) + .await?; + let new_session_id = uuid::Uuid::new_v4(); let new_session = session::UserSession::new(new_session_id.to_string(), token); state.session_store.put_user_session(&new_session).await?; @@ -390,6 +412,12 @@ fn htmx_redirect(url: &str) -> HeaderMap { h } +fn htmx_trigger(trigger: &str) -> HeaderMap { + let mut h = HeaderMap::new(); + h.insert("HX-Trigger", trigger.parse().unwrap()); + h +} + fn new_session_cookie(id: uuid::Uuid) -> Cookie<'static> { let mut cookie = Cookie::new("session_id", id.to_string()); cookie.set_path("/"); diff --git a/admin_frontend/src/web_app.rs b/admin_frontend/src/web_app.rs index bdf3d53e..e3ab5f19 100644 --- a/admin_frontend/src/web_app.rs +++ b/admin_frontend/src/web_app.rs @@ -32,8 +32,9 @@ pub fn component_router() -> Router { // User actions .route("/user/navigate", get(user_navigate_handler)) .route("/user/user", get(user_user_handler)) - .route("/user/change_password", get(user_change_password_handler)) + .route("/user/change-password", get(user_change_password_handler)) .route("/user/invite", get(user_invite_handler)) + .route("/user/shared-workspaces", get(shared_workspaces_handler)) .route("/user/user-usage", get(user_usage_handler)) .route("/user/workspace-usage", get(workspace_usage_handler)) @@ -93,14 +94,34 @@ pub async fn admin_navigate_handler() -> Result, WebAppError> { render_template(templates::AdminNavigate) } +pub async fn shared_workspaces_handler( + State(state): State, + session: UserSession, +) -> Result, WebAppError> { + let user_workspaces = + get_user_workspaces(&session.token.access_token, &state.appflowy_cloud_url).await?; + + let profile = get_user_profile( + session.token.access_token.as_str(), + state.appflowy_cloud_url.as_str(), + ) + .await?; + + let shared_workspaces = user_workspaces + .into_iter() + .filter(|workspace| workspace.owner_uid != profile.uid) + .collect::>(); + + render_template(templates::SharedWorkspaces { shared_workspaces }) +} + pub async fn user_invite_handler( State(state): State, session: UserSession, ) -> Result, WebAppError> { let user_workspaces = - get_user_workspaces(&session.token.access_token, &state.appflowy_cloud_url).await; + get_user_workspaces(&session.token.access_token, &state.appflowy_cloud_url).await?; - let user_workspaces = user_workspaces?; let profile = get_user_profile( session.token.access_token.as_str(), state.appflowy_cloud_url.as_str(), diff --git a/admin_frontend/templates/components/change_password.html b/admin_frontend/templates/components/change_password.html index 204d6910..1cd69bb9 100644 --- a/admin_frontend/templates/components/change_password.html +++ b/admin_frontend/templates/components/change_password.html @@ -1,6 +1,6 @@

Password Change

-
+ diff --git a/admin_frontend/templates/components/invite.html b/admin_frontend/templates/components/invite.html index 748b6299..1cf55635 100644 --- a/admin_frontend/templates/components/invite.html +++ b/admin_frontend/templates/components/invite.html @@ -24,20 +24,13 @@

Workspaces shared with you

-
New Password:
- - - - - - - {% for shared_workspace in shared_workspaces %} - - - - - {% endfor %} -
Workspace NameOwner Name
{{ shared_workspace.workspace_name|escape }} {{ shared_workspace.owner_name|escape }}
+
+ {% include "shared_workspaces.html" %} +

Invite another user to your workspace

@@ -87,7 +80,11 @@ {{ pending_workspace_invitation.workspace_name|default("")|escape }} {{ pending_workspace_invitation.inviter_email|default("")|escape }} - +
diff --git a/admin_frontend/templates/components/shared_workspaces.html b/admin_frontend/templates/components/shared_workspaces.html new file mode 100644 index 00000000..3e148a8d --- /dev/null +++ b/admin_frontend/templates/components/shared_workspaces.html @@ -0,0 +1,26 @@ + + + + + + + + +{% for shared_workspace in shared_workspaces %} + + + + + +{% endfor %} +
Workspace NameOwner NameAction
{{ shared_workspace.workspace_name|escape }} {{ shared_workspace.owner_name|escape }} + +
diff --git a/admin_frontend/templates/components/sidebar.html b/admin_frontend/templates/components/sidebar.html index fae7afa6..2141bf71 100644 --- a/admin_frontend/templates/components/sidebar.html +++ b/admin_frontend/templates/components/sidebar.html @@ -9,7 +9,7 @@ diff --git a/docker-compose.yml b/docker-compose.yml index 5f153940..4f5c0cc4 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -124,6 +124,7 @@ services: - RUST_LOG=${RUST_LOG:-info} - ADMIN_FRONTEND_REDIS_URL=${ADMIN_FRONTEND_REDIS_URL:-redis://redis:6379} - ADMIN_FRONTEND_GOTRUE_URL=${ADMIN_FRONTEND_GOTRUE_URL:-http://gotrue:9999} + - ADMIN_FRONTEND_APPFLOWY_CLOUD_URL=${ADMIN_FRONTEND_APPFLOWY_CLOUD_URL:-http://appflowy_cloud:8000} volumes: postgres_data: