mirror of
https://github.com/TeaOSLab/EdgeAPI.git
synced 2025-11-07 18:50:26 +08:00
[SSL证书]增加提前自动更新证书功能
This commit is contained in:
@@ -76,9 +76,11 @@ func (this *DNSDomainDAO) FindDNSDomainName(id int64) (string, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 创建域名
|
// 创建域名
|
||||||
func (this *DNSDomainDAO) CreateDomain(providerId int64, name string) (int64, error) {
|
func (this *DNSDomainDAO) CreateDomain(adminId int64, userId int64, providerId int64, name string) (int64, error) {
|
||||||
op := NewDNSDomainOperator()
|
op := NewDNSDomainOperator()
|
||||||
op.ProviderId = providerId
|
op.ProviderId = providerId
|
||||||
|
op.AdminId = adminId
|
||||||
|
op.UserId = userId
|
||||||
op.Name = name
|
op.Name = name
|
||||||
op.State = DNSDomainStateEnabled
|
op.State = DNSDomainStateEnabled
|
||||||
op.IsOn = true
|
op.IsOn = true
|
||||||
|
|||||||
@@ -31,6 +31,9 @@ const (
|
|||||||
MessageTypeNodeInactive MessageType = "NodeInactive"
|
MessageTypeNodeInactive MessageType = "NodeInactive"
|
||||||
MessageTypeNodeActive MessageType = "NodeActive"
|
MessageTypeNodeActive MessageType = "NodeActive"
|
||||||
MessageTypeClusterDNSSyncFailed MessageType = "ClusterDNSSyncFailed"
|
MessageTypeClusterDNSSyncFailed MessageType = "ClusterDNSSyncFailed"
|
||||||
|
MessageTypeSSLCertExpiring MessageType = "SSLCertExpiring" // SSL证书即将过期
|
||||||
|
MessageTypeSSLCertACMETaskFailed MessageType = "SSLCertACMETaskFailed" // SSL证书任务执行失败
|
||||||
|
MessageTypeSSLCertACMETaskSuccess MessageType = "SSLCertACMETaskSuccess" // SSL证书任务执行成功
|
||||||
)
|
)
|
||||||
|
|
||||||
type MessageDAO dbs.DAO
|
type MessageDAO dbs.DAO
|
||||||
@@ -96,6 +99,30 @@ func (this *MessageDAO) CreateNodeMessage(clusterId int64, nodeId int64, message
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 创建普通消息
|
||||||
|
func (this *MessageDAO) CreateMessage(adminId int64, userId int64, messageType MessageType, level string, body string, paramsJSON []byte) error {
|
||||||
|
h := md5.New()
|
||||||
|
h.Write([]byte(body))
|
||||||
|
h.Write(paramsJSON)
|
||||||
|
hash := fmt.Sprintf("%x", h.Sum(nil))
|
||||||
|
|
||||||
|
op := NewMessageOperator()
|
||||||
|
op.AdminId = adminId
|
||||||
|
op.UserId = userId
|
||||||
|
op.Type = messageType
|
||||||
|
op.Level = level
|
||||||
|
op.Body = body
|
||||||
|
if len(paramsJSON) > 0 {
|
||||||
|
op.Params = paramsJSON
|
||||||
|
}
|
||||||
|
op.State = MessageStateEnabled
|
||||||
|
op.IsRead = false
|
||||||
|
op.Day = timeutil.Format("Ymd")
|
||||||
|
op.Hash = hash
|
||||||
|
_, err := this.Save(op)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
// 删除某天之前的消息
|
// 删除某天之前的消息
|
||||||
func (this *MessageDAO) DeleteMessagesBeforeDay(dayTime time.Time) error {
|
func (this *MessageDAO) DeleteMessagesBeforeDay(dayTime time.Time) error {
|
||||||
day := timeutil.Format("Ymd", dayTime)
|
day := timeutil.Format("Ymd", dayTime)
|
||||||
|
|||||||
@@ -101,7 +101,7 @@ func (this *NodeClusterDAO) FindAllEnableClusters() (result []*NodeCluster, err
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 创建集群
|
// 创建集群
|
||||||
func (this *NodeClusterDAO) CreateCluster(name string, grantId int64, installDir string, dnsDomainId int64, dnsName string) (clusterId int64, err error) {
|
func (this *NodeClusterDAO) CreateCluster(adminId int64, name string, grantId int64, installDir string, dnsDomainId int64, dnsName string) (clusterId int64, err error) {
|
||||||
uniqueId, err := this.genUniqueId()
|
uniqueId, err := this.genUniqueId()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, err
|
return 0, err
|
||||||
@@ -114,6 +114,7 @@ func (this *NodeClusterDAO) CreateCluster(name string, grantId int64, installDir
|
|||||||
}
|
}
|
||||||
|
|
||||||
op := NewNodeClusterOperator()
|
op := NewNodeClusterOperator()
|
||||||
|
op.AdminId = adminId
|
||||||
op.Name = name
|
op.Name = name
|
||||||
op.GrantId = grantId
|
op.GrantId = grantId
|
||||||
op.InstallDir = installDir
|
op.InstallDir = installDir
|
||||||
@@ -522,6 +523,14 @@ func (this *NodeClusterDAO) CheckClusterDNS(cluster *NodeCluster) (issues []*pb.
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 查找集群所属管理员
|
||||||
|
func (this *NodeClusterDAO) FindClusterAdminId(clusterId int64) (int64, error) {
|
||||||
|
return this.Query().
|
||||||
|
Pk(clusterId).
|
||||||
|
Result("adminId").
|
||||||
|
FindInt64Col(0)
|
||||||
|
}
|
||||||
|
|
||||||
// 生成唯一ID
|
// 生成唯一ID
|
||||||
func (this *NodeClusterDAO) genUniqueId() (string, error) {
|
func (this *NodeClusterDAO) genUniqueId() (string, error) {
|
||||||
for {
|
for {
|
||||||
|
|||||||
@@ -80,7 +80,7 @@ func (this *NodeDAO) FindNodeName(id uint32) (string, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 创建节点
|
// 创建节点
|
||||||
func (this *NodeDAO) CreateNode(name string, clusterId int64, groupId int64) (nodeId int64, err error) {
|
func (this *NodeDAO) CreateNode(adminId int64, name string, clusterId int64, groupId int64) (nodeId int64, err error) {
|
||||||
uniqueId, err := this.genUniqueId()
|
uniqueId, err := this.genUniqueId()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, err
|
return 0, err
|
||||||
@@ -95,6 +95,7 @@ func (this *NodeDAO) CreateNode(name string, clusterId int64, groupId int64) (no
|
|||||||
}
|
}
|
||||||
|
|
||||||
op := NewNodeOperator()
|
op := NewNodeOperator()
|
||||||
|
op.AdminId = adminId
|
||||||
op.Name = name
|
op.Name = name
|
||||||
op.UniqueId = uniqueId
|
op.UniqueId = uniqueId
|
||||||
op.Secret = secret
|
op.Secret = secret
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import (
|
|||||||
"github.com/iwind/TeaGo/Tea"
|
"github.com/iwind/TeaGo/Tea"
|
||||||
"github.com/iwind/TeaGo/dbs"
|
"github.com/iwind/TeaGo/dbs"
|
||||||
"github.com/iwind/TeaGo/types"
|
"github.com/iwind/TeaGo/types"
|
||||||
|
timeutil "github.com/iwind/TeaGo/utils/time"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -283,3 +284,32 @@ func (this *SSLCertDAO) UpdateCertACME(certId int64, acmeTaskId int64) error {
|
|||||||
_, err := this.Save(op)
|
_, err := this.Save(op)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 查找需要自动更新的任务
|
||||||
|
// 这里我们只返回有限的字段以节省内存
|
||||||
|
func (this *SSLCertDAO) FindAllExpiringCerts(days int) (result []*SSLCert, err error) {
|
||||||
|
if days < 0 {
|
||||||
|
days = 0
|
||||||
|
}
|
||||||
|
|
||||||
|
deltaSeconds := int64(days * 86400)
|
||||||
|
_, err = this.Query().
|
||||||
|
State(SSLCertStateEnabled).
|
||||||
|
Where("FROM_UNIXTIME(timeEndAt, '%Y-%m-%d')=:day AND FROM_UNIXTIME(notifiedAt, '%Y-%m-%d')!=:today").
|
||||||
|
Param("day", timeutil.FormatTime("Y-m-d", time.Now().Unix()+deltaSeconds)).
|
||||||
|
Param("today", timeutil.Format("Y-m-d")).
|
||||||
|
Result("id", "adminId", "userId", "timeEndAt", "name", "dnsNames", "notifiedAt", "acmeTaskId").
|
||||||
|
Slice(&result).
|
||||||
|
AscPk().
|
||||||
|
FindAll()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// 设置当前证书事件通知时间
|
||||||
|
func (this *SSLCertDAO) UpdateCertNotifiedAt(certId int64) error {
|
||||||
|
_, err := this.Query().
|
||||||
|
Pk(certId).
|
||||||
|
Set("notifiedAt", time.Now().Unix()).
|
||||||
|
Update()
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ type SSLCert struct {
|
|||||||
CommonNames string `field:"commonNames"` // 发行单位列表
|
CommonNames string `field:"commonNames"` // 发行单位列表
|
||||||
IsACME uint8 `field:"isACME"` // 是否为ACME自动生成的
|
IsACME uint8 `field:"isACME"` // 是否为ACME自动生成的
|
||||||
AcmeTaskId uint64 `field:"acmeTaskId"` // ACME任务ID
|
AcmeTaskId uint64 `field:"acmeTaskId"` // ACME任务ID
|
||||||
|
NotifiedAt uint64 `field:"notifiedAt"` // 最后通知时间
|
||||||
}
|
}
|
||||||
|
|
||||||
type SSLCertOperator struct {
|
type SSLCertOperator struct {
|
||||||
@@ -45,6 +46,7 @@ type SSLCertOperator struct {
|
|||||||
CommonNames interface{} // 发行单位列表
|
CommonNames interface{} // 发行单位列表
|
||||||
IsACME interface{} // 是否为ACME自动生成的
|
IsACME interface{} // 是否为ACME自动生成的
|
||||||
AcmeTaskId interface{} // ACME任务ID
|
AcmeTaskId interface{} // ACME任务ID
|
||||||
|
NotifiedAt interface{} // 最后通知时间
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewSSLCertOperator() *SSLCertOperator {
|
func NewSSLCertOperator() *SSLCertOperator {
|
||||||
|
|||||||
@@ -10,6 +10,18 @@ import (
|
|||||||
type BaseService struct {
|
type BaseService struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 校验管理员
|
||||||
|
func (this *BaseService) ValidateAdmin(ctx context.Context, reqAdminId int64) (adminId int64, err error) {
|
||||||
|
_, reqUserId, err := rpcutils.ValidateRequest(ctx, rpcutils.UserTypeAdmin)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if reqAdminId > 0 && reqUserId != reqAdminId {
|
||||||
|
return 0, this.PermissionError()
|
||||||
|
}
|
||||||
|
return reqUserId, nil
|
||||||
|
}
|
||||||
|
|
||||||
// 校验管理员和用户
|
// 校验管理员和用户
|
||||||
func (this *BaseService) ValidateAdminAndUser(ctx context.Context, reqUserId int64) (adminId int64, userId int64, err error) {
|
func (this *BaseService) ValidateAdminAndUser(ctx context.Context, reqUserId int64) (adminId int64, userId int64, err error) {
|
||||||
reqUserType, reqUserId, err := rpcutils.ValidateRequest(ctx, rpcutils.UserTypeAdmin, rpcutils.UserTypeUser)
|
reqUserType, reqUserId, err := rpcutils.ValidateRequest(ctx, rpcutils.UserTypeAdmin, rpcutils.UserTypeUser)
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ type DNSDomainService struct {
|
|||||||
// 创建域名
|
// 创建域名
|
||||||
func (this *DNSDomainService) CreateDNSDomain(ctx context.Context, req *pb.CreateDNSDomainRequest) (*pb.CreateDNSDomainResponse, error) {
|
func (this *DNSDomainService) CreateDNSDomain(ctx context.Context, req *pb.CreateDNSDomainRequest) (*pb.CreateDNSDomainResponse, error) {
|
||||||
// 校验请求
|
// 校验请求
|
||||||
_, _, err := rpcutils.ValidateRequest(ctx, rpcutils.UserTypeAdmin)
|
adminId, userId, err := this.ValidateAdminAndUser(ctx, 0)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -39,7 +39,7 @@ func (this *DNSDomainService) CreateDNSDomain(ctx context.Context, req *pb.Creat
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
domainId, err := models.SharedDNSDomainDAO.CreateDomain(req.DnsProviderId, req.Name)
|
domainId, err := models.SharedDNSDomainDAO.CreateDomain(adminId, userId, req.DnsProviderId, req.Name)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -38,12 +38,12 @@ type NodeService struct {
|
|||||||
|
|
||||||
// 创建节点
|
// 创建节点
|
||||||
func (this *NodeService) CreateNode(ctx context.Context, req *pb.CreateNodeRequest) (*pb.CreateNodeResponse, error) {
|
func (this *NodeService) CreateNode(ctx context.Context, req *pb.CreateNodeRequest) (*pb.CreateNodeResponse, error) {
|
||||||
_, _, err := rpcutils.ValidateRequest(ctx, rpcutils.UserTypeAdmin)
|
adminId, err := this.ValidateAdmin(ctx, 0)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
nodeId, err := models.SharedNodeDAO.CreateNode(req.Name, req.ClusterId, req.GroupId)
|
nodeId, err := models.SharedNodeDAO.CreateNode(adminId, req.Name, req.ClusterId, req.GroupId)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -87,7 +87,12 @@ func (this *NodeService) RegisterClusterNode(ctx context.Context, req *pb.Regist
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
nodeId, err := models.SharedNodeDAO.CreateNode(req.Name, clusterId, 0)
|
adminId, err := models.SharedNodeClusterDAO.FindClusterAdminId(clusterId)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
nodeId, err := models.SharedNodeDAO.CreateNode(adminId, req.Name, clusterId, 0)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,12 +19,12 @@ type NodeClusterService struct {
|
|||||||
|
|
||||||
// 创建集群
|
// 创建集群
|
||||||
func (this *NodeClusterService) CreateNodeCluster(ctx context.Context, req *pb.CreateNodeClusterRequest) (*pb.CreateNodeClusterResponse, error) {
|
func (this *NodeClusterService) CreateNodeCluster(ctx context.Context, req *pb.CreateNodeClusterRequest) (*pb.CreateNodeClusterResponse, error) {
|
||||||
_, _, err := rpcutils.ValidateRequest(ctx, rpcutils.UserTypeAdmin)
|
adminId, err := this.ValidateAdmin(ctx, 0)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
clusterId, err := models.SharedNodeClusterDAO.CreateCluster(req.Name, req.GrantId, req.InstallDir, req.DnsDomainId, req.DnsName)
|
clusterId, err := models.SharedNodeClusterDAO.CreateCluster(adminId, req.Name, req.GrantId, req.InstallDir, req.DnsDomainId, req.DnsName)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -57,11 +57,29 @@ func upgradeV0_0_3(db *dbs.DB) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 升级edgeDNSDomains
|
||||||
|
_, err = db.Exec("UPDATE edgeDNSDomains SET adminId=? WHERE adminId=0 AND userId=0", adminId)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
// 升级edgeSSLCerts
|
// 升级edgeSSLCerts
|
||||||
_, err = db.Exec("UPDATE edgeSSLCerts SET adminId=? WHERE adminId=0 AND userId=0", adminId)
|
_, err = db.Exec("UPDATE edgeSSLCerts SET adminId=? WHERE adminId=0 AND userId=0", adminId)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 升级edgeNodeClusters
|
||||||
|
_, err = db.Exec("UPDATE edgeNodeClusters SET adminId=? WHERE adminId=0 AND userId=0", adminId)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// 升级edgeNodes
|
||||||
|
_, err = db.Exec("UPDATE edgeNodes SET adminId=? WHERE adminId=0 AND userId=0", adminId)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|||||||
204
internal/tasks/ssl_cert_expire_check_executor.go
Normal file
204
internal/tasks/ssl_cert_expire_check_executor.go
Normal file
@@ -0,0 +1,204 @@
|
|||||||
|
package tasks
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/TeaOSLab/EdgeAPI/internal/db/models"
|
||||||
|
"github.com/TeaOSLab/EdgeAPI/internal/utils/numberutils"
|
||||||
|
"github.com/iwind/TeaGo/dbs"
|
||||||
|
"github.com/iwind/TeaGo/logs"
|
||||||
|
"github.com/iwind/TeaGo/maps"
|
||||||
|
timeutil "github.com/iwind/TeaGo/utils/time"
|
||||||
|
"strconv"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
dbs.OnReady(func() {
|
||||||
|
go NewSSLCertExpireCheckExecutor().Start()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 证书检查任务
|
||||||
|
type SSLCertExpireCheckExecutor struct {
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewSSLCertExpireCheckExecutor() *SSLCertExpireCheckExecutor {
|
||||||
|
return &SSLCertExpireCheckExecutor{}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 启动任务
|
||||||
|
func (this *SSLCertExpireCheckExecutor) Start() {
|
||||||
|
seconds := int64(3600)
|
||||||
|
ticker := time.NewTicker(time.Duration(seconds) * time.Second)
|
||||||
|
for range ticker.C {
|
||||||
|
err := this.loop(seconds)
|
||||||
|
if err != nil {
|
||||||
|
logs.Println("[ERROR][SSLCertExpireCheckExecutor]" + err.Error())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 单次执行
|
||||||
|
func (this *SSLCertExpireCheckExecutor) loop(seconds int64) error {
|
||||||
|
// 检查上次运行时间,防止重复运行
|
||||||
|
settingKey := "sslCertExpiringCheck"
|
||||||
|
timestamp := time.Now().Unix()
|
||||||
|
c, err := models.SharedSysSettingDAO.CompareInt64Setting(settingKey, timestamp-seconds)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if c > 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// 记录时间
|
||||||
|
err = models.SharedSysSettingDAO.UpdateSetting(settingKey, []byte(numberutils.FormatInt64(timestamp)))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// 查找需要自动更新的证书
|
||||||
|
// 30, 14 ... 是到期的天数
|
||||||
|
for _, days := range []int{30, 14, 7} {
|
||||||
|
certs, err := models.SharedSSLCertDAO.FindAllExpiringCerts(days)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
for _, cert := range certs {
|
||||||
|
// 发送消息
|
||||||
|
msg := "SSL证书\"" + cert.Name + "\"(" + cert.DnsNames + ")在" + strconv.Itoa(days) + "天后将到期,"
|
||||||
|
|
||||||
|
// 是否有自动更新任务
|
||||||
|
if cert.AcmeTaskId > 0 {
|
||||||
|
task, err := models.SharedACMETaskDAO.FindEnabledACMETask(int64(cert.AcmeTaskId))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if task != nil {
|
||||||
|
if task.AutoRenew == 1 {
|
||||||
|
msg += "此证书是免费申请的证书,且已设置了自动续期,将会在到期前三天自动尝试续期。"
|
||||||
|
} else {
|
||||||
|
msg += "此证书是免费申请的证书,没有设置自动续期,请在到期前手动执行续期任务。"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
msg += "请及时更新证书。"
|
||||||
|
}
|
||||||
|
|
||||||
|
err = models.SharedMessageDAO.CreateMessage(int64(cert.AdminId), int64(cert.UserId), models.MessageTypeSSLCertExpiring, models.MessageLevelWarning, msg, maps.Map{
|
||||||
|
"certId": cert.Id,
|
||||||
|
"acmeTaskId": cert.AcmeTaskId,
|
||||||
|
}.AsJSON())
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// 设置最后通知时间
|
||||||
|
err = models.SharedSSLCertDAO.UpdateCertNotifiedAt(int64(cert.Id))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 自动续期
|
||||||
|
for _, days := range []int{3, 2, 1} {
|
||||||
|
certs, err := models.SharedSSLCertDAO.FindAllExpiringCerts(days)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
for _, cert := range certs {
|
||||||
|
// 发送消息
|
||||||
|
msg := "SSL证书\"" + cert.Name + "\"(" + cert.DnsNames + ")在" + strconv.Itoa(days) + "天后将到期,"
|
||||||
|
|
||||||
|
// 是否有自动更新任务
|
||||||
|
if cert.AcmeTaskId > 0 {
|
||||||
|
task, err := models.SharedACMETaskDAO.FindEnabledACMETask(int64(cert.AcmeTaskId))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if task != nil {
|
||||||
|
if task.AutoRenew == 1 {
|
||||||
|
isOk, errMsg, _ := models.SharedACMETaskDAO.RunTask(int64(cert.AcmeTaskId))
|
||||||
|
if isOk {
|
||||||
|
// 发送成功通知
|
||||||
|
msg = "系统已成功为你自动更新了证书\"" + cert.Name + "\"(" + cert.DnsNames + ")。"
|
||||||
|
err = models.SharedMessageDAO.CreateMessage(int64(cert.AdminId), int64(cert.UserId), models.MessageTypeSSLCertACMETaskSuccess, models.MessageLevelSuccess, msg, maps.Map{
|
||||||
|
"certId": cert.Id,
|
||||||
|
"acmeTaskId": cert.AcmeTaskId,
|
||||||
|
}.AsJSON())
|
||||||
|
|
||||||
|
// 更新通知时间
|
||||||
|
err = models.SharedSSLCertDAO.UpdateCertNotifiedAt(int64(cert.Id))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// 发送失败通知
|
||||||
|
msg = "系统在尝试自动更新证书\"" + cert.Name + "\"(" + cert.DnsNames + ")时发生错误:" + errMsg + "。请检查系统设置并修复错误。"
|
||||||
|
err = models.SharedMessageDAO.CreateMessage(int64(cert.AdminId), int64(cert.UserId), models.MessageTypeSSLCertACMETaskFailed, models.MessageLevelError, msg, maps.Map{
|
||||||
|
"certId": cert.Id,
|
||||||
|
"acmeTaskId": cert.AcmeTaskId,
|
||||||
|
}.AsJSON())
|
||||||
|
|
||||||
|
// 更新通知时间
|
||||||
|
err = models.SharedSSLCertDAO.UpdateCertNotifiedAt(int64(cert.Id))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 中止不发送消息
|
||||||
|
continue
|
||||||
|
|
||||||
|
} else {
|
||||||
|
msg += "此证书是免费申请的证书,没有设置自动续期,请在到期前手动执行续期任务。"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
msg += "请及时更新证书。"
|
||||||
|
}
|
||||||
|
|
||||||
|
err = models.SharedMessageDAO.CreateMessage(int64(cert.AdminId), int64(cert.UserId), models.MessageTypeSSLCertExpiring, models.MessageLevelWarning, msg, maps.Map{
|
||||||
|
"certId": cert.Id,
|
||||||
|
"acmeTaskId": cert.AcmeTaskId,
|
||||||
|
}.AsJSON())
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// 设置最后通知时间
|
||||||
|
err = models.SharedSSLCertDAO.UpdateCertNotifiedAt(int64(cert.Id))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 当天过期
|
||||||
|
for _, days := range []int{0} {
|
||||||
|
certs, err := models.SharedSSLCertDAO.FindAllExpiringCerts(days)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
for _, cert := range certs {
|
||||||
|
// 发送消息
|
||||||
|
today := timeutil.Format("Y-m-d")
|
||||||
|
msg := "SSL证书\"" + cert.Name + "\"(" + cert.DnsNames + ")在今天(" + today + ")过期,请及时更新证书,之后将不再重复提醒。"
|
||||||
|
err = models.SharedMessageDAO.CreateMessage(int64(cert.AdminId), int64(cert.UserId), models.MessageTypeSSLCertExpiring, models.MessageLevelWarning, msg, maps.Map{
|
||||||
|
"certId": cert.Id,
|
||||||
|
"acmeTaskId": cert.AcmeTaskId,
|
||||||
|
}.AsJSON())
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// 设置最后通知时间
|
||||||
|
err = models.SharedSSLCertDAO.UpdateCertNotifiedAt(int64(cert.Id))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
25
internal/tasks/ssl_cert_expire_check_executor_test.go
Normal file
25
internal/tasks/ssl_cert_expire_check_executor_test.go
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
package tasks
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/iwind/TeaGo/dbs"
|
||||||
|
timeutil "github.com/iwind/TeaGo/utils/time"
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestSSLCertExpireCheckExecutor_loop(t *testing.T) {
|
||||||
|
dbs.NotifyReady()
|
||||||
|
|
||||||
|
t.Log("30 days later: ", timeutil.FormatTime("Y-m-d", time.Now().Unix()+30*86400), time.Now().Unix()+30*86400)
|
||||||
|
t.Log("14 days later: ", timeutil.FormatTime("Y-m-d", time.Now().Unix()+14*86400), time.Now().Unix()+14*86400)
|
||||||
|
t.Log("7 days later: ", timeutil.FormatTime("Y-m-d", time.Now().Unix()+7*86400), time.Now().Unix()+7*86400)
|
||||||
|
t.Log("3 days later: ", timeutil.FormatTime("Y-m-d", time.Now().Unix()+3*86400), time.Now().Unix()+3*86400)
|
||||||
|
t.Log("today: ", timeutil.FormatTime("Y-m-d", time.Now().Unix()), time.Now().Unix())
|
||||||
|
|
||||||
|
executor := NewSSLCertExpireCheckExecutor()
|
||||||
|
err := executor.loop(0)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
t.Log("ok")
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user