fix: Fix the display exception problem when the database application does not exist (#8094)

This commit is contained in:
ssongliu 2025-03-07 15:43:58 +08:00 committed by GitHub
parent f80dffd908
commit 08e2a70206
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
18 changed files with 95 additions and 123 deletions

View file

@ -107,6 +107,8 @@ type SearchForSize struct {
DetailName string `json:"detailName"`
Info string `json:"info"`
CronjobID uint `json:"cronjobID"`
OrderBy string `json:"orderBy"`
Order string `json:"order"`
}
type RecordFileSize struct {
ID uint `json:"id"`

View file

@ -202,6 +202,13 @@ func handleAppRecover(install *model.AppInstall, parentTask *task.Task, recoverF
recoverApp := func(t *task.Task) error {
fileOp := files.NewFileOp()
if !isRollback {
rollbackFile = path.Join(global.Dir.TmpDir, fmt.Sprintf("app/%s_%s.tar.gz", install.Name, time.Now().Format(constant.DateTimeSlimLayout)))
if err := handleAppBackup(install, nil, path.Dir(rollbackFile), path.Base(rollbackFile), "", "", ""); err != nil {
t.Log(fmt.Sprintf("backup app %s for rollback before recover failed, err: %v", install.Name, err))
}
}
if err := fileOp.TarGzExtractPro(recoverFile, path.Dir(recoverFile), secret); err != nil {
return err
}
@ -226,13 +233,6 @@ func handleAppRecover(install *model.AppInstall, parentTask *task.Task, recoverF
return errors.New(i18n.GetMsgByKey("AppAttributesNotMatch"))
}
if !isRollback {
rollbackFile = path.Join(global.Dir.TmpDir, fmt.Sprintf("app/%s_%s.tar.gz", install.Name, time.Now().Format(constant.DateTimeSlimLayout)))
if err := handleAppBackup(install, nil, path.Dir(rollbackFile), path.Base(rollbackFile), "", "", ""); err != nil {
t.Log(fmt.Sprintf("backup app %s for rollback before recover failed, err: %v", install.Name, err))
}
}
newEnvFile := ""
resources, _ := appInstallResourceRepo.GetBy(appInstallResourceRepo.WithAppInstallId(install.ID))
for _, resource := range resources {
@ -348,7 +348,7 @@ func handleAppRecover(install *model.AppInstall, parentTask *task.Task, recoverF
}
if !isOk {
t.Log(i18n.GetMsgByKey("RecoverFailedStartRollBack"))
if err := handleAppRecover(install, t, rollbackFile, true, secret, ""); err != nil {
if err := handleAppRecover(install, t, rollbackFile, true, "", ""); err != nil {
t.LogFailedWithErr(i18n.GetMsgByKey("Rollback"), err)
return
}

View file

@ -206,7 +206,7 @@ func (u *BackupRecordService) LoadRecordSize(req dto.SearchForSize) ([]dto.Recor
var list []backupSizeHelper
switch req.Type {
case "snapshot":
_, records, err := snapshotRepo.Page(req.Page, req.PageSize, repo.WithByLikeName(req.Info))
_, records, err := snapshotRepo.Page(req.Page, req.PageSize, repo.WithByLikeName(req.Info), repo.WithOrderRuleBy(req.OrderBy, req.Order))
if err != nil {
return nil, err
}
@ -214,7 +214,7 @@ func (u *BackupRecordService) LoadRecordSize(req dto.SearchForSize) ([]dto.Recor
list = append(list, backupSizeHelper{ID: item.ID, DownloadID: item.DownloadAccountID, FilePath: fmt.Sprintf("system_snapshot/%s.tar.gz", item.Name)})
}
case "cronjob":
_, records, err := backupRepo.PageRecord(req.Page, req.PageSize, backupRepo.WithByCronID(req.CronjobID))
_, records, err := backupRepo.PageRecord(req.Page, req.PageSize, repo.WithOrderBy("created_at desc"), backupRepo.WithByCronID(req.CronjobID))
if err != nil {
return nil, err
}

View file

@ -46,6 +46,25 @@ func handleRuntimeBackup(runtime *model.Runtime, backupDir, fileName string, exc
func handleRuntimeRecover(runtime *model.Runtime, recoverFile string, isRollback bool, secret string) error {
isOk := false
if !isRollback {
rollbackFile := path.Join(global.Dir.TmpDir, fmt.Sprintf("runtime/%s_%s.tar.gz", runtime.Name, time.Now().Format(constant.DateTimeSlimLayout)))
if err := handleRuntimeBackup(runtime, path.Dir(rollbackFile), path.Base(rollbackFile), "", secret); err != nil {
return fmt.Errorf("backup runtime %s for rollback before recover failed, err: %v", runtime.Name, err)
}
defer func() {
if !isOk {
global.LOG.Info("recover failed, start to rollback now")
if err := handleRuntimeRecover(runtime, rollbackFile, true, ""); err != nil {
global.LOG.Errorf("rollback runtime %s from %s failed, err: %v", runtime.Name, rollbackFile, err)
return
}
global.LOG.Infof("rollback runtime %s from %s successful", runtime.Name, rollbackFile)
_ = os.RemoveAll(rollbackFile)
} else {
_ = os.RemoveAll(rollbackFile)
}
}()
}
fileOp := files.NewFileOp()
if err := fileOp.TarGzExtractPro(recoverFile, path.Dir(recoverFile), secret); err != nil {
return err
@ -71,26 +90,6 @@ func handleRuntimeRecover(runtime *model.Runtime, recoverFile string, isRollback
return errors.New("the current backup file does not match the application")
}
if !isRollback {
rollbackFile := path.Join(global.Dir.TmpDir, fmt.Sprintf("runtime/%s_%s.tar.gz", runtime.Name, time.Now().Format(constant.DateTimeSlimLayout)))
if err := handleRuntimeBackup(runtime, path.Dir(rollbackFile), path.Base(rollbackFile), "", secret); err != nil {
return fmt.Errorf("backup runtime %s for rollback before recover failed, err: %v", runtime.Name, err)
}
defer func() {
if !isOk {
global.LOG.Info("recover failed, start to rollback now")
if err := handleRuntimeRecover(runtime, rollbackFile, true, secret); err != nil {
global.LOG.Errorf("rollback runtime %s from %s failed, err: %v", runtime.Name, rollbackFile, err)
return
}
global.LOG.Infof("rollback runtime %s from %s successful", runtime.Name, rollbackFile)
_ = os.RemoveAll(rollbackFile)
} else {
_ = os.RemoveAll(rollbackFile)
}
}()
}
newEnvFile, err := coverEnvJsonToStr(runtime.Env)
if err != nil {
return err

View file

@ -78,6 +78,26 @@ func handleWebsiteRecover(website *model.Website, recoverFile string, isRollback
return err
}
recoverTask.AddSubTask(task.GetTaskName(website.PrimaryDomain, task.TaskRecover, task.TaskScopeWebsite), func(t *task.Task) error {
isOk := false
if !isRollback {
rollbackFile := path.Join(global.Dir.TmpDir, fmt.Sprintf("website/%s_%s.tar.gz", website.Alias, time.Now().Format(constant.DateTimeSlimLayout)))
if err := handleWebsiteBackup(website, path.Dir(rollbackFile), path.Base(rollbackFile), "", "", ""); err != nil {
return fmt.Errorf("backup website %s for rollback before recover failed, err: %v", website.Alias, err)
}
defer func() {
if !isOk {
t.LogStart(i18n.GetMsgByKey("Rollback"))
if err := handleWebsiteRecover(website, rollbackFile, true, "", taskID); err != nil {
t.LogFailedWithErr(i18n.GetMsgByKey("Rollback"), err)
return
}
t.LogSuccess(i18n.GetMsgByKey("Rollback"))
_ = os.RemoveAll(rollbackFile)
} else {
_ = os.RemoveAll(rollbackFile)
}
}()
}
fileOp := files.NewFileOp()
tmpPath := strings.ReplaceAll(recoverFile, ".tar.gz", "")
t.Log(i18n.GetWithName("DeCompressFile", recoverFile))
@ -112,27 +132,6 @@ func handleWebsiteRecover(website *model.Website, recoverFile string, isRollback
}
}
isOk := false
if !isRollback {
rollbackFile := path.Join(global.Dir.TmpDir, fmt.Sprintf("website/%s_%s.tar.gz", website.Alias, time.Now().Format(constant.DateTimeSlimLayout)))
if err := handleWebsiteBackup(website, path.Dir(rollbackFile), path.Base(rollbackFile), "", "", ""); err != nil {
return fmt.Errorf("backup website %s for rollback before recover failed, err: %v", website.Alias, err)
}
defer func() {
if !isOk {
t.LogStart(i18n.GetMsgByKey("Rollback"))
if err := handleWebsiteRecover(website, rollbackFile, true, "", taskID); err != nil {
t.LogFailedWithErr(i18n.GetMsgByKey("Rollback"), err)
return
}
t.LogSuccess(i18n.GetMsgByKey("Rollback"))
_ = os.RemoveAll(rollbackFile)
} else {
_ = os.RemoveAll(rollbackFile)
}
}()
}
nginxInfo, err := appInstallRepo.LoadBaseInfo(constant.AppOpenresty, "")
if err != nil {
return err

View file

@ -154,7 +154,7 @@ func (u *CronjobService) handleDirectory(cronjob model.Cronjob, startTime time.T
fileOp := files.NewFileOp()
if cronjob.IsDir {
if err := fileOp.TarGzCompressPro(true, cronjob.SourceDir, path.Join(backupDir, fileName), cronjob.ExclusionRules, cronjob.Secret); err != nil {
if err := fileOp.TarGzCompressPro(true, cronjob.SourceDir, path.Join(backupDir, fileName), cronjob.Secret, cronjob.ExclusionRules); err != nil {
return err
}
} else {

View file

@ -54,8 +54,6 @@ const openWithTaskID = (id: string, tail: boolean) => {
}
open.value = true;
bus.emit('refreshTask', true);
console.log('11111');
};
const openWithResourceID = (taskType: string, taskOperate: string, resourceID: number) => {

View file

@ -3126,7 +3126,6 @@ const message = {
favicon: 'Website Icon',
faviconHelper: 'Website icon (recommended image size: 16px*16px)',
reUpload: 'Choose File',
supportType: 'Only JPG, PNG, JPEG, and GIF files are supported.',
setDefault: 'Restore Default',
setHelper: 'The current settings will be saved. Do you want to continue?',
setDefaultHelper: 'All panel settings will be restored to default. Do you want to continue?',

View file

@ -2935,7 +2935,6 @@ const message = {
favicon: 'ウェブサイトアイコン',
faviconHelper: 'ウェブサイトアイコン推奨画像サイズ16px*16px',
reUpload: 'ファイルを選択',
supportType: 'JPGPNGJPEGGIFファイルのみサポートされています',
setDefault: 'デフォルトに戻す',
setHelper: '現在の設定が保存されます続けますか',
setDefaultHelper: 'すべてのパネル設定がデフォルトに戻されます続けますか',

View file

@ -2890,7 +2890,6 @@ const message = {
favicon: '웹사이트 아이콘',
faviconHelper: '웹사이트 아이콘 (권장 이미지 크기: 16px*16px)',
reUpload: '파일 선택',
supportType: 'JPG, PNG, JPEG, GIF 파일만 지원됩니다.',
setDefault: '기본값 복원',
setHelper: '현재 설정이 저장됩니다. 계속하시겠습니까?',
setDefaultHelper: '모든 패널 설정이 기본값으로 복원됩니다. 계속하시겠습니까?',

View file

@ -3005,7 +3005,6 @@ const message = {
favicon: 'Ikon Laman Web',
faviconHelper: 'Ikon laman web (saiz imej yang disarankan: 16px*16px)',
reUpload: 'Pilih Fail',
supportType: 'Hanya fail JPG, PNG, JPEG, dan GIF yang disokong.',
setDefault: 'Pulihkan Tetapan Asal',
setHelper: 'Tetapan semasa akan disimpan. Adakah anda ingin meneruskan?',
setDefaultHelper: 'Semua tetapan panel akan dikembalikan ke asal. Adakah anda ingin meneruskan?',

View file

@ -3008,7 +3008,6 @@ const message = {
favicon: 'Ícone do Site',
faviconHelper: 'Ícone do site (tamanho recomendado da imagem: 16px*16px)',
reUpload: 'Selecionar Arquivo',
supportType: 'Apenas arquivos JPG, PNG, JPEG e GIF são suportados.',
setDefault: 'Restaurar Padrão',
setHelper: 'As configurações atuais serão salvas. Deseja continuar?',
setDefaultHelper: 'Todas as configurações do painel serão restauradas para o padrão. Deseja continuar?',

View file

@ -2998,7 +2998,6 @@ const message = {
favicon: 'Иконка Сайта',
faviconHelper: 'Иконка сайта (рекомендуемый размер изображения: 16px*16px)',
reUpload: 'Выбрать Файл',
supportType: 'Поддерживаются только файлы JPG, PNG, JPEG и GIF.',
setDefault: 'Восстановить По Умолчанию',
setHelper: 'Текущие настройки будут сохранены. Вы хотите продолжить?',
setDefaultHelper: 'Все настройки панели будут восстановлены по умолчанию. Вы хотите продолжить?',

View file

@ -2897,7 +2897,6 @@ const message = {
favicon: '網站圖標',
faviconHelper: '網站圖標 (建議圖片大小為: 16px*16px)',
reUpload: '選擇文件',
supportType: '只能上傳 jpg/png/jpeg/gif 文件!',
setDefault: '復原預設',
setHelper: '即將儲存目前介面設定內容是否繼續',
setDefaultHelper: '即將復原所有界面設定到初始狀態是否繼續',

View file

@ -2880,7 +2880,6 @@ const message = {
favicon: '网站图标',
faviconHelper: '网站图标 (建议图片大小为: 16px*16px)',
reUpload: '选择文件',
supportType: '只能上传 jpg/png/jpeg/gif 文件!',
setHelper: '即将保存当前界面设置内容是否继续',
setDefaultHelper: '即将恢复所有界面设置到初始状态是否继续',
},

View file

@ -101,8 +101,9 @@
</el-select>
<TableSearch @search="search()" v-model:searchName="searchName" />
</template>
<template #main v-if="currentDB">
<template #main>
<ComplexTable
v-if="currentDB"
:pagination-config="paginationConfig"
:class="{ mask: maskShow }"
@sort-change="search"
@ -205,6 +206,18 @@
fix
/>
</ComplexTable>
<div v-if="dbOptionsLocal.length === 0 && dbOptionsRemote.length === 0" class="app-warn">
<div class="flex flex-col gap-2 items-center justify-center w-full sm:flex-row">
<span>{{ $t('app.checkInstalledWarn', [$t('database.noMysql')]) }}</span>
<span @click="goRouter('app')" class="flex items-center justify-center gap-0.5">
<el-icon><Position /></el-icon>
{{ $t('database.goInstall') }}
</span>
</div>
<div>
<img src="@/assets/images/no_app.svg" />
</div>
</div>
</template>
</LayoutContent>
@ -217,25 +230,6 @@
</span>
</el-card>
<div v-if="dbOptionsLocal.length === 0 && dbOptionsRemote.length === 0">
<LayoutContent :title="'MySQL ' + $t('menu.database')" :divider="true">
<template #main>
<div class="app-warn">
<div class="flex flex-col gap-2 items-center justify-center w-full sm:flex-row">
<span>{{ $t('app.checkInstalledWarn', [$t('database.noMysql')]) }}</span>
<span @click="goRouter('app')" class="flex items-center justify-center gap-0.5">
<el-icon><Position /></el-icon>
{{ $t('database.goInstall') }}
</span>
</div>
<div>
<img src="@/assets/images/no_app.svg" />
</div>
</div>
</template>
</LayoutContent>
</div>
<DialogPro v-model="open" :title="$t('app.checkTitle')" size="small">
<div class="flex justify-center items-center gap-2 flex-wrap">
{{ $t('app.checkInstalledWarn', [dashboardName]) }}
@ -509,7 +503,7 @@ const loadDBOptions = async () => {
}
}
if (currentDB.value) {
if (currentDB.value.from === 'remote') {
if (currentDB.value?.from === 'remote') {
maskShow.value = false;
}
globalStore.setCurrentDB('');
@ -529,7 +523,7 @@ const loadDBOptions = async () => {
if (currentDB.value) {
search();
}
if (currentDB.value.from === 'remote') {
if (currentDB.value?.from === 'remote') {
maskShow.value = false;
}
};

View file

@ -78,8 +78,9 @@
</el-select>
<TableSearch @search="search()" v-model:searchName="searchName" />
</template>
<template #main v-if="currentDB">
<template #main>
<ComplexTable
v-if="currentDB"
:class="{ mask: maskShow }"
:pagination-config="paginationConfig"
@sort-change="search"
@ -171,6 +172,18 @@
fix
/>
</ComplexTable>
<div class="app-warn" v-if="dbOptionsLocal.length === 0 && dbOptionsRemote.length === 0">
<div class="flex flex-col gap-2 items-center justify-center w-full sm:flex-row">
<span>{{ $t('app.checkInstalledWarn', [$t('database.noPostgresql')]) }}</span>
<span @click="goRouter('app')" class="flex items-center justify-center gap-0.5">
<el-icon><Position /></el-icon>
{{ $t('database.goInstall') }}
</span>
</div>
<div>
<img src="@/assets/images/no_app.svg" />
</div>
</div>
</template>
</LayoutContent>
@ -181,25 +194,6 @@
<span>{{ $t('commons.service.serviceNotStarted', ['PostgreSQL']) }}</span>
</el-card>
<div v-if="dbOptionsLocal.length === 0 && dbOptionsRemote.length === 0">
<LayoutContent :title="'PostgreSQL ' + $t('menu.database')" :divider="true">
<template #main>
<div class="app-warn">
<div class="flex flex-col gap-2 items-center justify-center w-full sm:flex-row">
<span>{{ $t('app.checkInstalledWarn', [$t('database.noPostgresql')]) }}</span>
<span @click="goRouter('app')" class="flex items-center justify-center gap-0.5">
<el-icon><Position /></el-icon>
{{ $t('database.goInstall') }}
</span>
</div>
<div>
<img src="@/assets/images/no_app.svg" />
</div>
</div>
</template>
</LayoutContent>
</div>
<DialogPro v-model="open" :title="$t('app.checkTitle')" size="small">
<div class="flex justify-center items-center gap-2 flex-wrap">
{{ $t('app.checkInstalledWarn', [dashboardName]) }}
@ -468,7 +462,7 @@ const loadDBOptions = async () => {
}
}
if (currentDB.value) {
if (currentDB.value.from === 'remote') {
if (currentDB.value?.from === 'remote') {
maskShow.value = false;
}
globalStore.setCurrentDB('');
@ -488,7 +482,7 @@ const loadDBOptions = async () => {
if (currentDB.value) {
search();
}
if (currentDB.value.from === 'remote') {
if (currentDB.value?.from === 'remote') {
maskShow.value = false;
}
};

View file

@ -95,23 +95,17 @@
</div>
</div>
<div v-if="dbOptionsLocal.length === 0 && dbOptionsRemote.length === 0">
<LayoutContent :title="'Redis ' + $t('menu.database')" :divider="true">
<template #main>
<div class="app-warn">
<div class="flex flex-col gap-2 items-center justify-center w-full sm:flex-row">
<span>{{ $t('app.checkInstalledWarn', ['Redis']) }}</span>
<span @click="goRouter('app')" class="flex items-center justify-center gap-0.5">
<el-icon><Position /></el-icon>
{{ $t('database.goInstall') }}
</span>
</div>
<div>
<img src="@/assets/images/no_app.svg" />
</div>
</div>
</template>
</LayoutContent>
<div class="app-warn" v-if="dbOptionsLocal.length === 0 && dbOptionsRemote.length === 0">
<div class="flex flex-col gap-2 items-center justify-center w-full sm:flex-row">
<span>{{ $t('app.checkInstalledWarn', ['Redis']) }}</span>
<span @click="goRouter('app')" class="flex items-center justify-center gap-0.5">
<el-icon><Position /></el-icon>
{{ $t('database.goInstall') }}
</span>
</div>
<div>
<img src="@/assets/images/no_app.svg" />
</div>
</div>
</template>
</LayoutContent>