fix
This commit is contained in:
@@ -217,34 +217,18 @@ func (s *Server) handleCreateInvite(w http.ResponseWriter, r *http.Request) {
|
||||
ClientOrLog(w, http.StatusBadRequest, "could not create invite", err, auth.ClientError)
|
||||
return
|
||||
}
|
||||
companyName, _ := s.Auth.CompanyName(r.Context(), cid)
|
||||
smtpOn := s.Mail != nil && s.Mail.Enabled()
|
||||
sendOK := false
|
||||
if s.Mail != nil {
|
||||
msg := mail.InviteMessage(s.Config.WebOrigin, inv.Email, token, companyName)
|
||||
if err := s.Mail.Send(msg); err == nil {
|
||||
sendOK = true
|
||||
}
|
||||
}
|
||||
// noop/disabled mailers return nil from Send; only count real SMTP as delivered.
|
||||
mailSent, includeToken := inviteMailResult(smtpOn, sendOK)
|
||||
resp := map[string]any{
|
||||
"id": inv.ID, "email": inv.Email, "role": inv.Role,
|
||||
"expires_at": inv.ExpiresAt, "mail_sent": mailSent, "smtp_enabled": smtpOn,
|
||||
}
|
||||
// Token returned when email was not delivered so operators can share the accept link.
|
||||
if includeToken {
|
||||
resp["token"] = token
|
||||
resp["accept_url"] = mail.AcceptInviteURL(s.Config.WebOrigin, token)
|
||||
}
|
||||
JSON(w, http.StatusCreated, resp)
|
||||
}
|
||||
|
||||
// inviteMailResult decides mail_sent and whether the accept token must be returned to the client.
|
||||
func inviteMailResult(smtpEnabled, sendOK bool) (mailSent bool, includeToken bool) {
|
||||
mailSent = smtpEnabled && sendOK
|
||||
includeToken = !mailSent
|
||||
return
|
||||
// Invites are link-based: always return the one-time accept URL for the admin to share.
|
||||
// Email delivery is not used for team invites (SMTP may still be used for password reset, etc.).
|
||||
JSON(w, http.StatusCreated, map[string]any{
|
||||
"id": inv.ID,
|
||||
"email": inv.Email,
|
||||
"role": inv.Role,
|
||||
"expires_at": inv.ExpiresAt,
|
||||
"mail_sent": false,
|
||||
"smtp_enabled": s.Mail != nil && s.Mail.Enabled(),
|
||||
"token": token,
|
||||
"accept_url": mail.AcceptInviteURL(s.Config.WebOrigin, token),
|
||||
})
|
||||
}
|
||||
|
||||
func (s *Server) handleRemoveMember(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
@@ -1,31 +1,20 @@
|
||||
package httpapi
|
||||
|
||||
import "testing"
|
||||
import (
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
func TestInviteMailResult(t *testing.T) {
|
||||
"github.com/descrybe/descrybe-v2/apps/api/internal/mail"
|
||||
)
|
||||
|
||||
func TestInviteAcceptURLAlwaysShareable(t *testing.T) {
|
||||
t.Parallel()
|
||||
cases := []struct {
|
||||
name string
|
||||
smtpEnabled bool
|
||||
sendOK bool
|
||||
wantMailSent bool
|
||||
wantToken bool
|
||||
}{
|
||||
{name: "smtp_ok", smtpEnabled: true, sendOK: true, wantMailSent: true, wantToken: false},
|
||||
{name: "smtp_send_fail", smtpEnabled: true, sendOK: false, wantMailSent: false, wantToken: true},
|
||||
{name: "noop_mailer_send_ok", smtpEnabled: false, sendOK: true, wantMailSent: false, wantToken: true},
|
||||
{name: "disabled_no_send", smtpEnabled: false, sendOK: false, wantMailSent: false, wantToken: true},
|
||||
url := mail.AcceptInviteURL("https://app.example.com/", "tok_abc")
|
||||
want := "https://app.example.com/accept-invite?token=tok_abc"
|
||||
if url != want {
|
||||
t.Fatalf("AcceptInviteURL = %q want %q", url, want)
|
||||
}
|
||||
for _, tc := range cases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
mailSent, includeToken := inviteMailResult(tc.smtpEnabled, tc.sendOK)
|
||||
if mailSent != tc.wantMailSent {
|
||||
t.Fatalf("mailSent=%v want %v", mailSent, tc.wantMailSent)
|
||||
}
|
||||
if includeToken != tc.wantToken {
|
||||
t.Fatalf("includeToken=%v want %v", includeToken, tc.wantToken)
|
||||
}
|
||||
})
|
||||
if strings.Contains(url, "mailto:") {
|
||||
t.Fatal("invite URL must not be mailto")
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user