fix: fix some snapshot problems (#8228)

This commit is contained in:
ssongliu 2025-03-24 17:38:45 +08:00 committed by GitHub
parent 6a2ab8598b
commit a617424786
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
20 changed files with 133 additions and 86 deletions

View file

@ -124,6 +124,7 @@ func (u *SnapshotService) LoadSnapshotData() (dto.SnapshotData, error) {
}
}
}
data.BackupData = itemBackups
return data, nil
}
@ -148,7 +149,7 @@ func (u *SnapshotService) Delete(req dto.SnapshotBatchDelete) error {
}
for _, item := range accounts {
global.LOG.Debugf("remove snapshot file %s.tar.gz from %s", snap.Name, item.name)
_, _ = item.client.Delete(path.Join("system_snapshot", snap.Name+".tar.gz"))
_, _ = item.client.Delete(path.Join(item.backupPath, "system_snapshot", snap.Name+".tar.gz"))
}
}

View file

@ -217,13 +217,13 @@ func loadDbConn(snap *snapHelper, targetDir string, req dto.SnapshotCreate) erro
return err
}
agentDb, err := common.LoadDBConnByPathWithErr(path.Join(targetDir, "db"), "agent.db")
agentDb, err := common.LoadDBConnByPathWithErr(path.Join(targetDir, "db/agent.db"), "agent.db")
snap.Task.LogWithStatus(i18n.GetWithName("SnapNewDB", "agent"), err)
if err != nil {
return err
}
snap.snapAgentDB = agentDb
coreDb, err := common.LoadDBConnByPathWithErr(path.Join(targetDir, "db"), "core.db")
coreDb, err := common.LoadDBConnByPathWithErr(path.Join(targetDir, "db/core.db"), "core.db")
snap.Task.LogWithStatus(i18n.GetWithName("SnapNewDB", "core"), err)
if err != nil {
return err
@ -253,7 +253,6 @@ func loadDbConn(snap *snapHelper, targetDir string, req dto.SnapshotCreate) erro
}
_ = snap.snapAgentDB.Where("id = ?", snap.SnapID).Delete(&model.Snapshot{}).Error
return nil
}

View file

@ -337,24 +337,22 @@ func recoverBaseData(src string, itemHelper *snapRecoverHelper) error {
}
if global.IsMaster {
err = itemHelper.FileOp.CopyFile(path.Join(src, "1panel"), "/usr/local/bin")
err = itemHelper.FileOp.CopyFile(path.Join(src, "1panel-core"), "/usr/local/bin")
itemHelper.Task.LogWithStatus(i18n.GetWithName("SnapCopy", "/usr/local/bin/1panel-core"), err)
if err != nil {
return err
}
err = itemHelper.FileOp.CopyFile(path.Join(src, "1panel-core.service"), "/etc/systemd/system")
itemHelper.Task.LogWithStatus(i18n.GetWithName("SnapCopy", "/etc/systemd/system/1panel-core.service"), err)
if err != nil {
return err
}
}
err = itemHelper.FileOp.CopyFile(path.Join(src, "1panel-agent"), "/usr/local/bin")
itemHelper.Task.LogWithStatus(i18n.GetWithName("SnapCopy", "/usr/local/bin/1panel-agent"), err)
if err != nil {
return err
}
if global.IsMaster {
err = itemHelper.FileOp.CopyFile(path.Join(src, "1panel-core.service"), "/etc/systemd/system")
itemHelper.Task.LogWithStatus(i18n.GetWithName("SnapCopy", "/etc/systemd/system/1panel-core.service"), err)
if err != nil {
return err
}
}
err = itemHelper.FileOp.CopyFile(path.Join(src, "1panel-agent.service"), "/etc/systemd/system")
itemHelper.Task.LogWithStatus(i18n.GetWithName("SnapCopy", "/etc/systemd/system/1panel-agent.service"), err)
if err != nil {

View file

@ -60,10 +60,13 @@ func CloseDB(db *gorm.DB) {
}
func GetDBWithPath(dbPath string) (*gorm.DB, error) {
db, _ := gorm.Open(sqlite.Open(dbPath), &gorm.Config{
db, err := gorm.Open(sqlite.Open(dbPath), &gorm.Config{
DisableForeignKeyConstraintWhenMigrating: true,
Logger: newLogger(),
})
if err != nil {
return nil, err
}
sqlDB, dbError := db.DB()
if dbError != nil {
return nil, dbError

View file

@ -86,12 +86,11 @@ func (u *UpgradeService) LoadNotes(req dto.Upgrade) (string, error) {
func (u *UpgradeService) Upgrade(req dto.Upgrade) error {
global.LOG.Info("start to upgrade now...")
timeStr := time.Now().Format(constant.DateTimeSlimLayout)
baseDir := path.Join(global.CONF.Base.InstallDir, fmt.Sprintf("1panel/tmp/upgrade/%s", req.Version))
rootDir := path.Join(baseDir, fmt.Sprintf("upgrade_%s/downloads", timeStr))
downloadDir := path.Join(baseDir, "downloads")
_ = os.RemoveAll(baseDir)
originalDir := path.Join(baseDir, fmt.Sprintf("upgrade_%s/original", timeStr))
if err := os.MkdirAll(rootDir, os.ModePerm); err != nil {
originalDir := path.Join(baseDir, "original")
if err := os.MkdirAll(downloadDir, os.ModePerm); err != nil {
return err
}
if err := os.MkdirAll(originalDir, os.ModePerm); err != nil {
@ -110,21 +109,21 @@ func (u *UpgradeService) Upgrade(req dto.Upgrade) error {
fileName := fmt.Sprintf("1panel-%s-%s-%s.tar.gz", req.Version, "linux", itemArch)
_ = settingRepo.Update("SystemStatus", "Upgrading")
go func() {
if err := files.DownloadFileWithProxy(downloadPath+"/"+fileName, rootDir+"/"+fileName); err != nil {
if err := files.DownloadFileWithProxy(downloadPath+"/"+fileName, downloadDir+"/"+fileName); err != nil {
global.LOG.Errorf("download service file failed, err: %v", err)
_ = settingRepo.Update("SystemStatus", "Free")
return
}
global.LOG.Info("download all file successful!")
defer func() {
_ = os.Remove(rootDir)
_ = os.Remove(downloadDir)
}()
if err := files.HandleUnTar(rootDir+"/"+fileName, rootDir, ""); err != nil {
if err := files.HandleUnTar(downloadDir+"/"+fileName, downloadDir, ""); err != nil {
global.LOG.Errorf("decompress file failed, err: %v", err)
_ = settingRepo.Update("SystemStatus", "Free")
return
}
tmpDir := rootDir + "/" + strings.ReplaceAll(fileName, ".tar.gz", "")
tmpDir := downloadDir + "/" + strings.ReplaceAll(fileName, ".tar.gz", "")
if err := u.handleBackup(originalDir); err != nil {
global.LOG.Errorf("handle backup original file failed, err: %v", err)

View file

@ -12,7 +12,6 @@ import (
"github.com/1Panel-dev/1Panel/core/i18n"
cmdUtils "github.com/1Panel-dev/1Panel/core/utils/cmd"
"github.com/1Panel-dev/1Panel/core/utils/files"
"github.com/pkg/errors"
"github.com/spf13/cobra"
)
@ -45,51 +44,47 @@ var restoreCmd = &cobra.Command{
return nil
}
tmpPath = path.Join(upgradeDir, tmpPath, "original")
fmt.Println(i18n.GetMsgWithMapForCmd("RestoreStep1", map[string]interface{}{"name": tmpPath}))
if err := files.CopyFile(path.Join(tmpPath, "1panel"), "/usr/local/bin", true); err != nil {
fmt.Println(i18n.GetMsgWithMapForCmd("RestoreStep1", map[string]interface{}{"name": tmpPath}))
if err := files.CopyFile(path.Join(tmpPath, "1panel-agent"), "/usr/local/bin/1panel-agent", true); err != nil {
return err
}
if err := files.CopyFile(path.Join(tmpPath, "1panel-core"), "/usr/local/bin/1panel-core", true); err != nil {
return err
}
sudo := cmdUtils.SudoHandleCmd()
_, _ = cmdUtils.Execf("%s chmod 755 /usr/local/bin/1panel-agent /usr/local/bin/1panel-core", sudo)
fmt.Println(i18n.GetMsgByKeyForCmd("RestoreStep2"))
if err := files.CopyFile(path.Join(tmpPath, "1pctl"), "/usr/local/bin", true); err != nil {
if err := files.CopyFile(path.Join(tmpPath, "1pctl"), "/usr/local/bin/1pctl", true); err != nil {
return err
}
_, _ = cmdUtils.Execf("%s chmod 755 /usr/local/bin/1pctl", sudo)
_, _ = cmdUtils.Execf("cp -r %s /usr/local/bin", path.Join(tmpPath, "lang"))
geoPath := path.Join(global.CONF.Base.InstallDir, "1panel/geo")
_, _ = cmdUtils.Execf("mkdir %s && cp %s %s/", geoPath, path.Join(tmpPath, "GeoIP.mmdb"), geoPath)
fmt.Println(i18n.GetMsgByKeyForCmd("RestoreStep3"))
if err := files.CopyFile(path.Join(tmpPath, "1panel-core.service"), "/etc/systemd/system", true); err != nil {
if err := files.CopyFile(path.Join(tmpPath, "1panel-core.service"), "/etc/systemd/system/1panel-core.service", true); err != nil {
return err
}
if err := files.CopyFile(path.Join(tmpPath, "1panel-agent.service"), "/etc/systemd/system", true); err != nil {
if err := files.CopyFile(path.Join(tmpPath, "1panel-agent.service"), "/etc/systemd/system/1panel-agent.service", true); err != nil {
return err
}
fmt.Println(i18n.GetMsgByKeyForCmd("RestoreStep4"))
checkPointOfWal()
if _, err := os.Stat(path.Join(tmpPath, "1Panel.db")); err == nil {
if err := files.CopyFile(path.Join(tmpPath, "1Panel.db"), path.Join(baseDir, "1panel/db"), true); err != nil {
return err
}
}
if _, err := os.Stat(path.Join(tmpPath, "db.tar.gz")); err == nil {
if err := handleUnTar(path.Join(tmpPath, "db.tar.gz"), path.Join(baseDir, "1panel")); err != nil {
return err
if _, err := os.Stat(path.Join(tmpPath, "db")); err == nil {
dbPath := path.Join(baseDir, "1panel/db")
if err := files.CopyItem(true, true, path.Join(tmpPath, "db"), dbPath); err != nil {
global.LOG.Errorf("rollback 1panel db failed, err: %v", err)
}
}
fmt.Println(i18n.GetMsgByKeyForCmd("RestoreStep5"))
fmt.Println(i18n.GetMsgByKeyForCmd("RestoreSuccessful"))
return nil
},
}
func checkPointOfWal() {
db, err := loadDBConn()
if err != nil {
return
}
_ = db.Exec("PRAGMA wal_checkpoint(TRUNCATE);").Error
}
func loadRestorePath(upgradeDir string) (string, error) {
if _, err := os.Stat(upgradeDir); err != nil && os.IsNotExist(err) {
return "no such file", nil
@ -98,32 +93,22 @@ func loadRestorePath(upgradeDir string) (string, error) {
if err != nil {
return "", err
}
var folders []string
type itemState struct {
Name string
CreateAt time.Time
}
var folders []itemState
for _, file := range files {
if file.IsDir() {
folders = append(folders, file.Name())
info, _ := file.Info()
folders = append(folders, itemState{Name: file.Name(), CreateAt: info.ModTime()})
}
}
if len(folders) == 0 {
return "no such file", nil
}
sort.Slice(folders, func(i, j int) bool {
return folders[i] > folders[j]
return folders[i].CreateAt.After(folders[j].CreateAt)
})
return folders[0], nil
}
func handleUnTar(sourceFile, targetDir string) error {
if _, err := os.Stat(targetDir); err != nil && os.IsNotExist(err) {
if err = os.MkdirAll(targetDir, os.ModePerm); err != nil {
return err
}
}
commands := fmt.Sprintf("tar zxvfC %s %s", sourceFile, targetDir)
stdout, err := cmdUtils.ExecWithTimeOut(commands, 20*time.Second)
if err != nil {
return errors.New(stdout)
}
return nil
return folders[0].Name, nil
}

View file

@ -3,7 +3,7 @@
<template #content>
<LayoutContent v-loading="loading" :title="$t('logs.task')">
<template #rightToolBar>
<el-select v-model="req.status" @change="search()" clearable class="p-w-200 mr-2.5">
<el-select v-model="req.status" @change="search()" clearable class="p-w-200">
<template #prefix>{{ $t('commons.table.status') }}</template>
<el-option :label="$t('commons.table.all')" value=""></el-option>
<el-option :label="$t('commons.status.success')" value="Success"></el-option>

View file

@ -6,7 +6,7 @@
element-loading-svg-view-box="-10, -10, 50, 50"
element-loading-background="rgba(122, 122, 122, 0.01)"
>
<div class="fixed">
<div class="fixed" v-if="!isCollapse">
<PrimaryMenu />
</div>
<Logo :isCollapse="isCollapse" />

View file

@ -13,7 +13,7 @@
</el-button>
</template>
<template #rightToolBar>
<el-select v-model="group" @change="search()" clearable class="p-w-200 mr-2.5">
<el-select v-model="group" @change="search()" clearable class="p-w-200">
<template #prefix>{{ $t('commons.table.group') }}</template>
<div v-for="item in groupOptions" :key="item.id">
<el-option :label="item.name" :value="item.id" />

View file

@ -28,7 +28,7 @@
</el-button>
</template>
<template #rightToolBar>
<el-select v-model="searchStrategy" @change="search()" clearable class="p-w-200 mr-2.5">
<el-select v-model="searchStrategy" @change="search()" clearable class="p-w-200">
<template #prefix>{{ $t('firewall.strategy') }}</template>
<el-option :label="$t('commons.table.all')" value=""></el-option>
<el-option :label="$t('firewall.allow')" value="accept"></el-option>

View file

@ -44,13 +44,13 @@
</el-button>
</template>
<template #rightToolBar>
<el-select v-model="searchStatus" @change="search()" clearable class="p-w-200 mr-2.5">
<el-select v-model="searchStatus" @change="search()" clearable class="p-w-200">
<template #prefix>{{ $t('commons.table.status') }}</template>
<el-option :label="$t('commons.table.all')" value=""></el-option>
<el-option :label="$t('firewall.unUsed')" value="free"></el-option>
<el-option :label="$t('firewall.used')" value="used"></el-option>
</el-select>
<el-select v-model="searchStrategy" @change="search()" clearable class="p-w-200 mr-2.5">
<el-select v-model="searchStrategy" @change="search()" clearable class="p-w-200">
<template #prefix>{{ $t('firewall.strategy') }}</template>
<el-option :label="$t('commons.table.all')" value=""></el-option>
<el-option :label="$t('firewall.accept')" value="accept"></el-option>

View file

@ -6,7 +6,7 @@
<div class="mt-2"><el-alert type="info" :title="$t('ssh.sshAlert')" :closable="false" /></div>
</template>
<template #rightToolBar>
<el-select v-model="searchStatus" @change="search()" class="p-w-200 mr-2.5">
<el-select v-model="searchStatus" @change="search()" class="p-w-200">
<template #prefix>{{ $t('commons.table.status') }}</template>
<el-option :label="$t('commons.table.all')" value="All"></el-option>
<el-option :label="$t('commons.status.success')" value="Success"></el-option>

View file

@ -10,7 +10,7 @@
</el-button>
</template>
<template #rightToolBar>
<el-select v-model="searchStatus" @change="search()" clearable class="p-w-200 mr-2.5">
<el-select v-model="searchStatus" @change="search()" clearable class="p-w-200">
<template #prefix>{{ $t('commons.table.status') }}</template>
<el-option :label="$t('commons.table.all')" value=""></el-option>
<el-option :label="$t('commons.status.success')" value="Success"></el-option>

View file

@ -5,7 +5,7 @@
<LogRouter current="SystemLog" />
</template>
<template #leftToolBar>
<el-select class="p-w-200 mr-2.5" v-model="itemName" @change="search()">
<el-select class="p-w-200" v-model="itemName" @change="search()">
<template #prefix>{{ $t('commons.table.date') }}</template>
<el-option v-for="(item, index) in fileList" :key="index" :label="item" :value="item" />
</el-select>

View file

@ -5,7 +5,7 @@
<LogRouter current="Task" />
</template>
<template #rightToolBar>
<el-select v-model="req.status" @change="search()" clearable class="p-w-200 mr-2.5">
<el-select v-model="req.status" @change="search()" clearable class="p-w-200">
<template #prefix>{{ $t('commons.table.status') }}</template>
<el-option :label="$t('commons.table.all')" value=""></el-option>
<el-option :label="$t('commons.status.success')" value="Success"></el-option>

View file

@ -20,7 +20,7 @@
</el-button>
</template>
<template #leftToolBar>
<el-select v-model="logConfig.id" @change="changeWebsite()" class="p-w-200 mr-2.5">
<el-select v-model="logConfig.id" @change="changeWebsite()" class="p-w-200">
<template #prefix>{{ $t('menu.website') }}</template>
<el-option
v-for="(website, index) in websites"

View file

@ -104,7 +104,7 @@
</el-tree>
</fu-step>
<fu-step id="backupData" :title="$t('setting.stepBackupData')">
<div class="mt-5 mb-5" v-if="!form.appData || form.appData.length === 0">
<div class="mt-5 mb-5" v-if="!form.backupData || form.backupData.length === 0">
<span class="input-help">{{ $t('setting.noBackupData') }}</span>
</div>
<div v-else>
@ -218,6 +218,8 @@ const drawerVisible = ref();
const emit = defineEmits(['search']);
const acceptParams = (): void => {
form.downloadAccountID = '';
form.fromAccounts = [];
search();
loadBackups();
drawerVisible.value = true;
@ -259,7 +261,7 @@ const beforeLeave = async (stepItem: any) => {
loadCheckForSubmit(panelChecks, form.panelData);
return true;
case 'backupData':
if (!form.appData || form.appData.length === 0) {
if (!form.backupData || form.backupData.length === 0) {
return true;
}
if (form.backupData && form.backupData.length !== 0) {
@ -500,7 +502,7 @@ const setPanelDefaultCheck = async (list: any) => {
}
};
const setBackupDefaultCheck = async (list: any) => {
if (!form.appData || form.appData.length === 0) {
if (!form.backupData || form.backupData.length === 0) {
return;
}
for (const item of list) {

View file

@ -90,29 +90,70 @@
<el-table-column :label="$t('commons.table.status')" min-width="80" prop="status">
<template #default="{ row }">
<div>
<el-button link v-if="row.status === 'Waiting'" type="primary">
<el-button
@click="openWithResource(row, 'create')"
link
v-if="row.status === 'Waiting'"
type="primary"
>
{{ $t('setting.snapshot') }}{{ $t('commons.status.waiting') }}
</el-button>
<el-button link v-if="row.status === 'Failed'" @click="reCreate(row)" type="danger">
<el-button
@click="openWithResource(row, 'create')"
link
v-if="row.status === 'Failed'"
type="danger"
>
{{ $t('setting.snapshot') }}{{ $t('commons.status.error') }}
</el-button>
<el-button link v-if="row.status === 'Success'" type="success">
<el-button
v-if="row.status === 'Failed'"
type="danger"
class="retry"
icon="Warning"
link
@click="reCreate(row)"
/>
<el-button
@click="openWithResource(row, 'create')"
link
v-if="row.status === 'Success'"
type="success"
>
{{ $t('setting.snapshot') }}{{ $t('commons.status.success') }}
</el-button>
</div>
<div v-if="row.recoverStatus">
<el-button link v-if="row.recoverStatus === 'Waiting'" type="primary">
<el-button
@click="openWithResource(row, 'recover')"
link
v-if="row.recoverStatus === 'Waiting'"
type="primary"
>
{{ $t('commons.button.recover') }}{{ $t('commons.status.waiting') }}
</el-button>
<el-button
v-if="row.recoverStatus === 'Failed'"
@click="onRecover(row)"
@click="openWithResource(row, 'recover')"
type="danger"
link
>
{{ $t('commons.button.recover') }}{{ $t('commons.status.error') }}
</el-button>
<el-button link v-if="row.recoverStatus === 'Success'" type="success">
<el-button
v-if="row.recoverStatus === 'Failed'"
type="danger"
class="retry"
icon="Warning"
link
@click="onRecover(row)"
/>
<el-button
@click="openWithResource(row, 'recover')"
link
v-if="row.recoverStatus === 'Success'"
type="success"
>
{{ $t('commons.button.recover') }}{{ $t('commons.status.success') }}
</el-button>
</div>
@ -128,6 +169,14 @@
>
{{ $t('setting.rollback') }}{{ $t('commons.status.error') }}
</el-button>
<el-button
v-if="row.rollbackStatus === 'Failed'"
icon="Warning"
class="retry"
type="danger"
link
@click="reRollback(row)"
/>
<el-button link v-if="row.recoverStatus === 'Success'" type="success">
{{ $t('setting.rollback') }}{{ $t('commons.status.success') }}
</el-button>
@ -173,7 +222,7 @@
</el-form>
</template>
</OpDialog>
<TaskLog ref="taskLogRef" width="70%" />
<TaskLog ref="taskLogRef" width="70%" @close="search()" />
<SnapRecover ref="recoverRef" />
</div>
</template>
@ -259,6 +308,10 @@ const reCreate = (row: any) => {
const openTaskLog = (taskID: string) => {
taskLogRef.value.openWithTaskID(taskID);
};
const openWithResource = (row: any, op: string) => {
op = 'create' ? 'TaskCreate' : 'TaskRecover';
taskLogRef.value.openWithResourceID('Snapshot', op, row.id);
};
const reRollback = (row: any) => {
ElMessageBox.confirm(row.rollbackMessage, i18n.global.t('setting.reRollback'), {
@ -437,3 +490,10 @@ onMounted(() => {
search();
});
</script>
<style lang="scss" scoped>
.retry {
margin-left: -1px;
margin-top: -2px;
}
</style>

View file

@ -16,7 +16,7 @@
</el-button>
</template>
<template #rightToolBar>
<el-select v-model="group" @change="search()" clearable class="p-w-200 mr-2.5">
<el-select v-model="group" @change="search()" clearable class="p-w-200">
<template #prefix>{{ $t('commons.table.group') }}</template>
<div v-for="item in groupList" :key="item.id">
<el-option

View file

@ -36,7 +36,7 @@
<el-select
v-model="req.websiteGroupId"
@change="search()"
class="p-w-200 mr-2.5"
class="p-w-200"
:disabled="nginxStatus != 'Running'"
>
<template #prefix>{{ $t('commons.table.group') }}</template>