package invitations import ( "fmt" "html" "strings" "time" "github.com/Silo-Server/silo-server/internal/mail" ) // emailContent is one rendered invitation email. type emailContent struct { Subject string Text string HTML string } // composeInvitationEmail renders the invitation message. inviterName and // note are admin-controlled but escaped anyway; claimURL is server-built. func composeInvitationEmail(inviterName, serverName, email, claimURL, note string, expiresAt time.Time, now time.Time) emailContent { inviter := strings.TrimSpace(inviterName) if inviter == "" { inviter = "An admin" } product := strings.TrimSpace(serverName) if product == "" { product = "Silo" } expiry := expiryPhrase(expiresAt, now) fine := fmt.Sprintf( "This link works once and expires %s. If you weren't expecting this, "+ "ignore it — no account is created until you use the link.", expiry) var text strings.Builder fmt.Fprintf(&text, "%s set up an account for you on %s.\n\n", inviter, product) if note != "" { fmt.Fprintf(&text, "%q\n\n", note) } fmt.Fprintf(&text, "To choose a password and get started, open this link:\n\n %s\n\n", claimURL) fmt.Fprintf(&text, "You'll sign in with this email address: %s\n\n%s\n", email, fine) var body strings.Builder body.WriteString(mail.EmailParagraph( fmt.Sprintf("%s set up an account for you on %s.", inviter, product))) if note != "" { body.WriteString(`
“` + html.EscapeString(note) + `”
`) } body.WriteString(mail.EmailButton("Set your password", claimURL)) body.WriteString(`Or paste this link into your browser:
`+
`%s