diff --git a/backend/app/dto/setting.go b/backend/app/dto/setting.go
index 7f7181900..6db7c01bb 100644
--- a/backend/app/dto/setting.go
+++ b/backend/app/dto/setting.go
@@ -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"`
diff --git a/backend/app/model/snapshot.go b/backend/app/model/snapshot.go
index c56c9cfdb..006bf3207 100644
--- a/backend/app/model/snapshot.go
+++ b/backend/app/model/snapshot.go
@@ -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"`
diff --git a/backend/app/service/cornjob.go b/backend/app/service/cornjob.go
index 6e9ca4d37..acc9c9f2f 100644
--- a/backend/app/service/cornjob.go
+++ b/backend/app/service/cornjob.go
@@ -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 {
diff --git a/backend/app/service/cronjob_helper.go b/backend/app/service/cronjob_helper.go
index b1b81fec3..cc63261c6 100644
--- a/backend/app/service/cronjob_helper.go
+++ b/backend/app/service/cronjob_helper.go
@@ -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
+}
diff --git a/backend/app/service/snapshot.go b/backend/app/service/snapshot.go
index 82ad97bf7..cf05d337f 100644
--- a/backend/app/service/snapshot.go
+++ b/backend/app/service/snapshot.go
@@ -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
+}
diff --git a/backend/app/service/snapshot_create.go b/backend/app/service/snapshot_create.go
index a11c0cb94..8c3d87e96 100644
--- a/backend/app/service/snapshot_create.go
+++ b/backend/app/service/snapshot_create.go
@@ -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 {
diff --git a/backend/app/service/upgrade.go b/backend/app/service/upgrade.go
index 0ea5b3820..e400b542f 100644
--- a/backend/app/service/upgrade.go
+++ b/backend/app/service/upgrade.go
@@ -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)
}
}
diff --git a/backend/init/hook/hook.go b/backend/init/hook/hook.go
index 0f2c3f990..aeaa40615 100644
--- a/backend/init/hook/hook.go
+++ b/backend/init/hook/hook.go
@@ -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
}
diff --git a/frontend/src/api/interface/setting.ts b/frontend/src/api/interface/setting.ts
index 3dc388b18..e295ee868 100644
--- a/frontend/src/api/interface/setting.ts
+++ b/frontend/src/api/interface/setting.ts
@@ -109,8 +109,6 @@ export namespace Setting {
}
export interface SnapshotStatus {
panel: string;
- panelCtl: string;
- panelService: string;
panelInfo: string;
daemonJson: string;
appData: string;
diff --git a/frontend/src/lang/modules/en.ts b/frontend/src/lang/modules/en.ts
index 04528c761..e16840db2 100644
--- a/frontend/src/lang/modules/en.ts
+++ b/frontend/src/lang/modules/en.ts
@@ -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',
diff --git a/frontend/src/lang/modules/tw.ts b/frontend/src/lang/modules/tw.ts
index 296ecf23c..062f5197b 100644
--- a/frontend/src/lang/modules/tw.ts
+++ b/frontend/src/lang/modules/tw.ts
@@ -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: '恢復詳情',
diff --git a/frontend/src/lang/modules/zh.ts b/frontend/src/lang/modules/zh.ts
index 25209e9b5..4c53c58db 100644
--- a/frontend/src/lang/modules/zh.ts
+++ b/frontend/src/lang/modules/zh.ts
@@ -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: '恢复详情',
diff --git a/frontend/src/views/cronjob/operate/index.vue b/frontend/src/views/cronjob/operate/index.vue
index 48a7f546f..1f903f20f 100644
--- a/frontend/src/views/cronjob/operate/index.vue
+++ b/frontend/src/views/cronjob/operate/index.vue
@@ -23,6 +23,7 @@