1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
|
package email
import (
"bytes"
"fmt"
"html/template"
"imageboard/config"
"imageboard/database"
"imageboard/models"
"net/smtp"
"regexp"
)
func extractEmailAddress(from string) string {
re := regexp.MustCompile(`<([^>]+)>`)
matches := re.FindStringSubmatch(from)
if len(matches) == 2 {
return matches[1]
}
return from
}
func SendMail(to, subject, body string) error {
var auth smtp.Auth
if config.SMTP.Username != "" {
auth = smtp.PlainAuth("", config.SMTP.Username, config.SMTP.Password, config.SMTP.Host)
} else {
auth = nil
}
fromHeader := config.SMTP.From
fromAddress := extractEmailAddress(config.SMTP.From)
msg := fmt.Sprintf("From: %s\r\nTo: %s\r\nSubject: %s\r\nMIME-Version: 1.0\r\nContent-Type: text/html; charset=UTF-8\r\n\r\n%s",
fromHeader, to, subject, body)
addr := fmt.Sprintf("%s:%d", config.SMTP.Host, config.SMTP.Port)
return smtp.SendMail(addr, auth, fromAddress, []string{to}, []byte(msg))
}
func SendVerificationEmail(user *models.User) error {
token, err := database.GenerateEmailToken(int(user.ID), config.EmailTokenTypeVerification)
if err != nil {
return fmt.Errorf("failed to generate verification token: %w", err)
}
tmpl, err := template.ParseFiles("templates/email/verification.html")
if err != nil {
return fmt.Errorf("failed to parse email template: %w", err)
}
verificationLink := fmt.Sprintf("%s%s?token=%s", config.Server.AppBaseURL, config.URL_VERIFY_EMAIL, token.Token)
data := struct {
Username string
Appname string
Link string
}{
Username: user.Username,
Appname: config.Server.AppName,
Link: verificationLink,
}
var body bytes.Buffer
if err := tmpl.Execute(&body, data); err != nil {
return fmt.Errorf("failed to execute email template: %w", err)
}
subject := fmt.Sprintf("Verify your email for %s", config.Server.AppName)
return SendMail(user.Email, subject, body.String())
}
// func SendPasswordResetEmail(user *models.User) error {
// token, err := user.GenerateToken(database.DB, models.EmailTokenTypePasswordReset)
// if err != nil {
// return fmt.Errorf("failed to generate password reset token: %w", err)
// }
// tmpl, err := template.ParseFiles("templates/email/password_reset.html")
// if err != nil {
// return fmt.Errorf("failed to parse email template: %w", err)
// }
// resetLink := fmt.Sprintf("%s/account/reset-password?token=%s", config.Server.AppBaseURL, token.Token)
// data := struct {
// Username string
// Link string
// }{
// Username: user.Username,
// Link: resetLink,
// }
// var body bytes.Buffer
// if err := tmpl.Execute(&body, data); err != nil {
// return fmt.Errorf("failed to execute email template: %w", err)
// }
// subject := fmt.Sprintf("Password reset for %s", config.Server.AppName)
// return SendMail(user.Email, subject, body.String())
// }
// func SendEmailChangeConfirmation(user *models.User, newEmail string) error {
// token, err := user.GenerateToken(database.DB, models.EmailTokenTypeChangeEmail)
// if err != nil {
// return fmt.Errorf("failed to generate email change token: %w", err)
// }
// tmpl, err := template.ParseFiles("templates/email/email_change.html")
// if err != nil {
// return fmt.Errorf("failed to parse email template: %w", err)
// }
// confirmLink := fmt.Sprintf("%s/account/confirm-email-change?token=%s&email=%s", config.Server.AppBaseURL, token.Token, newEmail)
// data := struct {
// Username string
// NewEmail string
// Link string
// }{
// Username: user.Username,
// NewEmail: newEmail,
// Link: confirmLink,
// }
// var body bytes.Buffer
// if err := tmpl.Execute(&body, data); err != nil {
// return fmt.Errorf("failed to execute email template: %w", err)
// }
// subject := fmt.Sprintf("Confirm email change for %s", config.Server.AppName)
// return SendMail(newEmail, subject, body.String())
// }
|