Browse Source

feat: 增加应用忽略升级功能 (#1515)

pull/1516/head
zhengkunwang223 1 year ago committed by GitHub
parent
commit
695d4b4a16
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 22
      backend/app/api/v1/app_install.go
  2. 5
      backend/app/dto/request/app.go
  3. 1
      backend/app/model/app_detail.go
  4. 13
      backend/app/service/app_install.go
  5. 5
      backend/app/service/app_utils.go
  6. 1
      backend/init/migration/migrate.go
  7. 13
      backend/init/migration/migrations/init.go
  8. 1
      backend/router/ro_app.go
  9. 114
      cmd/server/docs/docs.go
  10. 114
      cmd/server/docs/swagger.json
  11. 74
      cmd/server/docs/swagger.yaml
  12. 4
      frontend/src/api/modules/app.ts
  13. 1
      frontend/src/lang/modules/en.ts
  14. 1
      frontend/src/lang/modules/tw.ts
  15. 1
      frontend/src/lang/modules/zh.ts
  16. 15
      frontend/src/views/app-store/installed/index.vue
  17. 53
      frontend/src/views/app-store/installed/upgrade/index.vue

22
backend/app/api/v1/app_install.go

@ -305,3 +305,25 @@ func (b *BaseApi) UpdateInstalled(c *gin.Context) {
}
helper.SuccessWithData(c, nil)
}
// @Tags App
// @Summary ignore App Update
// @Description 忽略应用升级版本
// @Accept json
// @Param request body request.AppInstalledIgnoreUpgrade true "request"
// @Success 200
// @Security ApiKeyAuth
// @Router /apps/installed/ignore [post]
// @x-panel-log {"bodyKeys":["installId"],"paramKeys":[],"BeforeFuntions":[],"formatZH":"忽略应用 [installId] 版本升级","formatEN":"Application param update [installId]"}
func (b *BaseApi) IgnoreUpgrade(c *gin.Context) {
var req request.AppInstalledIgnoreUpgrade
if err := c.ShouldBindJSON(&req); err != nil {
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
return
}
if err := appInstallService.IgnoreUpgrade(req); err != nil {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
return
}
helper.SuccessWithOutData(c)
}

5
backend/app/dto/request/app.go

@ -66,6 +66,11 @@ type AppInstalledUpdate struct {
AppContainerConfig
}
type AppInstalledIgnoreUpgrade struct {
InstallId uint `json:"installId" validate:"required"`
DetailId uint `json:"detailId" validate:"required"`
}
type PortUpdate struct {
Key string `json:"key"`
Name string `json:"name"`

1
backend/app/model/app_detail.go

@ -12,4 +12,5 @@ type AppDetail struct {
DownloadUrl string `json:"downloadUrl" gorm:"type:varchar;"`
DownloadCallBackUrl string `json:"downloadCallBackUrl" gorm:"type:longtext;"`
Update bool `json:"update"`
IgnoreUpgrade bool `json:"ignoreUpgrade"`
}

13
backend/app/service/app_install.go

@ -45,6 +45,7 @@ type IAppInstallService interface {
SearchForWebsite(req request.AppInstalledSearch) ([]response.AppInstalledDTO, error)
Operate(req request.AppInstalledOperate) error
Update(req request.AppInstalledUpdate) error
IgnoreUpgrade(req request.AppInstalledIgnoreUpgrade) error
SyncAll(systemInit bool) error
GetServices(key string) ([]response.AppService, error)
GetUpdateVersions(installId uint) ([]dto.AppVersion, error)
@ -352,6 +353,15 @@ func (a *AppInstallService) Update(req request.AppInstalledUpdate) error {
return nil
}
func (a *AppInstallService) IgnoreUpgrade(req request.AppInstalledIgnoreUpgrade) error {
appDetail, err := appDetailRepo.GetFirst(commonRepo.WithByID(req.DetailId))
if err != nil {
return err
}
appDetail.IgnoreUpgrade = true
return appDetailRepo.Update(context.Background(), appDetail)
}
func (a *AppInstallService) SyncAll(systemInit bool) error {
allList, err := appInstallRepo.ListBy()
if err != nil {
@ -412,6 +422,9 @@ func (a *AppInstallService) GetUpdateVersions(installId uint) ([]dto.AppVersion,
return versions, err
}
for _, detail := range details {
if detail.IgnoreUpgrade {
continue
}
if common.IsCrossVersion(install.Version, detail.Version) && !app.CrossVersionUpdate {
continue
}

5
backend/app/service/app_utils.go

@ -751,7 +751,7 @@ func handleErr(install model.AppInstall, err error, out string) error {
func handleInstalled(appInstallList []model.AppInstall, updated bool) ([]response.AppInstalledDTO, error) {
var res []response.AppInstalledDTO
for _, installed := range appInstallList {
if updated && installed.App.Type == "php" {
if updated && installed.App.Type == "php" || installed.Status == constant.Installing {
continue
}
installDTO := response.AppInstalledDTO{
@ -768,6 +768,9 @@ func handleInstalled(appInstallList []model.AppInstall, updated bool) ([]respons
}
var versions []string
for _, detail := range details {
if detail.IgnoreUpgrade {
continue
}
if common.IsCrossVersion(installed.Version, detail.Version) && !app.CrossVersionUpdate {
continue
}

1
backend/init/migration/migrate.go

@ -33,6 +33,7 @@ func Init() {
migrations.UpdateWebsite,
migrations.AddBackupAccountDir,
migrations.AddMfaInterval,
migrations.UpdateAppDetail,
})
if err := m.Migrate(); err != nil {
global.LOG.Error(err)

13
backend/init/migration/migrations/init.go

@ -413,3 +413,16 @@ var AddMfaInterval = &gormigrate.Migration{
return nil
},
}
var UpdateAppDetail = &gormigrate.Migration{
ID: "20230704-update-app-detail",
Migrate: func(tx *gorm.DB) error {
if err := tx.AutoMigrate(&model.AppDetail{}); err != nil {
return err
}
if err := tx.Model(&model.AppDetail{}).Where("1 = 1").Update("ignore_upgrade", "0").Error; err != nil {
return err
}
return nil
},
}

1
backend/router/ro_app.go

@ -36,5 +36,6 @@ func (a *AppRouter) InitAppRouter(Router *gin.RouterGroup) {
appRouter.GET("/installed/conf/:key", baseApi.GetDefaultConfig)
appRouter.GET("/installed/params/:appInstallId", baseApi.GetParams)
appRouter.POST("/installed/params/update", baseApi.UpdateInstalled)
appRouter.POST("/installed/ignore", baseApi.IgnoreUpgrade)
}
}

114
cmd/server/docs/docs.go

@ -387,6 +387,48 @@ const docTemplate = `{
}
}
},
"/apps/installed/ignore": {
"post": {
"security": [
{
"ApiKeyAuth": []
}
],
"description": "忽略应用升级版本",
"consumes": [
"application/json"
],
"tags": [
"App"
],
"summary": "ignore App Update",
"parameters": [
{
"description": "request",
"name": "request",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/request.AppInstalledIgnoreUpgrade"
}
}
],
"responses": {
"200": {
"description": "OK"
}
},
"x-panel-log": {
"BeforeFuntions": [],
"bodyKeys": [
"installId"
],
"formatEN": "Application param update [installId]",
"formatZH": "忽略应用 [installId] 版本升级",
"paramKeys": []
}
}
},
"/apps/installed/loadport/:key": {
"get": {
"security": [
@ -1865,6 +1907,31 @@ const docTemplate = `{
}
}
},
"/containers/list": {
"post": {
"security": [
{
"ApiKeyAuth": []
}
],
"description": "获取容器名称",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"Container"
],
"summary": "List containers",
"responses": {
"200": {
"description": "OK"
}
}
}
},
"/containers/network": {
"post": {
"security": [
@ -9632,7 +9699,7 @@ const docTemplate = `{
"ApiKeyAuth": []
}
],
"description": "更新 php 配置",
"description": "更新 php 配置文件",
"consumes": [
"application/json"
],
@ -10967,6 +11034,9 @@ const docTemplate = `{
"type"
],
"properties": {
"containerName": {
"type": "string"
},
"day": {
"type": "integer"
},
@ -11046,6 +11116,9 @@ const docTemplate = `{
"specType"
],
"properties": {
"containerName": {
"type": "string"
},
"day": {
"type": "integer"
},
@ -12265,6 +12338,12 @@ const docTemplate = `{
"name": {
"type": "string"
},
"order": {
"type": "string"
},
"orderBy": {
"type": "string"
},
"page": {
"type": "integer"
},
@ -12819,6 +12898,12 @@ const docTemplate = `{
"info": {
"type": "string"
},
"order": {
"type": "string"
},
"orderBy": {
"type": "string"
},
"page": {
"type": "integer"
},
@ -12911,6 +12996,9 @@ const docTemplate = `{
"sslType": {
"type": "string"
},
"systemIP": {
"type": "string"
},
"systemVersion": {
"type": "string"
},
@ -13562,6 +13650,21 @@ const docTemplate = `{
}
}
},
"request.AppInstalledIgnoreUpgrade": {
"type": "object",
"required": [
"detailId",
"installId"
],
"properties": {
"detailId": {
"type": "integer"
},
"installId": {
"type": "integer"
}
}
},
"request.AppInstalledOperate": {
"type": "object",
"required": [
@ -14863,6 +14966,12 @@ const docTemplate = `{
"name": {
"type": "string"
},
"order": {
"type": "string"
},
"orderBy": {
"type": "string"
},
"page": {
"type": "integer"
},
@ -15078,6 +15187,9 @@ const docTemplate = `{
"id": {
"type": "integer"
},
"ignoreUpgrade": {
"type": "boolean"
},
"image": {
"type": "string"
},

114
cmd/server/docs/swagger.json

@ -380,6 +380,48 @@
}
}
},
"/apps/installed/ignore": {
"post": {
"security": [
{
"ApiKeyAuth": []
}
],
"description": "忽略应用升级版本",
"consumes": [
"application/json"
],
"tags": [
"App"
],
"summary": "ignore App Update",
"parameters": [
{
"description": "request",
"name": "request",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/request.AppInstalledIgnoreUpgrade"
}
}
],
"responses": {
"200": {
"description": "OK"
}
},
"x-panel-log": {
"BeforeFuntions": [],
"bodyKeys": [
"installId"
],
"formatEN": "Application param update [installId]",
"formatZH": "忽略应用 [installId] 版本升级",
"paramKeys": []
}
}
},
"/apps/installed/loadport/:key": {
"get": {
"security": [
@ -1858,6 +1900,31 @@
}
}
},
"/containers/list": {
"post": {
"security": [
{
"ApiKeyAuth": []
}
],
"description": "获取容器名称",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"Container"
],
"summary": "List containers",
"responses": {
"200": {
"description": "OK"
}
}
}
},
"/containers/network": {
"post": {
"security": [
@ -9625,7 +9692,7 @@
"ApiKeyAuth": []
}
],
"description": "更新 php 配置",
"description": "更新 php 配置文件",
"consumes": [
"application/json"
],
@ -10960,6 +11027,9 @@
"type"
],
"properties": {
"containerName": {
"type": "string"
},
"day": {
"type": "integer"
},
@ -11039,6 +11109,9 @@
"specType"
],
"properties": {
"containerName": {
"type": "string"
},
"day": {
"type": "integer"
},
@ -12258,6 +12331,12 @@
"name": {
"type": "string"
},
"order": {
"type": "string"
},
"orderBy": {
"type": "string"
},
"page": {
"type": "integer"
},
@ -12812,6 +12891,12 @@
"info": {
"type": "string"
},
"order": {
"type": "string"
},
"orderBy": {
"type": "string"
},
"page": {
"type": "integer"
},
@ -12904,6 +12989,9 @@
"sslType": {
"type": "string"
},
"systemIP": {
"type": "string"
},
"systemVersion": {
"type": "string"
},
@ -13555,6 +13643,21 @@
}
}
},
"request.AppInstalledIgnoreUpgrade": {
"type": "object",
"required": [
"detailId",
"installId"
],
"properties": {
"detailId": {
"type": "integer"
},
"installId": {
"type": "integer"
}
}
},
"request.AppInstalledOperate": {
"type": "object",
"required": [
@ -14856,6 +14959,12 @@
"name": {
"type": "string"
},
"order": {
"type": "string"
},
"orderBy": {
"type": "string"
},
"page": {
"type": "integer"
},
@ -15071,6 +15180,9 @@
"id": {
"type": "integer"
},
"ignoreUpgrade": {
"type": "boolean"
},
"image": {
"type": "string"
},

74
cmd/server/docs/swagger.yaml

@ -385,6 +385,8 @@ definitions:
type: object
dto.CronjobCreate:
properties:
containerName:
type: string
day:
type: integer
dbName:
@ -439,6 +441,8 @@ definitions:
type: object
dto.CronjobUpdate:
properties:
containerName:
type: string
day:
type: integer
dbName:
@ -1258,6 +1262,10 @@ definitions:
type: string
name:
type: string
order:
type: string
orderBy:
type: string
page:
type: integer
pageSize:
@ -1627,6 +1635,10 @@ definitions:
properties:
info:
type: string
order:
type: string
orderBy:
type: string
page:
type: integer
pageSize:
@ -1691,6 +1703,8 @@ definitions:
type: string
sslType:
type: string
systemIP:
type: string
systemVersion:
type: string
theme:
@ -2121,6 +2135,16 @@ definitions:
- appDetailId
- name
type: object
request.AppInstalledIgnoreUpgrade:
properties:
detailId:
type: integer
installId:
type: integer
required:
- detailId
- installId
type: object
request.AppInstalledOperate:
properties:
backupId:
@ -2993,6 +3017,10 @@ definitions:
properties:
name:
type: string
order:
type: string
orderBy:
type: string
page:
type: integer
pageSize:
@ -3140,6 +3168,8 @@ definitions:
type: boolean
id:
type: integer
ignoreUpgrade:
type: boolean
image:
type: string
lastModified:
@ -3644,6 +3674,33 @@ paths:
summary: Check before delete
tags:
- App
/apps/installed/ignore:
post:
consumes:
- application/json
description: 忽略应用升级版本
parameters:
- description: request
in: body
name: request
required: true
schema:
$ref: '#/definitions/request.AppInstalledIgnoreUpgrade'
responses:
"200":
description: OK
security:
- ApiKeyAuth: []
summary: ignore App Update
tags:
- App
x-panel-log:
BeforeFuntions: []
bodyKeys:
- installId
formatEN: Application param update [installId]
formatZH: 忽略应用 [installId] 版本升级
paramKeys: []
/apps/installed/loadport/:key:
get:
consumes:
@ -4586,6 +4643,21 @@ paths:
security:
- ApiKeyAuth: []
summary: Load container limis
/containers/list:
post:
consumes:
- application/json
description: 获取容器名称
produces:
- application/json
responses:
"200":
description: OK
security:
- ApiKeyAuth: []
summary: List containers
tags:
- Container
/containers/network:
post:
consumes:
@ -9521,7 +9593,7 @@ paths:
post:
consumes:
- application/json
description: 更新 php 配置
description: 更新 php 配置文件
parameters:
- description: request
in: body

4
frontend/src/api/modules/app.ts

@ -89,3 +89,7 @@ export const GetAppInstallParams = (id: number) => {
export const UpdateAppInstallParams = (req: any) => {
return http.post<any>(`apps/installed/params/update`, req);
};
export const IgnoreUpgrade = (req: any) => {
return http.post<any>(`apps/installed/ignore`, req);
};

1
frontend/src/lang/modules/en.ts

@ -44,6 +44,7 @@ const message = {
refresh: 'Refresh',
get: 'Get',
upgrade: 'Upgrade',
ignoreUpgrade: 'Ignore upgrade',
},
search: {
timeStart: 'Time start',

1
frontend/src/lang/modules/tw.ts

@ -44,6 +44,7 @@ const message = {
refresh: '刷新',
get: '獲取',
upgrade: '升級',
ignoreUpgrade: '忽略升級',
},
search: {
timeStart: '開始時間',

1
frontend/src/lang/modules/zh.ts

@ -44,6 +44,7 @@ const message = {
refresh: '刷新',
get: '获取',
upgrade: '升级',
ignoreUpgrade: '忽略升级',
},
search: {
timeStart: '开始时间',

15
frontend/src/views/app-store/installed/index.vue

@ -158,6 +158,17 @@
>
{{ $t('commons.button.backup') }}
</el-button>
<el-button
class="h-button"
type="primary"
plain
round
size="small"
@click="openOperate(installed, 'ignoreUpgrade')"
v-if="mode === 'upgrade'"
>
{{ $t('commons.button.ignoreUpgrade') }}
</el-button>
<el-button
class="h-button"
type="primary"
@ -322,8 +333,8 @@ const search = () => {
const openOperate = (row: any, op: string) => {
operateReq.installId = row.id;
operateReq.operate = op;
if (op == 'upgrade') {
upgradeRef.value.acceptParams(row.id, row.name);
if (op == 'upgrade' || op == 'ignoreUpgrade') {
upgradeRef.value.acceptParams(row.id, row.name, op);
} else if (op == 'delete') {
AppInstalledDeleteCheck(row.id).then(async (res) => {
const items = res.data;

53
frontend/src/views/app-store/installed/upgrade/index.vue

@ -1,7 +1,11 @@
<template>
<el-drawer :close-on-click-modal="false" v-model="open" size="30%">
<template #header>
<Header :header="$t('commons.button.upgrade')" :resource="resourceName" :back="handleClose"></Header>
<Header
:header="$t('commons.button.' + operateReq.operate)"
:resource="resourceName"
:back="handleClose"
></Header>
</template>
<el-row>
<el-col :span="22" :offset="1">
@ -38,7 +42,7 @@
</template>
<script lang="ts" setup>
import { App } from '@/api/interface/app';
import { GetAppUpdateVersions, InstalledOp } from '@/api/modules/app';
import { GetAppUpdateVersions, IgnoreUpgrade, InstalledOp } from '@/api/modules/app';
import i18n from '@/lang';
import { ElMessageBox, FormInstance } from 'element-plus';
import { reactive, ref, onBeforeUnmount } from 'vue';
@ -48,16 +52,16 @@ import { Rules } from '@/global/form-rules';
import bus from '../../bus';
const updateRef = ref<FormInstance>();
let open = ref(false);
let loading = ref(false);
let versions = ref<App.VersionDetail[]>();
let operateReq = reactive({
const open = ref(false);
const loading = ref(false);
const versions = ref<App.VersionDetail[]>();
const operateReq = reactive({
detailId: 0,
operate: 'upgrade',
installId: 0,
});
const resourceName = ref('');
let rules = ref<any>({
const rules = ref<any>({
detailId: [Rules.requiredSelect],
});
@ -67,8 +71,9 @@ const handleClose = () => {
em('close', open);
};
const acceptParams = (id: number, name: string) => {
const acceptParams = (id: number, name: string, op: string) => {
operateReq.installId = id;
operateReq.operate = op;
resourceName.value = name;
GetAppUpdateVersions(id).then((res) => {
versions.value = res.data;
@ -81,20 +86,32 @@ const acceptParams = (id: number, name: string) => {
const operate = async () => {
loading.value = true;
await InstalledOp(operateReq)
.then(() => {
MsgSuccess(i18n.global.t('app.upgradeStart'));
bus.emit('upgrade', true);
handleClose();
})
.finally(() => {
loading.value = false;
});
if (operateReq.operate === 'upgrade') {
await InstalledOp(operateReq)
.then(() => {
MsgSuccess(i18n.global.t('app.upgradeStart'));
bus.emit('upgrade', true);
handleClose();
})
.finally(() => {
loading.value = false;
});
} else {
await IgnoreUpgrade(operateReq)
.then(() => {
MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));
bus.emit('upgrade', true);
handleClose();
})
.finally(() => {
loading.value = false;
});
}
};
const onOperate = async () => {
ElMessageBox.confirm(
i18n.global.t('app.operatorHelper', [i18n.global.t('commons.button.upgrade')]),
i18n.global.t('app.operatorHelper', [i18n.global.t('commons.button.' + operateReq.operate)]),
i18n.global.t('commons.button.upgrade'),
{
confirmButtonText: i18n.global.t('commons.button.confirm'),

Loading…
Cancel
Save