mirror of
https://github.com/1Panel-dev/1Panel.git
synced 2025-01-31 22:18:07 +08:00
parent
e3329597bc
commit
e359e1c544
@ -135,6 +135,28 @@ func (b *BaseApi) SearchSnapshot(c *gin.Context) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// @Tags System Setting
|
||||||
|
// @Summary Load system snapshot size
|
||||||
|
// @Description 获取系统快照文件大小
|
||||||
|
// @Accept json
|
||||||
|
// @Param request body dto.SearchWithPage true "request"
|
||||||
|
// @Success 200
|
||||||
|
// @Security ApiKeyAuth
|
||||||
|
// @Router /settings/snapshot/size [post]
|
||||||
|
func (b *BaseApi) LoadSnapshotSize(c *gin.Context) {
|
||||||
|
var req dto.SearchWithPage
|
||||||
|
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
accounts, err := snapshotService.LoadSize(req)
|
||||||
|
if err != nil {
|
||||||
|
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
helper.SuccessWithData(c, accounts)
|
||||||
|
}
|
||||||
|
|
||||||
// @Tags System Setting
|
// @Tags System Setting
|
||||||
// @Summary Recover system backup
|
// @Summary Recover system backup
|
||||||
// @Description 从系统快照恢复
|
// @Description 从系统快照恢复
|
||||||
|
@ -101,3 +101,11 @@ type SnapshotInfo struct {
|
|||||||
RollbackMessage string `json:"rollbackMessage"`
|
RollbackMessage string `json:"rollbackMessage"`
|
||||||
LastRollbackedAt string `json:"lastRollbackedAt"`
|
LastRollbackedAt string `json:"lastRollbackedAt"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type SnapshotFile struct {
|
||||||
|
ID uint `json:"id"`
|
||||||
|
Name string `json:"name"`
|
||||||
|
From string `json:"from"`
|
||||||
|
DefaultDownload string `json:"defaultDownload"`
|
||||||
|
Size int64 `json:"size"`
|
||||||
|
}
|
||||||
|
@ -120,6 +120,7 @@ func (u *BackupService) CheckUsed(id uint) error {
|
|||||||
|
|
||||||
type loadSizeHelper struct {
|
type loadSizeHelper struct {
|
||||||
isOk bool
|
isOk bool
|
||||||
|
backupName string
|
||||||
backupPath string
|
backupPath string
|
||||||
client cloud_storage.CloudStorageClient
|
client cloud_storage.CloudStorageClient
|
||||||
}
|
}
|
||||||
|
@ -5,6 +5,7 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
"path"
|
"path"
|
||||||
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
@ -18,7 +19,6 @@ import (
|
|||||||
"github.com/docker/docker/api/types/image"
|
"github.com/docker/docker/api/types/image"
|
||||||
"github.com/google/uuid"
|
"github.com/google/uuid"
|
||||||
"github.com/jinzhu/copier"
|
"github.com/jinzhu/copier"
|
||||||
"github.com/pkg/errors"
|
|
||||||
"github.com/shirou/gopsutil/v3/host"
|
"github.com/shirou/gopsutil/v3/host"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -28,6 +28,7 @@ type SnapshotService struct {
|
|||||||
|
|
||||||
type ISnapshotService interface {
|
type ISnapshotService interface {
|
||||||
SearchWithPage(req dto.SearchWithPage) (int64, interface{}, error)
|
SearchWithPage(req dto.SearchWithPage) (int64, interface{}, error)
|
||||||
|
LoadSize(req dto.SearchWithPage) ([]dto.SnapshotFile, error)
|
||||||
LoadSnapshotData() (dto.SnapshotData, error)
|
LoadSnapshotData() (dto.SnapshotData, error)
|
||||||
SnapshotCreate(req dto.SnapshotCreate) error
|
SnapshotCreate(req dto.SnapshotCreate) error
|
||||||
SnapshotReCreate(id uint) error
|
SnapshotReCreate(id uint) error
|
||||||
@ -46,15 +47,64 @@ func NewISnapshotService() ISnapshotService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (u *SnapshotService) SearchWithPage(req dto.SearchWithPage) (int64, interface{}, error) {
|
func (u *SnapshotService) SearchWithPage(req dto.SearchWithPage) (int64, interface{}, error) {
|
||||||
total, systemBackups, err := snapshotRepo.Page(req.Page, req.PageSize, commonRepo.WithByLikeName(req.Info))
|
total, records, err := snapshotRepo.Page(req.Page, req.PageSize, commonRepo.WithByLikeName(req.Info))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, nil, err
|
return 0, nil, err
|
||||||
}
|
}
|
||||||
dtoSnap, err := loadSnapSize(systemBackups)
|
var datas []dto.SnapshotInfo
|
||||||
if err != nil {
|
for i := 0; i < len(records); i++ {
|
||||||
|
var item dto.SnapshotInfo
|
||||||
|
if err := copier.Copy(&item, &records[i]); err != nil {
|
||||||
return 0, nil, err
|
return 0, nil, err
|
||||||
}
|
}
|
||||||
return total, dtoSnap, err
|
datas = append(datas, item)
|
||||||
|
}
|
||||||
|
return total, datas, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (u *SnapshotService) LoadSize(req dto.SearchWithPage) ([]dto.SnapshotFile, error) {
|
||||||
|
_, records, err := snapshotRepo.Page(req.Page, req.PageSize, commonRepo.WithByLikeName(req.Info))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
var datas []dto.SnapshotFile
|
||||||
|
var wg sync.WaitGroup
|
||||||
|
clientMap := make(map[uint]loadSizeHelper)
|
||||||
|
for i := 0; i < len(records); i++ {
|
||||||
|
itemPath := fmt.Sprintf("system_snapshot/%s.tar.gz", records[i].Name)
|
||||||
|
data := dto.SnapshotFile{ID: records[i].ID, Name: records[i].Name}
|
||||||
|
accounts := strings.Split(records[i].SourceAccountIDs, ",")
|
||||||
|
var accountNames []string
|
||||||
|
for _, account := range accounts {
|
||||||
|
itemVal, _ := strconv.Atoi(account)
|
||||||
|
if _, ok := clientMap[uint(itemVal)]; !ok {
|
||||||
|
backup, client, err := NewBackupClientWithID(uint(itemVal))
|
||||||
|
if err != nil {
|
||||||
|
global.LOG.Errorf("load backup client from db failed, err: %v", err)
|
||||||
|
clientMap[records[i].DownloadAccountID] = loadSizeHelper{}
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
backupName := fmt.Sprintf("%s - %s", backup.Type, backup.Name)
|
||||||
|
clientMap[uint(itemVal)] = loadSizeHelper{backupPath: strings.TrimLeft(backup.BackupPath, "/"), client: client, isOk: true, backupName: backupName}
|
||||||
|
accountNames = append(accountNames, backupName)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
data.DefaultDownload = clientMap[records[i].DownloadAccountID].backupName
|
||||||
|
data.From = strings.Join(accountNames, ",")
|
||||||
|
if clientMap[records[i].DownloadAccountID].isOk {
|
||||||
|
wg.Add(1)
|
||||||
|
go func(index int) {
|
||||||
|
data.Size, _ = clientMap[records[index].DownloadAccountID].client.Size(path.Join(clientMap[records[index].DownloadAccountID].backupPath, itemPath))
|
||||||
|
datas = append(datas, data)
|
||||||
|
wg.Done()
|
||||||
|
}(i)
|
||||||
|
} else {
|
||||||
|
datas = append(datas, data)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
wg.Wait()
|
||||||
|
|
||||||
|
return datas, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (u *SnapshotService) SnapshotImport(req dto.SnapshotImport) error {
|
func (u *SnapshotService) SnapshotImport(req dto.SnapshotImport) error {
|
||||||
@ -175,44 +225,6 @@ func loadOs() string {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func loadSnapSize(records []model.Snapshot) ([]dto.SnapshotInfo, error) {
|
|
||||||
var datas []dto.SnapshotInfo
|
|
||||||
clientMap := make(map[uint]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].DownloadAccountID]; !ok {
|
|
||||||
backup, client, err := NewBackupClientWithID(records[i].DownloadAccountID)
|
|
||||||
if err != nil {
|
|
||||||
global.LOG.Errorf("load backup client from db failed, err: %v", err)
|
|
||||||
clientMap[records[i].DownloadAccountID] = loadSizeHelper{}
|
|
||||||
datas = append(datas, item)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
item.Size, _ = client.Size(path.Join(strings.TrimLeft(backup.BackupPath, "/"), itemPath))
|
|
||||||
datas = append(datas, item)
|
|
||||||
clientMap[records[i].DownloadAccountID] = loadSizeHelper{backupPath: strings.TrimLeft(backup.BackupPath, "/"), client: client, isOk: true}
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if clientMap[records[i].DownloadAccountID].isOk {
|
|
||||||
wg.Add(1)
|
|
||||||
go func(index int) {
|
|
||||||
item.Size, _ = clientMap[records[index].DownloadAccountID].client.Size(path.Join(clientMap[records[index].DownloadAccountID].backupPath, itemPath))
|
|
||||||
datas = append(datas, item)
|
|
||||||
wg.Done()
|
|
||||||
}(i)
|
|
||||||
} else {
|
|
||||||
datas = append(datas, item)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
wg.Wait()
|
|
||||||
return datas, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func loadApps(fileOp fileUtils.FileOp) ([]dto.DataTree, error) {
|
func loadApps(fileOp fileUtils.FileOp) ([]dto.DataTree, error) {
|
||||||
var data []dto.DataTree
|
var data []dto.DataTree
|
||||||
apps, err := appInstallRepo.ListBy()
|
apps, err := appInstallRepo.ListBy()
|
||||||
|
@ -19,6 +19,7 @@ func (s *SettingRouter) InitRouter(Router *gin.RouterGroup) {
|
|||||||
settingRouter.POST("/snapshot", baseApi.CreateSnapshot)
|
settingRouter.POST("/snapshot", baseApi.CreateSnapshot)
|
||||||
settingRouter.POST("/snapshot/recreate", baseApi.RecreateSnapshot)
|
settingRouter.POST("/snapshot/recreate", baseApi.RecreateSnapshot)
|
||||||
settingRouter.POST("/snapshot/search", baseApi.SearchSnapshot)
|
settingRouter.POST("/snapshot/search", baseApi.SearchSnapshot)
|
||||||
|
settingRouter.POST("/snapshot/size", baseApi.LoadSnapshotSize)
|
||||||
settingRouter.POST("/snapshot/import", baseApi.ImportSnapshot)
|
settingRouter.POST("/snapshot/import", baseApi.ImportSnapshot)
|
||||||
settingRouter.POST("/snapshot/del", baseApi.DeleteSnapshot)
|
settingRouter.POST("/snapshot/del", baseApi.DeleteSnapshot)
|
||||||
settingRouter.POST("/snapshot/recover", baseApi.RecoverSnapshot)
|
settingRouter.POST("/snapshot/recover", baseApi.RecoverSnapshot)
|
||||||
|
@ -166,6 +166,13 @@ export namespace Setting {
|
|||||||
rollbackStatus: string;
|
rollbackStatus: string;
|
||||||
rollbackMessage: string;
|
rollbackMessage: string;
|
||||||
}
|
}
|
||||||
|
export interface SnapshotFile {
|
||||||
|
id: number;
|
||||||
|
name: string;
|
||||||
|
from: string;
|
||||||
|
defaultDownload: string;
|
||||||
|
size: number;
|
||||||
|
}
|
||||||
export interface SnapshotData {
|
export interface SnapshotData {
|
||||||
appData: Array<DataTree>;
|
appData: Array<DataTree>;
|
||||||
panelData: Array<DataTree>;
|
panelData: Array<DataTree>;
|
||||||
|
@ -124,6 +124,9 @@ export const snapshotRollback = (param: Setting.SnapshotRecover) => {
|
|||||||
export const searchSnapshotPage = (param: SearchWithPage) => {
|
export const searchSnapshotPage = (param: SearchWithPage) => {
|
||||||
return http.post<ResPage<Setting.SnapshotInfo>>(`/settings/snapshot/search`, param);
|
return http.post<ResPage<Setting.SnapshotInfo>>(`/settings/snapshot/search`, param);
|
||||||
};
|
};
|
||||||
|
export const loadSnapshotSize = (param: SearchWithPage) => {
|
||||||
|
return http.post<Array<Setting.SnapshotFile>>(`/settings/snapshot/size`, param);
|
||||||
|
};
|
||||||
|
|
||||||
// upgrade
|
// upgrade
|
||||||
export const loadUpgradeInfo = () => {
|
export const loadUpgradeInfo = () => {
|
||||||
|
@ -38,11 +38,12 @@
|
|||||||
<el-table-column prop="version" :label="$t('app.version')" />
|
<el-table-column prop="version" :label="$t('app.version')" />
|
||||||
<el-table-column :label="$t('setting.backupAccount')" min-width="80" prop="from">
|
<el-table-column :label="$t('setting.backupAccount')" min-width="80" prop="from">
|
||||||
<template #default="{ row }">
|
<template #default="{ row }">
|
||||||
|
<div v-if="row.hasLoad">
|
||||||
<div v-for="(item, index) of row.from.split(',')" :key="index" class="mt-1">
|
<div v-for="(item, index) of row.from.split(',')" :key="index" class="mt-1">
|
||||||
<div v-if="row.expand || (!row.expand && index < 3)">
|
<div v-if="row.expand || (!row.expand && index < 3)">
|
||||||
<span v-if="row.from" type="info">
|
<span v-if="row.from" type="info">
|
||||||
<span>
|
<span>
|
||||||
{{ $t('setting.' + item) }}
|
{{ loadName(item) }}
|
||||||
</span>
|
</span>
|
||||||
<el-icon
|
<el-icon
|
||||||
v-if="item === row.defaultDownload"
|
v-if="item === row.defaultDownload"
|
||||||
@ -65,14 +66,23 @@
|
|||||||
{{ $t('commons.button.collapse') }}
|
{{ $t('commons.button.collapse') }}
|
||||||
</el-button>
|
</el-button>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
<div v-if="!row.hasLoad">
|
||||||
|
<el-button link loading></el-button>
|
||||||
|
</div>
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
<el-table-column :label="$t('file.size')" prop="size" min-width="60" show-overflow-tooltip>
|
<el-table-column :label="$t('file.size')" prop="size" min-width="60" show-overflow-tooltip>
|
||||||
<template #default="{ row }">
|
<template #default="{ row }">
|
||||||
|
<div v-if="row.hasLoad">
|
||||||
<span v-if="row.size">
|
<span v-if="row.size">
|
||||||
{{ computeSize(row.size) }}
|
{{ computeSize(row.size) }}
|
||||||
</span>
|
</span>
|
||||||
<span v-else>-</span>
|
<span v-else>-</span>
|
||||||
|
</div>
|
||||||
|
<div v-if="!row.hasLoad">
|
||||||
|
<el-button link loading></el-button>
|
||||||
|
</div>
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
<el-table-column :label="$t('commons.table.status')" min-width="80" prop="status">
|
<el-table-column :label="$t('commons.table.status')" min-width="80" prop="status">
|
||||||
@ -168,6 +178,7 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import {
|
import {
|
||||||
searchSnapshotPage,
|
searchSnapshotPage,
|
||||||
|
loadSnapshotSize,
|
||||||
snapshotDelete,
|
snapshotDelete,
|
||||||
snapshotRecreate,
|
snapshotRecreate,
|
||||||
snapshotRollback,
|
snapshotRollback,
|
||||||
@ -371,6 +382,7 @@ const search = async () => {
|
|||||||
await searchSnapshotPage(params)
|
await searchSnapshotPage(params)
|
||||||
.then((res) => {
|
.then((res) => {
|
||||||
loading.value = false;
|
loading.value = false;
|
||||||
|
loadSize();
|
||||||
cleanData.value = false;
|
cleanData.value = false;
|
||||||
data.value = res.data.items || [];
|
data.value = res.data.items || [];
|
||||||
paginationConfig.total = res.data.total;
|
paginationConfig.total = res.data.total;
|
||||||
@ -380,6 +392,40 @@ const search = async () => {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const loadSize = async () => {
|
||||||
|
let params = {
|
||||||
|
info: searchName.value,
|
||||||
|
page: paginationConfig.currentPage,
|
||||||
|
pageSize: paginationConfig.pageSize,
|
||||||
|
};
|
||||||
|
await loadSnapshotSize(params)
|
||||||
|
.then((res) => {
|
||||||
|
let stats = res.data || [];
|
||||||
|
if (stats.length === 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
for (const snap of data.value) {
|
||||||
|
for (const item of stats) {
|
||||||
|
if (snap.id === item.id) {
|
||||||
|
snap.hasLoad = true;
|
||||||
|
snap.from = item.from;
|
||||||
|
snap.defaultDownload = item.defaultDownload;
|
||||||
|
snap.size = item.size;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch(() => {
|
||||||
|
loading.value = false;
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const loadName = (from: any) => {
|
||||||
|
let items = from.split(' - ');
|
||||||
|
return i18n.global.t('setting.' + items[0]) + ' ' + items[1];
|
||||||
|
};
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
search();
|
search();
|
||||||
});
|
});
|
||||||
|
Loading…
x
Reference in New Issue
Block a user