package dangerzone import ( "fmt" "html" "strings" "time" "github.com/warmbly/warmbly/internal/models" ) // The danger-zone emails deliberately repeat the same essentials in every // message: what's being deleted, when it will happen (absolute UTC date), // and a single one-click cancel link. People skim, especially on phones. const emailWrapperOpen = `
` const emailWrapperClose = `

You're receiving this because a destructive action was scheduled on your account. If you didn't request this, cancel it right away and reset your password.

` func orgScheduledHTML(org *models.Organization, d *models.ScheduledDeletion, baseURL string) string { link := baseURL + "/organization/settings/danger-zone" var b strings.Builder b.WriteString(emailWrapperOpen) fmt.Fprintf(&b, `

Organization scheduled for deletion

`) fmt.Fprintf(&b, `

Your organization %s has been scheduled for permanent deletion.

`, html.EscapeString(org.Name)) b.WriteString(deletionDetailsBlock(d)) b.WriteString(`

What happens now

`) b.WriteString(``) fmt.Fprintf(&b, `

Cancel deletion

`, html.EscapeString(link)) b.WriteString(emailWrapperClose) return b.String() } func orgCancelledHTML(org *models.Organization, d *models.ScheduledDeletion) string { var b strings.Builder b.WriteString(emailWrapperOpen) fmt.Fprintf(&b, `

Deletion cancelled

`) fmt.Fprintf(&b, `

The scheduled deletion for %s has been cancelled. Your organization is safe and operating normally.

`, html.EscapeString(org.Name)) fmt.Fprintf(&b, `

Originally scheduled for: %s

`, formatUTC(d.ExecuteAfter)) b.WriteString(emailWrapperClose) return b.String() } func userScheduledHTML(user *models.User, d *models.ScheduledDeletion, baseURL string) string { link := baseURL + "/account/danger-zone" var b strings.Builder b.WriteString(emailWrapperOpen) fmt.Fprintf(&b, `

Account scheduled for deletion

`) fmt.Fprintf(&b, `

Hi %s, your Warmbly account has been scheduled for permanent deletion.

`, html.EscapeString(firstNameOrEmail(user))) b.WriteString(deletionDetailsBlock(d)) b.WriteString(`

What happens now

`) b.WriteString(``) fmt.Fprintf(&b, `

Cancel deletion

`, html.EscapeString(link)) b.WriteString(emailWrapperClose) return b.String() } func userCancelledHTML(user *models.User, d *models.ScheduledDeletion) string { var b strings.Builder b.WriteString(emailWrapperOpen) fmt.Fprintf(&b, `

Account deletion cancelled

`) fmt.Fprintf(&b, `

Hi %s, the scheduled deletion of your account has been cancelled. Your account is active and back to normal.

`, html.EscapeString(firstNameOrEmail(user))) fmt.Fprintf(&b, `

Originally scheduled for: %s

`, formatUTC(d.ExecuteAfter)) b.WriteString(emailWrapperClose) return b.String() } func reminderHTML(resourceName string, d *models.ScheduledDeletion, baseURL string) string { link := baseURL + "/account/danger-zone" if d.ResourceType == models.DeletionResourceOrganization { link = baseURL + "/organization/settings/danger-zone" } remaining := time.Until(d.ExecuteAfter) hours := int(remaining.Hours()) var window string switch { case hours <= 24: window = "less than 24 hours" case hours <= 24*8: window = fmt.Sprintf("about %d days", hours/24) default: window = fmt.Sprintf("%d days", hours/24) } var b strings.Builder b.WriteString(emailWrapperOpen) fmt.Fprintf(&b, `

Deletion in %s

`, window) fmt.Fprintf(&b, `

%s is scheduled to be permanently deleted on %s.

`, html.EscapeString(resourceName), formatUTC(d.ExecuteAfter)) b.WriteString(`

If you didn't mean to do this, cancel now while you still can. After the deletion runs, recovery is not possible.

`) fmt.Fprintf(&b, `

Cancel deletion

`, html.EscapeString(link)) b.WriteString(emailWrapperClose) return b.String() } func completionHTML(d *models.ScheduledDeletion) string { var b strings.Builder b.WriteString(emailWrapperOpen) fmt.Fprintf(&b, `

Deletion completed

`) fmt.Fprintf(&b, `

The scheduled deletion has been completed. All associated data has been permanently removed.

`) fmt.Fprintf(&b, `

Scheduled at: %s
Executed at: %s

`, formatUTC(d.ScheduledAt), formatUTC(time.Now())) b.WriteString(emailWrapperClose) return b.String() } func deletionDetailsBlock(d *models.ScheduledDeletion) string { var b strings.Builder b.WriteString(`
`) fmt.Fprintf(&b, `

Will be deleted on:

`) fmt.Fprintf(&b, `

%s

`, formatUTC(d.ExecuteAfter)) fmt.Fprintf(&b, `

Grace period: %d days. You can cancel anytime before that date.

`, d.GraceDays) b.WriteString(`
`) return b.String() } func formatUTC(t time.Time) string { return t.UTC().Format("Monday, 02 January 2006 at 15:04 UTC") } func firstNameOrEmail(u *models.User) string { if strings.TrimSpace(u.FirstName) != "" { return u.FirstName } return u.Email }