diff --git a/.sqlx/query-374c2d7b26f923328edd09b0d515d59426119615a8d5d8a46101f6ccec00d617.json b/.sqlx/query-374c2d7b26f923328edd09b0d515d59426119615a8d5d8a46101f6ccec00d617.json new file mode 100644 index 00000000..0c14c86a --- /dev/null +++ b/.sqlx/query-374c2d7b26f923328edd09b0d515d59426119615a8d5d8a46101f6ccec00d617.json @@ -0,0 +1,22 @@ +{ + "db_name": "PostgreSQL", + "query": "\n SELECT invitee_email\n FROM public.af_workspace_invitation\n WHERE workspace_id = $1\n AND status = 0\n ", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "invitee_email", + "type_info": "Text" + } + ], + "parameters": { + "Left": [ + "Uuid" + ] + }, + "nullable": [ + false + ] + }, + "hash": "374c2d7b26f923328edd09b0d515d59426119615a8d5d8a46101f6ccec00d617" +} diff --git a/Dockerfile b/Dockerfile index 2e7dcf7b..c2edccce 100644 --- a/Dockerfile +++ b/Dockerfile @@ -30,7 +30,8 @@ RUN cargo build --profile=release --features "${FEATURES}" --bin appflowy_cloud FROM debian:bookworm-slim AS runtime WORKDIR /app RUN apt-get update -y \ - && apt-get install -y --no-install-recommends openssl \ + && apt-get install -y --no-install-recommends openssl ca-certificates \ + && update-ca-certificates \ # Clean up && apt-get autoremove -y \ && apt-get clean -y \ diff --git a/libs/database/src/workspace.rs b/libs/database/src/workspace.rs index bcaf677a..188c2149 100644 --- a/libs/database/src/workspace.rs +++ b/libs/database/src/workspace.rs @@ -4,7 +4,7 @@ use sqlx::{ types::{uuid, Uuid}, Executor, PgPool, Postgres, Transaction, }; -use std::ops::DerefMut; +use std::{collections::HashSet, ops::DerefMut}; use tracing::{event, instrument}; use crate::pg_row::AFWorkspaceMemberPermRow; @@ -705,3 +705,22 @@ pub async fn select_workspace_member_count_from_workspace_id( .await?; Ok(workspace_count) } + +#[inline] +pub async fn select_workspace_pending_invitations( + pool: &PgPool, + workspace_id: &Uuid, +) -> Result, AppError> { + let invitee_emails = sqlx::query_scalar!( + r#" + SELECT invitee_email + FROM public.af_workspace_invitation + WHERE workspace_id = $1 + AND status = 0 + "#, + workspace_id + ) + .fetch_all(pool) + .await?; + Ok(invitee_emails.into_iter().collect()) +} diff --git a/src/api/workspace.rs b/src/api/workspace.rs index 02a2d0a6..23a2da2f 100644 --- a/src/api/workspace.rs +++ b/src/api/workspace.rs @@ -297,11 +297,11 @@ async fn post_accept_workspace_invite_handler( #[instrument(skip_all, err)] async fn get_workspace_members_handler( - user_uuid: UserUuid, + _user_uuid: UserUuid, state: Data, workspace_id: web::Path, ) -> Result>> { - let members = workspace::ops::get_workspace_members(&state.pg_pool, &user_uuid, &workspace_id) + let members = workspace::ops::get_workspace_members(&state.pg_pool, &workspace_id) .await? .into_iter() .map(|member| AFWorkspaceMember { diff --git a/src/biz/workspace/ops.rs b/src/biz/workspace/ops.rs index 96a5e02c..b509090a 100644 --- a/src/biz/workspace/ops.rs +++ b/src/biz/workspace/ops.rs @@ -178,19 +178,39 @@ pub async fn invite_workspace_members( .context("Begin transaction to invite workspace members")?; let admin_token = gotrue_admin.token(gotrue_client).await?; - for invitation in invitations { - let inviter_name = database::user::select_name_from_uuid(pg_pool, inviter).await?; - let workspace_name = - database::workspace::select_workspace_name_from_workspace_id(pg_pool, workspace_id) - .await? - .unwrap_or_default(); - let workspace_member_count = - database::workspace::select_workspace_member_count_from_workspace_id(pg_pool, workspace_id) - .await? - .unwrap_or_default() - .to_string(); + let inviter_name = database::user::select_name_from_uuid(pg_pool, inviter).await?; + let workspace_name = + database::workspace::select_workspace_name_from_workspace_id(pg_pool, workspace_id) + .await? + .unwrap_or_default(); + let workspace_member_count = + database::workspace::select_workspace_member_count_from_workspace_id(pg_pool, workspace_id) + .await? + .unwrap_or_default(); + let workspace_members_by_email: HashMap<_, _> = + database::workspace::select_workspace_member_list(pg_pool, workspace_id) + .await? + .into_iter() + .map(|row| (row.email, row.role)) + .collect(); + let pending_invitations = + database::workspace::select_workspace_pending_invitations(pg_pool, workspace_id).await?; - // default icon until we have workspace icon + for invitation in invitations { + if workspace_members_by_email.contains_key(&invitation.email) { + tracing::warn!("User already in workspace: {}", invitation.email); + continue; + } + if pending_invitations.contains(&invitation.email) { + tracing::warn!("User already invited: {}", invitation.email); + continue; + } + + let inviter_name = inviter_name.clone(); + let workspace_name = workspace_name.clone(); + let workspace_member_count = workspace_member_count.to_string(); + + // use default icon until we have workspace icon let workspace_icon_url = "https://miro.medium.com/v2/resize:fit:2400/1*mTPfm7CwU31-tLhtLNkyJw.png".to_string(); let user_icon_url = @@ -390,7 +410,6 @@ pub async fn remove_workspace_members( pub async fn get_workspace_members( pg_pool: &PgPool, - _user_uuid: &Uuid, workspace_id: &Uuid, ) -> Result, AppResponseError> { Ok(select_workspace_member_list(pg_pool, workspace_id).await?)