mirror of
https://github.com/songquanpeng/one-api.git
synced 2025-02-06 10:44:25 +00:00
feat: enhance email notifications with HTML templates and improved content
This commit is contained in:
parent
19998ebb8a
commit
8fa9df5b3c
34
common/message/template.go
Normal file
34
common/message/template.go
Normal file
@ -0,0 +1,34 @@
|
||||
package message
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/songquanpeng/one-api/common/config"
|
||||
)
|
||||
|
||||
// EmailTemplate 生成美观的 HTML 邮件内容
|
||||
func EmailTemplate(title, content string) string {
|
||||
return fmt.Sprintf(`
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
</head>
|
||||
<body style="margin: 0; padding: 20px; font-family: Arial, sans-serif; line-height: 1.6; background-color: #f4f4f4;">
|
||||
<div style="max-width: 600px; margin: 20px auto; padding: 30px; background-color: #ffffff; border-radius: 8px; box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);">
|
||||
<div style="text-align: center; margin-bottom: 30px;">
|
||||
<h2 style="color: #333; margin: 0; font-size: 24px;">%s</h2>
|
||||
</div>
|
||||
<div style="color: #555; font-size: 16px;">
|
||||
%s
|
||||
</div>
|
||||
<div style="margin-top: 40px; padding-top: 20px; border-top: 1px solid #eee; color: #888; font-size: 14px; text-align: center;">
|
||||
<p style="margin: 5px 0;">此邮件由系统自动发送,请勿直接回复</p>
|
||||
<p style="margin: 5px 0;">%s</p>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
`, title, content, config.SystemName)
|
||||
}
|
@ -116,10 +116,17 @@ func SendEmailVerification(c *gin.Context) {
|
||||
}
|
||||
code := common.GenerateVerificationCode(6)
|
||||
common.RegisterVerificationCodeWithKey(email, code, common.EmailVerificationPurpose)
|
||||
subject := fmt.Sprintf("%s邮箱验证邮件", config.SystemName)
|
||||
content := fmt.Sprintf("<p>您好,你正在进行%s邮箱验证。</p>"+
|
||||
"<p>您的验证码为: <strong>%s</strong></p>"+
|
||||
"<p>验证码 %d 分钟内有效,如果不是本人操作,请忽略。</p>", config.SystemName, code, common.VerificationValidMinutes)
|
||||
subject := fmt.Sprintf("%s 邮箱验证邮件", config.SystemName)
|
||||
content := message.EmailTemplate(
|
||||
subject,
|
||||
fmt.Sprintf(`
|
||||
<p>您好!</p>
|
||||
<p>您正在进行 %s 邮箱验证。</p>
|
||||
<p>您的验证码为:</p>
|
||||
<p style="font-size: 24px; font-weight: bold; color: #333; background-color: #f8f8f8; padding: 10px; text-align: center; border-radius: 4px;">%s</p>
|
||||
<p style="color: #666;">验证码 %d 分钟内有效,如果不是本人操作,请忽略。</p>
|
||||
`, config.SystemName, code, common.VerificationValidMinutes),
|
||||
)
|
||||
err := message.SendEmail(subject, email, content)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
@ -154,11 +161,21 @@ func SendPasswordResetEmail(c *gin.Context) {
|
||||
code := common.GenerateVerificationCode(0)
|
||||
common.RegisterVerificationCodeWithKey(email, code, common.PasswordResetPurpose)
|
||||
link := fmt.Sprintf("%s/user/reset?email=%s&token=%s", config.ServerAddress, email, code)
|
||||
subject := fmt.Sprintf("%s密码重置", config.SystemName)
|
||||
content := fmt.Sprintf("<p>您好,你正在进行%s密码重置。</p>"+
|
||||
"<p>点击 <a href='%s'>此处</a> 进行密码重置。</p>"+
|
||||
"<p>如果链接无法点击,请尝试点击下面的链接或将其复制到浏览器中打开:<br> %s </p>"+
|
||||
"<p>重置链接 %d 分钟内有效,如果不是本人操作,请忽略。</p>", config.SystemName, link, link, common.VerificationValidMinutes)
|
||||
subject := fmt.Sprintf("%s 密码重置", config.SystemName)
|
||||
content := message.EmailTemplate(
|
||||
subject,
|
||||
fmt.Sprintf(`
|
||||
<p>您好!</p>
|
||||
<p>您正在进行 %s 密码重置。</p>
|
||||
<p>请点击下面的按钮进行密码重置:</p>
|
||||
<p style="text-align: center; margin: 30px 0;">
|
||||
<a href="%s" style="background-color: #007bff; color: white; padding: 12px 24px; text-decoration: none; border-radius: 4px; display: inline-block;">重置密码</a>
|
||||
</p>
|
||||
<p style="color: #666;">如果按钮无法点击,请复制以下链接到浏览器中打开:</p>
|
||||
<p style="background-color: #f8f8f8; padding: 10px; border-radius: 4px; word-break: break-all;">%s</p>
|
||||
<p style="color: #666;">重置链接 %d 分钟内有效,如果不是本人操作,请忽略。</p>
|
||||
`, config.SystemName, link, link, common.VerificationValidMinutes),
|
||||
)
|
||||
err := message.SendEmail(subject, email, content)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
|
@ -3,12 +3,14 @@ package model
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"gorm.io/gorm"
|
||||
|
||||
"github.com/songquanpeng/one-api/common"
|
||||
"github.com/songquanpeng/one-api/common/config"
|
||||
"github.com/songquanpeng/one-api/common/helper"
|
||||
"github.com/songquanpeng/one-api/common/logger"
|
||||
"github.com/songquanpeng/one-api/common/message"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
const (
|
||||
@ -238,16 +240,31 @@ func PreConsumeTokenQuota(tokenId int, quota int64) (err error) {
|
||||
if err != nil {
|
||||
logger.SysError("failed to fetch user email: " + err.Error())
|
||||
}
|
||||
prompt := "您的额度即将用尽"
|
||||
prompt := "额度提醒"
|
||||
var contentText string
|
||||
if noMoreQuota {
|
||||
prompt = "您的额度已用尽"
|
||||
contentText = "您的额度已用尽"
|
||||
} else {
|
||||
contentText = "您的额度即将用尽"
|
||||
}
|
||||
if email != "" {
|
||||
topUpLink := fmt.Sprintf("%s/topup", config.ServerAddress)
|
||||
err = message.SendEmail(prompt, email,
|
||||
fmt.Sprintf("%s,当前剩余额度为 %d,为了不影响您的使用,请及时充值。<br/>充值链接:<a href='%s'>%s</a>", prompt, userQuota, topUpLink, topUpLink))
|
||||
content := message.EmailTemplate(
|
||||
prompt,
|
||||
fmt.Sprintf(`
|
||||
<p>您好!</p>
|
||||
<p>%s,当前剩余额度为 <strong>%d</strong>。</p>
|
||||
<p>为了不影响您的使用,请及时充值。</p>
|
||||
<p style="text-align: center; margin: 30px 0;">
|
||||
<a href="%s" style="background-color: #007bff; color: white; padding: 12px 24px; text-decoration: none; border-radius: 4px; display: inline-block;">立即充值</a>
|
||||
</p>
|
||||
<p style="color: #666;">如果按钮无法点击,请复制以下链接到浏览器中打开:</p>
|
||||
<p style="background-color: #f8f8f8; padding: 10px; border-radius: 4px; word-break: break-all;">%s</p>
|
||||
`, contentText, userQuota, topUpLink, topUpLink),
|
||||
)
|
||||
err = message.SendEmail(prompt, email, content)
|
||||
if err != nil {
|
||||
logger.SysError("failed to send email" + err.Error())
|
||||
logger.SysError("failed to send email: " + err.Error())
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
@ -2,6 +2,7 @@ package monitor
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/songquanpeng/one-api/common/config"
|
||||
"github.com/songquanpeng/one-api/common/logger"
|
||||
"github.com/songquanpeng/one-api/common/message"
|
||||
@ -30,17 +31,32 @@ func notifyRootUser(subject string, content string) {
|
||||
func DisableChannel(channelId int, channelName string, reason string) {
|
||||
model.UpdateChannelStatusById(channelId, model.ChannelStatusAutoDisabled)
|
||||
logger.SysLog(fmt.Sprintf("channel #%d has been disabled: %s", channelId, reason))
|
||||
subject := fmt.Sprintf("渠道「%s」(#%d)已被禁用", channelName, channelId)
|
||||
content := fmt.Sprintf("渠道「%s」(#%d)已被禁用,原因:%s", channelName, channelId, reason)
|
||||
subject := fmt.Sprintf("渠道状态变更提醒")
|
||||
content := message.EmailTemplate(
|
||||
subject,
|
||||
fmt.Sprintf(`
|
||||
<p>您好!</p>
|
||||
<p>渠道「<strong>%s</strong>」(#%d)已被禁用。</p>
|
||||
<p>禁用原因:</p>
|
||||
<p style="background-color: #f8f8f8; padding: 10px; border-radius: 4px;">%s</p>
|
||||
`, channelName, channelId, reason),
|
||||
)
|
||||
notifyRootUser(subject, content)
|
||||
}
|
||||
|
||||
func MetricDisableChannel(channelId int, successRate float64) {
|
||||
model.UpdateChannelStatusById(channelId, model.ChannelStatusAutoDisabled)
|
||||
logger.SysLog(fmt.Sprintf("channel #%d has been disabled due to low success rate: %.2f", channelId, successRate*100))
|
||||
subject := fmt.Sprintf("渠道 #%d 已被禁用", channelId)
|
||||
content := fmt.Sprintf("该渠道(#%d)在最近 %d 次调用中成功率为 %.2f%%,低于阈值 %.2f%%,因此被系统自动禁用。",
|
||||
channelId, config.MetricQueueSize, successRate*100, config.MetricSuccessRateThreshold*100)
|
||||
subject := fmt.Sprintf("渠道状态变更提醒")
|
||||
content := message.EmailTemplate(
|
||||
subject,
|
||||
fmt.Sprintf(`
|
||||
<p>您好!</p>
|
||||
<p>渠道 #%d 已被系统自动禁用。</p>
|
||||
<p>禁用原因:</p>
|
||||
<p style="background-color: #f8f8f8; padding: 10px; border-radius: 4px;">该渠道在最近 %d 次调用中成功率为 <strong>%.2f%%</strong>,低于系统阈值 <strong>%.2f%%</strong>。</p>
|
||||
`, channelId, config.MetricQueueSize, successRate*100, config.MetricSuccessRateThreshold*100),
|
||||
)
|
||||
notifyRootUser(subject, content)
|
||||
}
|
||||
|
||||
@ -48,7 +64,14 @@ func MetricDisableChannel(channelId int, successRate float64) {
|
||||
func EnableChannel(channelId int, channelName string) {
|
||||
model.UpdateChannelStatusById(channelId, model.ChannelStatusEnabled)
|
||||
logger.SysLog(fmt.Sprintf("channel #%d has been enabled", channelId))
|
||||
subject := fmt.Sprintf("渠道「%s」(#%d)已被启用", channelName, channelId)
|
||||
content := fmt.Sprintf("渠道「%s」(#%d)已被启用", channelName, channelId)
|
||||
subject := fmt.Sprintf("渠道状态变更提醒")
|
||||
content := message.EmailTemplate(
|
||||
subject,
|
||||
fmt.Sprintf(`
|
||||
<p>您好!</p>
|
||||
<p>渠道「<strong>%s</strong>」(#%d)已被重新启用。</p>
|
||||
<p>您现在可以继续使用该渠道了。</p>
|
||||
`, channelName, channelId),
|
||||
)
|
||||
notifyRootUser(subject, content)
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user