2
0
mirror of https://github.com/acepanel/panel.git synced 2026-02-04 05:31:44 +08:00

feat: 添加计划任务

This commit is contained in:
耗子
2024-10-12 02:25:41 +08:00
parent 0c4d97a512
commit 96d7e14631
15 changed files with 297 additions and 6 deletions

1
go.mod
View File

@@ -34,6 +34,7 @@ require (
github.com/libdns/tencentcloud v1.0.0
github.com/mholt/acmez/v2 v2.0.3
github.com/mholt/archiver/v4 v4.0.0-alpha.8
github.com/robfig/cron/v3 v3.0.1
github.com/sethvargo/go-limiter v1.0.0
github.com/shirou/gopsutil v2.21.11+incompatible
github.com/spf13/cast v1.7.0

2
go.sum
View File

@@ -263,6 +263,8 @@ github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZN
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec h1:W09IVJc94icq4NjY3clb7Lk8O1qJ8BdBEF8z0ibU0rE=
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo=
github.com/robfig/cron/v3 v3.0.1 h1:WdRxkvbJztn8LMz/QEvLN5sBU+xKpSqwwUO1Pjr4qDs=
github.com/robfig/cron/v3 v3.0.1/go.mod h1:eQICP3HwyT7UooqI/z+Ov+PtYAWygg1TEWWzGIFLtro=
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8=
github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs=

View File

@@ -6,6 +6,7 @@ import (
"github.com/go-playground/validator/v10"
"github.com/go-rat/sessions"
"github.com/knadh/koanf/v2"
"github.com/robfig/cron/v3"
"go.uber.org/zap"
"gorm.io/gorm"
@@ -19,6 +20,7 @@ var (
Validator *validator.Validate
Translator *ut.Translator
Session *sessions.Manager
Cron *cron.Cron
Queue *queue.Queue
Logger *zap.Logger
)

View File

@@ -28,4 +28,5 @@ type TaskRepo interface {
Delete(id uint) error
UpdateStatus(id uint, status TaskStatus) error
Push(task *Task) error
DispatchWaiting() error
}

View File

@@ -19,6 +19,7 @@ func BootWeb() {
boot()
initValidator()
initSession()
initCron()
initQueue()
go initHttp()

View File

@@ -7,11 +7,11 @@ import (
"strings"
"time"
"github.com/knadh/koanf/parsers/yaml"
"github.com/knadh/koanf/providers/file"
"github.com/knadh/koanf/v2"
"github.com/TheTNB/panel/internal/app"
"github.com/knadh/koanf/parsers/yaml"
"github.com/knadh/koanf/providers/file"
)
func initConf() {

View File

@@ -0,0 +1,29 @@
package bootstrap
import (
"fmt"
"github.com/robfig/cron/v3"
"github.com/TheTNB/panel/internal/app"
"github.com/TheTNB/panel/internal/job"
pkgcron "github.com/TheTNB/panel/pkg/cron"
)
func initCron() {
logger := pkgcron.NewLogger(app.Logger, app.Conf.Bool("app.debug"))
c := cron.New(
cron.WithParser(cron.NewParser(
cron.SecondOptional|cron.Minute|cron.Hour|cron.Dom|cron.Month|cron.Dow|cron.Descriptor,
)),
cron.WithLogger(logger),
cron.WithChain(cron.Recover(logger), cron.SkipIfStillRunning(logger)),
)
app.Cron = c
if err := job.Boot(app.Cron); err != nil {
panic(fmt.Sprintf("failed to boot cron jobs: %v", err))
}
c.Start()
}

View File

@@ -3,7 +3,7 @@ package data
import (
"github.com/TheTNB/panel/internal/app"
"github.com/TheTNB/panel/internal/biz"
"github.com/TheTNB/panel/internal/job"
"github.com/TheTNB/panel/internal/queuejob"
)
type taskRepo struct{}
@@ -43,7 +43,22 @@ func (r *taskRepo) Push(task *biz.Task) error {
if err := app.Orm.Create(task).Error; err != nil {
return err
}
return app.Queue.Push(job.NewProcessTask(r), []any{
return app.Queue.Push(queuejob.NewProcessTask(r), []any{
task.ID,
})
}
func (r *taskRepo) DispatchWaiting() error {
var tasks []biz.Task
if err := app.Orm.Where("status = ?", biz.TaskStatusWaiting).Find(&tasks).Error; err != nil {
return err
}
for _, task := range tasks {
if err := r.Push(&task); err != nil {
return err
}
}
return nil
}

View File

@@ -0,0 +1,58 @@
package job
import (
"time"
"go.uber.org/zap"
"github.com/TheTNB/panel/internal/app"
"github.com/TheTNB/panel/internal/biz"
"github.com/TheTNB/panel/internal/data"
pkgcert "github.com/TheTNB/panel/pkg/cert"
"github.com/TheTNB/panel/pkg/types"
)
// CertRenew 证书续签
type CertRenew struct {
cert biz.CertRepo
}
func NewCertRenew() *CertRenew {
return &CertRenew{
cert: data.NewCertRepo(),
}
}
func (receiver *CertRenew) Run() {
if types.Status != types.StatusNormal {
return
}
var certs []biz.Cert
if err := app.Orm.Preload("Website").Preload("Account").Preload("DNS").Find(&certs).Error; err != nil {
app.Logger.Error("获取证书失败", zap.Error(err))
return
}
for _, cert := range certs {
if !cert.AutoRenew {
continue
}
decode, err := pkgcert.ParseCert(cert.Cert)
if err != nil {
continue
}
// 结束时间大于 7 天的证书不续签
now := time.Now()
if decode.NotAfter.Sub(now).Hours() > 24*7 {
continue
}
_, err = receiver.cert.Renew(cert.ID)
if err != nil {
app.Logger.Error("续签证书失败", zap.Error(err))
}
}
}

19
internal/job/init.go Normal file
View File

@@ -0,0 +1,19 @@
package job
import (
"github.com/robfig/cron/v3"
)
func Boot(c *cron.Cron) error {
if _, err := c.AddJob("* * * * *", NewMonitoring()); err != nil {
return err
}
if _, err := c.AddJob("0 4 * * *", NewCertRenew()); err != nil {
return err
}
if _, err := c.AddJob("0 2 * * *", NewPanelTask()); err != nil {
return err
}
return nil
}

View File

@@ -0,0 +1,69 @@
package job
import (
"time"
"github.com/spf13/cast"
"go.uber.org/zap"
"github.com/TheTNB/panel/internal/app"
"github.com/TheTNB/panel/internal/biz"
"github.com/TheTNB/panel/internal/data"
"github.com/TheTNB/panel/pkg/tools"
"github.com/TheTNB/panel/pkg/types"
)
// Monitoring 系统监控
type Monitoring struct {
setting biz.SettingRepo
}
func NewMonitoring() *Monitoring {
return &Monitoring{
setting: data.NewSettingRepo(),
}
}
func (receiver *Monitoring) Run() {
if types.Status != types.StatusNormal {
return
}
// 将等待中的任务分发
//task := data.NewTaskRepo()
//_ = task.DispatchWaiting()
monitor, err := receiver.setting.Get(biz.SettingKeyMonitor)
if err != nil || !cast.ToBool(monitor) {
return
}
info := tools.GetMonitoringInfo()
// 去除部分数据以减少数据库存储
info.Disk = nil
info.Cpus = nil
if types.Status != types.StatusNormal {
return
}
if err = app.Orm.Create(&biz.Monitor{Info: info}).Error; err != nil {
app.Logger.Error("记录系统监控失败", zap.Error(err))
return
}
// 删除过期数据
dayStr, err := receiver.setting.Get(biz.SettingKeyMonitorDays)
if err != nil {
return
}
day := cast.ToInt(dayStr)
if day <= 0 || types.Status != types.StatusNormal {
return
}
if err = app.Orm.Where("created_at < ?", time.Now().AddDate(0, 0, -day).Format("2006-01-02 15:04:05")).Delete(&biz.Monitor{}).Error; err != nil {
app.Logger.Error("删除过期系统监控失败", zap.Error(err))
return
}
}

View File

@@ -0,0 +1,51 @@
package job
import (
"path/filepath"
"runtime"
"runtime/debug"
"time"
"go.uber.org/zap"
"github.com/TheTNB/panel/internal/app"
"github.com/TheTNB/panel/pkg/io"
"github.com/TheTNB/panel/pkg/shell"
"github.com/TheTNB/panel/pkg/types"
)
// PanelTask 面板每日任务
type PanelTask struct {
}
func NewPanelTask() *PanelTask {
return &PanelTask{}
}
func (receiver *PanelTask) Run() {
types.Status = types.StatusMaintain
// 优化数据库
if err := app.Orm.Exec("VACUUM").Error; err != nil {
types.Status = types.StatusFailed
app.Logger.Error("优化面板数据库失败", zap.Error(err))
}
// 备份面板
if err := io.Compress([]string{"/www/panel"}, filepath.Join(app.Root, "backup", "panel", "panel-"+time.Now().Format(time.DateOnly)+".zip"), io.Zip); err != nil {
types.Status = types.StatusFailed
app.Logger.Error("备份面板失败", zap.Error(err))
}
// 清理 7 天前的备份
if _, err := shell.Execf(`find %s -mtime +7 -name "*.zip" -exec rm -rf {} \;`, filepath.Join(app.Root, "backup", "panel")); err != nil {
types.Status = types.StatusFailed
app.Logger.Error("清理面板备份失败", zap.Error(err))
}
// 回收内存
runtime.GC()
debug.FreeOSMemory()
types.Status = types.StatusNormal
}

View File

@@ -1,4 +1,4 @@
package job
package queuejob
import (
"errors"

View File

@@ -4,13 +4,13 @@ import (
"context"
"errors"
"fmt"
"gorm.io/gorm"
"path/filepath"
"github.com/go-rat/utils/hash"
"github.com/goccy/go-yaml"
"github.com/gookit/color"
"github.com/urfave/cli/v3"
"gorm.io/gorm"
"github.com/TheTNB/panel/internal/app"
"github.com/TheTNB/panel/internal/biz"

43
pkg/cron/logger.go Normal file
View File

@@ -0,0 +1,43 @@
package cron
import "go.uber.org/zap"
type Logger struct {
log *zap.Logger
debug bool
}
func NewLogger(log *zap.Logger, debug bool) *Logger {
return &Logger{
debug: debug,
log: log,
}
}
func (log *Logger) Info(msg string, keysAndValues ...any) {
if !log.debug {
return
}
log.log.Info(msg, log.toZapFields(keysAndValues...)...)
}
func (log *Logger) Error(err error, msg string, keysAndValues ...any) {
fields := log.toZapFields(keysAndValues...)
fields = append(fields, zap.Error(err))
log.log.Error(msg, fields...)
}
func (log *Logger) toZapFields(keysAndValues ...any) []zap.Field {
fields := make([]zap.Field, 0, len(keysAndValues)/2)
for i := 0; i < len(keysAndValues); i += 2 {
if i+1 < len(keysAndValues) {
key, okKey := keysAndValues[i].(string)
value := keysAndValues[i+1]
if okKey {
fields = append(fields, zap.Any(key, value))
}
}
}
return fields
}