mirror of
https://github.com/1Panel-dev/1Panel.git
synced 2025-10-08 22:46:51 +08:00
This commit is contained in:
parent
f3fac23a84
commit
afcf509ec0
7 changed files with 177 additions and 1 deletions
115
backend/app/task/task.go
Normal file
115
backend/app/task/task.go
Normal file
|
@ -0,0 +1,115 @@
|
||||||
|
package task
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"github.com/1Panel-dev/1Panel/backend/constant"
|
||||||
|
"github.com/1Panel-dev/1Panel/backend/i18n"
|
||||||
|
"log"
|
||||||
|
"os"
|
||||||
|
"path"
|
||||||
|
"strconv"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
type ActionFunc func() error
|
||||||
|
type RollbackFunc func()
|
||||||
|
|
||||||
|
type Task struct {
|
||||||
|
Name string
|
||||||
|
Logger *log.Logger
|
||||||
|
SubTasks []*SubTask
|
||||||
|
Rollbacks []RollbackFunc
|
||||||
|
logFile *os.File
|
||||||
|
}
|
||||||
|
|
||||||
|
type SubTask struct {
|
||||||
|
Name string
|
||||||
|
Retry int
|
||||||
|
Timeout time.Duration
|
||||||
|
Action ActionFunc
|
||||||
|
Rollback RollbackFunc
|
||||||
|
Error error
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewTask(name string, taskType string) (*Task, error) {
|
||||||
|
logPath := path.Join(constant.LogDir, taskType)
|
||||||
|
//TODO 增加插入到日志表的逻辑
|
||||||
|
file, err := os.OpenFile(logPath, os.O_TRUNC|os.O_CREATE|os.O_WRONLY, 0666)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to open log file: %w", err)
|
||||||
|
}
|
||||||
|
logger := log.New(file, "", log.LstdFlags)
|
||||||
|
return &Task{Name: name, logFile: file, Logger: logger}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *Task) AddSubTask(name string, action ActionFunc, rollback RollbackFunc) {
|
||||||
|
subTask := &SubTask{Name: name, Retry: 0, Timeout: 10 * time.Minute, Action: action, Rollback: rollback}
|
||||||
|
t.SubTasks = append(t.SubTasks, subTask)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *Task) AddSubTaskWithOps(name string, action ActionFunc, rollback RollbackFunc, retry int, timeout time.Duration) {
|
||||||
|
subTask := &SubTask{Name: name, Retry: retry, Timeout: timeout, Action: action, Rollback: rollback}
|
||||||
|
t.SubTasks = append(t.SubTasks, subTask)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *SubTask) Execute(logger *log.Logger) bool {
|
||||||
|
logger.Printf(i18n.GetWithName("SubTaskStart", s.Name))
|
||||||
|
for i := 0; i < s.Retry+1; i++ {
|
||||||
|
if i > 0 {
|
||||||
|
logger.Printf(i18n.GetWithName("TaskRetry", strconv.Itoa(i)))
|
||||||
|
}
|
||||||
|
ctx, cancel := context.WithTimeout(context.Background(), s.Timeout)
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
|
done := make(chan error)
|
||||||
|
go func() {
|
||||||
|
done <- s.Action()
|
||||||
|
}()
|
||||||
|
|
||||||
|
select {
|
||||||
|
case <-ctx.Done():
|
||||||
|
logger.Printf(i18n.GetWithName("TaskTimeout", s.Name))
|
||||||
|
case err := <-done:
|
||||||
|
if err != nil {
|
||||||
|
s.Error = err
|
||||||
|
logger.Printf(i18n.GetWithNameAndErr("TaskFailed", s.Name, err))
|
||||||
|
} else {
|
||||||
|
logger.Printf(i18n.GetWithName("TaskSuccess", s.Name))
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if i == s.Retry {
|
||||||
|
if s.Rollback != nil {
|
||||||
|
s.Rollback()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
time.Sleep(1 * time.Second)
|
||||||
|
}
|
||||||
|
if s.Error != nil {
|
||||||
|
s.Error = fmt.Errorf(i18n.GetWithName("TaskFailed", s.Name))
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *Task) Execute() error {
|
||||||
|
t.Logger.Printf(i18n.GetWithName("TaskStart", t.Name))
|
||||||
|
var err error
|
||||||
|
for _, subTask := range t.SubTasks {
|
||||||
|
if subTask.Execute(t.Logger) {
|
||||||
|
if subTask.Rollback != nil {
|
||||||
|
t.Rollbacks = append(t.Rollbacks, subTask.Rollback)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
err = subTask.Error
|
||||||
|
for _, rollback := range t.Rollbacks {
|
||||||
|
rollback()
|
||||||
|
}
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
t.Logger.Printf(i18n.GetWithName("TaskEnd", t.Name))
|
||||||
|
_ = t.logFile.Close()
|
||||||
|
return err
|
||||||
|
}
|
|
@ -17,4 +17,5 @@ var (
|
||||||
RuntimeDir = path.Join(DataDir, "runtime")
|
RuntimeDir = path.Join(DataDir, "runtime")
|
||||||
RecycleBinDir = "/.1panel_clash"
|
RecycleBinDir = "/.1panel_clash"
|
||||||
SSLLogDir = path.Join(global.CONF.System.DataDir, "log", "ssl")
|
SSLLogDir = path.Join(global.CONF.System.DataDir, "log", "ssl")
|
||||||
|
LogDir = path.Join(global.CONF.System.DataDir, "log")
|
||||||
)
|
)
|
||||||
|
|
|
@ -74,6 +74,39 @@ func GetMsgByKey(key string) string {
|
||||||
return content
|
return content
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func GetWithName(key string, name string) string {
|
||||||
|
var (
|
||||||
|
dataMap = make(map[string]interface{})
|
||||||
|
)
|
||||||
|
dataMap["name"] = name
|
||||||
|
content, _ := global.I18n.Localize(&i18n.LocalizeConfig{
|
||||||
|
MessageID: key,
|
||||||
|
TemplateData: dataMap,
|
||||||
|
})
|
||||||
|
return content
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetWithMap(key string, dataMap map[string]string) string {
|
||||||
|
content, _ := global.I18n.Localize(&i18n.LocalizeConfig{
|
||||||
|
MessageID: key,
|
||||||
|
TemplateData: dataMap,
|
||||||
|
})
|
||||||
|
return content
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetWithNameAndErr(key string, name string, err error) string {
|
||||||
|
var (
|
||||||
|
dataMap = make(map[string]interface{})
|
||||||
|
)
|
||||||
|
dataMap["name"] = name
|
||||||
|
dataMap["err"] = err.Error()
|
||||||
|
content, _ := global.I18n.Localize(&i18n.LocalizeConfig{
|
||||||
|
MessageID: key,
|
||||||
|
TemplateData: dataMap,
|
||||||
|
})
|
||||||
|
return content
|
||||||
|
}
|
||||||
|
|
||||||
//go:embed lang/*
|
//go:embed lang/*
|
||||||
var fs embed.FS
|
var fs embed.FS
|
||||||
var bundle *i18n.Bundle
|
var bundle *i18n.Bundle
|
||||||
|
|
|
@ -196,3 +196,12 @@ ErrLicenseSync: "Failed to sync license information, no license information dete
|
||||||
ErrXpackNotFound: "This section is a professional edition feature, please import the license first in Panel Settings-License interface"
|
ErrXpackNotFound: "This section is a professional edition feature, please import the license first in Panel Settings-License interface"
|
||||||
ErrXpackNotActive: "This section is a professional edition feature, please synchronize the license status first in Panel Settings-License interface"
|
ErrXpackNotActive: "This section is a professional edition feature, please synchronize the license status first in Panel Settings-License interface"
|
||||||
ErrXpackOutOfDate: "The current license has expired, please re-import the license in Panel Settings-License interface"
|
ErrXpackOutOfDate: "The current license has expired, please re-import the license in Panel Settings-License interface"
|
||||||
|
|
||||||
|
#task
|
||||||
|
TaskStart: "{{.name}} started [START]"
|
||||||
|
TaskEnd: "{{.name}} ended [COMPLETED]"
|
||||||
|
TaskFailed: "{{.name}} failed: {{.err}}"
|
||||||
|
TaskTimeout: "{{.name}} timed out"
|
||||||
|
TaskSuccess: "{{.name}} succeeded"
|
||||||
|
TaskRetry: "Start {{.name}} retry"
|
||||||
|
SubTaskStart: "Start {{.name}}"
|
||||||
|
|
|
@ -198,3 +198,12 @@ ErrLicenseSync: "許可證信息同步失敗,資料庫中未檢測到許可證
|
||||||
ErrXpackNotFound: "該部分為專業版功能,請先在 面板設置-許可證 界面導入許可證"
|
ErrXpackNotFound: "該部分為專業版功能,請先在 面板設置-許可證 界面導入許可證"
|
||||||
ErrXpackNotActive: "該部分為專業版功能,請先在 面板設置-許可證 界面同步許可證狀態"
|
ErrXpackNotActive: "該部分為專業版功能,請先在 面板設置-許可證 界面同步許可證狀態"
|
||||||
ErrXpackOutOfDate: "當前許可證已過期,請重新在 面板設置-許可證 界面導入許可證"
|
ErrXpackOutOfDate: "當前許可證已過期,請重新在 面板設置-許可證 界面導入許可證"
|
||||||
|
|
||||||
|
#task
|
||||||
|
TaskStart: "{{.name}} 開始 [START]"
|
||||||
|
TaskEnd: "{{.name}} 結束 [COMPLETED]"
|
||||||
|
TaskFailed: "{{.name}} 失敗: {{.err}}"
|
||||||
|
TaskTimeout: "{{.name}} 逾時"
|
||||||
|
TaskSuccess: "{{.name}} 成功"
|
||||||
|
TaskRetry: "開始第 {{.name}} 次重試"
|
||||||
|
SubTaskStart: "開始 {{.name}}"
|
||||||
|
|
|
@ -200,3 +200,11 @@ ErrXpackNotFound: "该部分为专业版功能,请先在 面板设置-许可
|
||||||
ErrXpackNotActive: "该部分为专业版功能,请先在 面板设置-许可证 界面同步许可证状态"
|
ErrXpackNotActive: "该部分为专业版功能,请先在 面板设置-许可证 界面同步许可证状态"
|
||||||
ErrXpackOutOfDate: "当前许可证已过期,请重新在 面板设置-许可证 界面导入许可证"
|
ErrXpackOutOfDate: "当前许可证已过期,请重新在 面板设置-许可证 界面导入许可证"
|
||||||
|
|
||||||
|
#task
|
||||||
|
TaskStart: "{{.name}} 开始 [START]"
|
||||||
|
TaskEnd: "{{.name}} 结束 [COMPLETED]"
|
||||||
|
TaskFailed: "{{.name}} 失败: {{.err}}"
|
||||||
|
TaskTimeout: "{{.name}} 超时"
|
||||||
|
TaskSuccess: "{{.name}} 成功"
|
||||||
|
TaskRetry: "开始第 {{.name}} 次重试"
|
||||||
|
SubTaskStart: "开始 {{.name}}"
|
||||||
|
|
|
@ -21,7 +21,8 @@ func Init() {
|
||||||
constant.LocalAppInstallDir = path.Join(constant.AppInstallDir, "local")
|
constant.LocalAppInstallDir = path.Join(constant.AppInstallDir, "local")
|
||||||
constant.RemoteAppResourceDir = path.Join(constant.AppResourceDir, "remote")
|
constant.RemoteAppResourceDir = path.Join(constant.AppResourceDir, "remote")
|
||||||
|
|
||||||
constant.SSLLogDir = path.Join(global.CONF.System.DataDir, "log", "ssl")
|
constant.LogDir = path.Join(global.CONF.System.DataDir, "log")
|
||||||
|
constant.SSLLogDir = path.Join(constant.LogDir, "ssl")
|
||||||
|
|
||||||
dirs := []string{constant.DataDir, constant.ResourceDir, constant.AppResourceDir, constant.AppInstallDir,
|
dirs := []string{constant.DataDir, constant.ResourceDir, constant.AppResourceDir, constant.AppInstallDir,
|
||||||
global.CONF.System.Backup, constant.RuntimeDir, constant.LocalAppResourceDir, constant.RemoteAppResourceDir, constant.SSLLogDir}
|
global.CONF.System.Backup, constant.RuntimeDir, constant.LocalAppResourceDir, constant.RemoteAppResourceDir, constant.SSLLogDir}
|
||||||
|
|
Loading…
Add table
Reference in a new issue