2
0
mirror of https://github.com/acepanel/panel.git synced 2026-02-04 13:47:15 +08:00
Files
panel/internal/job/cert_renew.go
Copilot f2e41a3364 feat: 添加操作日志记录功能和前端日志查看页面 (#1227)
* Initial plan

* feat: 添加操作日志记录功能和前端日志查看页面

Co-authored-by: devhaozi <115467771+devhaozi@users.noreply.github.com>

* fix: 修复 HttpLog 中 IP 字符串处理的类型安全问题

Co-authored-by: devhaozi <115467771+devhaozi@users.noreply.github.com>

* fix: lint

* feat: 完善面板内日志记录,添加网站/数据库/项目/计划任务/备份CRUD日志

Co-authored-by: devhaozi <115467771+devhaozi@users.noreply.github.com>

* feat: 完善更多模块的日志记录(证书/SSH/Webhook/用户/设置/安全)

Co-authored-by: devhaozi <115467771+devhaozi@users.noreply.github.com>

* fix: 改进cert_dns删除方法,在删除前获取实体信息用于日志记录

Co-authored-by: devhaozi <115467771+devhaozi@users.noreply.github.com>

* fix: lint

* feat: 从session获取操作员ID并在前端显示用户名

Co-authored-by: devhaozi <115467771+devhaozi@users.noreply.github.com>

* fix: lint

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: devhaozi <115467771+devhaozi@users.noreply.github.com>
Co-authored-by: 耗子 <haozi@loli.email>
2026-01-12 23:31:22 +08:00

127 lines
4.0 KiB
Go

package job
import (
"encoding/json"
"log/slog"
"path/filepath"
"time"
"gorm.io/gorm"
"github.com/acepanel/panel/internal/app"
"github.com/acepanel/panel/internal/biz"
"github.com/acepanel/panel/internal/http/request"
pkgcert "github.com/acepanel/panel/pkg/cert"
"github.com/acepanel/panel/pkg/config"
"github.com/acepanel/panel/pkg/tools"
)
// CertRenew 证书续签
type CertRenew struct {
conf *config.Config
db *gorm.DB
log *slog.Logger
settingRepo biz.SettingRepo
certRepo biz.CertRepo
certAccountRepo biz.CertAccountRepo
}
func NewCertRenew(conf *config.Config, db *gorm.DB, log *slog.Logger, setting biz.SettingRepo, cert biz.CertRepo, certAccount biz.CertAccountRepo) *CertRenew {
return &CertRenew{
conf: conf,
db: db,
log: log,
settingRepo: setting,
certRepo: cert,
certAccountRepo: certAccount,
}
}
func (r *CertRenew) Run() {
if app.Status != app.StatusNormal {
return
}
var certs []biz.Cert
if err := r.db.Preload("Website").Preload("Account").Preload("DNS").Find(&certs).Error; err != nil {
r.log.Warn("failed to get certs", slog.String("type", biz.OperationTypeCert), slog.Uint64("operator_id", 0), slog.Any("err", err))
return
}
for _, cert := range certs {
// 跳过上传类型或未开启自动续签的证书
if cert.Type == "upload" || !cert.AutoRenewal {
continue
}
// 刷新续签信息
if cert.RenewalInfo.NeedsRefresh() {
renewInfo, err := r.certRepo.RefreshRenewalInfo(cert.ID)
if err != nil {
r.log.Warn("failed to refresh renewal info", slog.String("type", biz.OperationTypeCert), slog.Uint64("operator_id", 0), slog.Any("err", err))
continue
}
cert.RenewalInfo = renewInfo
}
// 到达建议时间,续签证书
if time.Now().After(cert.RenewalInfo.SelectedTime) {
if _, err := r.certRepo.Renew(cert.ID); err != nil {
r.log.Warn("failed to renew cert", slog.String("type", biz.OperationTypeCert), slog.Uint64("operator_id", 0), slog.Any("err", err))
}
}
}
// 面板证书续签
if r.conf.HTTP.ACME {
decode, err := pkgcert.ParseCert(filepath.Join(app.Root, "panel/storage/cert.pem"))
if err != nil {
r.log.Warn("failed to parse panel cert", slog.String("type", biz.OperationTypeCert), slog.Uint64("operator_id", 0), slog.Any("err", err))
return
}
// 结束时间大于 2 天不续签
if time.Until(decode.NotAfter) > 24*2*time.Hour {
return
}
ip, err := r.settingRepo.Get(biz.SettingKeyPublicIPs)
if err != nil {
r.log.Warn("failed to get panel IP", slog.String("type", biz.OperationTypeCert), slog.Uint64("operator_id", 0), slog.Any("err", err))
return
}
var ips []string
if err = json.Unmarshal([]byte(ip), &ips); err != nil || len(ips) == 0 {
r.log.Warn("panel public IPs not set", slog.String("type", biz.OperationTypeCert), slog.Uint64("operator_id", 0), slog.Any("err", err))
return
}
var user biz.User
if err = r.db.First(&user).Error; err != nil {
r.log.Warn("failed to get a panel user", slog.String("type", biz.OperationTypeCert), slog.Uint64("operator_id", 0), slog.Any("err", err))
return
}
account, err := r.certAccountRepo.GetDefault(user.ID)
if err != nil {
r.log.Warn("failed to get panel ACME account", slog.String("type", biz.OperationTypeCert), slog.Uint64("operator_id", 0), slog.Any("err", err))
return
}
crt, key, err := r.certRepo.ObtainPanel(account, ips)
if err != nil {
r.log.Warn("failed to obtain ACME cert", slog.String("type", biz.OperationTypeCert), slog.Uint64("operator_id", 0), slog.Any("err", err))
return
}
if err = r.settingRepo.UpdateCert(&request.SettingCert{
Cert: string(crt),
Key: string(key),
}); err != nil {
r.log.Warn("failed to update panel cert", slog.String("type", biz.OperationTypeCert), slog.Uint64("operator_id", 0), slog.Any("err", err))
return
}
r.log.Info("panel cert renewed successfully", slog.String("type", biz.OperationTypeCert), slog.Uint64("operator_id", 0))
tools.RestartPanel()
}
}