167 lines
6.0 KiB
Go
167 lines
6.0 KiB
Go
|
|
package email
|
|||
|
|
|
|||
|
|
import "fmt"
|
|||
|
|
|
|||
|
|
// BuildVerifyCodeEmail returns a locale-appropriate subject and HTML body for
|
|||
|
|
// the verification code email. Chinese domains get a Chinese email; all others
|
|||
|
|
// get an English one.
|
|||
|
|
func BuildVerifyCodeEmail(toEmail, code string) (subject, body string) {
|
|||
|
|
domain := toEmail[lastIndex(toEmail, '@')+1:]
|
|||
|
|
if chineseDomainRe.MatchString(domain) {
|
|||
|
|
return buildVerifyCodeZH(code)
|
|||
|
|
}
|
|||
|
|
return buildVerifyCodeEN(code)
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
func buildVerifyCodeZH(code string) (subject, body string) {
|
|||
|
|
subject = "您的验证码"
|
|||
|
|
body = fmt.Sprintf(`<!DOCTYPE html>
|
|||
|
|
<html lang="zh">
|
|||
|
|
<head>
|
|||
|
|
<meta charset="UTF-8" />
|
|||
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|||
|
|
<title>验证码</title>
|
|||
|
|
</head>
|
|||
|
|
<body style="margin:0;padding:0;background:#f4f4f5;font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,Helvetica,Arial,sans-serif;">
|
|||
|
|
<table width="100%%" cellpadding="0" cellspacing="0" style="background:#f4f4f5;padding:40px 0;">
|
|||
|
|
<tr>
|
|||
|
|
<td align="center">
|
|||
|
|
<table width="480" cellpadding="0" cellspacing="0" style="background:#ffffff;border-radius:12px;overflow:hidden;box-shadow:0 1px 4px rgba(0,0,0,.08);">
|
|||
|
|
|
|||
|
|
<!-- Header -->
|
|||
|
|
<tr>
|
|||
|
|
<td style="background:#0a0a0a;padding:32px 40px;">
|
|||
|
|
<span style="color:#ffffff;font-size:20px;font-weight:700;letter-spacing:-0.3px;">TexPixel</span>
|
|||
|
|
</td>
|
|||
|
|
</tr>
|
|||
|
|
|
|||
|
|
<!-- Body -->
|
|||
|
|
<tr>
|
|||
|
|
<td style="padding:40px 40px 32px;">
|
|||
|
|
<p style="margin:0 0 8px;font-size:24px;font-weight:700;color:#0a0a0a;line-height:1.3;">验证您的邮箱</p>
|
|||
|
|
<p style="margin:0 0 32px;font-size:15px;color:#6b7280;line-height:1.6;">
|
|||
|
|
请使用以下验证码完成注册。验证码仅对您本人有效,请勿分享给他人。
|
|||
|
|
</p>
|
|||
|
|
|
|||
|
|
<!-- Code block -->
|
|||
|
|
<table width="100%%" cellpadding="0" cellspacing="0">
|
|||
|
|
<tr>
|
|||
|
|
<td align="center" style="background:#f9fafb;border:1px solid #e5e7eb;border-radius:8px;padding:28px 0;">
|
|||
|
|
<span style="font-size:40px;font-weight:700;letter-spacing:12px;color:#0a0a0a;font-family:'Courier New',Courier,monospace;">%s</span>
|
|||
|
|
</td>
|
|||
|
|
</tr>
|
|||
|
|
</table>
|
|||
|
|
|
|||
|
|
<p style="margin:24px 0 0;font-size:13px;color:#9ca3af;text-align:center;">
|
|||
|
|
验证码 <strong>10 分钟</strong>内有效,请尽快使用
|
|||
|
|
</p>
|
|||
|
|
</td>
|
|||
|
|
</tr>
|
|||
|
|
|
|||
|
|
<!-- Divider -->
|
|||
|
|
<tr>
|
|||
|
|
<td style="padding:0 40px;">
|
|||
|
|
<div style="height:1px;background:#f3f4f6;"></div>
|
|||
|
|
</td>
|
|||
|
|
</tr>
|
|||
|
|
|
|||
|
|
<!-- Footer -->
|
|||
|
|
<tr>
|
|||
|
|
<td style="padding:24px 40px 32px;">
|
|||
|
|
<p style="margin:0;font-size:12px;color:#9ca3af;line-height:1.7;">
|
|||
|
|
如果您没有请求此验证码,可以忽略本邮件,您的账户仍然安全。<br/>
|
|||
|
|
© 2025 TexPixel. 保留所有权利。
|
|||
|
|
</p>
|
|||
|
|
</td>
|
|||
|
|
</tr>
|
|||
|
|
|
|||
|
|
</table>
|
|||
|
|
</td>
|
|||
|
|
</tr>
|
|||
|
|
</table>
|
|||
|
|
</body>
|
|||
|
|
</html>`, code)
|
|||
|
|
return
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
func buildVerifyCodeEN(code string) (subject, body string) {
|
|||
|
|
subject = "Your verification code"
|
|||
|
|
body = fmt.Sprintf(`<!DOCTYPE html>
|
|||
|
|
<html lang="en">
|
|||
|
|
<head>
|
|||
|
|
<meta charset="UTF-8" />
|
|||
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|||
|
|
<title>Verification Code</title>
|
|||
|
|
</head>
|
|||
|
|
<body style="margin:0;padding:0;background:#f4f4f5;font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,Helvetica,Arial,sans-serif;">
|
|||
|
|
<table width="100%%" cellpadding="0" cellspacing="0" style="background:#f4f4f5;padding:40px 0;">
|
|||
|
|
<tr>
|
|||
|
|
<td align="center">
|
|||
|
|
<table width="480" cellpadding="0" cellspacing="0" style="background:#ffffff;border-radius:12px;overflow:hidden;box-shadow:0 1px 4px rgba(0,0,0,.08);">
|
|||
|
|
|
|||
|
|
<!-- Header -->
|
|||
|
|
<tr>
|
|||
|
|
<td style="background:#0a0a0a;padding:32px 40px;">
|
|||
|
|
<span style="color:#ffffff;font-size:20px;font-weight:700;letter-spacing:-0.3px;">TexPixel</span>
|
|||
|
|
</td>
|
|||
|
|
</tr>
|
|||
|
|
|
|||
|
|
<!-- Body -->
|
|||
|
|
<tr>
|
|||
|
|
<td style="padding:40px 40px 32px;">
|
|||
|
|
<p style="margin:0 0 8px;font-size:24px;font-weight:700;color:#0a0a0a;line-height:1.3;">Verify your email address</p>
|
|||
|
|
<p style="margin:0 0 32px;font-size:15px;color:#6b7280;line-height:1.6;">
|
|||
|
|
Use the verification code below to complete your registration. Never share this code with anyone.
|
|||
|
|
</p>
|
|||
|
|
|
|||
|
|
<!-- Code block -->
|
|||
|
|
<table width="100%%" cellpadding="0" cellspacing="0">
|
|||
|
|
<tr>
|
|||
|
|
<td align="center" style="background:#f9fafb;border:1px solid #e5e7eb;border-radius:8px;padding:28px 0;">
|
|||
|
|
<span style="font-size:40px;font-weight:700;letter-spacing:12px;color:#0a0a0a;font-family:'Courier New',Courier,monospace;">%s</span>
|
|||
|
|
</td>
|
|||
|
|
</tr>
|
|||
|
|
</table>
|
|||
|
|
|
|||
|
|
<p style="margin:24px 0 0;font-size:13px;color:#9ca3af;text-align:center;">
|
|||
|
|
This code expires in <strong>10 minutes</strong>
|
|||
|
|
</p>
|
|||
|
|
</td>
|
|||
|
|
</tr>
|
|||
|
|
|
|||
|
|
<!-- Divider -->
|
|||
|
|
<tr>
|
|||
|
|
<td style="padding:0 40px;">
|
|||
|
|
<div style="height:1px;background:#f3f4f6;"></div>
|
|||
|
|
</td>
|
|||
|
|
</tr>
|
|||
|
|
|
|||
|
|
<!-- Footer -->
|
|||
|
|
<tr>
|
|||
|
|
<td style="padding:24px 40px 32px;">
|
|||
|
|
<p style="margin:0;font-size:12px;color:#9ca3af;line-height:1.7;">
|
|||
|
|
If you didn’t request this code, you can safely ignore this email. Your account is still secure.<br/>
|
|||
|
|
© 2025 TexPixel. All rights reserved.
|
|||
|
|
</p>
|
|||
|
|
</td>
|
|||
|
|
</tr>
|
|||
|
|
|
|||
|
|
</table>
|
|||
|
|
</td>
|
|||
|
|
</tr>
|
|||
|
|
</table>
|
|||
|
|
</body>
|
|||
|
|
</html>`, code)
|
|||
|
|
return
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// lastIndex returns the last index of sep in s, or -1.
|
|||
|
|
func lastIndex(s string, sep byte) int {
|
|||
|
|
for i := len(s) - 1; i >= 0; i-- {
|
|||
|
|
if s[i] == sep {
|
|||
|
|
return i
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
return -1
|
|||
|
|
}
|