feat: 增加忽略应用列表和取消忽略功能 (#1566)

增加忽略应用列表和取消忽略功能
This commit is contained in:
zhengkunwang223 2023-07-06 18:48:22 +08:00 committed by GitHub
parent 0ac2b9df7a
commit 10427ddd65
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
19 changed files with 897 additions and 584 deletions

View File

@ -130,6 +130,22 @@ func (b *BaseApi) GetAppDetailByID(c *gin.Context) {
helper.SuccessWithData(c, appDetailDTO)
}
// @Tags App
// @Summary Get Ignore App
// @Description 获取忽略的应用版本
// @Accept json
// @Success 200 {object} response.IgnoredApp
// @Security ApiKeyAuth
// @Router /apps/ingored [get]
func (b *BaseApi) GetIgnoredApp(c *gin.Context) {
res, err := appService.GetIgnoredApp()
if err != nil {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
return
}
helper.SuccessWithData(c, res)
}
// @Tags App
// @Summary Install app
// @Description 安装应用

View File

@ -67,8 +67,8 @@ type AppInstalledUpdate struct {
}
type AppInstalledIgnoreUpgrade struct {
InstallId uint `json:"installId" validate:"required"`
DetailId uint `json:"detailId" validate:"required"`
DetailID uint `json:"detailID" validate:"required"`
Operate string `json:"operate" validate:"required,oneof=cancel ignore"`
}
type PortUpdate struct {

View File

@ -48,6 +48,13 @@ type AppDetailDTO struct {
Image string `json:"image"`
}
type IgnoredApp struct {
Icon string `json:"icon"`
Name string `json:"name"`
Version string `json:"version"`
DetailID uint `json:"detailID"`
}
type AppInstalledDTO struct {
model.AppInstall
Total int `json:"total"`

View File

@ -13,6 +13,7 @@ type AppDetailRepo struct {
type IAppDetailRepo interface {
WithVersion(version string) DBOption
WithAppId(id uint) DBOption
WithIgnored() DBOption
GetFirst(opts ...DBOption) (model.AppDetail, error)
Update(ctx context.Context, detail model.AppDetail) error
BatchCreate(ctx context.Context, details []model.AppDetail) error
@ -31,12 +32,19 @@ func (a AppDetailRepo) WithVersion(version string) DBOption {
return g.Where("version = ?", version)
}
}
func (a AppDetailRepo) WithAppId(id uint) DBOption {
return func(g *gorm.DB) *gorm.DB {
return g.Where("app_id = ?", id)
}
}
func (a AppDetailRepo) WithIgnored() DBOption {
return func(g *gorm.DB) *gorm.DB {
return g.Where("ignore_upgrade = 1")
}
}
func (a AppDetailRepo) GetFirst(opts ...DBOption) (model.AppDetail, error) {
var detail model.AppDetail
err := getDb(opts...).Model(&model.AppDetail{}).Find(&detail).Error

View File

@ -39,6 +39,7 @@ type IAppService interface {
GetAppUpdate() (*response.AppUpdateRes, error)
GetAppDetailByID(id uint) (*response.AppDetailDTO, error)
SyncAppListFromLocal()
GetIgnoredApp() ([]response.IgnoredApp, error)
}
func NewIAppService() IAppService {
@ -232,6 +233,27 @@ func (a AppService) GetAppDetailByID(id uint) (*response.AppDetailDTO, error) {
return res, nil
}
func (a AppService) GetIgnoredApp() ([]response.IgnoredApp, error) {
var res []response.IgnoredApp
details, _ := appDetailRepo.GetBy(appDetailRepo.WithIgnored())
if len(details) == 0 {
return res, nil
}
for _, detail := range details {
app, err := appRepo.GetFirst(commonRepo.WithByID(detail.AppId))
if err != nil {
return nil, err
}
res = append(res, response.IgnoredApp{
Name: app.Name,
Version: detail.Version,
DetailID: detail.ID,
Icon: app.Icon,
})
}
return res, nil
}
func (a AppService) Install(ctx context.Context, req request.AppInstallCreate) (appInstall *model.AppInstall, err error) {
if err = docker.CreateDefaultDockerNetwork(); err != nil {
err = buserr.WithDetail(constant.Err1PanelNetworkFailed, err.Error(), nil)

View File

@ -359,7 +359,7 @@ func (a *AppInstallService) IgnoreUpgrade(req request.AppInstalledIgnoreUpgrade)
if err != nil {
return err
}
appDetail.IgnoreUpgrade = true
appDetail.IgnoreUpgrade = req.Operate == "ignore"
return appDetailRepo.Update(context.Background(), appDetail)
}

View File

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

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -187,4 +187,11 @@ export namespace App {
allowPort: boolean;
dockerCompose: string;
}
export interface IgnoredApp {
name: string;
detailID: number;
version: string;
icon: string;
}
}

View File

@ -93,3 +93,7 @@ export const UpdateAppInstallParams = (req: any) => {
export const IgnoreUpgrade = (req: any) => {
return http.post<any>(`apps/installed/ignore`, req);
};
export const GetIgnoredApp = () => {
return http.get<App.IgnoredApp>(`apps/ignored/detail`);
};

View File

@ -44,7 +44,7 @@ const message = {
refresh: 'Refresh',
get: 'Get',
upgrade: 'Upgrade',
ignoreUpgrade: 'Ignore upgrade',
ignore: 'Ignore upgrade',
copy: 'Copy',
random: 'Random',
},
@ -1198,6 +1198,9 @@ const message = {
'Currently, if the port external access is not checked, it will not be able to access through the external network IP: port. Do you want to continue?',
restoreWarn:
'The restore operation will delete the current data of the application and restart it. This operation cannot be rolled back, continue?',
showIgnore: 'View ignore application',
cancelIgnore: 'Cancel ignore',
ignoreList: 'ignore list',
},
website: {
website: 'Website',

View File

@ -44,7 +44,7 @@ const message = {
refresh: '刷新',
get: '獲取',
upgrade: '升級',
ignoreUpgrade: '忽略升級',
ignore: '忽略升級',
copy: '復製',
random: '隨機密碼',
},
@ -1139,6 +1139,9 @@ const message = {
upgradeHelper: '異常應用需要先同步到正常狀態',
installWarn: '當前未勾選端口外部訪問將無法通過外網IP:端口訪問是否繼續 ',
restoreWarn: '恢復操作將刪除該應用當前數據並重啟此操作不可回滾是否繼續?',
showIgnore: '查看忽略應用',
cancelIgnore: '取消忽略',
ignoreList: '忽略列表',
},
website: {
website: '網站',

View File

@ -44,7 +44,7 @@ const message = {
refresh: '刷新',
get: '获取',
upgrade: '升级',
ignoreUpgrade: '忽略升级',
ignore: '忽略升级',
copy: '复制',
random: '随机密码',
},
@ -1145,6 +1145,9 @@ const message = {
upgradeHelper: '异常应用需要先同步到正常状态',
installWarn: '当前未勾选端口外部访问将无法通过外网IP:端口访问是否继续',
restoreWarn: '恢复操作将删除该应用当前数据并重启此操作不可回滚是否继续?',
showIgnore: '查看忽略应用',
cancelIgnore: '取消忽略',
ignoreList: '忽略列表',
},
website: {
website: '网站',

View File

@ -0,0 +1,85 @@
<template>
<el-drawer :close-on-click-modal="false" v-model="open" size="30%">
<template #header>
<Header :header="$t('app.ignoreList')" :back="handleClose"></Header>
</template>
<el-row :gutter="5">
<el-col v-for="(app, index) in apps" :key="index">
<el-card class="app-margin">
<el-row :gutter="20">
<el-col :span="6">
<el-avatar shape="square" :size="60" :src="'data:image/png;base64,' + app.icon" />
</el-col>
<el-col :span="12">
<span>{{ app.name }}</span>
<div class="app-margin">
<el-tag>{{ app.version }}</el-tag>
</div>
</el-col>
<el-col :span="6">
<el-button type="primary" link @click="cancelIngore(app.detailID)">
{{ $t('app.cancelIgnore') }}
</el-button>
</el-col>
</el-row>
</el-card>
</el-col>
</el-row>
<template #footer>
<span class="dialog-footer">
<el-button @click="handleClose" :disabled="loading">{{ $t('commons.button.cancel') }}</el-button>
</span>
</template>
</el-drawer>
</template>
<script lang="ts" setup>
import { GetIgnoredApp, IgnoreUpgrade } from '@/api/modules/app';
import { ref } from 'vue';
import Header from '@/components/drawer-header/index.vue';
import { MsgSuccess } from '@/utils/message';
import i18n from '@/lang';
const open = ref(false);
const loading = ref(false);
const apps = ref();
const em = defineEmits(['close']);
const handleClose = () => {
open.value = false;
em('close', open);
};
const acceptParams = () => {
open.value = true;
getApps();
};
const getApps = async () => {
try {
const res = await GetIgnoredApp();
apps.value = res.data;
} catch (error) {}
};
const cancelIngore = async (id: number) => {
loading.value = true;
await IgnoreUpgrade({ detailID: id, operate: 'cancel' })
.then(() => {
MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));
})
.finally(() => {
getApps();
loading.value = false;
});
};
defineExpose({
acceptParams,
});
</script>
<style scoped>
.app-margin {
margin-top: 10px;
}
</style>

View File

@ -46,6 +46,9 @@
<el-button @click="sync" type="primary" link v-if="mode === 'installed' && data != null">
{{ $t('app.sync') }}
</el-button>
<el-button @click="openIngore" type="primary" link v-if="mode === 'upgrade'">
{{ $t('app.showIgnore') }}
</el-button>
</template>
<template #main>
@ -164,10 +167,10 @@
plain
round
size="small"
@click="openOperate(installed, 'ignoreUpgrade')"
@click="openOperate(installed, 'ignore')"
v-if="mode === 'upgrade'"
>
{{ $t('commons.button.ignoreUpgrade') }}
{{ $t('commons.button.ignore') }}
</el-button>
<el-button
class="h-button"
@ -239,8 +242,8 @@
<AppDelete ref="deleteRef" @close="search" />
<AppParams ref="appParamRef" />
<AppUpgrade ref="upgradeRef" @close="search" />
<PortJumpDialog ref="dialogPortJumpRef" />
<AppIgnore ref="ignoreRef" @close="search" />
</template>
<script lang="ts" setup>
@ -261,6 +264,7 @@ import AppResources from './check/index.vue';
import AppDelete from './delete/index.vue';
import AppParams from './detail/index.vue';
import AppUpgrade from './upgrade/index.vue';
import AppIgnore from './ignore/index.vue';
import { App } from '@/api/interface/app';
import Status from '@/components/status/index.vue';
import { getAge } from '@/utils/util';
@ -289,6 +293,7 @@ const checkRef = ref();
const deleteRef = ref();
const appParamRef = ref();
const upgradeRef = ref();
const ignoreRef = ref();
const dialogPortJumpRef = ref();
const tags = ref<App.Tag[]>([]);
const activeTag = ref('all');
@ -348,7 +353,7 @@ const goDashboard = async (port: any) => {
const openOperate = (row: any, op: string) => {
operateReq.installId = row.id;
operateReq.operate = op;
if (op == 'upgrade' || op == 'ignoreUpgrade') {
if (op == 'upgrade' || op == 'ignore') {
upgradeRef.value.acceptParams(row.id, row.name, op);
} else if (op == 'delete') {
AppInstalledDeleteCheck(row.id).then(async (res) => {
@ -364,6 +369,10 @@ const openOperate = (row: any, op: string) => {
}
};
const openIngore = () => {
ignoreRef.value.acceptParams();
};
const operate = async () => {
open.value = false;
loading.value = true;

6
go.mod
View File

@ -47,7 +47,7 @@ require (
github.com/subosito/gotenv v1.4.1
github.com/swaggo/files v0.0.0-20220728132757-551d4a08d97a
github.com/swaggo/gin-swagger v1.5.3
github.com/swaggo/swag v1.8.4
github.com/swaggo/swag v1.16.1
github.com/tencentyun/cos-go-sdk-v5 v0.7.41
github.com/xlzd/gotp v0.0.0-20220817083547-a63b9d03d72f
golang.org/x/crypto v0.9.0
@ -245,11 +245,11 @@ require (
go4.org v0.0.0-20200411211856-f5505b9728dd // indirect
golang.org/x/arch v0.3.0 // indirect
golang.org/x/image v0.5.0 // indirect
golang.org/x/mod v0.8.0 // indirect
golang.org/x/mod v0.9.0 // indirect
golang.org/x/sync v0.1.0 // indirect
golang.org/x/term v0.8.0 // indirect
golang.org/x/time v0.1.0 // indirect
golang.org/x/tools v0.6.0 // indirect
golang.org/x/tools v0.7.0 // indirect
google.golang.org/appengine v1.6.7 // indirect
google.golang.org/genproto v0.0.0-20230110181048-76db0878b65f // indirect
google.golang.org/grpc v1.53.0 // indirect

6
go.sum
View File

@ -845,6 +845,8 @@ github.com/swaggo/gin-swagger v1.5.3/go.mod h1:3XJKSfHjDMB5dBo/0rrTXidPmgLeqsX89
github.com/swaggo/swag v1.8.1/go.mod h1:ugemnJsPZm/kRwFUnzBlbHRd0JY9zE1M4F+uy2pAaPQ=
github.com/swaggo/swag v1.8.4 h1:oGB351qH1JqUqK1tsMYEE5qTBbPk394BhsZxmUfebcI=
github.com/swaggo/swag v1.8.4/go.mod h1:jMLeXOOmYyjk8PvHTsXBdrubsNd9gUJTTCzL5iBnseg=
github.com/swaggo/swag v1.16.1 h1:fTNRhKstPKxcnoKsytm4sahr8FaYzUcT7i1/3nd/fBg=
github.com/swaggo/swag v1.16.1/go.mod h1:9/LMvHycG3NFHfR6LwvikHv5iFvmPADQ359cKikGxto=
github.com/syndtr/gocapability v0.0.0-20200815063812-42c35b437635/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww=
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.194/go.mod h1:7sCQWVkxcsR38nffDW057DRGk8mUjK1Ing/EFOK8s8Y=
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/kms v1.0.194/go.mod h1:yrBKWhChnDqNz1xuXdSbWXG56XawEq0G5j1lg4VwBD4=
@ -1011,6 +1013,8 @@ golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
golang.org/x/mod v0.8.0 h1:LUYupSeNrTNCGzR/hVBk2NHZO4hXcVaW1k4Qx7rjPx8=
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/mod v0.9.0 h1:KENHtAZL2y3NLMYZeHY9DW8HW8V+kQyJsY/V9JlKvCs=
golang.org/x/mod v0.9.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/net v0.7.0 h1:rJrUqqhjsgNp7KqAIc25s9pZnjU7TUcSY7HcVZjdn1g=
golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
@ -1202,6 +1206,8 @@ golang.org/x/tools v0.1.7/go.mod h1:LGqMHiF4EqQNHR1JncWGqT5BVaXmza+X+BDGol+dOxo=
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
golang.org/x/tools v0.6.0 h1:BOw41kyTf3PuCW1pVQf8+Cyg8pMlkYB1oo9iJ6D/lKM=
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
golang.org/x/tools v0.7.0 h1:W4OVu8VVOaIO0yzWMNdepAulS7YfoS3Zabrm8DOXXU4=
golang.org/x/tools v0.7.0/go.mod h1:4pg6aUX35JBAogB10C9AtvVL+qowtN4pT3CGSQex14s=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=