mirror of
https://github.com/1Panel-dev/1Panel.git
synced 2025-01-19 08:19:15 +08:00
fix: 删除应用增加提示
This commit is contained in:
parent
098b735c7a
commit
c61e2cb191
@ -53,6 +53,22 @@ func (b *BaseApi) CheckAppInstalled(c *gin.Context) {
|
|||||||
helper.SuccessWithData(c, checkData)
|
helper.SuccessWithData(c, checkData)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (b *BaseApi) DeleteCheck(c *gin.Context) {
|
||||||
|
|
||||||
|
appInstallId, err := helper.GetIntParamByKey(c, "appInstallId")
|
||||||
|
if err != nil {
|
||||||
|
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInternalServer, nil)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
checkData, err := appInstallService.DeleteCheck(appInstallId)
|
||||||
|
if err != nil {
|
||||||
|
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
helper.SuccessWithData(c, checkData)
|
||||||
|
}
|
||||||
|
|
||||||
func (b *BaseApi) SyncInstalled(c *gin.Context) {
|
func (b *BaseApi) SyncInstalled(c *gin.Context) {
|
||||||
if err := appInstallService.SyncAll(); err != nil {
|
if err := appInstallService.SyncAll(); err != nil {
|
||||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||||
|
@ -177,3 +177,8 @@ type AppFormFields struct {
|
|||||||
Default string `json:"default"`
|
Default string `json:"default"`
|
||||||
EnvKey string `json:"env_variable"`
|
EnvKey string `json:"env_variable"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type AppResource struct {
|
||||||
|
Type string `json:"type"`
|
||||||
|
Name string `json:"name"`
|
||||||
|
}
|
||||||
|
@ -17,6 +17,12 @@ func (a AppInstallResourceRpo) WithAppInstallId(appInstallId uint) DBOption {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (a AppInstallResourceRpo) WithLinkId(linkId uint) DBOption {
|
||||||
|
return func(db *gorm.DB) *gorm.DB {
|
||||||
|
return db.Where("link_id = ?", linkId)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func (a AppInstallResourceRpo) GetBy(opts ...DBOption) ([]model.AppInstallResource, error) {
|
func (a AppInstallResourceRpo) GetBy(opts ...DBOption) ([]model.AppInstallResource, error) {
|
||||||
db := global.DB.Model(&model.AppInstallResource{})
|
db := global.DB.Model(&model.AppInstallResource{})
|
||||||
var resources []model.AppInstallResource
|
var resources []model.AppInstallResource
|
||||||
|
@ -295,6 +295,58 @@ func (a AppInstallService) ChangeAppPort(req dto.PortUpdate) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (a AppInstallService) DeleteCheck(installId uint) ([]dto.AppResource, error) {
|
||||||
|
var res []dto.AppResource
|
||||||
|
appInstall, err := appInstallRepo.GetFirst(commonRepo.WithByID(installId))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
app, err := appRepo.GetFirst(commonRepo.WithByID(appInstall.AppId))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if app.Type == "website" {
|
||||||
|
websites, _ := websiteRepo.GetBy(websiteRepo.WithAppInstallId(appInstall.ID))
|
||||||
|
for _, website := range websites {
|
||||||
|
res = append(res, dto.AppResource{
|
||||||
|
Type: "website",
|
||||||
|
Name: website.PrimaryDomain,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if app.Key == "nginx" {
|
||||||
|
websites, _ := websiteRepo.GetBy()
|
||||||
|
for _, website := range websites {
|
||||||
|
res = append(res, dto.AppResource{
|
||||||
|
Type: "website",
|
||||||
|
Name: website.PrimaryDomain,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if app.Type == "runtime" {
|
||||||
|
resources, _ := appInstallResourceRepo.GetBy(appInstallResourceRepo.WithLinkId(appInstall.ID))
|
||||||
|
for _, resource := range resources {
|
||||||
|
linkInstall, _ := appInstallRepo.GetFirst(commonRepo.WithByID(resource.AppInstallId))
|
||||||
|
res = append(res, dto.AppResource{
|
||||||
|
Type: "app",
|
||||||
|
Name: linkInstall.Name,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if app.Key == "mysql" {
|
||||||
|
databases, _ := mysqlRepo.List()
|
||||||
|
for _, database := range databases {
|
||||||
|
res = append(res, dto.AppResource{
|
||||||
|
Type: "database",
|
||||||
|
Name: database.Name,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return res, nil
|
||||||
|
}
|
||||||
|
|
||||||
func syncById(installId uint) error {
|
func syncById(installId uint) error {
|
||||||
appInstall, err := appInstallRepo.GetFirst(commonRepo.WithByID(installId))
|
appInstall, err := appInstallRepo.GetFirst(commonRepo.WithByID(installId))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -175,6 +175,10 @@ func deleteAppInstall(ctx context.Context, install model.AppInstall) error {
|
|||||||
if err := deleteLink(ctx, &install); err != nil {
|
if err := deleteLink(ctx, &install); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
backups, _ := appInstallBackupRepo.GetBy(appInstallBackupRepo.WithAppInstallID(install.ID))
|
||||||
|
for _, backup := range backups {
|
||||||
|
_ = op.DeleteDir(backup.Path)
|
||||||
|
}
|
||||||
if err := appInstallBackupRepo.Delete(ctx, appInstallBackupRepo.WithAppInstallID(install.ID)); err != nil {
|
if err := appInstallBackupRepo.Delete(ctx, appInstallBackupRepo.WithAppInstallID(install.ID)); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -22,6 +22,7 @@ func (a *AppRouter) InitAppRouter(Router *gin.RouterGroup) {
|
|||||||
appRouter.POST("/install", baseApi.InstallApp)
|
appRouter.POST("/install", baseApi.InstallApp)
|
||||||
appRouter.GET("/installed/:appInstallId/versions", baseApi.GetUpdateVersions)
|
appRouter.GET("/installed/:appInstallId/versions", baseApi.GetUpdateVersions)
|
||||||
appRouter.GET("/installed/check/:key", baseApi.CheckAppInstalled)
|
appRouter.GET("/installed/check/:key", baseApi.CheckAppInstalled)
|
||||||
|
appRouter.GET("/installed/delete/check/:appInstallId", baseApi.DeleteCheck)
|
||||||
appRouter.POST("/installed", baseApi.SearchAppInstalled)
|
appRouter.POST("/installed", baseApi.SearchAppInstalled)
|
||||||
appRouter.POST("/installed/op", baseApi.OperateInstalled)
|
appRouter.POST("/installed/op", baseApi.OperateInstalled)
|
||||||
appRouter.POST("/installed/sync", baseApi.SyncInstalled)
|
appRouter.POST("/installed/sync", baseApi.SyncInstalled)
|
||||||
|
@ -98,6 +98,11 @@ export namespace App {
|
|||||||
containerName: string;
|
containerName: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface AppInstallResource {
|
||||||
|
type: string;
|
||||||
|
name: string;
|
||||||
|
}
|
||||||
|
|
||||||
export interface AppInstalledOp {
|
export interface AppInstalledOp {
|
||||||
installId: number;
|
installId: number;
|
||||||
operate: string;
|
operate: string;
|
||||||
|
@ -34,6 +34,10 @@ export const CheckAppInstalled = (key: string) => {
|
|||||||
return http.get<App.CheckInstalled>(`apps/installed/check/${key}`);
|
return http.get<App.CheckInstalled>(`apps/installed/check/${key}`);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const AppInstalledDeleteCheck = (appInstallId: number) => {
|
||||||
|
return http.get<App.AppInstallResource[]>(`apps/installed/delete/check/${appInstallId}`);
|
||||||
|
};
|
||||||
|
|
||||||
export const GetAppInstalled = (search: App.AppInstalledSearch) => {
|
export const GetAppInstalled = (search: App.AppInstalledSearch) => {
|
||||||
return http.post<App.AppInstalled[]>('apps/installed', search);
|
return http.post<App.AppInstalled[]>('apps/installed', search);
|
||||||
};
|
};
|
||||||
|
@ -3,31 +3,31 @@
|
|||||||
<div class="app-content" v-if="data.isExist">
|
<div class="app-content" v-if="data.isExist">
|
||||||
<el-card class="app-card" v-loading="loading">
|
<el-card class="app-card" v-loading="loading">
|
||||||
<el-row :gutter="20">
|
<el-row :gutter="20">
|
||||||
<el-col :xs="8" :sm="6" :md="4" :lg="3" :xl="1">
|
<el-col :lg="2" :xl="1">
|
||||||
<div>
|
<div>
|
||||||
<el-tag effect="dark" type="success">{{ data.app }}</el-tag>
|
<el-tag effect="dark" type="success">{{ data.app }}</el-tag>
|
||||||
</div>
|
</div>
|
||||||
</el-col>
|
</el-col>
|
||||||
<el-col :xs="8" :sm="6" :md="4" :lg="3" :xl="2">
|
<el-col :lg="4" :xl="2">
|
||||||
<div>
|
<div>
|
||||||
{{ $t('app.version') }}:
|
{{ $t('app.version') }}:
|
||||||
<el-tag type="info">{{ data.version }}</el-tag>
|
<el-tag type="info">{{ data.version }}</el-tag>
|
||||||
</div>
|
</div>
|
||||||
</el-col>
|
</el-col>
|
||||||
<el-col :xs="8" :sm="6" :md="4" :lg="3" :xl="2">
|
<el-col :lg="4" :xl="2">
|
||||||
<div>
|
<div>
|
||||||
{{ $t('commons.table.status') }}:
|
{{ $t('commons.table.status') }}:
|
||||||
<el-tag type="success">{{ data.status }}</el-tag>
|
<el-tag type="success">{{ data.status }}</el-tag>
|
||||||
</div>
|
</div>
|
||||||
</el-col>
|
</el-col>
|
||||||
<el-col :xs="8" :sm="6" :md="4" :lg="3" :xl="2">
|
<el-col :lg="8" :xl="4">
|
||||||
<div>
|
<div>
|
||||||
{{ $t('website.lastBackupAt') }}:
|
{{ $t('website.lastBackupAt') }}:
|
||||||
<el-tag v-if="data.lastBackupAt != ''" type="info">{{ data.lastBackupAt }}</el-tag>
|
<el-tag v-if="data.lastBackupAt != ''" type="info">{{ data.lastBackupAt }}</el-tag>
|
||||||
<span else>{{ $t('website.null') }}</span>
|
<span v-else>{{ $t('website.null') }}</span>
|
||||||
</div>
|
</div>
|
||||||
</el-col>
|
</el-col>
|
||||||
<el-col :xs="8" :sm="6" :md="4" :lg="3" :xl="6">
|
<el-col :lg="4" :xl="6">
|
||||||
<el-button type="primary" link @click="onOperate('restart')">{{ $t('app.restart') }}</el-button>
|
<el-button type="primary" link @click="onOperate('restart')">{{ $t('app.restart') }}</el-button>
|
||||||
<el-divider direction="vertical" />
|
<el-divider direction="vertical" />
|
||||||
<el-button type="primary" link @click="setting">{{ $t('commons.button.set') }}</el-button>
|
<el-button type="primary" link @click="setting">{{ $t('commons.button.set') }}</el-button>
|
||||||
|
@ -733,6 +733,10 @@ export default {
|
|||||||
gotoInstalled: '去安装',
|
gotoInstalled: '去安装',
|
||||||
search: '搜索',
|
search: '搜索',
|
||||||
limitHelper: '该应用已安装,不支持重复安装',
|
limitHelper: '该应用已安装,不支持重复安装',
|
||||||
|
deleteHelper: '应用已经关联以下资源,无法删除',
|
||||||
|
checkTitle: '提示',
|
||||||
|
website: '网站',
|
||||||
|
database: '数据库',
|
||||||
},
|
},
|
||||||
website: {
|
website: {
|
||||||
website: '网站',
|
website: '网站',
|
||||||
|
54
frontend/src/views/app-store/installed/check/index.vue
Normal file
54
frontend/src/views/app-store/installed/check/index.vue
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
<template>
|
||||||
|
<el-dialog
|
||||||
|
v-model="open"
|
||||||
|
:title="$t('app.checkTitle')"
|
||||||
|
width="50%"
|
||||||
|
:close-on-click-modal="false"
|
||||||
|
:destroy-on-close="true"
|
||||||
|
>
|
||||||
|
<el-row>
|
||||||
|
<el-alert type="warning" :description="$t('app.deleteHelper')" center show-icon :closable="false" />
|
||||||
|
<el-col :span="12" :offset="6">
|
||||||
|
<br />
|
||||||
|
<el-descriptions border :column="1">
|
||||||
|
<el-descriptions-item v-for="(item, key) in map" :key="key" :label="$t('app.' + item[0])">
|
||||||
|
{{ map.get(item[0]).toString() }}
|
||||||
|
</el-descriptions-item>
|
||||||
|
</el-descriptions>
|
||||||
|
</el-col>
|
||||||
|
</el-row>
|
||||||
|
</el-dialog>
|
||||||
|
</template>
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import { App } from '@/api/interface/app';
|
||||||
|
import { ref } from 'vue';
|
||||||
|
|
||||||
|
interface InstallRrops {
|
||||||
|
items: App.AppInstallResource[];
|
||||||
|
}
|
||||||
|
const installData = ref<InstallRrops>({
|
||||||
|
items: [],
|
||||||
|
});
|
||||||
|
let open = ref(false);
|
||||||
|
let map = new Map();
|
||||||
|
|
||||||
|
const acceptParams = (props: InstallRrops) => {
|
||||||
|
map.clear();
|
||||||
|
installData.value.items = [];
|
||||||
|
installData.value.items = props.items;
|
||||||
|
installData.value.items.forEach((item) => {
|
||||||
|
if (map.has(item.type)) {
|
||||||
|
const array = map.get(item.type);
|
||||||
|
array.push(item.name);
|
||||||
|
map.set(item.type, array);
|
||||||
|
} else {
|
||||||
|
map.set(item.type, [item.name]);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
open.value = true;
|
||||||
|
};
|
||||||
|
|
||||||
|
defineExpose({
|
||||||
|
acceptParams,
|
||||||
|
});
|
||||||
|
</script>
|
@ -104,17 +104,25 @@
|
|||||||
</template>
|
</template>
|
||||||
</el-dialog>
|
</el-dialog>
|
||||||
<Backups ref="backupRef" @close="search"></Backups>
|
<Backups ref="backupRef" @close="search"></Backups>
|
||||||
|
<AppResources ref="checkRef"></AppResources>
|
||||||
</el-card>
|
</el-card>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { SearchAppInstalled, InstalledOp, SyncInstalledApp, GetAppUpdateVersions } from '@/api/modules/app';
|
import {
|
||||||
|
SearchAppInstalled,
|
||||||
|
InstalledOp,
|
||||||
|
SyncInstalledApp,
|
||||||
|
GetAppUpdateVersions,
|
||||||
|
AppInstalledDeleteCheck,
|
||||||
|
} from '@/api/modules/app';
|
||||||
import { onMounted, onUnmounted, reactive, ref } from 'vue';
|
import { onMounted, onUnmounted, reactive, ref } from 'vue';
|
||||||
import ComplexTable from '@/components/complex-table/index.vue';
|
import ComplexTable from '@/components/complex-table/index.vue';
|
||||||
import { dateFromat } from '@/utils/util';
|
import { dateFromat } from '@/utils/util';
|
||||||
import i18n from '@/lang';
|
import i18n from '@/lang';
|
||||||
import { ElMessage, ElMessageBox } from 'element-plus';
|
import { ElMessage, ElMessageBox } from 'element-plus';
|
||||||
import Backups from './backups.vue';
|
import Backups from './backups.vue';
|
||||||
|
import AppResources from './check/index.vue';
|
||||||
import { App } from '@/api/interface/app';
|
import { App } from '@/api/interface/app';
|
||||||
|
|
||||||
let data = ref<any>();
|
let data = ref<any>();
|
||||||
@ -133,6 +141,7 @@ let operateReq = reactive({
|
|||||||
});
|
});
|
||||||
let versions = ref<App.VersionDetail[]>();
|
let versions = ref<App.VersionDetail[]>();
|
||||||
const backupRef = ref();
|
const backupRef = ref();
|
||||||
|
const checkRef = ref();
|
||||||
let searchName = ref('');
|
let searchName = ref('');
|
||||||
|
|
||||||
const sync = () => {
|
const sync = () => {
|
||||||
@ -171,6 +180,15 @@ const openOperate = (row: any, op: string) => {
|
|||||||
}
|
}
|
||||||
open.value = true;
|
open.value = true;
|
||||||
});
|
});
|
||||||
|
} else if (op == 'delete') {
|
||||||
|
AppInstalledDeleteCheck(row.id).then((res) => {
|
||||||
|
const items = res.data;
|
||||||
|
if (res.data && res.data.length > 0) {
|
||||||
|
checkRef.value.acceptParams({ items: items });
|
||||||
|
} else {
|
||||||
|
onOperate(op);
|
||||||
|
}
|
||||||
|
});
|
||||||
} else {
|
} else {
|
||||||
onOperate(op);
|
onOperate(op);
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user