138 lines
4.0 KiB
Go
138 lines
4.0 KiB
Go
|
|
package domain
|
|||
|
|
|
|||
|
|
import (
|
|||
|
|
"fmt"
|
|||
|
|
|
|||
|
|
"github.com/matcornic/hermes/v2"
|
|||
|
|
)
|
|||
|
|
|
|||
|
|
// BuildHermesHTML renders a hermes email skeleton (same stack as stand-alone notification).
|
|||
|
|
func BuildHermesHTML(brand Brand, body hermes.Body) (string, error) {
|
|||
|
|
if brand.Name == "" {
|
|||
|
|
brand.Name = "Harbor Desk"
|
|||
|
|
}
|
|||
|
|
h := hermes.Hermes{
|
|||
|
|
Product: hermes.Product{
|
|||
|
|
Name: brand.Name,
|
|||
|
|
Link: brand.Link,
|
|||
|
|
Logo: brand.LogoURL,
|
|||
|
|
Copyright: brand.Copyright,
|
|||
|
|
},
|
|||
|
|
// Default theme is clean table layout suitable for most clients.
|
|||
|
|
}
|
|||
|
|
return h.GenerateHTML(hermes.Email{Body: body})
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// GenerateTemplate builds subject + HTML for a kind/lang with placeholders still present
|
|||
|
|
// ({{.Username}}, {{.VerifyCode}}, {{.ResetURL}} filled later by text/template).
|
|||
|
|
func GenerateTemplate(kind TemplateKind, lang Language, brand Brand) (*EmailTemplate, error) {
|
|||
|
|
lang = NormalizeLanguage(string(lang))
|
|||
|
|
switch kind {
|
|||
|
|
case TemplateEmailVerify:
|
|||
|
|
return generateEmailVerify(lang, brand)
|
|||
|
|
case TemplatePasswordReset:
|
|||
|
|
return generatePasswordReset(lang, brand)
|
|||
|
|
default:
|
|||
|
|
return nil, fmt.Errorf("unknown template kind: %s", kind)
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
func generateEmailVerify(lang Language, brand Brand) (*EmailTemplate, error) {
|
|||
|
|
var subject string
|
|||
|
|
var intros, outros []string
|
|||
|
|
var instr string
|
|||
|
|
if lang == LangEnUS {
|
|||
|
|
subject = "[Harbor Desk] Verify your email"
|
|||
|
|
intros = []string{
|
|||
|
|
"You received this email because we need to verify your email address.",
|
|||
|
|
}
|
|||
|
|
instr = "Copy this verification code and enter it on the verify page (valid for 30 minutes):"
|
|||
|
|
outros = []string{
|
|||
|
|
"If you did not create an account, you can ignore this email.",
|
|||
|
|
"This is an automated message — please do not reply.",
|
|||
|
|
}
|
|||
|
|
} else {
|
|||
|
|
subject = "【Harbor Desk】信箱驗證碼"
|
|||
|
|
intros = []string{
|
|||
|
|
"您收到此信是因為需要驗證您的電子信箱。",
|
|||
|
|
}
|
|||
|
|
instr = "請複製下方驗證碼,於 30 分鐘內到驗證頁完成確認:"
|
|||
|
|
outros = []string{
|
|||
|
|
"若您並未註冊帳號,可忽略本信。",
|
|||
|
|
"此為系統自動發出,請勿直接回覆。",
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
html, err := BuildHermesHTML(brand, hermes.Body{
|
|||
|
|
Name: "{{.Username}}",
|
|||
|
|
Intros: intros,
|
|||
|
|
Actions: []hermes.Action{
|
|||
|
|
{
|
|||
|
|
Instructions: instr,
|
|||
|
|
InviteCode: "{{.VerifyCode}}",
|
|||
|
|
},
|
|||
|
|
},
|
|||
|
|
Outros: outros,
|
|||
|
|
Signature: " ",
|
|||
|
|
})
|
|||
|
|
if err != nil {
|
|||
|
|
return nil, err
|
|||
|
|
}
|
|||
|
|
return &EmailTemplate{Subject: subject, Body: html}, nil
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
func generatePasswordReset(lang Language, brand Brand) (*EmailTemplate, error) {
|
|||
|
|
var subject, instr, btnText string
|
|||
|
|
var intros, outros []string
|
|||
|
|
if lang == LangEnUS {
|
|||
|
|
subject = "[Harbor Desk] Reset your password"
|
|||
|
|
intros = []string{
|
|||
|
|
"You received this email because a password reset was requested for your account.",
|
|||
|
|
}
|
|||
|
|
instr = "Copy this code, or open the reset page with the button below (valid for 30 minutes):"
|
|||
|
|
btnText = "Open reset page"
|
|||
|
|
outros = []string{
|
|||
|
|
"If you did not request a password reset, no further action is required.",
|
|||
|
|
"This is an automated message — please do not reply.",
|
|||
|
|
}
|
|||
|
|
} else {
|
|||
|
|
subject = "【Harbor Desk】重設密碼"
|
|||
|
|
intros = []string{
|
|||
|
|
"您收到此信是因為有人申請重設您的帳戶密碼。",
|
|||
|
|
}
|
|||
|
|
instr = "請複製下方驗證碼,或點擊按鈕開啟重設頁(30 分鐘內有效):"
|
|||
|
|
btnText = "開啟重設密碼頁"
|
|||
|
|
outros = []string{
|
|||
|
|
"若您並未申請重設密碼,可忽略本信,帳號不會有任何變更。",
|
|||
|
|
"此為系統自動發出,請勿直接回覆。",
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
actions := []hermes.Action{
|
|||
|
|
{
|
|||
|
|
Instructions: instr,
|
|||
|
|
InviteCode: "{{.VerifyCode}}",
|
|||
|
|
},
|
|||
|
|
}
|
|||
|
|
// Button only when ResetURL placeholder present after render — always include;
|
|||
|
|
// empty href is avoided by filling ResetURL before send.
|
|||
|
|
actions = append(actions, hermes.Action{
|
|||
|
|
Button: hermes.Button{
|
|||
|
|
Color: "#0f172a",
|
|||
|
|
Text: btnText,
|
|||
|
|
Link: "{{.ResetURL}}",
|
|||
|
|
},
|
|||
|
|
})
|
|||
|
|
|
|||
|
|
html, err := BuildHermesHTML(brand, hermes.Body{
|
|||
|
|
Name: "{{.Username}}",
|
|||
|
|
Intros: intros,
|
|||
|
|
Actions: actions,
|
|||
|
|
Outros: outros,
|
|||
|
|
Signature: " ",
|
|||
|
|
})
|
|||
|
|
if err != nil {
|
|||
|
|
return nil, err
|
|||
|
|
}
|
|||
|
|
return &EmailTemplate{Subject: subject, Body: html}, nil
|
|||
|
|
}
|