package email import ( "bytes" "encoding/json" "fmt" "net/http" "os" "text/template" ) // EmailService handles transactional email sending type EmailService struct { apiKey string fromAddr string baseURL string } // NewEmailService creates a new email service using Resend API func NewEmailService() *EmailService { apiKey := os.Getenv("RESEND_API_KEY") fromAddr := os.Getenv("EMAIL_FROM") if fromAddr == "" { fromAddr = "noreply@gohorsejobs.com" } return &EmailService{ apiKey: apiKey, fromAddr: fromAddr, baseURL: "https://api.resend.com/emails", } } // IsConfigured returns true if email service is properly configured func (s *EmailService) IsConfigured() bool { return s.apiKey != "" } // EmailPayload represents the Resend API request body type EmailPayload struct { From string `json:"from"` To []string `json:"to"` Subject string `json:"subject"` HTML string `json:"html"` Text string `json:"text,omitempty"` } // SendEmail sends an email via Resend API func (s *EmailService) SendEmail(to []string, subject, htmlBody, textBody string) error { if !s.IsConfigured() { return fmt.Errorf("email service not configured: RESEND_API_KEY missing") } payload := EmailPayload{ From: s.fromAddr, To: to, Subject: subject, HTML: htmlBody, Text: textBody, } jsonBody, err := json.Marshal(payload) if err != nil { return fmt.Errorf("failed to marshal email payload: %w", err) } req, err := http.NewRequest("POST", s.baseURL, bytes.NewBuffer(jsonBody)) if err != nil { return fmt.Errorf("failed to create request: %w", err) } req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", s.apiKey)) req.Header.Set("Content-Type", "application/json") client := &http.Client{} resp, err := client.Do(req) if err != nil { return fmt.Errorf("failed to send email: %w", err) } defer resp.Body.Close() if resp.StatusCode >= 400 { return fmt.Errorf("email API error: status %d", resp.StatusCode) } return nil } // WelcomeEmailData contains data for welcome email template type WelcomeEmailData struct { Name string Email string AppURL string AppName string } // SendWelcome sends a welcome email to new users func (s *EmailService) SendWelcome(to, name string) error { data := WelcomeEmailData{ Name: name, Email: to, AppURL: os.Getenv("APP_URL"), AppName: "GoHorse Jobs", } html, err := renderTemplate(welcomeTemplate, data) if err != nil { return err } return s.SendEmail([]string{to}, "🐴 Bem-vindo ao GoHorse Jobs!", html, "") } // PasswordResetData contains data for password reset email type PasswordResetData struct { Name string ResetLink string AppName string ExpiresIn string } // SendPasswordReset sends a password reset email func (s *EmailService) SendPasswordReset(to, name, resetToken string) error { appURL := os.Getenv("APP_URL") if appURL == "" { appURL = "https://gohorsejobs.com" } data := PasswordResetData{ Name: name, ResetLink: fmt.Sprintf("%s/reset-password?token=%s", appURL, resetToken), AppName: "GoHorse Jobs", ExpiresIn: "1 hora", } html, err := renderTemplate(passwordResetTemplate, data) if err != nil { return err } return s.SendEmail([]string{to}, "🔑 Redefinição de senha - GoHorse Jobs", html, "") } // ApplicationReceivedData contains data for application notification type ApplicationReceivedData struct { ApplicantName string JobTitle string CompanyName string JobURL string AppName string } // SendApplicationReceived notifies recruiter about new application func (s *EmailService) SendApplicationReceived(to, applicantName, jobTitle, companyName, jobID string) error { appURL := os.Getenv("APP_URL") if appURL == "" { appURL = "https://gohorsejobs.com" } data := ApplicationReceivedData{ ApplicantName: applicantName, JobTitle: jobTitle, CompanyName: companyName, JobURL: fmt.Sprintf("%s/jobs/%s/applications", appURL, jobID), AppName: "GoHorse Jobs", } html, err := renderTemplate(applicationReceivedTemplate, data) if err != nil { return err } subject := fmt.Sprintf("📩 Nova candidatura para %s", jobTitle) return s.SendEmail([]string{to}, subject, html, "") } // Helper function to render templates func renderTemplate(tmplStr string, data interface{}) (string, error) { tmpl, err := template.New("email").Parse(tmplStr) if err != nil { return "", fmt.Errorf("failed to parse template: %w", err) } var buf bytes.Buffer if err := tmpl.Execute(&buf, data); err != nil { return "", fmt.Errorf("failed to execute template: %w", err) } return buf.String(), nil } // Email templates const welcomeTemplate = `

🐴 Bem-vindo ao {{.AppName}}!

Olá, {{.Name}}!

Sua conta foi criada com sucesso. Agora você pode:

Ver Vagas Disponíveis
` const passwordResetTemplate = `

🔑 Redefinição de Senha

Olá, {{.Name}}!

Recebemos uma solicitação para redefinir a senha da sua conta.

Redefinir Minha Senha
⚠️ Atenção: Este link expira em {{.ExpiresIn}}. Se você não solicitou esta redefinição, ignore este email.
` const applicationReceivedTemplate = `

📩 Nova Candidatura!

Uma nova candidatura foi recebida para a vaga em {{.CompanyName}}:

{{.JobTitle}}

Candidato: {{.ApplicantName}}

Ver Candidatura
`