Files
2026-09-10 12:06:29 +00:00

56 lines
1.7 KiB
Go

package mail
import (
"fmt"
"time"
)
// VerifyWindow is how long a verification or invitation link stays usable. It
// is stated in the email, so it lives beside the message rather than in the
// caller.
const VerifyWindow = 24 * time.Hour
// SendVerification asks someone to confirm the address they signed up with.
// The link is built from the Sender's PublicURL, because the address of the
// portal is a property of the deployment, not of the call site.
func (s Sender) SendVerification(to, token string) error {
return s.sendTemplate(to, "", "verification", s.verificationData(to, token))
}
// verificationData is split from its Send method, as every message's is, so
// render_test.go renders the exact struct the message is sent with.
func (s Sender) verificationData(to, token string) any {
return struct {
Email string
Link string
TTLHours int
Expires time.Time
}{
Email: to,
Link: fmt.Sprintf("%s/verify?token=%s", s.PublicURL, token),
TTLHours: int(VerifyWindow.Hours()),
Expires: time.Now().UTC().Add(VerifyWindow),
}
}
// SendInvite asks someone to join an existing account and set their own
// password. It names the account, because an unexpected invitation from a
// service you have never used is otherwise indistinguishable from spam.
func (s Sender) SendInvite(to, accountName, token string) error {
return s.sendTemplate(to, "", "invite", s.inviteData(to, accountName, token))
}
func (s Sender) inviteData(to, accountName, token string) any {
return struct {
Email string
AccountName string
Link string
TTLHours int
}{
Email: to,
AccountName: accountName,
Link: fmt.Sprintf("%s/accept-invite?token=%s", s.PublicURL, token),
TTLHours: int(VerifyWindow.Hours()),
}
}