mirror of
https://github.com/1Panel-dev/1Panel.git
synced 2025-10-09 23:17:21 +08:00
fix: 应用删除增加强制删除选项
This commit is contained in:
parent
1a06571bec
commit
a6239a7359
8 changed files with 137 additions and 18 deletions
|
@ -35,10 +35,12 @@ type AppBackupDelete struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
type AppInstalledOperate struct {
|
type AppInstalledOperate struct {
|
||||||
InstallId uint `json:"installId" validate:"required"`
|
InstallId uint `json:"installId" validate:"required"`
|
||||||
BackupId uint `json:"backupId"`
|
BackupId uint `json:"backupId"`
|
||||||
DetailId uint `json:"detailId"`
|
DetailId uint `json:"detailId"`
|
||||||
Operate constant.AppOperate `json:"operate" validate:"required"`
|
Operate constant.AppOperate `json:"operate" validate:"required"`
|
||||||
|
ForceDelete bool `json:"forceDelete"`
|
||||||
|
DeleteBackup bool `json:"deleteBackup"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type PortUpdate struct {
|
type PortUpdate struct {
|
||||||
|
|
|
@ -143,7 +143,7 @@ func (a AppInstallService) Operate(req request.AppInstalledOperate) error {
|
||||||
install.Status = constant.Running
|
install.Status = constant.Running
|
||||||
case constant.Delete:
|
case constant.Delete:
|
||||||
tx, ctx := getTxAndContext()
|
tx, ctx := getTxAndContext()
|
||||||
if err := deleteAppInstall(ctx, install); err != nil {
|
if err := deleteAppInstall(ctx, install, req.ForceDelete, req.DeleteBackup); err != nil {
|
||||||
tx.Rollback()
|
tx.Rollback()
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
|
@ -156,7 +156,7 @@ func createLink(ctx context.Context, app model.App, appInstall *model.AppInstall
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func deleteAppInstall(ctx context.Context, install model.AppInstall) error {
|
func deleteAppInstall(ctx context.Context, install model.AppInstall, forceDelete bool, deleteBackup bool) error {
|
||||||
op := files.NewFileOp()
|
op := files.NewFileOp()
|
||||||
appDir := install.GetPath()
|
appDir := install.GetPath()
|
||||||
dir, _ := os.Stat(appDir)
|
dir, _ := os.Stat(appDir)
|
||||||
|
@ -169,19 +169,20 @@ func deleteAppInstall(ctx context.Context, install model.AppInstall) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := appInstallRepo.Delete(ctx, install); err != nil {
|
if err := appInstallRepo.Delete(ctx, install); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if err := deleteLink(ctx, &install); err != nil {
|
if err := deleteLink(ctx, &install); err != nil && !forceDelete {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
backups, _ := appInstallBackupRepo.GetBy(appInstallBackupRepo.WithAppInstallID(install.ID))
|
if deleteBackup {
|
||||||
for _, backup := range backups {
|
backups, _ := appInstallBackupRepo.GetBy(appInstallBackupRepo.WithAppInstallID(install.ID))
|
||||||
_ = op.DeleteDir(backup.Path)
|
for _, backup := range backups {
|
||||||
}
|
_ = op.DeleteDir(backup.Path)
|
||||||
if err := appInstallBackupRepo.Delete(ctx, appInstallBackupRepo.WithAppInstallID(install.ID)); err != nil {
|
}
|
||||||
return err
|
if err := appInstallBackupRepo.Delete(ctx, appInstallBackupRepo.WithAppInstallID(install.ID)); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -263,7 +263,7 @@ func (w WebsiteService) DeleteWebsite(req request.WebsiteDelete) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if !reflect.DeepEqual(model.AppInstall{}, appInstall) {
|
if !reflect.DeepEqual(model.AppInstall{}, appInstall) {
|
||||||
if err := deleteAppInstall(ctx, appInstall); err != nil {
|
if err := deleteAppInstall(ctx, appInstall, req.ForceDelete, true); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -108,6 +108,8 @@ export namespace App {
|
||||||
operate: string;
|
operate: string;
|
||||||
backupId?: number;
|
backupId?: number;
|
||||||
detailId?: number;
|
detailId?: number;
|
||||||
|
forceDelete?: boolean;
|
||||||
|
deleteBackup?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface AppInstalledSearch {
|
export interface AppInstalledSearch {
|
||||||
|
|
|
@ -789,6 +789,10 @@ export default {
|
||||||
database: '数据库',
|
database: '数据库',
|
||||||
defaultConfig: '默认配置',
|
defaultConfig: '默认配置',
|
||||||
defaultConfigHelper: '已恢复为默认配置,保存后生效',
|
defaultConfigHelper: '已恢复为默认配置,保存后生效',
|
||||||
|
forceDelete: '强制删除',
|
||||||
|
forceDeleteHelper: '强制删除,会忽略删除过程中碰到的问题,最终删除元数据',
|
||||||
|
deleteBackup: '删除备份',
|
||||||
|
deleteBackupHelper: '同时删除应用备份',
|
||||||
},
|
},
|
||||||
website: {
|
website: {
|
||||||
website: '网站',
|
website: '网站',
|
||||||
|
|
105
frontend/src/views/app-store/installed/delete/index.vue
Normal file
105
frontend/src/views/app-store/installed/delete/index.vue
Normal file
|
@ -0,0 +1,105 @@
|
||||||
|
<template>
|
||||||
|
<el-dialog
|
||||||
|
v-model="open"
|
||||||
|
:title="$t('commons.button.delete') + ' - ' + appInstallName"
|
||||||
|
width="30%"
|
||||||
|
:close-on-click-modal="false"
|
||||||
|
:before-close="handleClose"
|
||||||
|
>
|
||||||
|
<el-form ref="deleteForm" label-position="left">
|
||||||
|
<el-form-item>
|
||||||
|
<el-checkbox v-model="deleteReq.forceDelete" :label="$t('app.forceDelete')" />
|
||||||
|
</el-form-item>
|
||||||
|
<div class="helper">
|
||||||
|
<span class="input-help">
|
||||||
|
{{ $t('app.forceDeleteHelper') }}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<el-form-item>
|
||||||
|
<el-checkbox v-model="deleteReq.deleteBackup" :label="$t('app.deleteBackup')" />
|
||||||
|
</el-form-item>
|
||||||
|
<div class="helper">
|
||||||
|
<span class="input-help">
|
||||||
|
{{ $t('app.deleteBackupHelper') }}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<br />
|
||||||
|
<span v-html="deleteHelper"></span>
|
||||||
|
<el-form-item>
|
||||||
|
<el-input v-model="deleteInfo" :placeholder="appInstallName" />
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
<template #footer>
|
||||||
|
<span class="dialog-footer">
|
||||||
|
<el-button @click="handleClose" :loading="loading">
|
||||||
|
{{ $t('commons.button.cancel') }}
|
||||||
|
</el-button>
|
||||||
|
<el-button type="primary" @click="submit" :loading="loading" :disabled="deleteInfo != appInstallName">
|
||||||
|
{{ $t('commons.button.confirm') }}
|
||||||
|
</el-button>
|
||||||
|
</span>
|
||||||
|
</template>
|
||||||
|
</el-dialog>
|
||||||
|
</template>
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import { ElMessage, FormInstance } from 'element-plus';
|
||||||
|
import { ref } from 'vue';
|
||||||
|
import { App } from '@/api/interface/app';
|
||||||
|
import { InstalledOp } from '@/api/modules/app';
|
||||||
|
import i18n from '@/lang';
|
||||||
|
|
||||||
|
let deleteReq = ref({
|
||||||
|
operate: 'delete',
|
||||||
|
installId: 0,
|
||||||
|
deleteBackup: false,
|
||||||
|
forceDelete: false,
|
||||||
|
});
|
||||||
|
let open = ref(false);
|
||||||
|
let loading = ref(false);
|
||||||
|
let deleteHelper = ref('');
|
||||||
|
let deleteInfo = ref('');
|
||||||
|
let appInstallName = ref('');
|
||||||
|
|
||||||
|
const deleteForm = ref<FormInstance>();
|
||||||
|
const em = defineEmits(['close']);
|
||||||
|
|
||||||
|
const handleClose = () => {
|
||||||
|
open.value = false;
|
||||||
|
em('close', open);
|
||||||
|
};
|
||||||
|
|
||||||
|
const acceptParams = async (app: App.AppInstalled) => {
|
||||||
|
deleteReq.value = {
|
||||||
|
operate: 'delete',
|
||||||
|
installId: 0,
|
||||||
|
deleteBackup: false,
|
||||||
|
forceDelete: false,
|
||||||
|
};
|
||||||
|
deleteReq.value.installId = app.id;
|
||||||
|
deleteHelper.value = i18n.global.t('website.deleteConfirmHelper', [app.name]);
|
||||||
|
appInstallName.value = app.name;
|
||||||
|
open.value = true;
|
||||||
|
};
|
||||||
|
|
||||||
|
const submit = async () => {
|
||||||
|
loading.value = true;
|
||||||
|
InstalledOp(deleteReq.value)
|
||||||
|
.then(() => {
|
||||||
|
handleClose();
|
||||||
|
ElMessage.success(i18n.global.t('commons.msg.deleteSuccess'));
|
||||||
|
})
|
||||||
|
.finally(() => {
|
||||||
|
loading.value = false;
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
defineExpose({
|
||||||
|
acceptParams,
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss">
|
||||||
|
.helper {
|
||||||
|
margin-top: -20px;
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -109,6 +109,7 @@
|
||||||
</el-dialog>
|
</el-dialog>
|
||||||
<Backups ref="backupRef" @close="search"></Backups>
|
<Backups ref="backupRef" @close="search"></Backups>
|
||||||
<AppResources ref="checkRef"></AppResources>
|
<AppResources ref="checkRef"></AppResources>
|
||||||
|
<AppDelete ref="deleteRef" @close="search"></AppDelete>
|
||||||
</el-card>
|
</el-card>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
@ -127,8 +128,9 @@ 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 AppResources from './check/index.vue';
|
||||||
|
import AppDelete from './delete/index.vue';
|
||||||
import { App } from '@/api/interface/app';
|
import { App } from '@/api/interface/app';
|
||||||
import { useDeleteData } from '@/hooks/use-delete-data';
|
// import { useDeleteData } from '@/hooks/use-delete-data';
|
||||||
import Status from '@/components/status/index.vue';
|
import Status from '@/components/status/index.vue';
|
||||||
|
|
||||||
let data = ref<any>();
|
let data = ref<any>();
|
||||||
|
@ -148,6 +150,7 @@ let operateReq = reactive({
|
||||||
let versions = ref<App.VersionDetail[]>();
|
let versions = ref<App.VersionDetail[]>();
|
||||||
const backupRef = ref();
|
const backupRef = ref();
|
||||||
const checkRef = ref();
|
const checkRef = ref();
|
||||||
|
const deleteRef = ref();
|
||||||
let searchName = ref('');
|
let searchName = ref('');
|
||||||
|
|
||||||
const sync = () => {
|
const sync = () => {
|
||||||
|
@ -192,8 +195,10 @@ const openOperate = (row: any, op: string) => {
|
||||||
if (res.data && res.data.length > 0) {
|
if (res.data && res.data.length > 0) {
|
||||||
checkRef.value.acceptParams({ items: items });
|
checkRef.value.acceptParams({ items: items });
|
||||||
} else {
|
} else {
|
||||||
await useDeleteData(InstalledOp, operateReq, 'app.deleteWarn');
|
deleteRef.value.acceptParams(row);
|
||||||
search();
|
|
||||||
|
// await useDeleteData(InstalledOp, operateReq, 'app.deleteWarn');
|
||||||
|
// search();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
|
|
Loading…
Add table
Reference in a new issue