Skip to content

Commit ab1ed91

Browse files
committed
add a breadcrumb for better url resolution for new user invites
1 parent e17e3d5 commit ab1ed91

2 files changed

Lines changed: 18 additions & 1 deletion

File tree

internal/ent/hooks/invite.go

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,12 +102,23 @@ func HookInvite() ent.Hook {
102102
return retValue, ErrInternalServerError
103103
}
104104

105+
// check if the recipient already has an account so the invite link can route accordingly
106+
recipientExists, err := m.Client().User.Query().
107+
Where(user.EmailEqualFold(emailAddress)).
108+
Exist(privacy.DecisionContext(ctx, privacy.Allow))
109+
if err != nil {
110+
logx.FromContext(ctx).Error().Err(err).Msg("error checking recipient account existence")
111+
112+
recipientExists = true
113+
}
114+
105115
if err := sendSystemEmail(ctx, m.Client(), emaildef.InviteOp.Name(), emaildef.InviteRequest{
106116
RecipientInfo: emaildef.RecipientInfo{Email: emailAddress},
107117
InviterName: inviterName,
108118
OrgName: orgName,
109119
Role: string(role),
110120
Token: tokenValue,
121+
NewUser: !recipientExists,
111122
}); err != nil {
112123
logx.FromContext(ctx).Error().Err(err).Msg("error sending email to user")
113124

internal/integrations/definitions/email/system_emails.go

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,8 @@ type InviteRequest struct {
9999
Role string `json:"role,omitempty" jsonschema:"description=Invited role"`
100100
// Token is the invite token appended to the invite URL
101101
Token string `json:"token" jsonschema:"required,description=Invite token"`
102+
// NewUser indicates the recipient has no existing account
103+
NewUser bool `json:"newUser,omitempty" jsonschema:"description=Whether the recipient has no existing account"`
102104
}
103105

104106
// InviteJoinedRequest is the input for the invite-accepted notification
@@ -320,7 +322,11 @@ var _ = RegisterEmailOperation(Operation[InviteRequest]{
320322
return "Join Your Teammate " + req.InviterName + " on " + cfg.CompanyName + "!"
321323
},
322324
Build: func(cfg RuntimeEmailConfig, req InviteRequest) render.ContentBody {
323-
inviteURL := tokenURL(cfg.ProductURL, "/invite", req.Token)
325+
// append the recipient email and new-account hint so the invite page can route the recipient
326+
inviteURL := tokenURL(cfg.ProductURL, "/invite", req.Token) + "&email=" + url.QueryEscape(req.Email)
327+
if req.NewUser {
328+
inviteURL += "&new=true"
329+
}
324330
inviteIntro := template.HTML("You're in - let's build trust without the busywork. "+html.EscapeString(req.InviterName)+" has invited you to collaborate in "+html.EscapeString(cfg.CompanyName)+", as part of the ") + //nolint:gosec // all user input is escaped via html.EscapeString
325331
render.Bold(req.OrgName) + " organization"
326332
if req.Role != "" {

0 commit comments

Comments
 (0)