diff --git a/backend/app/dto/dashboard.go b/backend/app/dto/dashboard.go index 94c07f3b7..a7d3a5c95 100644 --- a/backend/app/dto/dashboard.go +++ b/backend/app/dto/dashboard.go @@ -30,6 +30,8 @@ type OsInfo struct { PlatformFamily string `json:"platformFamily"` KernelArch string `json:"kernelArch"` KernelVersion string `json:"kernelVersion"` + + DiskSize int64 `json:"diskSize"` } type DashboardCurrent struct { diff --git a/backend/app/dto/setting.go b/backend/app/dto/setting.go index b4b68666d..29f8b24a2 100644 --- a/backend/app/dto/setting.go +++ b/backend/app/dto/setting.go @@ -143,6 +143,7 @@ type SnapshotInfo struct { Message string `json:"message"` CreatedAt time.Time `json:"createdAt"` Version string `json:"version"` + Size int64 `json:"size"` InterruptStep string `json:"interruptStep"` RecoverStatus string `json:"recoverStatus"` diff --git a/backend/app/service/dashboard.go b/backend/app/service/dashboard.go index 298a5947d..c7bebff30 100644 --- a/backend/app/service/dashboard.go +++ b/backend/app/service/dashboard.go @@ -64,6 +64,11 @@ func (u *DashboardService) LoadOsInfo() (*dto.OsInfo, error) { baseInfo.KernelArch = hostInfo.KernelArch baseInfo.KernelVersion = hostInfo.KernelVersion + diskInfo, err := disk.Usage(global.CONF.System.BaseDir) + if err == nil { + baseInfo.DiskSize = int64(diskInfo.Free) + } + if baseInfo.KernelArch == "armv7l" { baseInfo.KernelArch = "armv7" } diff --git a/backend/app/service/snapshot.go b/backend/app/service/snapshot.go index 18357a9eb..79168da7e 100644 --- a/backend/app/service/snapshot.go +++ b/backend/app/service/snapshot.go @@ -48,13 +48,9 @@ func NewISnapshotService() ISnapshotService { func (u *SnapshotService) SearchWithPage(req dto.SearchWithPage) (int64, interface{}, error) { total, systemBackups, err := snapshotRepo.Page(req.Page, req.PageSize, commonRepo.WithLikeName(req.Info)) - var dtoSnap []dto.SnapshotInfo - for _, systemBackup := range systemBackups { - var item dto.SnapshotInfo - if err := copier.Copy(&item, &systemBackup); err != nil { - return 0, nil, errors.WithMessage(constant.ErrStructTransform, err.Error()) - } - dtoSnap = append(dtoSnap, item) + dtoSnap, err := loadSnapSize(systemBackups) + if err != nil { + return 0, nil, err } return total, dtoSnap, err } @@ -510,3 +506,48 @@ func loadOs() string { return hostInfo.KernelArch } } + +func loadSnapSize(records []model.Snapshot) ([]dto.SnapshotInfo, error) { + var datas []dto.SnapshotInfo + clientMap := make(map[string]loadSizeHelper) + var wg sync.WaitGroup + for i := 0; i < len(records); i++ { + var item dto.SnapshotInfo + if err := copier.Copy(&item, &records[i]); err != nil { + return nil, errors.WithMessage(constant.ErrStructTransform, err.Error()) + } + itemPath := fmt.Sprintf("system_snapshot/%s.tar.gz", item.Name) + if _, ok := clientMap[records[i].DefaultDownload]; !ok { + backup, err := backupRepo.Get(commonRepo.WithByType(records[i].DefaultDownload)) + if err != nil { + global.LOG.Errorf("load backup model %s from db failed, err: %v", records[i].DefaultDownload, err) + clientMap[records[i].DefaultDownload] = loadSizeHelper{} + datas = append(datas, item) + continue + } + client, err := NewIBackupService().NewClient(&backup) + if err != nil { + global.LOG.Errorf("load backup client %s from db failed, err: %v", records[i].DefaultDownload, err) + clientMap[records[i].DefaultDownload] = loadSizeHelper{} + datas = append(datas, item) + continue + } + item.Size, _ = client.Size(path.Join(strings.TrimLeft(backup.BackupPath, "/"), itemPath)) + datas = append(datas, item) + clientMap[records[i].DefaultDownload] = loadSizeHelper{backupPath: strings.TrimLeft(backup.BackupPath, "/"), client: client, isOk: true} + continue + } + if clientMap[records[i].DefaultDownload].isOk { + wg.Add(1) + go func(index int) { + item.Size, _ = clientMap[records[index].DefaultDownload].client.Size(path.Join(clientMap[records[index].DefaultDownload].backupPath, itemPath)) + datas = append(datas, item) + wg.Done() + }(i) + } else { + datas = append(datas, item) + } + } + wg.Wait() + return datas, nil +} diff --git a/backend/app/service/snapshot_recover.go b/backend/app/service/snapshot_recover.go index f5a7e2f08..bfb2320e2 100644 --- a/backend/app/service/snapshot_recover.go +++ b/backend/app/service/snapshot_recover.go @@ -73,8 +73,8 @@ func (u *SnapshotService) HandleSnapshotRecover(snap model.Snapshot, isRecover b if snap.InterruptStep == "Readjson" { req.IsNew = true } - if req.IsNew || snap.InterruptStep == "AppData" { - if err := recoverAppData(snapFileDir); err != nil { + if isRecover && (req.IsNew || snap.InterruptStep == "AppData") { + if err := recoverAppData(snapFileDir); err == nil { updateRecoverStatus(snap.ID, isRecover, "DockerDir", constant.StatusFailed, fmt.Sprintf("handle recover app data failed, err: %v", err)) return } @@ -163,14 +163,14 @@ func backupBeforeRecover(snap model.Snapshot, secret string) error { _ = os.MkdirAll(path.Join(baseDir, "1panel"), os.ModePerm) _ = os.MkdirAll(path.Join(baseDir, "docker"), os.ModePerm) - wg.Add(5) + wg.Add(4) itemHelper.Wg = &wg go snapJson(itemHelper, jsonItem, baseDir) go snapPanel(itemHelper, path.Join(baseDir, "1panel")) go snapDaemonJson(itemHelper, path.Join(baseDir, "docker")) - go snapAppData(itemHelper, path.Join(baseDir, "docker")) go snapBackup(itemHelper, global.CONF.System.Backup, path.Join(baseDir, "1panel")) wg.Wait() + itemHelper.Status.AppData = constant.StatusDone allDone, msg := checkAllDone(status) if !allDone { diff --git a/cmd/server/docs/docs.go b/cmd/server/docs/docs.go index 29e428f75..14ba06543 100644 --- a/cmd/server/docs/docs.go +++ b/cmd/server/docs/docs.go @@ -1111,6 +1111,34 @@ const docTemplate = `{ } } }, + "/containers/commit": { + "post": { + "description": "容器提交生成新镜像", + "consumes": [ + "application/json" + ], + "tags": [ + "Container" + ], + "summary": "Commit Container", + "parameters": [ + { + "description": "request", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/dto.ContainerCommit" + } + } + ], + "responses": { + "200": { + "description": "OK" + } + } + } + }, "/containers/compose": { "post": { "security": [ @@ -8006,6 +8034,48 @@ const docTemplate = `{ } } }, + "/hosts/firewall/forward": { + "post": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "description": "更新防火墙端口转发规则", + "consumes": [ + "application/json" + ], + "tags": [ + "Firewall" + ], + "summary": "Create group", + "parameters": [ + { + "description": "request", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/dto.ForwardRuleOperate" + } + } + ], + "responses": { + "200": { + "description": "OK" + } + }, + "x-panel-log": { + "BeforeFunctions": [], + "bodyKeys": [ + "source_port" + ], + "formatEN": "update port forward rules [source_port]", + "formatZH": "更新端口转发规则 [source_port]", + "paramKeys": [] + } + } + }, "/hosts/firewall/ip": { "post": { "security": [ @@ -15136,6 +15206,32 @@ const docTemplate = `{ } } }, + "dto.ContainerCommit": { + "type": "object", + "required": [ + "containerID" + ], + "properties": { + "author": { + "type": "string" + }, + "comment": { + "type": "string" + }, + "containerID": { + "type": "string" + }, + "containerName": { + "type": "string" + }, + "newImageName": { + "type": "string" + }, + "pause": { + "type": "boolean" + } + } + }, "dto.ContainerListStats": { "type": "object", "properties": { @@ -16344,6 +16440,52 @@ const docTemplate = `{ } } }, + "dto.ForwardRuleOperate": { + "type": "object", + "properties": { + "rules": { + "type": "array", + "items": { + "type": "object", + "required": [ + "operation", + "port", + "protocol", + "targetPort" + ], + "properties": { + "num": { + "type": "string" + }, + "operation": { + "type": "string", + "enum": [ + "add", + "remove" + ] + }, + "port": { + "type": "string" + }, + "protocol": { + "type": "string", + "enum": [ + "tcp", + "udp", + "tcp/udp" + ] + }, + "targetIP": { + "type": "string" + }, + "targetPort": { + "type": "string" + } + } + } + } + } + }, "dto.FtpBaseInfo": { "type": "object", "properties": { @@ -17541,6 +17683,9 @@ const docTemplate = `{ "dto.OsInfo": { "type": "object", "properties": { + "diskSize": { + "type": "integer" + }, "kernelArch": { "type": "string" }, @@ -21207,6 +21352,9 @@ const docTemplate = `{ "enable": { "type": "boolean" }, + "hsts": { + "type": "boolean" + }, "httpConfig": { "type": "string", "enum": [ @@ -22371,6 +22519,9 @@ const docTemplate = `{ "enable": { "type": "boolean" }, + "hsts": { + "type": "boolean" + }, "httpConfig": { "type": "string" } diff --git a/cmd/server/docs/swagger.json b/cmd/server/docs/swagger.json index 9372a97a1..8785d51e2 100644 --- a/cmd/server/docs/swagger.json +++ b/cmd/server/docs/swagger.json @@ -1104,6 +1104,34 @@ } } }, + "/containers/commit": { + "post": { + "description": "容器提交生成新镜像", + "consumes": [ + "application/json" + ], + "tags": [ + "Container" + ], + "summary": "Commit Container", + "parameters": [ + { + "description": "request", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/dto.ContainerCommit" + } + } + ], + "responses": { + "200": { + "description": "OK" + } + } + } + }, "/containers/compose": { "post": { "security": [ @@ -7999,6 +8027,48 @@ } } }, + "/hosts/firewall/forward": { + "post": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "description": "更新防火墙端口转发规则", + "consumes": [ + "application/json" + ], + "tags": [ + "Firewall" + ], + "summary": "Create group", + "parameters": [ + { + "description": "request", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/dto.ForwardRuleOperate" + } + } + ], + "responses": { + "200": { + "description": "OK" + } + }, + "x-panel-log": { + "BeforeFunctions": [], + "bodyKeys": [ + "source_port" + ], + "formatEN": "update port forward rules [source_port]", + "formatZH": "更新端口转发规则 [source_port]", + "paramKeys": [] + } + } + }, "/hosts/firewall/ip": { "post": { "security": [ @@ -15129,6 +15199,32 @@ } } }, + "dto.ContainerCommit": { + "type": "object", + "required": [ + "containerID" + ], + "properties": { + "author": { + "type": "string" + }, + "comment": { + "type": "string" + }, + "containerID": { + "type": "string" + }, + "containerName": { + "type": "string" + }, + "newImageName": { + "type": "string" + }, + "pause": { + "type": "boolean" + } + } + }, "dto.ContainerListStats": { "type": "object", "properties": { @@ -16337,6 +16433,52 @@ } } }, + "dto.ForwardRuleOperate": { + "type": "object", + "properties": { + "rules": { + "type": "array", + "items": { + "type": "object", + "required": [ + "operation", + "port", + "protocol", + "targetPort" + ], + "properties": { + "num": { + "type": "string" + }, + "operation": { + "type": "string", + "enum": [ + "add", + "remove" + ] + }, + "port": { + "type": "string" + }, + "protocol": { + "type": "string", + "enum": [ + "tcp", + "udp", + "tcp/udp" + ] + }, + "targetIP": { + "type": "string" + }, + "targetPort": { + "type": "string" + } + } + } + } + } + }, "dto.FtpBaseInfo": { "type": "object", "properties": { @@ -17534,6 +17676,9 @@ "dto.OsInfo": { "type": "object", "properties": { + "diskSize": { + "type": "integer" + }, "kernelArch": { "type": "string" }, @@ -21200,6 +21345,9 @@ "enable": { "type": "boolean" }, + "hsts": { + "type": "boolean" + }, "httpConfig": { "type": "string", "enum": [ @@ -22364,6 +22512,9 @@ "enable": { "type": "boolean" }, + "hsts": { + "type": "boolean" + }, "httpConfig": { "type": "string" } diff --git a/cmd/server/docs/swagger.yaml b/cmd/server/docs/swagger.yaml index 2aa64ac59..c6bc32b8c 100644 --- a/cmd/server/docs/swagger.yaml +++ b/cmd/server/docs/swagger.yaml @@ -403,6 +403,23 @@ definitions: - name - path type: object + dto.ContainerCommit: + properties: + author: + type: string + comment: + type: string + containerID: + type: string + containerName: + type: string + newImageName: + type: string + pause: + type: boolean + required: + - containerID + type: object dto.ContainerListStats: properties: containerID: @@ -1218,6 +1235,38 @@ definitions: - type - vars type: object + dto.ForwardRuleOperate: + properties: + rules: + items: + properties: + num: + type: string + operation: + enum: + - add + - remove + type: string + port: + type: string + protocol: + enum: + - tcp + - udp + - tcp/udp + type: string + targetIP: + type: string + targetPort: + type: string + required: + - operation + - port + - protocol + - targetPort + type: object + type: array + type: object dto.FtpBaseInfo: properties: isActive: @@ -2029,6 +2078,8 @@ definitions: type: object dto.OsInfo: properties: + diskSize: + type: integer kernelArch: type: string kernelVersion: @@ -4487,6 +4538,8 @@ definitions: type: string enable: type: boolean + hsts: + type: boolean httpConfig: enum: - HTTPSOnly @@ -5266,6 +5319,8 @@ definitions: type: string enable: type: boolean + hsts: + type: boolean httpConfig: type: string type: object @@ -6001,6 +6056,24 @@ paths: formatEN: clean container [name] logs formatZH: 清理容器 [name] 日志 paramKeys: [] + /containers/commit: + post: + consumes: + - application/json + description: 容器提交生成新镜像 + parameters: + - description: request + in: body + name: request + required: true + schema: + $ref: '#/definitions/dto.ContainerCommit' + responses: + "200": + description: OK + summary: Commit Container + tags: + - Container /containers/compose: post: consumes: @@ -10377,6 +10450,33 @@ paths: summary: Create group tags: - Firewall + /hosts/firewall/forward: + post: + consumes: + - application/json + description: 更新防火墙端口转发规则 + parameters: + - description: request + in: body + name: request + required: true + schema: + $ref: '#/definitions/dto.ForwardRuleOperate' + responses: + "200": + description: OK + security: + - ApiKeyAuth: [] + summary: Create group + tags: + - Firewall + x-panel-log: + BeforeFunctions: [] + bodyKeys: + - source_port + formatEN: update port forward rules [source_port] + formatZH: 更新端口转发规则 [source_port] + paramKeys: [] /hosts/firewall/ip: post: consumes: diff --git a/frontend/src/api/interface/dashboard.ts b/frontend/src/api/interface/dashboard.ts index 30730631c..e593f9e4e 100644 --- a/frontend/src/api/interface/dashboard.ts +++ b/frontend/src/api/interface/dashboard.ts @@ -5,6 +5,8 @@ export namespace Dashboard { platformFamily: string; kernelArch: string; kernelVersion: string; + + diskSize: number; } export interface BaseInfo { websiteNumber: number; diff --git a/frontend/src/lang/modules/en.ts b/frontend/src/lang/modules/en.ts index 2c04532ad..77f5c6239 100644 --- a/frontend/src/lang/modules/en.ts +++ b/frontend/src/lang/modules/en.ts @@ -1512,15 +1512,20 @@ const message = { lastRecoverAt: 'Last recovery time', lastRollbackAt: 'Last rollback time', reDownload: 'Download the backup file again', - recoverRecord: 'Recover record', + statusSuccess: 'Success', + statusFailed: 'Failed', + recoverErrArch: 'Snapshot recovery between different server architectures is not supported!', + recoverErrSize: 'Detected insufficient disk space, please check or clean up and try again!', recoverHelper: - 'The recovery is about to start from snapshot {0}, and the recovery needs to restart docker and 1panel service, do you want to continue?', - recoverHelper1: - 'Will start restoring from snapshot {0}, please ensure that the server architecture matches the one where the snapshot was created.', - recoverHelper2: 'Restoring snapshots between different server architectures is not supported.', + 'Starting recovery from snapshot {0}, please confirm the following information before proceeding:', + recoverHelper1: 'Recovery requires restarting Docker and 1Panel services', + recoverHelper2: + 'Please ensure there is sufficient disk space on the server (Snapshot file size: {0}, Available space: {1})', + recoverHelper3: + 'Please ensure the server architecture matches the architecture of the server where the snapshot was created (Current server architecture: {0})', rollback: 'Rollback', rollbackHelper: - 'This recovery is about to be rolled back, which will replace all the files recovered this time. In the process, docker and 1panel services may need to be restarted. Do you want to continue?', + 'Rolling back this recovery will replace all files from this recovery, and may require restarting Docker and 1Panel services. Do you want to continue?', upgradeHelper: 'The upgrade requires restarting the 1Panel service. Do you want to continue?', noUpgrade: 'It is currently the latest version', @@ -1554,8 +1559,7 @@ const message = { menu: 'Menu', confirmMessage: 'The page will be refreshed to update the advanced menu list. Continue?', compressPassword: 'Compression Password', - backupRecoverMessage: - 'If you need to set a compression or decompression password, please enter it. (Leave blank if not needed)', + backupRecoverMessage: 'Please enter the compression or decompression password (leave blank to not set)', }, license: { community: 'Community Edition: ', diff --git a/frontend/src/lang/modules/tw.ts b/frontend/src/lang/modules/tw.ts index dadb4396b..97a2f0fd3 100644 --- a/frontend/src/lang/modules/tw.ts +++ b/frontend/src/lang/modules/tw.ts @@ -1334,12 +1334,15 @@ const message = { reDownload: '重新下載備份文件', statusSuccess: '成功', statusFailed: '失敗', - recoverHelper: '即將從快照 {0} 開始恢復,恢復需要重啟 docker 以及 1panel 服務,是否繼續?', - recoverHelper1: '即將從快照 {0} 開始恢復,請確保伺服器架構與創建快照伺服器架構信息保持一致。', - recoverHelper2: '不支持在不同伺服器架構之間進行快照恢復操作。', + recoverErrArch: '不支援在不同伺服器架構之間進行快照恢復操作!', + recoverErrSize: '檢測到目前磁碟空間不足,請檢查或清理後重試!', + recoverHelper: '即將從快照 {0} 開始恢復,恢復前請確認以下資訊:', + recoverHelper1: '恢復需要重新啟動 Docker 以及 1Panel 服務', + recoverHelper2: '請確保伺服器磁碟空間充足 ( 快照檔案大小: {0}, 可用空間: {1} )', + recoverHelper3: '請確保伺服器架構與建立快照伺服器架構資訊保持一致 (目前伺服器架構: {0} )', rollback: '回滾', rollbackHelper: - '即將回滾本次恢復,回滾將替換所有本次恢復的文件,過程中可能需要重啟 docker 以及 1panel 服務,是否繼續?', + '即將回滾本次恢復,回滾將替換所有本次恢復的檔案,過程中可能需要重新啟動 Docker 以及 1Panel 服務,是否繼續?', upgrading: '正在升級中,請稍候...', upgradeHelper: '升級操作需要重啟 1Panel 服務,是否繼續?', @@ -1444,7 +1447,7 @@ const message = { menu: '選單', confirmMessage: '即將刷新頁面更新高級功能菜單列表,是否繼續?', compressPassword: '壓縮密碼', - backupRecoverMessage: '如果需要設定壓縮或者解壓縮密碼,請輸入。(不填則不設定)', + backupRecoverMessage: '請輸入壓縮或解壓縮密碼(留空則不設定)', }, license: { community: '社區版:', diff --git a/frontend/src/lang/modules/zh.ts b/frontend/src/lang/modules/zh.ts index 14f26a837..e55c13407 100644 --- a/frontend/src/lang/modules/zh.ts +++ b/frontend/src/lang/modules/zh.ts @@ -1336,12 +1336,15 @@ const message = { reDownload: '重新下载备份文件', statusSuccess: '成功', statusFailed: '失败', - recoverHelper: '即将从快照 {0} 开始恢复,恢复需要重启 docker 以及 1panel 服务,是否继续?', - recoverHelper1: '即将从快照 {0} 开始恢复,请确保服务器架构与创建快照服务器架构信息保持一致。', - recoverHelper2: '不支持在不同服务器架构之间进行快照恢复操作。', + recoverErrArch: '不支持在不同服务器架构之间进行快照恢复操作!', + recoverErrSize: '检测到当前磁盘空间不足,请检查或清理后重试!', + recoverHelper: '即将从快照 {0} 开始恢复,恢复前请确认以下信息:', + recoverHelper1: '恢复需要重启 Docker 以及 1Panel 服务', + recoverHelper2: '请确保服务器磁盘空间充足 ( 快照文件大小: {0}, 可用空间: {1} )', + recoverHelper3: '请确保服务器架构与创建快照服务器架构信息保持一致 (当前服务器架构: {0} )', rollback: '回滚', rollbackHelper: - '即将回滚本次恢复,回滚将替换所有本次恢复的文件,过程中可能需要重启 docker 以及 1panel 服务,是否继续?', + '即将回滚本次恢复,回滚将替换所有本次恢复的文件,过程中可能需要重启 Docker 以及 1Panel 服务,是否继续?', upgrading: '正在升级中,请稍候...', upgradeHelper: '升级操作需要重启 1Panel 服务,是否继续?', @@ -1446,7 +1449,7 @@ const message = { menu: '菜单', confirmMessage: '即将刷新页面更新高级功能菜单列表,是否继续?', compressPassword: '压缩密码', - backupRecoverMessage: '如果需要设置压缩或者解压缩密码,请输入。(不填则不设置)', + backupRecoverMessage: '请输入压缩或解压缩密码(留空则不设置)', }, license: { community: '社区版:', diff --git a/frontend/src/views/database/redis/setting/persistence/index.vue b/frontend/src/views/database/redis/setting/persistence/index.vue index c88d46fc0..7462c9550 100644 --- a/frontend/src/views/database/redis/setting/persistence/index.vue +++ b/frontend/src/views/database/redis/setting/persistence/index.vue @@ -193,7 +193,7 @@ const search = async () => { }; const onBackup = async () => { emit('loading', true); - await handleBackup({ name: database.value, detailName: '', type: 'redis' }) + await handleBackup({ name: database.value, detailName: '', type: 'redis', secret: '' }) .then(() => { emit('loading', false); search(); @@ -210,6 +210,7 @@ const onRecover = async () => { name: database.value, detailName: '', file: currentRow.value.fileDir + '/' + currentRow.value.fileName, + secret: '', }; emit('loading', true); await handleRecover(param) diff --git a/frontend/src/views/setting/snapshot/index.vue b/frontend/src/views/setting/snapshot/index.vue index 78f0cf86f..d7bd08b75 100644 --- a/frontend/src/views/setting/snapshot/index.vue +++ b/frontend/src/views/setting/snapshot/index.vue @@ -71,6 +71,14 @@ + + + + {{ computeSize(row.size) }} + + - + + - - + + {{ $t('setting.recoverHelper', [recoverReq.name]) }} + + + + {{ $t('setting.recoverHelper1') }} + + + + {{ $t('setting.recoverHelper2', [computeSize(recoverReq.size), computeSize(recoverReq.freeSize)]) }} + + + + {{ $t('setting.recoverHelper3', [recoverReq.arch]) }} + + + + {{ $t('setting.reDownload') }} + + @@ -30,6 +58,7 @@ import { FormInstance } from 'element-plus'; import i18n from '@/lang'; import { MsgSuccess } from '@/utils/message'; import { snapshotRecover } from '@/api/modules/setting'; +import { computeSize } from '@/utils/util'; let loading = ref(false); let open = ref(false); @@ -39,14 +68,22 @@ const emit = defineEmits<{ (e: 'search'): void; (e: 'close'): void }>(); interface DialogProps { id: number; isNew: boolean; + name: string; reDownload: boolean; + arch: string; + size: number; + freeSize: number; } let recoverReq = ref({ id: 0, isNew: true, + name: '', reDownload: true, secret: '', + arch: '', + size: 0, + freeSize: 0, }); const handleClose = () => { @@ -56,12 +93,29 @@ const acceptParams = (params: DialogProps): void => { recoverReq.value = { id: params.id, isNew: params.isNew, + name: params.name, reDownload: params.reDownload, secret: '', + arch: params.arch, + size: params.size, + freeSize: params.freeSize, }; open.value = true; }; +const isSizeOk = () => { + if (recoverReq.value.size === 0 || recoverReq.value.freeSize === 0) { + return false; + } + return recoverReq.value.size * 2 < recoverReq.value.freeSize; +}; +const isArchOk = () => { + if (recoverReq.value.arch.length === 0) { + return false; + } + return recoverReq.value.name.indexOf(recoverReq.value.arch) !== -1; +}; + const submit = async () => { loading.value = true; await snapshotRecover({ diff --git a/frontend/src/views/setting/snapshot/status/index.vue b/frontend/src/views/setting/snapshot/status/index.vue index b29104126..14c0b1605 100644 --- a/frontend/src/views/setting/snapshot/status/index.vue +++ b/frontend/src/views/setting/snapshot/status/index.vue @@ -62,7 +62,7 @@ - + {{ $t('commons.button.retry') }} @@ -138,30 +138,6 @@ - - - - {{ $t('commons.button.retry') }} - - - - {{ $t('setting.reDownload') }} - - - - {{ $t('setting.recoverHelper', [snapInfo.name]) }} - - - - - {{ $t('commons.button.cancel') }} - - - {{ $t('commons.button.confirm') }} - - - - @@ -174,7 +150,7 @@ import { ElMessageBox } from 'element-plus'; import i18n from '@/lang'; import DrawerHeader from '@/components/drawer-header/index.vue'; import { snapshotRollback } from '@/api/modules/setting'; -import { MsgError, MsgSuccess } from '@/utils/message'; +import { MsgSuccess } from '@/utils/message'; import { loadOsInfo } from '@/api/modules/dashboard'; import SnapRecover from '@/views/setting/snapshot/recover/index.vue'; @@ -182,8 +158,6 @@ const drawerVisible = ref(false); const snapInfo = ref(); const loading = ref(); -const dialogVisible = ref(); -const reDownload = ref(); const recoverRef = ref(); interface DialogProps { @@ -197,61 +171,42 @@ const emit = defineEmits(['search']); const handleClose = () => { drawerVisible.value = false; - dialogVisible.value = false; -}; - -const doRecover = async (isNew: boolean) => { - let params = { - id: snapInfo.value.id, - isNew: isNew, - reDownload: reDownload.value, - secret: snapInfo.value.secret, - }; - recoverRef.value.acceptParams(params); }; const recoverSnapshot = async (isNew: boolean) => { - let msg = i18n.global.t('setting.recoverHelper', [snapInfo.value.name]); - if ( - snapInfo.value.name.indexOf('amd64') === -1 && - snapInfo.value.name.indexOf('arm64') === -1 && - snapInfo.value.name.indexOf('armv7') === -1 && - snapInfo.value.name.indexOf('ppc64le') === -1 && - snapInfo.value.name.indexOf('s390x') === -1 - ) { - msg = i18n.global.t('setting.recoverHelper1', [snapInfo.value.name]); - } else { - const res = await loadOsInfo(); - let osVal = res.data.kernelArch; - if (osVal === '') { - msg = i18n.global.t('setting.recoverHelper1', [snapInfo.value.name]); - } else if (snapInfo.value.name.indexOf(osVal) === -1) { - MsgError(i18n.global.t('setting.recoverHelper2')); - return; - } - } + loading.value = true; + await loadOsInfo() + .then((res) => { + loading.value = false; + let params = { + id: snapInfo.value.id, + isNew: isNew, + name: snapInfo.value.name, + reDownload: false, + secret: snapInfo.value.secret, - ElMessageBox.confirm(msg, i18n.global.t('commons.button.recover'), { - confirmButtonText: i18n.global.t('commons.button.confirm'), - cancelButtonText: i18n.global.t('commons.button.cancel'), - type: 'info', - }).then(async () => { - doRecover(isNew); - }); + arch: res.data.kernelArch, + size: snapInfo.value.size, + freeSize: res.data.diskSize, + }; + recoverRef.value.acceptParams(params); + }) + .catch(() => { + loading.value = false; + }); }; const rollbackSnapshot = async () => { - ElMessageBox.confirm(i18n.global.t('setting.rollbackHelper'), { + ElMessageBox.confirm(i18n.global.t('setting.rollbackHelper'), i18n.global.t('setting.rollback'), { confirmButtonText: i18n.global.t('commons.button.confirm'), cancelButtonText: i18n.global.t('commons.button.cancel'), type: 'info', }).then(async () => { loading.value = true; - await snapshotRollback({ id: snapInfo.value.id, isNew: false, reDownload: false }) + await snapshotRollback({ id: snapInfo.value.id, isNew: false, reDownload: false, secret: '' }) .then(() => { emit('search'); loading.value = false; - dialogVisible.value = false; drawerVisible.value = false; MsgSuccess(i18n.global.t('commons.msg.operationSuccess')); }) @@ -284,7 +239,4 @@ defineExpose({ line-height: 25px; color: var(--el-button-text-color, var(--el-text-color-regular)); } -.card-logo { - font-size: 7px; -}