mirror of
https://github.com/woodchen-ink/certimate.git
synced 2025-07-18 17:31:55 +08:00
163 lines
4.7 KiB
Go
163 lines
4.7 KiB
Go
package domains
|
||
|
||
import (
|
||
"context"
|
||
"fmt"
|
||
"strings"
|
||
"time"
|
||
|
||
"github.com/pocketbase/pocketbase/models"
|
||
|
||
"golang.org/x/exp/slices"
|
||
|
||
"github.com/usual2970/certimate/internal/applicant"
|
||
"github.com/usual2970/certimate/internal/deployer"
|
||
"github.com/usual2970/certimate/internal/utils/app"
|
||
|
||
"github.com/usual2970/certimate/internal/pkg/utils/x509"
|
||
)
|
||
|
||
type Phase string
|
||
|
||
const (
|
||
checkPhase Phase = "check"
|
||
applyPhase Phase = "apply"
|
||
deployPhase Phase = "deploy"
|
||
)
|
||
|
||
func deploy(ctx context.Context, record *models.Record) error {
|
||
defer func() {
|
||
if r := recover(); r != nil {
|
||
app.GetApp().Logger().Error("部署失败", "err", r)
|
||
}
|
||
}()
|
||
var certificate *applicant.Certificate
|
||
|
||
history := NewHistory(record)
|
||
defer history.commit()
|
||
|
||
// ############1.检查域名配置
|
||
history.record(checkPhase, "开始检查", nil)
|
||
|
||
currRecord, err := app.GetApp().Dao().FindRecordById("domains", record.Id)
|
||
if err != nil {
|
||
app.GetApp().Logger().Error("获取记录失败", "err", err)
|
||
history.record(checkPhase, "获取域名配置失败", &RecordInfo{Err: err})
|
||
return err
|
||
}
|
||
history.record(checkPhase, "获取记录成功", nil)
|
||
|
||
cert := currRecord.GetString("certificate")
|
||
expiredAt := currRecord.GetDateTime("expiredAt").Time()
|
||
|
||
// 检查证书是否包含设置的所有域名
|
||
included := isAllDomainsIncludedInCert(cert, currRecord.GetString("domain"))
|
||
|
||
if cert != "" && time.Until(expiredAt) > time.Hour*24*10 && currRecord.GetBool("deployed") && included {
|
||
app.GetApp().Logger().Info("证书在有效期内")
|
||
history.record(checkPhase, "证书在有效期内且已部署,跳过", &RecordInfo{
|
||
Info: []string{fmt.Sprintf("证书有效期至 %s", expiredAt.Format("2006-01-02"))},
|
||
}, true)
|
||
|
||
// 跳过的情况也算成功
|
||
history.setWholeSuccess(true)
|
||
return nil
|
||
}
|
||
history.record(checkPhase, "检查通过", nil, true)
|
||
|
||
// ############2.申请证书
|
||
history.record(applyPhase, "开始申请", nil)
|
||
|
||
if cert != "" && time.Until(expiredAt) > time.Hour*24 && included {
|
||
history.record(applyPhase, "证书在有效期内,跳过", &RecordInfo{
|
||
Info: []string{fmt.Sprintf("证书有效期至 %s", expiredAt.Format("2006-01-02"))},
|
||
})
|
||
} else {
|
||
applicant, err := applicant.Get(currRecord)
|
||
if err != nil {
|
||
history.record(applyPhase, "获取applicant失败", &RecordInfo{Err: err})
|
||
app.GetApp().Logger().Error("获取applicant失败", "err", err)
|
||
return err
|
||
}
|
||
certificate, err = applicant.Apply()
|
||
if err != nil {
|
||
history.record(applyPhase, "申请证书失败", &RecordInfo{Err: err})
|
||
app.GetApp().Logger().Error("申请证书失败", "err", err)
|
||
return err
|
||
}
|
||
history.record(applyPhase, "申请证书成功", &RecordInfo{
|
||
Info: []string{fmt.Sprintf("证书地址: %s", certificate.CertUrl)},
|
||
})
|
||
history.setCert(certificate)
|
||
}
|
||
|
||
history.record(applyPhase, "保存证书成功", nil, true)
|
||
|
||
// ############3.部署证书
|
||
history.record(deployPhase, "开始部署", nil, false)
|
||
deployers, err := deployer.Gets(currRecord, certificate)
|
||
if err != nil {
|
||
history.record(deployPhase, "获取deployer失败", &RecordInfo{Err: err})
|
||
app.GetApp().Logger().Error("获取deployer失败", "err", err)
|
||
return err
|
||
}
|
||
|
||
// 没有部署配置,也算成功
|
||
if len(deployers) == 0 {
|
||
history.record(deployPhase, "没有部署配置", &RecordInfo{Info: []string{"没有部署配置"}})
|
||
history.setWholeSuccess(true)
|
||
return nil
|
||
}
|
||
|
||
for _, deployer := range deployers {
|
||
if err = deployer.Deploy(ctx); err != nil {
|
||
|
||
app.GetApp().Logger().Error("部署失败", "err", err)
|
||
history.record(deployPhase, "部署失败", &RecordInfo{Err: err, Info: deployer.GetInfos()})
|
||
return err
|
||
}
|
||
history.record(deployPhase, fmt.Sprintf("[%s]-部署成功", deployer.GetID()), &RecordInfo{
|
||
Info: deployer.GetInfos(),
|
||
}, false)
|
||
|
||
}
|
||
|
||
app.GetApp().Logger().Info("部署成功")
|
||
history.record(deployPhase, "部署成功", nil, true)
|
||
|
||
history.setWholeSuccess(true)
|
||
|
||
return nil
|
||
}
|
||
|
||
func isAllDomainsIncludedInCert(certificate, domains string) bool {
|
||
// 如果证书为空,直接返回false
|
||
if certificate == "" {
|
||
return false
|
||
}
|
||
|
||
// 解析证书
|
||
cert, err := x509.ParseCertificateFromPEM(certificate)
|
||
if err != nil {
|
||
app.GetApp().Logger().Error("解析证书失败", "err", err)
|
||
return false
|
||
}
|
||
|
||
// 遍历域名列表,检查是否都在证书中,找到第一个不存在证书中域名时提前返回false
|
||
for _, domain := range strings.Split(domains, ";") {
|
||
if !slices.Contains(cert.DNSNames, domain) && !slices.Contains(cert.DNSNames, "*."+removeLastSubdomain(domain)) {
|
||
return false
|
||
}
|
||
}
|
||
|
||
return true
|
||
}
|
||
|
||
func removeLastSubdomain(domain string) string {
|
||
parts := strings.Split(domain, ".")
|
||
if len(parts) > 1 {
|
||
return strings.Join(parts[1:], ".")
|
||
}
|
||
return domain
|
||
}
|