feat: 增加应用更新功能

This commit is contained in:
zhengkunwang223 2022-10-13 18:56:53 +08:00 committed by zhengkunwang223
parent d7fc670136
commit 226a81ecf3
12 changed files with 167 additions and 25 deletions

View file

@ -159,3 +159,24 @@ func (b *BaseApi) GetServices(c *gin.Context) {
helper.SuccessWithData(c, services)
}
func (b *BaseApi) GetUpdateVersions(c *gin.Context) {
appInstallId := c.Param("appInstallId")
if appInstallId == "" {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, nil)
return
}
id, err := strconv.Atoi(appInstallId)
if err != nil {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
return
}
versions, err := appService.GetUpdateVersions(uint(id))
if err != nil {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
return
}
helper.SuccessWithData(c, versions)
}

View file

@ -111,11 +111,13 @@ var (
Sync AppOperate = "sync"
Backup AppOperate = "backup"
Restore AppOperate = "restore"
Update AppOperate = "update"
)
type AppInstallOperate struct {
InstallId uint `json:"installId" validate:"required"`
BackupId uint `json:"backupId"`
DetailId uint `json:"detailId"`
Operate AppOperate `json:"operate" validate:"required"`
}
@ -140,3 +142,8 @@ type ContainerExec struct {
DbParam AppDatabase `json:"dbParam"`
Auth AuthParam `json:"auth"`
}
type AppVersion struct {
Version string `json:"version"`
DetailId uint `json:"detailId"`
}

View file

@ -4,7 +4,7 @@ type AppInstallBackup struct {
BaseModel
Name string `gorm:"type:varchar(64);not null" json:"name"`
Path string `gorm:"type:varchar(64);not null" json:"path"`
Param string `json:"param" gorm:"type:longtext;"`
AppDetailId uint `gorm:"type:varchar(64);not null" json:"app_detail_id"`
Param string `gorm:"type:longtext;" json:"param"`
AppDetailId uint `gorm:"type:integer;not null" json:"app_detail_id"`
AppInstallId uint `gorm:"type:integer;not null" json:"app_install_id"`
}

View file

@ -218,6 +218,8 @@ func (a AppService) OperateInstall(req dto.AppInstallOperate) error {
return err
}
return restoreInstall(install, installBackup)
case dto.Update:
return updateInstall(install.ID, req.DetailId)
default:
return errors.New("operate not support")
}
@ -352,25 +354,6 @@ func (a AppService) DeleteBackup(req dto.AppBackupDeleteRequest) error {
return nil
}
func (a AppService) GetServices(key string) ([]dto.AppService, error) {
app, err := appRepo.GetFirst(appRepo.WithKey(key))
if err != nil {
return nil, err
}
installs, err := appInstallRepo.GetBy(appInstallRepo.WithAppId(app.ID), appInstallRepo.WithStatus(constant.Running))
if err != nil {
return nil, err
}
var res []dto.AppService
for _, install := range installs {
res = append(res, dto.AppService{
Label: install.Name,
Value: install.ServiceName,
})
}
return res, nil
}
func (a AppService) SyncInstalled(installId uint) error {
appInstall, err := appInstallRepo.GetFirst(commonRepo.WithByID(installId))
if err != nil {
@ -686,3 +669,44 @@ func syncCanUpdate() {
}
}
func (a AppService) GetServices(key string) ([]dto.AppService, error) {
app, err := appRepo.GetFirst(appRepo.WithKey(key))
if err != nil {
return nil, err
}
installs, err := appInstallRepo.GetBy(appInstallRepo.WithAppId(app.ID), appInstallRepo.WithStatus(constant.Running))
if err != nil {
return nil, err
}
var res []dto.AppService
for _, install := range installs {
res = append(res, dto.AppService{
Label: install.Name,
Value: install.ServiceName,
})
}
return res, nil
}
func (a AppService) GetUpdateVersions(installId uint) ([]dto.AppVersion, error) {
install, err := appInstallRepo.GetFirst(commonRepo.WithByID(installId))
var versions []dto.AppVersion
if err != nil {
return versions, err
}
app, err := appRepo.GetFirst(commonRepo.WithByID(install.AppId))
if err != nil {
return versions, err
}
details, err := appDetailRepo.GetBy(appDetailRepo.WithAppId(app.ID))
for _, detail := range details {
if common.CompareVersion(detail.Version, install.Version) {
versions = append(versions, dto.AppVersion{
Version: detail.Version,
DetailId: detail.ID,
})
}
}
return versions, nil
}

View file

@ -19,6 +19,7 @@ import (
"path"
"reflect"
"strconv"
"strings"
"time"
)
@ -162,6 +163,38 @@ func deleteLink(ctx context.Context, install *model.AppInstall) error {
return appInstallResourceRepo.DeleteBy(ctx, appInstallResourceRepo.WithAppInstallId(install.ID))
}
func updateInstall(installId uint, detailId uint) error {
install, err := appInstallRepo.GetFirst(commonRepo.WithByID(installId))
if err != nil {
return err
}
detail, err := appDetailRepo.GetFirst(commonRepo.WithByID(detailId))
if err != nil {
return err
}
if install.Version == detail.Version {
return errors.New("two version is save")
}
tx, ctx := getTxAndContext()
if err := backupInstall(ctx, install); err != nil {
return err
}
tx.Commit()
if _, err = compose.Down(install.GetComposePath()); err != nil {
return err
}
install.DockerCompose = detail.DockerCompose
install.Version = detail.Version
fileOp := files.NewFileOp()
if err := fileOp.WriteFile(install.GetComposePath(), strings.NewReader(install.DockerCompose), 0775); err != nil {
return err
}
if _, err = compose.Up(install.GetComposePath()); err != nil {
return err
}
return appInstallRepo.Save(&install)
}
func backupInstall(ctx context.Context, install model.AppInstall) error {
var backup model.AppInstallBackup
appPath := install.GetPath()
@ -251,6 +284,7 @@ func restoreInstall(install model.AppInstall, backup model.AppInstallBackup) err
if out, err := compose.Up(install.GetComposePath()); err != nil {
return handleErr(install, err, out)
}
install.AppDetailId = backup.AppDetailId
install.Status = constant.Running
return appInstallRepo.Save(&install)
}

View file

@ -20,6 +20,7 @@ func (a *AppRouter) InitAppRouter(Router *gin.RouterGroup) {
appRouter.GET("/:id", baseApi.GetApp)
appRouter.GET("/detail/:appid/:version", baseApi.GetAppDetail)
appRouter.POST("/install", baseApi.InstallApp)
appRouter.GET("/installed/:appInstallId/versions", baseApi.GetUpdateVersions)
appRouter.POST("/installed", baseApi.SearchInstalled)
appRouter.POST("/installed/op", baseApi.OperateInstalled)
appRouter.POST("/installed/sync", baseApi.SyncInstalled)

View file

@ -13,6 +13,9 @@ import (
)
func CompareVersion(version1 string, version2 string) bool {
if version1 == version2 {
return false
}
version1s := strings.Split(version1, ".")
version2s := strings.Split(version2, ".")

View file

@ -79,6 +79,7 @@ export namespace App {
installId: number;
operate: string;
backupId?: number;
detailId?: number;
}
export interface AppService {
@ -100,4 +101,9 @@ export namespace App {
appInstallId: string;
appDetail: AppDetail;
}
export interface VersionDetail {
version: string;
detailId: number;
}
}

View file

@ -45,3 +45,7 @@ export const GetAppBackups = (info: App.AppBackupReq) => {
export const DelAppBackups = (req: App.AppBackupDelReq) => {
return http.post<any>('apps/installed/backups/del', req);
};
export const GetAppUpdateVersions = (id: number) => {
return http.get<any>(`apps/installed/${id}/versions`);
};

View file

@ -425,5 +425,7 @@ export default {
restore: 'Restore',
restoreWarn:
'The rollback operation will restart the application and replace the data. This operation cannot be rolled back. Do you want to continue?',
update: 'Update',
versioneSelect: 'Please Select version',
},
};

View file

@ -416,5 +416,7 @@ export default {
backupdate: '备份时间',
restore: '回滚',
restoreWarn: '回滚操作会重启应用,并替换数据,此操作不可回滚,是否继续?',
update: '更新',
versioneSelect: '请选择版本',
},
};

View file

@ -51,11 +51,30 @@
/>
</ComplexTable>
<el-dialog v-model="open" :title="$t('commons.msg.operate')" :before-close="handleClose" width="30%">
<el-alert :title="getMsg(operateReq.operate)" type="warning" :closable="false" show-icon />
<el-alert
v-if="operateReq.operate != 'update'"
:title="getMsg(operateReq.operate)"
type="warning"
:closable="false"
show-icon
/>
<div v-else style="text-align: center">
<p>{{ $t('app.versioneSelect') }}</p>
<el-select v-model="operateReq.detailId">
<el-option
v-for="(version, index) in versions"
:key="index"
:value="version.detailId"
:label="version.version"
></el-option>
</el-select>
</div>
<template #footer>
<span class="dialog-footer">
<el-button @click="handleClose">{{ $t('commons.button.cancel') }}</el-button>
<el-button type="primary" @click="operate">{{ $t('commons.button.confirm') }}</el-button>
<el-button type="primary" @click="operate" :disabled="versions == null">
{{ $t('commons.button.confirm') }}
</el-button>
</span>
</template>
</el-dialog>
@ -63,13 +82,14 @@
</template>
<script lang="ts" setup>
import { GetAppInstalled, InstalledOp, SyncInstalledApp } from '@/api/modules/app';
import { GetAppInstalled, InstalledOp, SyncInstalledApp, GetAppUpdateVersions } from '@/api/modules/app';
import { onMounted, reactive, ref } from 'vue';
import ComplexTable from '@/components/complex-table/index.vue';
import { dateFromat } from '@/utils/util';
import i18n from '@/lang';
import { ElMessage } from 'element-plus';
import Backups from './backups.vue';
import { App } from '@/api/interface/app';
let data = ref<any>();
let loading = ref(false);
@ -82,7 +102,9 @@ let open = ref(false);
let operateReq = reactive({
installId: 0,
operate: '',
detailId: 0,
});
let versions = ref<App.VersionDetail[]>();
const backupRef = ref();
const sync = () => {
@ -112,7 +134,17 @@ const search = () => {
const openOperate = (row: any, op: string) => {
operateReq.installId = row.id;
operateReq.operate = op;
open.value = true;
if (op == 'update') {
GetAppUpdateVersions(row.id).then((res) => {
versions.value = res.data;
if (res.data != null && res.data.length > 0) {
operateReq.detailId = res.data[0].detailId;
}
open.value = true;
});
} else {
open.value = true;
}
};
const operate = async () => {
@ -162,6 +194,12 @@ const buttons = [
openOperate(row, 'sync');
},
},
{
label: i18n.global.t('app.update'),
click: (row: any) => {
openOperate(row, 'update');
},
},
{
label: i18n.global.t('app.restart'),
click: (row: any) => {