Initial commit: Homelab API
This commit is contained in:
133
internal/service/email.go
Normal file
133
internal/service/email.go
Normal file
@@ -0,0 +1,133 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
type EmailService struct {
|
||||
apiKey string
|
||||
fromEmail string
|
||||
fromName string
|
||||
baseURL string
|
||||
}
|
||||
|
||||
type ResendRequest struct {
|
||||
From string `json:"from"`
|
||||
To []string `json:"to"`
|
||||
Subject string `json:"subject"`
|
||||
HTML string `json:"html"`
|
||||
}
|
||||
|
||||
func NewEmailService(apiKey, fromEmail, fromName, baseURL string) *EmailService {
|
||||
return &EmailService{
|
||||
apiKey: apiKey,
|
||||
fromEmail: fromEmail,
|
||||
fromName: fromName,
|
||||
baseURL: baseURL,
|
||||
}
|
||||
}
|
||||
|
||||
func (s *EmailService) SendVerificationEmail(toEmail, username, token string) error {
|
||||
verifyURL := fmt.Sprintf("%s/verify-email?token=%s", s.baseURL, token)
|
||||
|
||||
html := fmt.Sprintf(`
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<style>
|
||||
body { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; line-height: 1.6; color: #333; background: #FAF9F6; margin: 0; padding: 20px; }
|
||||
.container { max-width: 500px; margin: 0 auto; padding: 40px; background: white; border-radius: 24px; }
|
||||
h2 { color: #0f766e; margin-bottom: 8px; }
|
||||
.button { display: inline-block; padding: 14px 28px; background: linear-gradient(135deg, #14b8a6, #0f766e); color: white; text-decoration: none; border-radius: 16px; font-weight: 600; }
|
||||
.link { color: #0f766e; word-break: break-all; }
|
||||
.footer { margin-top: 30px; font-size: 13px; color: #888; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="container">
|
||||
<h2>Привет, %s! 👋</h2>
|
||||
<p>Спасибо за регистрацию в Pulse! Подтверди свой email, нажав на кнопку ниже:</p>
|
||||
<p style="margin: 30px 0;"><a href="%s" class="button">Подтвердить email</a></p>
|
||||
<p>Или скопируй ссылку:<br><a href="%s" class="link">%s</a></p>
|
||||
<p class="footer">Ссылка действительна 24 часа. Если ты не регистрировался — просто проигнорируй это письмо.</p>
|
||||
</div>
|
||||
</body>
|
||||
</html>`, username, verifyURL, verifyURL, verifyURL)
|
||||
|
||||
return s.send(toEmail, "Подтверди свой email — Pulse", html)
|
||||
}
|
||||
|
||||
func (s *EmailService) SendPasswordResetEmail(toEmail, username, token string) error {
|
||||
resetURL := fmt.Sprintf("%s/reset-password?token=%s", s.baseURL, token)
|
||||
|
||||
html := fmt.Sprintf(`
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<style>
|
||||
body { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; line-height: 1.6; color: #333; background: #FAF9F6; margin: 0; padding: 20px; }
|
||||
.container { max-width: 500px; margin: 0 auto; padding: 40px; background: white; border-radius: 24px; }
|
||||
h2 { color: #0f766e; margin-bottom: 8px; }
|
||||
.button { display: inline-block; padding: 14px 28px; background: linear-gradient(135deg, #14b8a6, #0f766e); color: white; text-decoration: none; border-radius: 16px; font-weight: 600; }
|
||||
.link { color: #0f766e; word-break: break-all; }
|
||||
.footer { margin-top: 30px; font-size: 13px; color: #888; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="container">
|
||||
<h2>Сброс пароля</h2>
|
||||
<p>Привет, %s! Мы получили запрос на сброс пароля для твоего аккаунта в Pulse.</p>
|
||||
<p style="margin: 30px 0;"><a href="%s" class="button">Сбросить пароль</a></p>
|
||||
<p>Или скопируй ссылку:<br><a href="%s" class="link">%s</a></p>
|
||||
<p class="footer">Ссылка действительна 1 час. Если ты не запрашивал сброс пароля — просто проигнорируй это письмо.</p>
|
||||
</div>
|
||||
</body>
|
||||
</html>`, username, resetURL, resetURL, resetURL)
|
||||
|
||||
return s.send(toEmail, "Сброс пароля — Pulse", html)
|
||||
}
|
||||
|
||||
func (s *EmailService) send(to, subject, html string) error {
|
||||
if s.apiKey == "" {
|
||||
fmt.Printf("[EMAIL] Would send to %s: %s\n", to, subject)
|
||||
return nil
|
||||
}
|
||||
|
||||
payload := ResendRequest{
|
||||
From: fmt.Sprintf("%s <%s>", s.fromName, s.fromEmail),
|
||||
To: []string{to},
|
||||
Subject: subject,
|
||||
HTML: html,
|
||||
}
|
||||
|
||||
jsonData, err := json.Marshal(payload)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
req, err := http.NewRequest("POST", "https://api.resend.com/emails", bytes.NewBuffer(jsonData))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
req.Header.Set("Authorization", "Bearer "+s.apiKey)
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
|
||||
client := &http.Client{}
|
||||
resp, err := client.Do(req)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
if resp.StatusCode >= 400 {
|
||||
return fmt.Errorf("resend API error: %d", resp.StatusCode)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
Reference in New Issue
Block a user