mirror of
https://github.com/1Panel-dev/1Panel.git
synced 2025-01-18 22:22:59 +08:00
parent
684f20a5dc
commit
6a8bd490bf
@ -76,14 +76,12 @@ type PortUpdate struct {
|
||||
}
|
||||
|
||||
type SnapshotStatus struct {
|
||||
Panel string `json:"panel"`
|
||||
PanelCtl string `json:"panelCtl"`
|
||||
PanelService string `json:"panelService"`
|
||||
PanelInfo string `json:"panelInfo"`
|
||||
DaemonJson string `json:"daemonJson"`
|
||||
AppData string `json:"appData"`
|
||||
PanelData string `json:"panelData"`
|
||||
BackupData string `json:"backupData"`
|
||||
Panel string `json:"panel"`
|
||||
PanelInfo string `json:"panelInfo"`
|
||||
DaemonJson string `json:"daemonJson"`
|
||||
AppData string `json:"appData"`
|
||||
PanelData string `json:"panelData"`
|
||||
BackupData string `json:"backupData"`
|
||||
|
||||
Compress string `json:"compress"`
|
||||
Upload string `json:"upload"`
|
||||
|
@ -20,15 +20,13 @@ type Snapshot struct {
|
||||
|
||||
type SnapshotStatus struct {
|
||||
BaseModel
|
||||
SnapID uint `gorm:"type:decimal" json:"snapID"`
|
||||
Panel string `json:"panel" gorm:"type:varchar(64);default:Running"`
|
||||
PanelCtl string `json:"panelCtl" gorm:"type:varchar(64);default:Running"`
|
||||
PanelService string `json:"panelService" gorm:"type:varchar(64);default:Running"`
|
||||
PanelInfo string `json:"panelInfo" gorm:"type:varchar(64);default:Running"`
|
||||
DaemonJson string `json:"daemonJson" gorm:"type:varchar(64);default:Running"`
|
||||
AppData string `json:"appData" gorm:"type:varchar(64);default:Running"`
|
||||
PanelData string `json:"panelData" gorm:"type:varchar(64);default:Running"`
|
||||
BackupData string `json:"backupData" gorm:"type:varchar(64);default:Running"`
|
||||
SnapID uint `gorm:"type:decimal" json:"snapID"`
|
||||
Panel string `json:"panel" gorm:"type:varchar(64);default:Running"`
|
||||
PanelInfo string `json:"panelInfo" gorm:"type:varchar(64);default:Running"`
|
||||
DaemonJson string `json:"daemonJson" gorm:"type:varchar(64);default:Running"`
|
||||
AppData string `json:"appData" gorm:"type:varchar(64);default:Running"`
|
||||
PanelData string `json:"panelData" gorm:"type:varchar(64);default:Running"`
|
||||
BackupData string `json:"backupData" gorm:"type:varchar(64);default:Running"`
|
||||
|
||||
Compress string `json:"compress" gorm:"type:varchar(64);default:Waiting"`
|
||||
Upload string `json:"upload" gorm:"type:varchar(64);default:Waiting"`
|
||||
|
@ -46,7 +46,7 @@ func (u *CronjobService) SearchWithPage(search dto.SearchWithPage) (int64, inter
|
||||
if err := copier.Copy(&item, &cronjob); err != nil {
|
||||
return 0, nil, errors.WithMessage(constant.ErrStructTransform, err.Error())
|
||||
}
|
||||
if item.Type == "app" || item.Type == "website" || item.Type == "database" || item.Type == "directory" {
|
||||
if item.Type == "app" || item.Type == "website" || item.Type == "database" || item.Type == "directory" || item.Type == "snapshot" {
|
||||
backup, _ := backupRepo.Get(commonRepo.WithByID(uint(item.TargetDirID)))
|
||||
if len(backup.Type) != 0 {
|
||||
item.TargetDir = backup.Type
|
||||
@ -103,7 +103,7 @@ func (u *CronjobService) CleanRecord(req dto.CronjobClean) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if req.CleanData && (cronjob.Type == "app" || cronjob.Type == "database" || cronjob.Type == "website" || cronjob.Type == "directory") {
|
||||
if req.CleanData && (cronjob.Type == "app" || cronjob.Type == "database" || cronjob.Type == "website" || cronjob.Type == "directory" || cronjob.Type == "snapshot") {
|
||||
cronjob.RetainCopies = 0
|
||||
backup, err := backupRepo.Get(commonRepo.WithByID(uint(cronjob.TargetDirID)))
|
||||
if err != nil {
|
||||
|
@ -10,6 +10,7 @@ import (
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/1Panel-dev/1Panel/backend/app/dto"
|
||||
"github.com/1Panel-dev/1Panel/backend/app/model"
|
||||
"github.com/1Panel-dev/1Panel/backend/constant"
|
||||
"github.com/1Panel-dev/1Panel/backend/global"
|
||||
@ -38,6 +39,10 @@ func (u *CronjobService) HandleJob(cronjob *model.Cronjob) {
|
||||
message, err = u.handleShell(cronjob.Type, cronjob.Name, cronjob.Script)
|
||||
}
|
||||
u.HandleRmExpired("LOCAL", "", "", cronjob, nil)
|
||||
case "snapshot":
|
||||
messageItem := ""
|
||||
messageItem, record.File, err = u.handleSnapshot(cronjob, record.StartTime)
|
||||
message = []byte(messageItem)
|
||||
case "curl":
|
||||
if len(cronjob.URL) == 0 {
|
||||
return
|
||||
@ -60,6 +65,7 @@ func (u *CronjobService) HandleJob(cronjob *model.Cronjob) {
|
||||
global.LOG.Errorf("cut website log file failed, err: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
cronjobRepo.EndRecords(record, constant.StatusFailed, err.Error(), string(message))
|
||||
return
|
||||
@ -83,7 +89,7 @@ func (u *CronjobService) handleShell(cronType, cornName, script string) ([]byte,
|
||||
}
|
||||
stdout, err := cmd.ExecCronjobWithTimeOut(script, handleDir, 24*time.Hour)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return []byte(stdout), err
|
||||
}
|
||||
return []byte(stdout), nil
|
||||
}
|
||||
@ -187,6 +193,10 @@ func (u *CronjobService) HandleRmExpired(backType, backupPath, localDir string,
|
||||
fileItem = strings.TrimPrefix(file, localDir+"/")
|
||||
}
|
||||
}
|
||||
|
||||
if cronjob.Type == "snapshot" {
|
||||
_ = snapshotRepo.Delete(commonRepo.WithByName(strings.TrimSuffix(path.Base(fileItem), ".tar.gz")))
|
||||
}
|
||||
_, _ = backClient.Delete(fileItem)
|
||||
}
|
||||
}
|
||||
@ -222,7 +232,7 @@ func handleTar(sourceDir, targetDir, name, exclusionRules string) error {
|
||||
path = sourceDir
|
||||
}
|
||||
|
||||
commands := fmt.Sprintf("tar -zcf %s %s %s", targetDir+"/"+name, excludeRules, path)
|
||||
commands := fmt.Sprintf("tar -zcf --warning=no-file-changed --ignore-failed-read %s %s %s", targetDir+"/"+name, excludeRules, path)
|
||||
global.LOG.Debug(commands)
|
||||
stdout, err := cmd.ExecWithTimeOut(commands, 24*time.Hour)
|
||||
if err != nil {
|
||||
@ -550,3 +560,27 @@ func (u *CronjobService) handleWebsite(cronjob model.Cronjob, backup model.Backu
|
||||
u.HandleRmExpired(backup.Type, backup.BackupPath, localDir, &cronjob, client)
|
||||
return paths, nil
|
||||
}
|
||||
|
||||
func (u *CronjobService) handleSnapshot(cronjob *model.Cronjob, startTime time.Time) (string, string, error) {
|
||||
backup, err := backupRepo.Get(commonRepo.WithByID(uint(cronjob.TargetDirID)))
|
||||
if err != nil {
|
||||
return "", "", err
|
||||
}
|
||||
client, err := NewIBackupService().NewClient(&backup)
|
||||
if err != nil {
|
||||
return "", "", err
|
||||
}
|
||||
|
||||
req := dto.SnapshotCreate{
|
||||
From: backup.Type,
|
||||
}
|
||||
message, name, err := NewISnapshotService().HandleSnapshot(true, req, startTime.Format("20060102150405"))
|
||||
if err != nil {
|
||||
return message, "", err
|
||||
}
|
||||
|
||||
path := path.Join(strings.TrimPrefix(backup.BackupPath, "/"), "system_snapshot", name+".tar.gz")
|
||||
|
||||
u.HandleRmExpired(backup.Type, backup.BackupPath, "", cronjob, client)
|
||||
return message, path, nil
|
||||
}
|
||||
|
@ -39,6 +39,8 @@ type ISnapshotService interface {
|
||||
|
||||
UpdateDescription(req dto.UpdateDescription) error
|
||||
readFromJson(path string) (SnapshotJson, error)
|
||||
|
||||
HandleSnapshot(isCronjob bool, req dto.SnapshotCreate, timeNow string) (string, string, error)
|
||||
}
|
||||
|
||||
func NewISnapshotService() ISnapshotService {
|
||||
@ -128,103 +130,9 @@ type SnapshotJson struct {
|
||||
}
|
||||
|
||||
func (u *SnapshotService) SnapshotCreate(req dto.SnapshotCreate) error {
|
||||
localDir, err := loadLocalDir()
|
||||
if err != nil {
|
||||
if _, _, err := u.HandleSnapshot(false, req, time.Now().Format("20060102150405")); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var (
|
||||
snap model.Snapshot
|
||||
snapStatus model.SnapshotStatus
|
||||
rootDir string
|
||||
)
|
||||
|
||||
if req.ID == 0 {
|
||||
timeNow := time.Now().Format("20060102150405")
|
||||
versionItem, _ := settingRepo.Get(settingRepo.WithByKey("SystemVersion"))
|
||||
rootDir = path.Join(localDir, fmt.Sprintf("system/1panel_%s_%s", versionItem.Value, timeNow))
|
||||
|
||||
snap = model.Snapshot{
|
||||
Name: fmt.Sprintf("1panel_%s_%s", versionItem.Value, timeNow),
|
||||
Description: req.Description,
|
||||
From: req.From,
|
||||
Version: versionItem.Value,
|
||||
Status: constant.StatusWaiting,
|
||||
}
|
||||
_ = snapshotRepo.Create(&snap)
|
||||
snapStatus.SnapID = snap.ID
|
||||
_ = snapshotRepo.CreateStatus(&snapStatus)
|
||||
} else {
|
||||
snap, err = snapshotRepo.Get(commonRepo.WithByID(req.ID))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
snapStatus, _ = snapshotRepo.GetStatus(snap.ID)
|
||||
if snapStatus.ID == 0 {
|
||||
snapStatus.SnapID = snap.ID
|
||||
_ = snapshotRepo.CreateStatus(&snapStatus)
|
||||
}
|
||||
rootDir = path.Join(localDir, fmt.Sprintf("system/%s", snap.Name))
|
||||
}
|
||||
|
||||
var wg sync.WaitGroup
|
||||
itemHelper := snapHelper{SnapID: snap.ID, Wg: &wg, FileOp: files.NewFileOp(), Ctx: context.Background()}
|
||||
backupPanelDir := path.Join(rootDir, "1panel")
|
||||
_ = os.MkdirAll(backupPanelDir, os.ModePerm)
|
||||
backupDockerDir := path.Join(rootDir, "docker")
|
||||
_ = os.MkdirAll(backupDockerDir, os.ModePerm)
|
||||
|
||||
jsonItem := SnapshotJson{
|
||||
BaseDir: global.CONF.System.BaseDir,
|
||||
BackupDataDir: localDir,
|
||||
PanelDataDir: path.Join(global.CONF.System.BaseDir, "1panel"),
|
||||
}
|
||||
|
||||
if snapStatus.PanelInfo != constant.StatusDone {
|
||||
wg.Add(1)
|
||||
go snapJson(itemHelper, snapStatus.ID, jsonItem, rootDir)
|
||||
}
|
||||
if snapStatus.Panel != constant.StatusDone {
|
||||
wg.Add(1)
|
||||
go snapPanel(itemHelper, snapStatus.ID, backupPanelDir)
|
||||
}
|
||||
if snapStatus.PanelCtl != constant.StatusDone {
|
||||
wg.Add(1)
|
||||
go snapPanelCtl(itemHelper, snapStatus.ID, backupPanelDir)
|
||||
}
|
||||
if snapStatus.PanelService != constant.StatusDone {
|
||||
wg.Add(1)
|
||||
go snapPanelService(itemHelper, snapStatus.ID, backupPanelDir)
|
||||
}
|
||||
if snapStatus.DaemonJson != constant.StatusDone {
|
||||
wg.Add(1)
|
||||
go snapDaemonJson(itemHelper, snapStatus.ID, backupDockerDir)
|
||||
}
|
||||
if snapStatus.AppData != constant.StatusDone {
|
||||
wg.Add(1)
|
||||
go snapAppData(itemHelper, snapStatus.ID, backupDockerDir)
|
||||
}
|
||||
if snapStatus.BackupData != constant.StatusDone {
|
||||
wg.Add(1)
|
||||
go snapBackup(itemHelper, snapStatus.ID, localDir, backupPanelDir)
|
||||
}
|
||||
if snapStatus.PanelData != constant.StatusDone {
|
||||
wg.Add(1)
|
||||
go snapPanelData(itemHelper, snapStatus.ID, localDir, backupPanelDir)
|
||||
}
|
||||
|
||||
go func() {
|
||||
wg.Wait()
|
||||
if checkIsAllDone(snap.ID) {
|
||||
snapCompress(itemHelper, snapStatus.ID, rootDir)
|
||||
|
||||
snapUpload(req.From, snapStatus.ID, fmt.Sprintf("%s.tar.gz", rootDir))
|
||||
_ = snapshotRepo.Update(snap.ID, map[string]interface{}{"status": constant.StatusSuccess})
|
||||
} else {
|
||||
_ = snapshotRepo.Update(snap.ID, map[string]interface{}{"status": constant.StatusFailed})
|
||||
}
|
||||
}()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@ -559,6 +467,127 @@ func (u *SnapshotService) readFromJson(path string) (SnapshotJson, error) {
|
||||
return snap, nil
|
||||
}
|
||||
|
||||
func (u *SnapshotService) HandleSnapshot(isCronjob bool, req dto.SnapshotCreate, timeNow string) (string, string, error) {
|
||||
localDir, err := loadLocalDir()
|
||||
if err != nil {
|
||||
return "", "", err
|
||||
}
|
||||
var (
|
||||
rootDir string
|
||||
snap model.Snapshot
|
||||
snapStatus model.SnapshotStatus
|
||||
)
|
||||
|
||||
if req.ID == 0 {
|
||||
versionItem, _ := settingRepo.Get(settingRepo.WithByKey("SystemVersion"))
|
||||
name := fmt.Sprintf("1panel_%s_%s", versionItem.Value, timeNow)
|
||||
if isCronjob {
|
||||
name = fmt.Sprintf("snapshot_1panel_%s_%s", versionItem.Value, timeNow)
|
||||
}
|
||||
rootDir = path.Join(localDir, "system", name)
|
||||
|
||||
snap = model.Snapshot{
|
||||
Name: name,
|
||||
Description: req.Description,
|
||||
From: req.From,
|
||||
Version: versionItem.Value,
|
||||
Status: constant.StatusWaiting,
|
||||
}
|
||||
_ = snapshotRepo.Create(&snap)
|
||||
snapStatus.SnapID = snap.ID
|
||||
_ = snapshotRepo.CreateStatus(&snapStatus)
|
||||
} else {
|
||||
snap, err = snapshotRepo.Get(commonRepo.WithByID(req.ID))
|
||||
if err != nil {
|
||||
return "", "", err
|
||||
}
|
||||
snapStatus, _ = snapshotRepo.GetStatus(snap.ID)
|
||||
if snapStatus.ID == 0 {
|
||||
snapStatus.SnapID = snap.ID
|
||||
_ = snapshotRepo.CreateStatus(&snapStatus)
|
||||
}
|
||||
rootDir = path.Join(localDir, fmt.Sprintf("system/%s", snap.Name))
|
||||
}
|
||||
|
||||
var wg sync.WaitGroup
|
||||
itemHelper := snapHelper{SnapID: snap.ID, Status: &snapStatus, Wg: &wg, FileOp: files.NewFileOp(), Ctx: context.Background()}
|
||||
backupPanelDir := path.Join(rootDir, "1panel")
|
||||
_ = os.MkdirAll(backupPanelDir, os.ModePerm)
|
||||
backupDockerDir := path.Join(rootDir, "docker")
|
||||
_ = os.MkdirAll(backupDockerDir, os.ModePerm)
|
||||
|
||||
jsonItem := SnapshotJson{
|
||||
BaseDir: global.CONF.System.BaseDir,
|
||||
BackupDataDir: localDir,
|
||||
PanelDataDir: path.Join(global.CONF.System.BaseDir, "1panel"),
|
||||
}
|
||||
|
||||
if snapStatus.PanelInfo != constant.StatusDone {
|
||||
wg.Add(1)
|
||||
go snapJson(itemHelper, jsonItem, rootDir)
|
||||
}
|
||||
if snapStatus.Panel != constant.StatusDone {
|
||||
wg.Add(1)
|
||||
go snapPanel(itemHelper, backupPanelDir)
|
||||
}
|
||||
if snapStatus.DaemonJson != constant.StatusDone {
|
||||
wg.Add(1)
|
||||
go snapDaemonJson(itemHelper, backupDockerDir)
|
||||
}
|
||||
if snapStatus.AppData != constant.StatusDone {
|
||||
wg.Add(1)
|
||||
go snapAppData(itemHelper, backupDockerDir)
|
||||
}
|
||||
if snapStatus.BackupData != constant.StatusDone {
|
||||
wg.Add(1)
|
||||
go snapBackup(itemHelper, localDir, backupPanelDir)
|
||||
}
|
||||
if snapStatus.PanelData != constant.StatusDone {
|
||||
wg.Add(1)
|
||||
go snapPanelData(itemHelper, localDir, backupPanelDir)
|
||||
}
|
||||
|
||||
if !isCronjob {
|
||||
go func() {
|
||||
wg.Wait()
|
||||
if !checkIsAllDone(snap.ID) {
|
||||
_ = snapshotRepo.Update(snap.ID, map[string]interface{}{"status": constant.StatusFailed})
|
||||
return
|
||||
}
|
||||
snapCompress(itemHelper, rootDir)
|
||||
if snapStatus.Compress != constant.StatusDone {
|
||||
_ = snapshotRepo.Update(snap.ID, map[string]interface{}{"status": constant.StatusFailed})
|
||||
return
|
||||
}
|
||||
|
||||
snapUpload(itemHelper, req.From, fmt.Sprintf("%s.tar.gz", rootDir))
|
||||
if snapStatus.Upload != constant.StatusDone {
|
||||
_ = snapshotRepo.Update(snap.ID, map[string]interface{}{"status": constant.StatusFailed})
|
||||
return
|
||||
}
|
||||
_ = snapshotRepo.Update(snap.ID, map[string]interface{}{"status": constant.StatusSuccess})
|
||||
}()
|
||||
return "", "", nil
|
||||
}
|
||||
wg.Wait()
|
||||
if !checkIsAllDone(snap.ID) {
|
||||
_ = snapshotRepo.Update(snap.ID, map[string]interface{}{"status": constant.StatusFailed})
|
||||
return loadLogByStatus(snapStatus), snap.Name, fmt.Errorf("snapshot %s backup failed", snap.Name)
|
||||
}
|
||||
snapCompress(itemHelper, rootDir)
|
||||
if snapStatus.Compress != constant.StatusDone {
|
||||
_ = snapshotRepo.Update(snap.ID, map[string]interface{}{"status": constant.StatusFailed})
|
||||
return loadLogByStatus(snapStatus), snap.Name, fmt.Errorf("snapshot %s compress failed", snap.Name)
|
||||
}
|
||||
snapUpload(itemHelper, req.From, fmt.Sprintf("%s.tar.gz", rootDir))
|
||||
if snapStatus.Upload != constant.StatusDone {
|
||||
_ = snapshotRepo.Update(snap.ID, map[string]interface{}{"status": constant.StatusFailed})
|
||||
return loadLogByStatus(snapStatus), snap.Name, fmt.Errorf("snapshot %s upload failed", snap.Name)
|
||||
}
|
||||
_ = snapshotRepo.Update(snap.ID, map[string]interface{}{"status": constant.StatusSuccess})
|
||||
return loadLogByStatus(snapStatus), snap.Name, nil
|
||||
}
|
||||
|
||||
func (u *SnapshotService) handleDockerDatas(fileOp files.FileOp, operation string, source, target string) error {
|
||||
switch operation {
|
||||
case "snapshot":
|
||||
@ -644,7 +673,7 @@ func (u *SnapshotService) handlePanelBinary(fileOp files.FileOp, operation strin
|
||||
if _, err := os.Stat(panelPath); err != nil {
|
||||
return fmt.Errorf("1panel binary is not found in %s, err: %v", panelPath, err)
|
||||
} else {
|
||||
if err := cpBinary(panelPath, target); err != nil {
|
||||
if err := cpBinary([]string{panelPath}, target); err != nil {
|
||||
return fmt.Errorf("backup 1panel binary failed, err: %v", err)
|
||||
}
|
||||
}
|
||||
@ -653,7 +682,7 @@ func (u *SnapshotService) handlePanelBinary(fileOp files.FileOp, operation strin
|
||||
if _, err := os.Stat(source); err != nil {
|
||||
return fmt.Errorf("1panel binary is not found in snapshot, err: %v", err)
|
||||
} else {
|
||||
if err := cpBinary(source, "/usr/local/bin/1panel"); err != nil {
|
||||
if err := cpBinary([]string{source}, "/usr/local/bin/1panel"); err != nil {
|
||||
return fmt.Errorf("recover 1panel binary failed, err: %v", err)
|
||||
}
|
||||
}
|
||||
@ -668,7 +697,7 @@ func (u *SnapshotService) handlePanelctlBinary(fileOp files.FileOp, operation st
|
||||
if _, err := os.Stat(panelctlPath); err != nil {
|
||||
return fmt.Errorf("1pctl binary is not found in %s, err: %v", panelctlPath, err)
|
||||
} else {
|
||||
if err := cpBinary(panelctlPath, target); err != nil {
|
||||
if err := cpBinary([]string{panelctlPath}, target); err != nil {
|
||||
return fmt.Errorf("backup 1pctl binary failed, err: %v", err)
|
||||
}
|
||||
}
|
||||
@ -677,7 +706,7 @@ func (u *SnapshotService) handlePanelctlBinary(fileOp files.FileOp, operation st
|
||||
if _, err := os.Stat(source); err != nil {
|
||||
return fmt.Errorf("1pctl binary is not found in snapshot, err: %v", err)
|
||||
} else {
|
||||
if err := cpBinary(source, "/usr/local/bin/1pctl"); err != nil {
|
||||
if err := cpBinary([]string{source}, "/usr/local/bin/1pctl"); err != nil {
|
||||
return fmt.Errorf("recover 1pctl binary failed, err: %v", err)
|
||||
}
|
||||
}
|
||||
@ -692,7 +721,7 @@ func (u *SnapshotService) handlePanelService(fileOp files.FileOp, operation stri
|
||||
if _, err := os.Stat(panelServicePath); err != nil {
|
||||
return fmt.Errorf("1panel service is not found in %s, err: %v", panelServicePath, err)
|
||||
} else {
|
||||
if err := cpBinary(panelServicePath, target); err != nil {
|
||||
if err := cpBinary([]string{panelServicePath}, target); err != nil {
|
||||
return fmt.Errorf("backup 1panel service failed, err: %v", err)
|
||||
}
|
||||
}
|
||||
@ -701,7 +730,7 @@ func (u *SnapshotService) handlePanelService(fileOp files.FileOp, operation stri
|
||||
if _, err := os.Stat(source); err != nil {
|
||||
return fmt.Errorf("1panel service is not found in snapshot, err: %v", err)
|
||||
} else {
|
||||
if err := cpBinary(source, "/etc/systemd/system/1panel.service"); err != nil {
|
||||
if err := cpBinary([]string{source}, "/etc/systemd/system/1panel.service"); err != nil {
|
||||
return fmt.Errorf("recover 1panel service failed, err: %v", err)
|
||||
}
|
||||
}
|
||||
@ -798,10 +827,12 @@ func (u *SnapshotService) Delete(req dto.BatchDeleteReq) error {
|
||||
return err
|
||||
}
|
||||
for _, snap := range backups {
|
||||
itemFile := path.Join(localDir, fmt.Sprintf("system/%s/%s.tar.gz", snap.Name, snap.Name))
|
||||
if _, err := os.Stat(itemFile); err == nil {
|
||||
_ = os.Remove(itemFile)
|
||||
}
|
||||
itemFile := path.Join(localDir, "system", snap.Name)
|
||||
_ = os.RemoveAll(itemFile)
|
||||
|
||||
itemTarFile := path.Join(global.CONF.System.TmpDir, "system", snap.Name+".tar.gz")
|
||||
_ = os.Remove(itemTarFile)
|
||||
|
||||
_ = snapshotRepo.DeleteStatus(snap.ID)
|
||||
}
|
||||
if err := snapshotRepo.Delete(commonRepo.WithIdsIn(req.Ids)); err != nil {
|
||||
@ -850,8 +881,8 @@ func updateRollbackStatus(id uint, status string, message string) {
|
||||
}
|
||||
}
|
||||
|
||||
func cpBinary(src, dst string) error {
|
||||
stderr, err := cmd.Exec(fmt.Sprintf("\\cp -f %s %s", src, dst))
|
||||
func cpBinary(src []string, dst string) error {
|
||||
stderr, err := cmd.Exec(fmt.Sprintf("\\cp -f %s %s", strings.Join(src, " "), dst))
|
||||
if err != nil {
|
||||
return errors.New(stderr)
|
||||
}
|
||||
@ -981,12 +1012,6 @@ func checkIsAllDone(snapID uint) bool {
|
||||
if status.Panel != constant.StatusDone {
|
||||
return false
|
||||
}
|
||||
if status.PanelCtl != constant.StatusDone {
|
||||
return false
|
||||
}
|
||||
if status.PanelService != constant.StatusDone {
|
||||
return false
|
||||
}
|
||||
if status.PanelInfo != constant.StatusDone {
|
||||
return false
|
||||
}
|
||||
@ -1004,3 +1029,17 @@ func checkIsAllDone(snapID uint) bool {
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func loadLogByStatus(status model.SnapshotStatus) string {
|
||||
logs := ""
|
||||
logs += fmt.Sprintf("Write 1Panel basic information: %s \n", status.PanelInfo)
|
||||
logs += fmt.Sprintf("Backup 1Panel system files: %s \n", status.Panel)
|
||||
logs += fmt.Sprintf("Backup Docker configuration file: %s \n", status.DaemonJson)
|
||||
logs += fmt.Sprintf("Backup installed apps from 1Panel: %s \n", status.AppData)
|
||||
logs += fmt.Sprintf("Backup 1Panel data directory: %s \n", status.PanelData)
|
||||
logs += fmt.Sprintf("Backup local backup directory for 1Panel: %s \n", status.BackupData)
|
||||
logs += fmt.Sprintf("Create snapshot file: %s \n", status.BackupData)
|
||||
logs += fmt.Sprintf("Upload snapshot file: %s \n", status.BackupData)
|
||||
|
||||
return logs
|
||||
}
|
||||
|
@ -11,6 +11,7 @@ import (
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/1Panel-dev/1Panel/backend/app/model"
|
||||
"github.com/1Panel-dev/1Panel/backend/constant"
|
||||
"github.com/1Panel-dev/1Panel/backend/global"
|
||||
"github.com/1Panel-dev/1Panel/backend/utils/cmd"
|
||||
@ -19,76 +20,64 @@ import (
|
||||
|
||||
type snapHelper struct {
|
||||
SnapID uint
|
||||
Status *model.SnapshotStatus
|
||||
Ctx context.Context
|
||||
FileOp files.FileOp
|
||||
Wg *sync.WaitGroup
|
||||
}
|
||||
|
||||
func snapJson(snap snapHelper, statusID uint, snapJson SnapshotJson, targetDir string) {
|
||||
func snapJson(snap snapHelper, snapJson SnapshotJson, targetDir string) {
|
||||
defer snap.Wg.Done()
|
||||
_ = snapshotRepo.UpdateStatus(statusID, map[string]interface{}{"panel_info": constant.Running})
|
||||
_ = snapshotRepo.UpdateStatus(snap.Status.ID, map[string]interface{}{"panel_info": constant.Running})
|
||||
status := constant.StatusDone
|
||||
remarkInfo, _ := json.MarshalIndent(snapJson, "", "\t")
|
||||
if err := os.WriteFile(fmt.Sprintf("%s/snapshot.json", targetDir), remarkInfo, 0640); err != nil {
|
||||
status = err.Error()
|
||||
}
|
||||
_ = snapshotRepo.UpdateStatus(statusID, map[string]interface{}{"panel_info": status})
|
||||
snap.Status.PanelInfo = status
|
||||
_ = snapshotRepo.UpdateStatus(snap.Status.ID, map[string]interface{}{"panel_info": status})
|
||||
}
|
||||
|
||||
func snapPanel(snap snapHelper, statusID uint, targetDir string) {
|
||||
func snapPanel(snap snapHelper, targetDir string) {
|
||||
defer snap.Wg.Done()
|
||||
_ = snapshotRepo.UpdateStatus(statusID, map[string]interface{}{"panel": constant.Running})
|
||||
_ = snapshotRepo.UpdateStatus(snap.Status.ID, map[string]interface{}{"panel": constant.Running})
|
||||
status := constant.StatusDone
|
||||
if err := cpBinary("/usr/local/bin/1panel", path.Join(targetDir, "1panel")); err != nil {
|
||||
if err := cpBinary([]string{"/usr/local/bin/1panel", "/usr/local/bin/1pctl", "/etc/systemd/system/1panel.service"}, targetDir); err != nil {
|
||||
status = err.Error()
|
||||
}
|
||||
_ = snapshotRepo.UpdateStatus(statusID, map[string]interface{}{"panel": status})
|
||||
snap.Status.Panel = status
|
||||
_ = snapshotRepo.UpdateStatus(snap.Status.ID, map[string]interface{}{"panel": status})
|
||||
}
|
||||
|
||||
func snapPanelCtl(snap snapHelper, statusID uint, targetDir string) {
|
||||
func snapDaemonJson(snap snapHelper, targetDir string) {
|
||||
defer snap.Wg.Done()
|
||||
_ = snapshotRepo.UpdateStatus(statusID, map[string]interface{}{"panel_ctl": constant.Running})
|
||||
status := constant.StatusDone
|
||||
if err := cpBinary("/usr/local/bin/1pctl", path.Join(targetDir, "1pctl")); err != nil {
|
||||
status = err.Error()
|
||||
}
|
||||
_ = snapshotRepo.UpdateStatus(statusID, map[string]interface{}{"panel_ctl": status})
|
||||
}
|
||||
|
||||
func snapPanelService(snap snapHelper, statusID uint, targetDir string) {
|
||||
defer snap.Wg.Done()
|
||||
_ = snapshotRepo.UpdateStatus(statusID, map[string]interface{}{"panel_service": constant.Running})
|
||||
status := constant.StatusDone
|
||||
if err := cpBinary("/etc/systemd/system/1panel.service", path.Join(targetDir, "1panel.service")); err != nil {
|
||||
status = err.Error()
|
||||
}
|
||||
_ = snapshotRepo.UpdateStatus(statusID, map[string]interface{}{"panel_service": status})
|
||||
}
|
||||
|
||||
func snapDaemonJson(snap snapHelper, statusID uint, targetDir string) {
|
||||
defer snap.Wg.Done()
|
||||
if !snap.FileOp.Stat("/etc/docker/daemon.json") {
|
||||
snap.Status.DaemonJson = status
|
||||
_ = snapshotRepo.UpdateStatus(snap.Status.ID, map[string]interface{}{"daemon_json": status})
|
||||
return
|
||||
}
|
||||
_ = snapshotRepo.UpdateStatus(statusID, map[string]interface{}{"daemon_json": constant.Running})
|
||||
status := constant.StatusDone
|
||||
if err := cpBinary("/etc/docker/daemon.json", path.Join(targetDir, "daemon.json")); err != nil {
|
||||
_ = snapshotRepo.UpdateStatus(snap.Status.ID, map[string]interface{}{"daemon_json": constant.Running})
|
||||
if err := cpBinary([]string{"/etc/docker/daemon.json"}, path.Join(targetDir, "daemon.json")); err != nil {
|
||||
status = err.Error()
|
||||
}
|
||||
_ = snapshotRepo.UpdateStatus(statusID, map[string]interface{}{"daemon_json": status})
|
||||
snap.Status.DaemonJson = status
|
||||
_ = snapshotRepo.UpdateStatus(snap.Status.ID, map[string]interface{}{"daemon_json": status})
|
||||
}
|
||||
|
||||
func snapAppData(snap snapHelper, statusID uint, targetDir string) {
|
||||
func snapAppData(snap snapHelper, targetDir string) {
|
||||
defer snap.Wg.Done()
|
||||
_ = snapshotRepo.UpdateStatus(statusID, map[string]interface{}{"app_data": constant.Running})
|
||||
_ = snapshotRepo.UpdateStatus(snap.Status.ID, map[string]interface{}{"app_data": constant.Running})
|
||||
appInstalls, err := appInstallRepo.ListBy()
|
||||
if err != nil {
|
||||
_ = snapshotRepo.UpdateStatus(statusID, map[string]interface{}{"app_data": err.Error()})
|
||||
snap.Status.AppData = err.Error()
|
||||
_ = snapshotRepo.UpdateStatus(snap.Status.ID, map[string]interface{}{"app_data": err.Error()})
|
||||
return
|
||||
}
|
||||
runtimes, err := runtimeRepo.List()
|
||||
if err != nil {
|
||||
_ = snapshotRepo.UpdateStatus(statusID, map[string]interface{}{"app_data": err.Error()})
|
||||
snap.Status.AppData = err.Error()
|
||||
_ = snapshotRepo.UpdateStatus(snap.Status.ID, map[string]interface{}{"app_data": err.Error()})
|
||||
return
|
||||
}
|
||||
imageRegex := regexp.MustCompile(`image:\s*(.*)`)
|
||||
@ -119,28 +108,31 @@ func snapAppData(snap snapHelper, statusID uint, targetDir string) {
|
||||
global.LOG.Debugf("docker save %s | gzip -c > %s", strings.Join(imageSaveList, " "), path.Join(targetDir, "docker_image.tar"))
|
||||
std, err := cmd.Execf("docker save %s | gzip -c > %s", strings.Join(imageSaveList, " "), path.Join(targetDir, "docker_image.tar"))
|
||||
if err != nil {
|
||||
_ = snapshotRepo.UpdateStatus(statusID, map[string]interface{}{"app_data": std})
|
||||
snap.Status.AppData = err.Error()
|
||||
_ = snapshotRepo.UpdateStatus(snap.Status.ID, map[string]interface{}{"app_data": std})
|
||||
return
|
||||
}
|
||||
_ = snapshotRepo.UpdateStatus(statusID, map[string]interface{}{"app_data": constant.StatusDone})
|
||||
snap.Status.AppData = constant.StatusDone
|
||||
_ = snapshotRepo.UpdateStatus(snap.Status.ID, map[string]interface{}{"app_data": constant.StatusDone})
|
||||
}
|
||||
|
||||
func snapBackup(snap snapHelper, statusID uint, localDir, targetDir string) {
|
||||
func snapBackup(snap snapHelper, localDir, targetDir string) {
|
||||
defer snap.Wg.Done()
|
||||
_ = snapshotRepo.UpdateStatus(statusID, map[string]interface{}{"backup_data": constant.Running})
|
||||
_ = snapshotRepo.UpdateStatus(snap.Status.ID, map[string]interface{}{"backup_data": constant.Running})
|
||||
status := constant.StatusDone
|
||||
if err := handleSnapTar(localDir, targetDir, "1panel_backup.tar.gz", "./system;"); err != nil {
|
||||
status = err.Error()
|
||||
}
|
||||
_ = snapshotRepo.UpdateStatus(statusID, map[string]interface{}{"backup_data": status})
|
||||
snap.Status.BackupData = status
|
||||
_ = snapshotRepo.UpdateStatus(snap.Status.ID, map[string]interface{}{"backup_data": status})
|
||||
}
|
||||
|
||||
func snapPanelData(snap snapHelper, statusID uint, localDir, targetDir string) {
|
||||
func snapPanelData(snap snapHelper, localDir, targetDir string) {
|
||||
defer snap.Wg.Done()
|
||||
_ = snapshotRepo.UpdateStatus(statusID, map[string]interface{}{"panel_data": constant.Running})
|
||||
_ = snapshotRepo.UpdateStatus(snap.Status.ID, map[string]interface{}{"panel_data": constant.Running})
|
||||
status := constant.StatusDone
|
||||
dataDir := path.Join(global.CONF.System.BaseDir, "1panel")
|
||||
exclusionRules := "./tmp;./log;./cache;./db/1Panel.db-*;"
|
||||
exclusionRules := "./tmp;./log;./cache;"
|
||||
if strings.Contains(localDir, dataDir) {
|
||||
exclusionRules += ("." + strings.ReplaceAll(localDir, dataDir, "") + ";")
|
||||
}
|
||||
@ -150,48 +142,57 @@ func snapPanelData(snap snapHelper, statusID uint, localDir, targetDir string) {
|
||||
status = err.Error()
|
||||
}
|
||||
_ = snapshotRepo.Update(snap.SnapID, map[string]interface{}{"status": constant.StatusWaiting})
|
||||
_ = snapshotRepo.UpdateStatus(statusID, map[string]interface{}{"panel_data": status})
|
||||
|
||||
snap.Status.PanelData = status
|
||||
_ = snapshotRepo.UpdateStatus(snap.Status.ID, map[string]interface{}{"panel_data": status})
|
||||
}
|
||||
|
||||
func snapCompress(snap snapHelper, statusID uint, rootDir string) {
|
||||
func snapCompress(snap snapHelper, rootDir string) {
|
||||
defer func() {
|
||||
global.LOG.Debugf("remove snapshot file %s", rootDir)
|
||||
_ = os.RemoveAll(rootDir)
|
||||
}()
|
||||
_ = snapshotRepo.UpdateStatus(statusID, map[string]interface{}{"compress": constant.StatusRunning})
|
||||
_ = snapshotRepo.UpdateStatus(snap.Status.ID, map[string]interface{}{"compress": constant.StatusRunning})
|
||||
tmpDir := path.Join(global.CONF.System.TmpDir, "system")
|
||||
fileName := fmt.Sprintf("%s.tar.gz", path.Base(rootDir))
|
||||
if err := snap.FileOp.Compress([]string{rootDir}, tmpDir, fileName, files.TarGz); err != nil {
|
||||
_ = snapshotRepo.UpdateStatus(statusID, map[string]interface{}{"compress": err.Error()})
|
||||
snap.Status.Compress = err.Error()
|
||||
_ = snapshotRepo.UpdateStatus(snap.Status.ID, map[string]interface{}{"compress": err.Error()})
|
||||
return
|
||||
}
|
||||
_ = snapshotRepo.UpdateStatus(statusID, map[string]interface{}{"compress": constant.StatusDone})
|
||||
|
||||
snap.Status.Compress = constant.StatusDone
|
||||
_ = snapshotRepo.UpdateStatus(snap.Status.ID, map[string]interface{}{"compress": constant.StatusDone})
|
||||
}
|
||||
|
||||
func snapUpload(account string, statusID uint, file string) {
|
||||
func snapUpload(snap snapHelper, account string, file string) {
|
||||
source := path.Join(global.CONF.System.TmpDir, "system", path.Base(file))
|
||||
defer func() {
|
||||
global.LOG.Debugf("remove snapshot file %s", source)
|
||||
_ = os.Remove(source)
|
||||
}()
|
||||
|
||||
_ = snapshotRepo.UpdateStatus(statusID, map[string]interface{}{"upload": constant.StatusUploading})
|
||||
_ = snapshotRepo.UpdateStatus(snap.Status.ID, map[string]interface{}{"upload": constant.StatusUploading})
|
||||
backup, err := backupRepo.Get(commonRepo.WithByType(account))
|
||||
if err != nil {
|
||||
_ = snapshotRepo.UpdateStatus(statusID, map[string]interface{}{"upload": err.Error()})
|
||||
snap.Status.Upload = err.Error()
|
||||
_ = snapshotRepo.UpdateStatus(snap.Status.ID, map[string]interface{}{"upload": err.Error()})
|
||||
return
|
||||
}
|
||||
client, err := NewIBackupService().NewClient(&backup)
|
||||
if err != nil {
|
||||
_ = snapshotRepo.UpdateStatus(statusID, map[string]interface{}{"upload": err.Error()})
|
||||
snap.Status.Upload = err.Error()
|
||||
_ = snapshotRepo.UpdateStatus(snap.Status.ID, map[string]interface{}{"upload": err.Error()})
|
||||
return
|
||||
}
|
||||
target := path.Join(backup.BackupPath, "system_snapshot", path.Base(file))
|
||||
if _, err := client.Upload(source, target); err != nil {
|
||||
_ = snapshotRepo.UpdateStatus(statusID, map[string]interface{}{"upload": err.Error()})
|
||||
snap.Status.Upload = err.Error()
|
||||
_ = snapshotRepo.UpdateStatus(snap.Status.ID, map[string]interface{}{"upload": err.Error()})
|
||||
return
|
||||
}
|
||||
_ = snapshotRepo.UpdateStatus(statusID, map[string]interface{}{"upload": constant.StatusDone})
|
||||
snap.Status.Upload = constant.StatusDone
|
||||
_ = snapshotRepo.UpdateStatus(snap.Status.ID, map[string]interface{}{"upload": constant.StatusDone})
|
||||
}
|
||||
|
||||
func handleSnapTar(sourceDir, targetDir, name, exclusionRules string) error {
|
||||
@ -211,7 +212,7 @@ func handleSnapTar(sourceDir, targetDir, name, exclusionRules string) error {
|
||||
exStr += exclude
|
||||
}
|
||||
|
||||
commands := fmt.Sprintf("tar --warning=no-file-changed -zcf %s %s -C %s .", targetDir+"/"+name, exStr, sourceDir)
|
||||
commands := fmt.Sprintf("tar --warning=no-file-changed --ignore-failed-read -zcf %s %s -C %s .", targetDir+"/"+name, exStr, sourceDir)
|
||||
global.LOG.Debug(commands)
|
||||
stdout, err := cmd.ExecWithTimeOut(commands, 30*time.Minute)
|
||||
if err != nil {
|
||||
|
@ -127,13 +127,13 @@ func (u *UpgradeService) Upgrade(req dto.Upgrade) error {
|
||||
}
|
||||
global.LOG.Info("backup original data successful, now start to upgrade!")
|
||||
|
||||
if err := cpBinary(tmpDir+"/1panel", "/usr/local/bin/1panel"); err != nil {
|
||||
if err := cpBinary([]string{tmpDir + "/1panel"}, "/usr/local/bin/1panel"); err != nil {
|
||||
u.handleRollback(fileOp, originalDir, 1)
|
||||
global.LOG.Errorf("upgrade 1panel failed, err: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
if err := cpBinary(tmpDir+"/1pctl", "/usr/local/bin/1pctl"); err != nil {
|
||||
if err := cpBinary([]string{tmpDir + "/1pctl"}, "/usr/local/bin/1pctl"); err != nil {
|
||||
u.handleRollback(fileOp, originalDir, 2)
|
||||
global.LOG.Errorf("upgrade 1pctl failed, err: %v", err)
|
||||
return
|
||||
@ -144,7 +144,7 @@ func (u *UpgradeService) Upgrade(req dto.Upgrade) error {
|
||||
return
|
||||
}
|
||||
|
||||
if err := cpBinary(tmpDir+"/1panel.service", "/etc/systemd/system/1panel.service"); err != nil {
|
||||
if err := cpBinary([]string{tmpDir + "/1panel.service"}, "/etc/systemd/system/1panel.service"); err != nil {
|
||||
u.handleRollback(fileOp, originalDir, 3)
|
||||
global.LOG.Errorf("upgrade 1panel.service failed, err: %v", err)
|
||||
return
|
||||
@ -179,22 +179,22 @@ func (u *UpgradeService) handleBackup(fileOp files.FileOp, originalDir string) e
|
||||
func (u *UpgradeService) handleRollback(fileOp files.FileOp, originalDir string, errStep int) {
|
||||
dbPath := global.CONF.System.DbPath + "/1Panel.db"
|
||||
_ = settingRepo.Update("SystemStatus", "Free")
|
||||
if err := cpBinary(originalDir+"/1Panel.db", dbPath); err != nil {
|
||||
if err := cpBinary([]string{originalDir + "/1Panel.db"}, dbPath); err != nil {
|
||||
global.LOG.Errorf("rollback 1panel failed, err: %v", err)
|
||||
}
|
||||
if err := cpBinary(originalDir+"/1panel", "/usr/local/bin/1panel"); err != nil {
|
||||
if err := cpBinary([]string{originalDir + "/1panel"}, "/usr/local/bin/1panel"); err != nil {
|
||||
global.LOG.Errorf("rollback 1pctl failed, err: %v", err)
|
||||
}
|
||||
if errStep == 1 {
|
||||
return
|
||||
}
|
||||
if err := cpBinary(originalDir+"/1pctl", "/usr/local/bin/1pctl"); err != nil {
|
||||
if err := cpBinary([]string{originalDir + "/1pctl"}, "/usr/local/bin/1pctl"); err != nil {
|
||||
global.LOG.Errorf("rollback 1panel failed, err: %v", err)
|
||||
}
|
||||
if errStep == 2 {
|
||||
return
|
||||
}
|
||||
if err := cpBinary(originalDir+"/1panel.service", "/etc/systemd/system/1panel.service"); err != nil {
|
||||
if err := cpBinary([]string{originalDir + "/1panel.service"}, "/etc/systemd/system/1panel.service"); err != nil {
|
||||
global.LOG.Errorf("rollback 1panel failed, err: %v", err)
|
||||
}
|
||||
}
|
||||
|
@ -86,12 +86,6 @@ func handleSnapStatus() {
|
||||
if statu.Panel == constant.StatusRunning {
|
||||
updatas["panel"] = constant.StatusFailed
|
||||
}
|
||||
if statu.PanelCtl == constant.StatusRunning {
|
||||
updatas["panel_ctl"] = constant.StatusFailed
|
||||
}
|
||||
if statu.PanelService == constant.StatusRunning {
|
||||
updatas["panel_service"] = constant.StatusFailed
|
||||
}
|
||||
if statu.PanelInfo == constant.StatusRunning {
|
||||
updatas["panel_info"] = constant.StatusFailed
|
||||
}
|
||||
|
@ -109,8 +109,6 @@ export namespace Setting {
|
||||
}
|
||||
export interface SnapshotStatus {
|
||||
panel: string;
|
||||
panelCtl: string;
|
||||
panelService: string;
|
||||
panelInfo: string;
|
||||
daemonJson: string;
|
||||
appData: string;
|
||||
|
@ -709,6 +709,7 @@ const message = {
|
||||
'This operation records all job execution records, backup files, and log files. Do you want to continue?',
|
||||
directory: 'Backup directory',
|
||||
sourceDir: 'Backup directory',
|
||||
snapshot: 'System Snapshot',
|
||||
allOptionHelper:
|
||||
'The current task plan is to back up all [{0}]. Direct download is not supported at the moment. You can check the backup list of [{0}] menu.',
|
||||
exclusionRules: 'Exclusive rule',
|
||||
@ -1110,15 +1111,13 @@ const message = {
|
||||
|
||||
snapshot: 'Snapshot',
|
||||
status: 'Snapshot status',
|
||||
panelBin: 'Backup 1Panel binary',
|
||||
panelCtl: 'Backup 1Panel script',
|
||||
panelService: 'Backup 1Panel service',
|
||||
panelInfo: 'Backup 1Panel basic information',
|
||||
daemonJson: 'Backup Docker daemon.json',
|
||||
appData: 'Backup 1Panel application',
|
||||
panelInfo: 'Write 1Panel basic information',
|
||||
panelBin: 'Backup 1Panel system files',
|
||||
daemonJson: 'Backup Docker configuration file',
|
||||
appData: 'Backup installed apps from 1Panel',
|
||||
panelData: 'Backup 1Panel data directory',
|
||||
backupData: 'Backup 1Panel local backup directory',
|
||||
compress: 'Compress snapshot file',
|
||||
backupData: 'Backup local backup directory for 1Panel',
|
||||
compress: 'Create snapshot file',
|
||||
upload: 'Upload snapshot file',
|
||||
thirdPartySupport: 'Only third-party accounts are supported',
|
||||
recoverDetail: 'Recover detail',
|
||||
|
@ -680,6 +680,7 @@ const message = {
|
||||
cleanHelper: '該操作將所有任務執行記錄、備份文件和日誌文件,是否繼續?',
|
||||
directory: '備份目錄',
|
||||
sourceDir: '備份目錄',
|
||||
snapshot: '系統快照',
|
||||
allOptionHelper: '當前計劃任務為備份所有【{0}】,暫不支持直接下載,可在【{0}】備份列表中查看',
|
||||
exclusionRules: '排除規則',
|
||||
saveLocal: '同時保留本地備份(和雲存儲保留份數一致)',
|
||||
@ -1001,15 +1002,13 @@ const message = {
|
||||
|
||||
snapshot: '快照',
|
||||
status: '快照狀態',
|
||||
panelBin: '備份 1Panel 二進製',
|
||||
panelCtl: '備份 1Panel 腳本',
|
||||
panelService: '備份 1Panel 服務',
|
||||
panelInfo: '備份 1Panel 基礎信息',
|
||||
daemonJson: '備份 Docker 配置',
|
||||
appData: '備份 1Panel 應用',
|
||||
panelInfo: '寫入 1Panel 基礎信息',
|
||||
panelBin: '備份 1Panel 系統文件',
|
||||
daemonJson: '備份 Docker 配置文件',
|
||||
appData: '備份 1Panel 已安裝應用',
|
||||
panelData: '備份 1Panel 數據目錄',
|
||||
backupData: '備份 1Panel 本地備份目錄',
|
||||
compress: '壓縮快照文件',
|
||||
compress: '製作快照文件',
|
||||
upload: '上傳快照文件',
|
||||
thirdPartySupport: '僅支持第三方賬號',
|
||||
recoverDetail: '恢復詳情',
|
||||
|
@ -680,6 +680,7 @@ const message = {
|
||||
cleanHelper: '该操作将所有任务执行记录、备份文件和日志文件,是否继续?',
|
||||
directory: '备份目录',
|
||||
sourceDir: '备份目录',
|
||||
snapshot: '系统快照',
|
||||
allOptionHelper: '当前计划任务为备份所有【{0}】,暂不支持直接下载,可在【{0}】备份列表中查看',
|
||||
exclusionRules: '排除规则',
|
||||
saveLocal: '同时保留本地备份(和云存储保留份数一致)',
|
||||
@ -1001,15 +1002,13 @@ const message = {
|
||||
|
||||
snapshot: '快照',
|
||||
status: '快照状态',
|
||||
panelBin: '备份 1Panel 二进制',
|
||||
panelCtl: '备份 1Panel 脚本',
|
||||
panelService: '备份 1Panel 服务',
|
||||
panelInfo: '备份 1Panel 基础信息',
|
||||
daemonJson: '备份 Docker 配置',
|
||||
appData: '备份 1Panel 应用',
|
||||
panelInfo: '写入 1Panel 基础信息',
|
||||
panelBin: '备份 1Panel 系统文件',
|
||||
daemonJson: '备份 Docker 配置文件',
|
||||
appData: '备份 1Panel 已安装应用',
|
||||
panelData: '备份 1Panel 数据目录',
|
||||
backupData: '备份 1Panel 本地备份目录',
|
||||
compress: '压缩快照文件',
|
||||
compress: '制作快照文件',
|
||||
upload: '上传快照文件',
|
||||
thirdPartySupport: '仅支持第三方账号',
|
||||
recoverDetail: '恢复详情',
|
||||
|
@ -23,6 +23,7 @@
|
||||
<el-option value="website" :label="$t('cronjob.website')" />
|
||||
<el-option value="database" :label="$t('cronjob.database')" />
|
||||
<el-option value="directory" :label="$t('cronjob.directory')" />
|
||||
<el-option value="snapshot" :label="$t('cronjob.snapshot')" />
|
||||
<el-option value="curl" :label="$t('cronjob.curl')" />
|
||||
<el-option value="ntp" :label="$t('cronjob.ntp')" />
|
||||
<el-option value="cutWebsiteLog" :label="$t('cronjob.cutWebsiteLog')" />
|
||||
@ -165,12 +166,13 @@
|
||||
<div v-if="isBackup()">
|
||||
<el-form-item :label="$t('cronjob.target')" prop="targetDirID">
|
||||
<el-select class="selectClass" v-model="dialogData.rowData!.targetDirID">
|
||||
<el-option
|
||||
v-for="item in backupOptions"
|
||||
:key="item.label"
|
||||
:value="item.value"
|
||||
:label="item.label"
|
||||
/>
|
||||
<div v-for="item in backupOptions" :key="item.label">
|
||||
<el-option
|
||||
v-if="item.label !== $t('setting.LOCAL') || dialogData.rowData!.type !== 'snapshot'"
|
||||
:value="item.value"
|
||||
:label="item.label"
|
||||
/>
|
||||
</div>
|
||||
</el-select>
|
||||
<span class="input-help">
|
||||
{{ $t('cronjob.targetHelper') }}
|
||||
@ -184,7 +186,9 @@
|
||||
</el-link>
|
||||
</span>
|
||||
</el-form-item>
|
||||
<el-form-item v-if="dialogData.rowData!.targetDirID !== localDirID">
|
||||
<el-form-item
|
||||
v-if="dialogData.rowData!.targetDirID !== localDirID && dialogData.rowData!.type !== 'snapshot'"
|
||||
>
|
||||
<el-checkbox v-model="dialogData.rowData!.keepLocal">
|
||||
{{ $t('cronjob.saveLocal') }}
|
||||
</el-checkbox>
|
||||
@ -448,6 +452,20 @@ const changeType = () => {
|
||||
dialogData.value.rowData.hour = 1;
|
||||
dialogData.value.rowData.minute = 30;
|
||||
break;
|
||||
case 'snapshot':
|
||||
dialogData.value.rowData.specType = 'perWeek';
|
||||
dialogData.value.rowData.week = 1;
|
||||
dialogData.value.rowData.hour = 1;
|
||||
dialogData.value.rowData.minute = 30;
|
||||
dialogData.value.rowData.keepLocal = false;
|
||||
dialogData.value.rowData.targetDirID = null;
|
||||
for (const item of backupOptions.value) {
|
||||
if (item.label !== i18n.global.t('setting.LOCAL')) {
|
||||
dialogData.value.rowData.targetDirID = item.value;
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 'directory':
|
||||
dialogData.value.rowData.specType = 'perDay';
|
||||
dialogData.value.rowData.hour = 1;
|
||||
@ -504,7 +522,8 @@ function isBackup() {
|
||||
dialogData.value.rowData!.type === 'app' ||
|
||||
dialogData.value.rowData!.type === 'website' ||
|
||||
dialogData.value.rowData!.type === 'database' ||
|
||||
dialogData.value.rowData!.type === 'directory'
|
||||
dialogData.value.rowData!.type === 'directory' ||
|
||||
dialogData.value.rowData!.type === 'snapshot'
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -167,7 +167,7 @@
|
||||
</template>
|
||||
<span class="status-count">{{ dialogData.rowData!.targetDir }}</span>
|
||||
<el-button
|
||||
v-if="currentRecord?.status === 'Success'"
|
||||
v-if="currentRecord?.status === 'Success' && dialogData.rowData!.type !== 'snapshot'"
|
||||
type="primary"
|
||||
style="margin-left: 10px"
|
||||
link
|
||||
@ -238,6 +238,10 @@
|
||||
</template>
|
||||
<span class="status-count">{{ dialogData.rowData!.retainCopies }}</span>
|
||||
</el-form-item>
|
||||
<el-form-item
|
||||
class="description"
|
||||
v-if="dialogData.rowData!.type === 'snapshot'"
|
||||
></el-form-item>
|
||||
</el-row>
|
||||
<el-form-item class="description" v-if=" dialogData.rowData!.type === 'directory'">
|
||||
<template #label>
|
||||
@ -678,7 +682,8 @@ function isBackup() {
|
||||
dialogData.value.rowData!.type === 'app' ||
|
||||
dialogData.value.rowData!.type === 'website' ||
|
||||
dialogData.value.rowData!.type === 'database' ||
|
||||
dialogData.value.rowData!.type === 'directory'
|
||||
dialogData.value.rowData!.type === 'directory' ||
|
||||
dialogData.value.rowData!.type === 'snapshot'
|
||||
);
|
||||
}
|
||||
function loadWeek(i: number) {
|
||||
|
@ -57,7 +57,7 @@
|
||||
<el-table-column :label="$t('commons.table.status')" min-width="80" prop="status">
|
||||
<template #default="{ row }">
|
||||
<el-button
|
||||
v-if="row.status === 'Waiting' || row.status === 'onSaveData'"
|
||||
v-if="row.status === 'Waiting' || row.status === 'OnSaveData'"
|
||||
type="primary"
|
||||
@click="onLoadStatus(row)"
|
||||
link
|
||||
|
@ -12,30 +12,6 @@
|
||||
</div>
|
||||
</template>
|
||||
<div v-loading="loading">
|
||||
<el-alert :type="loadStatus(status.panel)" :closable="false">
|
||||
<template #title>
|
||||
<el-button :icon="loadIcon(status.panel)" link>{{ $t('setting.panelBin') }}</el-button>
|
||||
<div v-if="showErrorMsg(status.panel)" class="top-margin">
|
||||
<span class="err-message">{{ status.panel }}</span>
|
||||
</div>
|
||||
</template>
|
||||
</el-alert>
|
||||
<el-alert :type="loadStatus(status.panelCtl)" :closable="false">
|
||||
<template #title>
|
||||
<el-button :icon="loadIcon(status.panelCtl)" link>{{ $t('setting.panelCtl') }}</el-button>
|
||||
<div v-if="showErrorMsg(status.panelCtl)" class="top-margin">
|
||||
<span class="err-message">{{ status.panelCtl }}</span>
|
||||
</div>
|
||||
</template>
|
||||
</el-alert>
|
||||
<el-alert :type="loadStatus(status.panelService)" :closable="false">
|
||||
<template #title>
|
||||
<el-button :icon="loadIcon(status.panelService)" link>{{ $t('setting.panelService') }}</el-button>
|
||||
<div v-if="showErrorMsg(status.panelService)" class="top-margin">
|
||||
<span class="err-message">{{ status.panelService }}</span>
|
||||
</div>
|
||||
</template>
|
||||
</el-alert>
|
||||
<el-alert :type="loadStatus(status.panelInfo)" :closable="false">
|
||||
<template #title>
|
||||
<el-button :icon="loadIcon(status.panelInfo)" link>{{ $t('setting.panelInfo') }}</el-button>
|
||||
@ -44,6 +20,14 @@
|
||||
</div>
|
||||
</template>
|
||||
</el-alert>
|
||||
<el-alert :type="loadStatus(status.panel)" :closable="false">
|
||||
<template #title>
|
||||
<el-button :icon="loadIcon(status.panel)" link>{{ $t('setting.panelBin') }}</el-button>
|
||||
<div v-if="showErrorMsg(status.panel)" class="top-margin">
|
||||
<span class="err-message">{{ status.panel }}</span>
|
||||
</div>
|
||||
</template>
|
||||
</el-alert>
|
||||
<el-alert :type="loadStatus(status.daemonJson)" :closable="false">
|
||||
<template #title>
|
||||
<el-button :icon="loadIcon(status.daemonJson)" link>{{ $t('setting.daemonJson') }}</el-button>
|
||||
@ -113,8 +97,6 @@ import { nextTick, onBeforeUnmount, reactive, ref } from 'vue';
|
||||
|
||||
const status = reactive<Setting.SnapshotStatus>({
|
||||
panel: '',
|
||||
panelCtl: '',
|
||||
panelService: '',
|
||||
panelInfo: '',
|
||||
daemonJson: '',
|
||||
appData: '',
|
||||
@ -158,8 +140,6 @@ const loadCurrentStatus = async () => {
|
||||
.then((res) => {
|
||||
loading.value = false;
|
||||
status.panel = res.data.panel;
|
||||
status.panelCtl = res.data.panelCtl;
|
||||
status.panelService = res.data.panelService;
|
||||
status.panelInfo = res.data.panelInfo;
|
||||
status.daemonJson = res.data.daemonJson;
|
||||
status.appData = res.data.appData;
|
||||
@ -196,8 +176,6 @@ const onWatch = () => {
|
||||
if (keepLoadStatus()) {
|
||||
const res = await loadSnapStatus(snapID.value);
|
||||
status.panel = res.data.panel;
|
||||
status.panelCtl = res.data.panelCtl;
|
||||
status.panelService = res.data.panelService;
|
||||
status.panelInfo = res.data.panelInfo;
|
||||
status.daemonJson = res.data.daemonJson;
|
||||
status.appData = res.data.appData;
|
||||
@ -214,12 +192,6 @@ const keepLoadStatus = () => {
|
||||
if (status.panel === 'Running') {
|
||||
return true;
|
||||
}
|
||||
if (status.panelCtl === 'Running') {
|
||||
return true;
|
||||
}
|
||||
if (status.panelService === 'Running') {
|
||||
return true;
|
||||
}
|
||||
if (status.panelInfo === 'Running') {
|
||||
return true;
|
||||
}
|
||||
@ -255,12 +227,6 @@ const showRetry = () => {
|
||||
if (status.panel !== 'Running' && status.panel !== 'Done') {
|
||||
return true;
|
||||
}
|
||||
if (status.panelCtl !== 'Running' && status.panelCtl !== 'Done') {
|
||||
return true;
|
||||
}
|
||||
if (status.panelService !== 'Running' && status.panelService !== 'Done') {
|
||||
return true;
|
||||
}
|
||||
if (status.panelInfo !== 'Running' && status.panelInfo !== 'Done') {
|
||||
return true;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user