feat: improve email workspace invitations

This commit is contained in:
Zack Fu Zi Xiang 2024-04-30 11:07:29 +08:00
parent 721f0c759b
commit 9adf3f883c
No known key found for this signature in database
5 changed files with 78 additions and 17 deletions

View File

@ -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"
}

View File

@ -30,7 +30,8 @@ RUN cargo build --profile=release --features "${FEATURES}" --bin appflowy_cloud
FROM debian:bookworm-slim AS runtime FROM debian:bookworm-slim AS runtime
WORKDIR /app WORKDIR /app
RUN apt-get update -y \ 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 # Clean up
&& apt-get autoremove -y \ && apt-get autoremove -y \
&& apt-get clean -y \ && apt-get clean -y \

View File

@ -4,7 +4,7 @@ use sqlx::{
types::{uuid, Uuid}, types::{uuid, Uuid},
Executor, PgPool, Postgres, Transaction, Executor, PgPool, Postgres, Transaction,
}; };
use std::ops::DerefMut; use std::{collections::HashSet, ops::DerefMut};
use tracing::{event, instrument}; use tracing::{event, instrument};
use crate::pg_row::AFWorkspaceMemberPermRow; use crate::pg_row::AFWorkspaceMemberPermRow;
@ -705,3 +705,22 @@ pub async fn select_workspace_member_count_from_workspace_id(
.await?; .await?;
Ok(workspace_count) Ok(workspace_count)
} }
#[inline]
pub async fn select_workspace_pending_invitations(
pool: &PgPool,
workspace_id: &Uuid,
) -> Result<HashSet<String>, 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())
}

View File

@ -297,11 +297,11 @@ async fn post_accept_workspace_invite_handler(
#[instrument(skip_all, err)] #[instrument(skip_all, err)]
async fn get_workspace_members_handler( async fn get_workspace_members_handler(
user_uuid: UserUuid, _user_uuid: UserUuid,
state: Data<AppState>, state: Data<AppState>,
workspace_id: web::Path<Uuid>, workspace_id: web::Path<Uuid>,
) -> Result<JsonAppResponse<Vec<AFWorkspaceMember>>> { ) -> Result<JsonAppResponse<Vec<AFWorkspaceMember>>> {
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? .await?
.into_iter() .into_iter()
.map(|member| AFWorkspaceMember { .map(|member| AFWorkspaceMember {

View File

@ -178,19 +178,39 @@ pub async fn invite_workspace_members(
.context("Begin transaction to invite workspace members")?; .context("Begin transaction to invite workspace members")?;
let admin_token = gotrue_admin.token(gotrue_client).await?; 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 inviter_name = database::user::select_name_from_uuid(pg_pool, inviter).await?; let workspace_name =
let workspace_name = database::workspace::select_workspace_name_from_workspace_id(pg_pool, workspace_id)
database::workspace::select_workspace_name_from_workspace_id(pg_pool, workspace_id) .await?
.await? .unwrap_or_default();
.unwrap_or_default(); let workspace_member_count =
let workspace_member_count = database::workspace::select_workspace_member_count_from_workspace_id(pg_pool, workspace_id)
database::workspace::select_workspace_member_count_from_workspace_id(pg_pool, workspace_id) .await?
.await? .unwrap_or_default();
.unwrap_or_default() let workspace_members_by_email: HashMap<_, _> =
.to_string(); 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 = let workspace_icon_url =
"https://miro.medium.com/v2/resize:fit:2400/1*mTPfm7CwU31-tLhtLNkyJw.png".to_string(); "https://miro.medium.com/v2/resize:fit:2400/1*mTPfm7CwU31-tLhtLNkyJw.png".to_string();
let user_icon_url = let user_icon_url =
@ -390,7 +410,6 @@ pub async fn remove_workspace_members(
pub async fn get_workspace_members( pub async fn get_workspace_members(
pg_pool: &PgPool, pg_pool: &PgPool,
_user_uuid: &Uuid,
workspace_id: &Uuid, workspace_id: &Uuid,
) -> Result<Vec<AFWorkspaceMemberRow>, AppResponseError> { ) -> Result<Vec<AFWorkspaceMemberRow>, AppResponseError> {
Ok(select_workspace_member_list(pg_pool, workspace_id).await?) Ok(select_workspace_member_list(pg_pool, workspace_id).await?)