Hallo {{.Name}},
Vielen Dank für Ihre Registrierung! Bitte bestätigen Sie Ihre E-Mail-Adresse, um Ihr Konto zu aktivieren.
Dieser Link ist 24 Stunden gültig.
Falls Sie sich nicht bei BreakPilot registriert haben, können Sie diese E-Mail ignorieren.
package services import ( "bytes" "fmt" "html/template" "net/smtp" "strings" ) // EmailConfig holds SMTP configuration type EmailConfig struct { Host string Port int Username string Password string FromName string FromAddr string BaseURL string // Frontend URL for links } // EmailService handles sending emails type EmailService struct { config EmailConfig } // NewEmailService creates a new EmailService func NewEmailService(config EmailConfig) *EmailService { return &EmailService{config: config} } // SendEmail sends an email func (s *EmailService) SendEmail(to, subject, htmlBody, textBody string) error { // Build MIME message var msg bytes.Buffer msg.WriteString(fmt.Sprintf("From: %s <%s>\r\n", s.config.FromName, s.config.FromAddr)) msg.WriteString(fmt.Sprintf("To: %s\r\n", to)) msg.WriteString(fmt.Sprintf("Subject: %s\r\n", subject)) msg.WriteString("MIME-Version: 1.0\r\n") msg.WriteString("Content-Type: multipart/alternative; boundary=\"boundary42\"\r\n") msg.WriteString("\r\n") // Text part msg.WriteString("--boundary42\r\n") msg.WriteString("Content-Type: text/plain; charset=\"UTF-8\"\r\n") msg.WriteString("\r\n") msg.WriteString(textBody) msg.WriteString("\r\n") // HTML part msg.WriteString("--boundary42\r\n") msg.WriteString("Content-Type: text/html; charset=\"UTF-8\"\r\n") msg.WriteString("\r\n") msg.WriteString(htmlBody) msg.WriteString("\r\n") msg.WriteString("--boundary42--\r\n") // Send email addr := fmt.Sprintf("%s:%d", s.config.Host, s.config.Port) auth := smtp.PlainAuth("", s.config.Username, s.config.Password, s.config.Host) err := smtp.SendMail(addr, auth, s.config.FromAddr, []string{to}, msg.Bytes()) if err != nil { return fmt.Errorf("failed to send email: %w", err) } return nil } // SendVerificationEmail sends an email verification email func (s *EmailService) SendVerificationEmail(to, name, token string) error { verifyLink := fmt.Sprintf("%s/verify-email?token=%s", s.config.BaseURL, token) subject := "Bitte bestätigen Sie Ihre E-Mail-Adresse - BreakPilot" textBody := fmt.Sprintf(`Hallo %s, Willkommen bei BreakPilot! Bitte bestätigen Sie Ihre E-Mail-Adresse, indem Sie den folgenden Link öffnen: %s Dieser Link ist 24 Stunden gültig. Falls Sie sich nicht bei BreakPilot registriert haben, können Sie diese E-Mail ignorieren. Mit freundlichen Grüßen, Ihr BreakPilot Team`, getDisplayName(name), verifyLink) htmlBody := s.renderTemplate("verification", map[string]interface{}{ "Name": getDisplayName(name), "VerifyLink": verifyLink, }) return s.SendEmail(to, subject, htmlBody, textBody) } // SendPasswordResetEmail sends a password reset email func (s *EmailService) SendPasswordResetEmail(to, name, token string) error { resetLink := fmt.Sprintf("%s/reset-password?token=%s", s.config.BaseURL, token) subject := "Passwort zurücksetzen - BreakPilot" textBody := fmt.Sprintf(`Hallo %s, Sie haben eine Anfrage zum Zurücksetzen Ihres Passworts gestellt. Klicken Sie auf den folgenden Link, um Ihr Passwort zurückzusetzen: %s Dieser Link ist 1 Stunde gültig. Falls Sie keine Passwort-Zurücksetzung angefordert haben, können Sie diese E-Mail ignorieren. Mit freundlichen Grüßen, Ihr BreakPilot Team`, getDisplayName(name), resetLink) htmlBody := s.renderTemplate("password_reset", map[string]interface{}{ "Name": getDisplayName(name), "ResetLink": resetLink, }) return s.SendEmail(to, subject, htmlBody, textBody) } // SendNewVersionNotification sends a notification about new document version func (s *EmailService) SendNewVersionNotification(to, name, documentName, documentType string, deadlineDays int) error { consentLink := fmt.Sprintf("%s/app?consent=pending", s.config.BaseURL) subject := fmt.Sprintf("Neue Version: %s - Bitte bestätigen Sie innerhalb von %d Tagen", documentName, deadlineDays) textBody := fmt.Sprintf(`Hallo %s, Wir haben unsere %s aktualisiert. Bitte lesen und bestätigen Sie die neuen Bedingungen innerhalb der nächsten %d Tage: %s Falls Sie nicht innerhalb dieser Frist bestätigen, wird Ihr Account vorübergehend gesperrt. Mit freundlichen Grüßen, Ihr BreakPilot Team`, getDisplayName(name), documentName, deadlineDays, consentLink) htmlBody := s.renderTemplate("new_version", map[string]interface{}{ "Name": getDisplayName(name), "DocumentName": documentName, "DeadlineDays": deadlineDays, "ConsentLink": consentLink, }) return s.SendEmail(to, subject, htmlBody, textBody) } // SendConsentReminder sends a consent reminder email func (s *EmailService) SendConsentReminder(to, name string, documents []string, daysLeft int) error { consentLink := fmt.Sprintf("%s/app?consent=pending", s.config.BaseURL) urgency := "Erinnerung" if daysLeft <= 7 { urgency = "Dringend" } if daysLeft <= 2 { urgency = "Letzte Warnung" } subject := fmt.Sprintf("%s: Noch %d Tage um ausstehende Dokumente zu bestätigen", urgency, daysLeft) docList := strings.Join(documents, "\n- ") textBody := fmt.Sprintf(`Hallo %s, Dies ist eine freundliche Erinnerung, dass Sie noch ausstehende rechtliche Dokumente bestätigen müssen. Ausstehende Dokumente: - %s Sie haben noch %d Tage Zeit. Nach Ablauf dieser Frist wird Ihr Account vorübergehend gesperrt. Bitte bestätigen Sie hier: %s Mit freundlichen Grüßen, Ihr BreakPilot Team`, getDisplayName(name), docList, daysLeft, consentLink) htmlBody := s.renderTemplate("reminder", map[string]interface{}{ "Name": getDisplayName(name), "Documents": documents, "DaysLeft": daysLeft, "Urgency": urgency, "ConsentLink": consentLink, }) return s.SendEmail(to, subject, htmlBody, textBody) } // SendAccountSuspendedNotification sends notification when account is suspended func (s *EmailService) SendAccountSuspendedNotification(to, name string, documents []string) error { consentLink := fmt.Sprintf("%s/app?consent=pending", s.config.BaseURL) subject := "Ihr Account wurde vorübergehend gesperrt - BreakPilot" docList := strings.Join(documents, "\n- ") textBody := fmt.Sprintf(`Hallo %s, Ihr Account wurde vorübergehend gesperrt, da Sie die folgenden rechtlichen Dokumente nicht innerhalb der Frist bestätigt haben: - %s Um Ihren Account zu entsperren, bestätigen Sie bitte alle ausstehenden Dokumente: %s Sobald Sie alle Dokumente bestätigt haben, wird Ihr Account automatisch entsperrt. Mit freundlichen Grüßen, Ihr BreakPilot Team`, getDisplayName(name), docList, consentLink) htmlBody := s.renderTemplate("suspended", map[string]interface{}{ "Name": getDisplayName(name), "Documents": documents, "ConsentLink": consentLink, }) return s.SendEmail(to, subject, htmlBody, textBody) } // SendAccountReactivatedNotification sends notification when account is reactivated func (s *EmailService) SendAccountReactivatedNotification(to, name string) error { appLink := fmt.Sprintf("%s/app", s.config.BaseURL) subject := "Ihr Account wurde wieder aktiviert - BreakPilot" textBody := fmt.Sprintf(`Hallo %s, Vielen Dank für die Bestätigung der rechtlichen Dokumente! Ihr Account wurde wieder aktiviert und Sie können BreakPilot wie gewohnt nutzen: %s Mit freundlichen Grüßen, Ihr BreakPilot Team`, getDisplayName(name), appLink) htmlBody := s.renderTemplate("reactivated", map[string]interface{}{ "Name": getDisplayName(name), "AppLink": appLink, }) return s.SendEmail(to, subject, htmlBody, textBody) } // renderTemplate renders an email HTML template func (s *EmailService) renderTemplate(templateName string, data map[string]interface{}) string { templates := map[string]string{ "verification": `
Hallo {{.Name}},
Vielen Dank für Ihre Registrierung! Bitte bestätigen Sie Ihre E-Mail-Adresse, um Ihr Konto zu aktivieren.
Dieser Link ist 24 Stunden gültig.
Falls Sie sich nicht bei BreakPilot registriert haben, können Sie diese E-Mail ignorieren.
Hallo {{.Name}},
Sie haben eine Anfrage zum Zurücksetzen Ihres Passworts gestellt.
Falls Sie keine Passwort-Zurücksetzung angefordert haben, können Sie diese E-Mail ignorieren.
Hallo {{.Name}},
Wir haben unsere {{.DocumentName}} aktualisiert.
Falls Sie nicht innerhalb dieser Frist bestätigen, wird Ihr Account vorübergehend gesperrt.
Hallo {{.Name}},
Dies ist eine freundliche Erinnerung, dass Sie noch ausstehende rechtliche Dokumente bestätigen müssen.
Hallo {{.Name}},
Um Ihren Account zu entsperren, bestätigen Sie bitte alle ausstehenden Dokumente:
Dokumente bestätigen & Account entsperren
Sobald Sie alle Dokumente bestätigt haben, wird Ihr Account automatisch entsperrt.
Hallo {{.Name}},
Sie können BreakPilot ab sofort wieder wie gewohnt nutzen.
{{.Body}}