This commit is contained in:
yoan
2024-08-21 12:19:12 +08:00
commit 50fa238d88
89 changed files with 11835 additions and 0 deletions

132
internal/domains/deploy.go Normal file
View File

@@ -0,0 +1,132 @@
package domains
import (
"certimate/internal/applicant"
"certimate/internal/deployer"
"certimate/internal/utils/app"
"context"
"errors"
"fmt"
"time"
"github.com/pocketbase/pocketbase/models"
)
type Phase string
const (
checkPhase Phase = "check"
applyPhase Phase = "apply"
deployPhase Phase = "deploy"
)
func deploy(ctx context.Context, record *models.Record) error {
currRecord, err := app.GetApp().Dao().FindRecordById("domains", record.Id)
history := NewHistory(record)
defer history.commit()
// ############1.检查域名配置
history.record(checkPhase, "开始检查", nil)
if err != nil {
app.GetApp().Logger().Error("获取记录失败", "err", err)
history.record(checkPhase, "获取域名配置失败", err)
return err
}
history.record(checkPhase, "获取记录成功", nil)
if errs := app.GetApp().Dao().ExpandRecord(currRecord, []string{"access", "targetAccess"}, nil); len(errs) > 0 {
errList := make([]error, 0)
for name, err := range errs {
errList = append(errList, fmt.Errorf("展开记录失败,%s: %w", name, err))
}
err = errors.Join(errList...)
app.GetApp().Logger().Error("展开记录失败", "err", err)
history.record(checkPhase, "获取授权信息失败", err)
return err
}
history.record(checkPhase, "获取授权信息成功", nil)
cert := currRecord.GetString("certificate")
expiredAt := currRecord.GetDateTime("expiredAt").Time()
if cert != "" && time.Until(expiredAt) > time.Hour*24 && currRecord.GetBool("deployed") {
app.GetApp().Logger().Info("证书在有效期内")
history.record(checkPhase, "证书在有效期内且已部署,跳过", nil, true)
return err
}
history.record(checkPhase, "检查通过", nil, true)
// ############2.申请证书
history.record(applyPhase, "开始申请", nil)
if cert != "" && time.Until(expiredAt) > time.Hour*24 {
history.record(applyPhase, "证书在有效期内,跳过", nil)
} else {
applicant, err := applicant.Get(currRecord)
if err != nil {
history.record(applyPhase, "获取applicant失败", err)
app.GetApp().Logger().Error("获取applicant失败", "err", err)
return err
}
certificate, err := applicant.Apply()
if err != nil {
history.record(applyPhase, "申请证书失败", err)
app.GetApp().Logger().Error("申请证书失败", "err", err)
return err
}
history.record(applyPhase, "申请证书成功", nil)
if err = saveCert(ctx, record, certificate); err != nil {
history.record(applyPhase, "保存证书失败", err)
app.GetApp().Logger().Error("保存证书失败", "err", err)
return err
}
}
history.record(applyPhase, "保存证书成功", nil, true)
// ############3.部署证书
history.record(deployPhase, "开始部署", nil)
deployer, err := deployer.Get(currRecord)
if err != nil {
history.record(deployPhase, "获取deployer失败", err)
app.GetApp().Logger().Error("获取deployer失败", "err", err)
return err
}
if err = deployer.Deploy(ctx); err != nil {
setDeployed(ctx, record, false)
app.GetApp().Logger().Error("部署失败", "err", err)
history.record(deployPhase, "部署失败", err)
return err
}
setDeployed(ctx, record, true)
app.GetApp().Logger().Info("部署成功")
history.record(deployPhase, "部署成功", nil, true)
return nil
}
func setDeployed(ctx context.Context, record *models.Record, deployed bool) error {
record.Set("deployed", deployed)
if err := app.GetApp().Dao().SaveRecord(record); err != nil {
return err
}
return nil
}
func saveCert(ctx context.Context, record *models.Record, cert *applicant.Certificate) error {
record.Set("certUrl", cert.CertUrl)
record.Set("certStableUrl", cert.CertStableUrl)
record.Set("privateKey", cert.PrivateKey)
record.Set("certificate", cert.Certificate)
record.Set("issuerCertificate", cert.IssuerCertificate)
record.Set("csr", cert.Csr)
record.Set("expiredAt", time.Now().Add(time.Hour*24*90))
if err := app.GetApp().Dao().SaveRecord(record); err != nil {
return err
}
return nil
}

View File

@@ -0,0 +1,82 @@
package domains
import (
"certimate/internal/utils/app"
"context"
"fmt"
"github.com/pocketbase/pocketbase/models"
)
func create(ctx context.Context, record *models.Record) error {
if !record.GetBool("enabled") {
return nil
}
if record.GetBool("rightnow") {
defer func() {
setRightnow(ctx, record, false)
}()
if err := deploy(ctx, record); err != nil {
return err
}
}
scheduler := app.GetScheduler()
err := scheduler.Add(record.Id, record.GetString("crontab"), func() {
deploy(ctx, record)
})
if err != nil {
app.GetApp().Logger().Error("add cron job failed", "err", err)
return fmt.Errorf("add cron job failed: %w", err)
}
app.GetApp().Logger().Error("add cron job failed", "domain", record.GetString("domain"))
scheduler.Start()
return nil
}
func update(ctx context.Context, record *models.Record) error {
scheduler := app.GetScheduler()
scheduler.Remove(record.Id)
if !record.GetBool("enabled") {
return nil
}
if record.GetBool("rightnow") {
defer func() {
setRightnow(ctx, record, false)
}()
if err := deploy(ctx, record); err != nil {
return err
}
}
err := scheduler.Add(record.Id, record.GetString("crontab"), func() {
deploy(ctx, record)
})
if err != nil {
app.GetApp().Logger().Error("update cron job failed", "err", err)
return fmt.Errorf("update cron job failed: %w", err)
}
app.GetApp().Logger().Error("update cron job failed", "domain", record.GetString("domain"))
scheduler.Start()
return nil
}
func delete(_ context.Context, record *models.Record) error {
scheduler := app.GetScheduler()
scheduler.Remove(record.Id)
scheduler.Start()
return nil
}
func setRightnow(ctx context.Context, record *models.Record, ok bool) error {
record.Set("rightnow", ok)
return app.GetApp().Dao().SaveRecord(record)
}

27
internal/domains/event.go Normal file
View File

@@ -0,0 +1,27 @@
package domains
import (
"certimate/internal/utils/app"
"github.com/pocketbase/pocketbase/core"
)
const tableName = "domains"
func AddEvent() error {
app := app.GetApp()
app.OnRecordAfterCreateRequest(tableName).Add(func(e *core.RecordCreateEvent) error {
return create(e.HttpContext.Request().Context(), e.Record)
})
app.OnRecordAfterUpdateRequest(tableName).Add(func(e *core.RecordUpdateEvent) error {
return update(e.HttpContext.Request().Context(), e.Record)
})
app.OnRecordAfterDeleteRequest(tableName).Add(func(e *core.RecordDeleteEvent) error {
return delete(e.HttpContext.Request().Context(), e.Record)
})
return nil
}

View File

@@ -0,0 +1,86 @@
package domains
import (
"certimate/internal/utils/app"
"certimate/internal/utils/xtime"
"time"
"github.com/pocketbase/pocketbase/models"
)
type historyItem struct {
Time string `json:"time"`
Message string `json:"message"`
Error string `json:"error"`
}
type history struct {
Domain string `json:"domain"`
Log map[Phase][]historyItem `json:"log"`
Phase Phase `json:"phase"`
PhaseSuccess bool `json:"phaseSuccess"`
DeployedAt string `json:"deployedAt"`
}
func NewHistory(record *models.Record) *history {
return &history{
Domain: record.Id,
DeployedAt: time.Now().UTC().Format("2006-01-02T15:04:05Z"),
Log: make(map[Phase][]historyItem),
Phase: checkPhase,
PhaseSuccess: false,
}
}
func (a *history) record(phase Phase, msg string, err error, pass ...bool) {
a.Phase = phase
if len(pass) > 0 && pass[0] {
a.PhaseSuccess = true
}
errMsg := ""
if err != nil {
errMsg = err.Error()
a.PhaseSuccess = false
}
a.Log[phase] = append(a.Log[phase], historyItem{
Message: msg,
Error: errMsg,
Time: xtime.BeijingTimeStr(),
})
}
func (a *history) commit() error {
collection, err := app.GetApp().Dao().FindCollectionByNameOrId("deployments")
if err != nil {
return err
}
record := models.NewRecord(collection)
record.Set("domain", a.Domain)
record.Set("deployedAt", a.DeployedAt)
record.Set("log", a.Log)
record.Set("phase", string(a.Phase))
record.Set("phaseSuccess", a.PhaseSuccess)
if err := app.GetApp().Dao().SaveRecord(record); err != nil {
return err
}
domainRecord, err := app.GetApp().Dao().FindRecordById("domains", a.Domain)
if err != nil {
return err
}
domainRecord.Set("lastDeployedAt", a.DeployedAt)
domainRecord.Set("lastDeployment", record.Id)
if err := app.GetApp().Dao().SaveRecord(domainRecord); err != nil {
return err
}
return nil
}