Skip to content

Commit a0690a6

Browse files
authored
feat: support options to delete plugins config and data (#3280)
* - 为插件管理页面中,删除插件提供一致的二次确认(原本只有卡片视图有二次确认) - 二次确认时可选删除插件配置和持久化数据 - 添加对应的i18n支持 * ruff * 移除未使用的 const $confirm = inject('$confirm');
1 parent c51609b commit a0690a6

File tree

9 files changed

+271
-23
lines changed

9 files changed

+271
-23
lines changed

astrbot/core/star/star_manager.py

Lines changed: 54 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -680,11 +680,18 @@ async def install_plugin(self, repo_url: str, proxy=""):
680680

681681
return plugin_info
682682

683-
async def uninstall_plugin(self, plugin_name: str):
683+
async def uninstall_plugin(
684+
self,
685+
plugin_name: str,
686+
delete_config: bool = False,
687+
delete_data: bool = False,
688+
):
684689
"""卸载指定的插件。
685690
686691
Args:
687692
plugin_name (str): 要卸载的插件名称
693+
delete_config (bool): 是否删除插件配置文件,默认为 False
694+
delete_data (bool): 是否删除插件数据,默认为 False
688695
689696
Raises:
690697
Exception: 当插件不存在、是保留插件时,或删除插件文件夹失败时抛出异常
@@ -714,13 +721,59 @@ async def uninstall_plugin(self, plugin_name: str):
714721

715722
await self._unbind_plugin(plugin_name, plugin.module_path)
716723

724+
# 删除插件文件夹
717725
try:
718726
remove_dir(os.path.join(ppath, root_dir_name))
719727
except Exception as e:
720728
raise Exception(
721729
f"移除插件成功,但是删除插件文件夹失败: {e!s}。您可以手动删除该文件夹,位于 addons/plugins/ 下。",
722730
)
723731

732+
# 删除插件配置文件
733+
if delete_config and root_dir_name:
734+
config_file = os.path.join(
735+
self.plugin_config_path,
736+
f"{root_dir_name}_config.json",
737+
)
738+
if os.path.exists(config_file):
739+
try:
740+
os.remove(config_file)
741+
logger.info(f"已删除插件 {plugin_name} 的配置文件")
742+
except Exception as e:
743+
logger.warning(f"删除插件配置文件失败: {e!s}")
744+
745+
# 删除插件持久化数据
746+
# 注意:需要检查两个可能的目录名(plugin_data 和 plugins_data)
747+
# data/temp 目录可能被多个插件共享,不自动删除以防误删
748+
if delete_data and root_dir_name:
749+
data_base_dir = os.path.dirname(ppath) # data/
750+
751+
# 删除 data/plugin_data 下的插件持久化数据(单数形式,新版本)
752+
plugin_data_dir = os.path.join(
753+
data_base_dir, "plugin_data", root_dir_name
754+
)
755+
if os.path.exists(plugin_data_dir):
756+
try:
757+
remove_dir(plugin_data_dir)
758+
logger.info(
759+
f"已删除插件 {plugin_name} 的持久化数据 (plugin_data)"
760+
)
761+
except Exception as e:
762+
logger.warning(f"删除插件持久化数据失败 (plugin_data): {e!s}")
763+
764+
# 删除 data/plugins_data 下的插件持久化数据(复数形式,旧版本兼容)
765+
plugins_data_dir = os.path.join(
766+
data_base_dir, "plugins_data", root_dir_name
767+
)
768+
if os.path.exists(plugins_data_dir):
769+
try:
770+
remove_dir(plugins_data_dir)
771+
logger.info(
772+
f"已删除插件 {plugin_name} 的持久化数据 (plugins_data)"
773+
)
774+
except Exception as e:
775+
logger.warning(f"删除插件持久化数据失败 (plugins_data): {e!s}")
776+
724777
async def _unbind_plugin(self, plugin_name: str, plugin_module_path: str):
725778
"""解绑并移除一个插件。
726779

astrbot/dashboard/routes/plugin.py

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -395,9 +395,15 @@ async def uninstall_plugin(self):
395395

396396
post_data = await request.json
397397
plugin_name = post_data["name"]
398+
delete_config = post_data.get("delete_config", False)
399+
delete_data = post_data.get("delete_data", False)
398400
try:
399401
logger.info(f"正在卸载插件 {plugin_name}")
400-
await self.plugin_manager.uninstall_plugin(plugin_name)
402+
await self.plugin_manager.uninstall_plugin(
403+
plugin_name,
404+
delete_config=delete_config,
405+
delete_data=delete_data,
406+
)
401407
logger.info(f"卸载插件 {plugin_name} 成功")
402408
return Response().ok(None, "卸载成功").__dict__
403409
except Exception as e:

dashboard/src/components/shared/ExtensionCard.vue

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
import { ref, computed, inject } from 'vue';
33
import { useCustomizerStore } from "@/stores/customizer";
44
import { useModuleI18n } from '@/i18n/composables';
5+
import UninstallConfirmDialog from './UninstallConfirmDialog.vue';
56
67
const props = defineProps({
78
extension: {
@@ -31,6 +32,7 @@ const emit = defineEmits([
3132
]);
3233
3334
const reveal = ref(false);
35+
const showUninstallDialog = ref(false);
3436
3537
// 国际化
3638
const { tm } = useModuleI18n('features/extension');
@@ -55,19 +57,11 @@ const installExtension = async () => {
5557
};
5658
5759
const uninstallExtension = async () => {
58-
if (typeof $confirm !== "function") {
59-
console.error(tm("card.errors.confirmNotRegistered"));
60-
return;
61-
}
62-
63-
const confirmed = await $confirm({
64-
title: tm("dialogs.uninstall.title"),
65-
message: tm("dialogs.uninstall.message"),
66-
});
60+
showUninstallDialog.value = true;
61+
};
6762
68-
if (confirmed) {
69-
emit("uninstall", props.extension);
70-
}
63+
const handleUninstallConfirm = (options: { deleteConfig: boolean; deleteData: boolean }) => {
64+
emit("uninstall", props.extension, options);
7165
};
7266
7367
const toggleActivation = () => {
@@ -220,6 +214,12 @@ const viewReadme = () => {
220214
</v-card-actions>
221215
</v-card>
222216

217+
<!-- 卸载确认对话框 -->
218+
<UninstallConfirmDialog
219+
v-model="showUninstallDialog"
220+
@confirm="handleUninstallConfirm"
221+
/>
222+
223223
</template>
224224

225225
<style scoped>
Lines changed: 135 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,135 @@
1+
<template>
2+
<v-dialog
3+
v-model="show"
4+
max-width="500"
5+
@click:outside="handleCancel"
6+
@keydown.esc="handleCancel"
7+
>
8+
<v-card>
9+
<v-card-title class="text-h5">
10+
{{ tm('dialogs.uninstall.title') }}
11+
</v-card-title>
12+
13+
<v-card-text>
14+
<div class="mb-4">
15+
{{ tm('dialogs.uninstall.message') }}
16+
</div>
17+
18+
<v-divider class="my-4"></v-divider>
19+
20+
<div class="text-subtitle-2 mb-3">{{ t('core.common.actions') }}:</div>
21+
22+
<v-checkbox
23+
v-model="deleteConfig"
24+
:label="tm('dialogs.uninstall.deleteConfig')"
25+
color="warning"
26+
hide-details
27+
class="mb-2"
28+
>
29+
<template v-slot:append>
30+
<v-tooltip location="top">
31+
<template v-slot:activator="{ props }">
32+
<v-icon v-bind="props" size="small" color="grey">mdi-information-outline</v-icon>
33+
</template>
34+
<span>{{ tm('dialogs.uninstall.configHint') }}</span>
35+
</v-tooltip>
36+
</template>
37+
</v-checkbox>
38+
39+
<v-checkbox
40+
v-model="deleteData"
41+
:label="tm('dialogs.uninstall.deleteData')"
42+
color="error"
43+
hide-details
44+
>
45+
<template v-slot:append>
46+
<v-tooltip location="top">
47+
<template v-slot:activator="{ props }">
48+
<v-icon v-bind="props" size="small" color="grey">mdi-information-outline</v-icon>
49+
</template>
50+
<span>{{ tm('dialogs.uninstall.dataHint') }}</span>
51+
</v-tooltip>
52+
</template>
53+
</v-checkbox>
54+
55+
<v-alert
56+
v-if="deleteConfig || deleteData"
57+
type="warning"
58+
variant="tonal"
59+
density="compact"
60+
class="mt-4"
61+
>
62+
<template v-slot:prepend>
63+
<v-icon>mdi-alert</v-icon>
64+
</template>
65+
{{ t('messages.validation.operation_cannot_be_undone') }}
66+
</v-alert>
67+
</v-card-text>
68+
69+
<v-card-actions>
70+
<v-spacer></v-spacer>
71+
<v-btn
72+
color="grey"
73+
variant="text"
74+
@click="handleCancel"
75+
>
76+
{{ t('core.common.cancel') }}
77+
</v-btn>
78+
<v-btn
79+
color="error"
80+
variant="elevated"
81+
@click="handleConfirm"
82+
>
83+
{{ t('core.common.confirm') }}
84+
</v-btn>
85+
</v-card-actions>
86+
</v-card>
87+
</v-dialog>
88+
</template>
89+
90+
<script setup lang="ts">
91+
import { ref, watch } from 'vue';
92+
import { useI18n, useModuleI18n } from '@/i18n/composables';
93+
94+
const props = defineProps({
95+
modelValue: {
96+
type: Boolean,
97+
default: false,
98+
},
99+
});
100+
101+
const emit = defineEmits(['update:modelValue', 'confirm', 'cancel']);
102+
103+
const { t } = useI18n();
104+
const { tm } = useModuleI18n('features/extension');
105+
106+
const show = ref(props.modelValue);
107+
const deleteConfig = ref(false);
108+
const deleteData = ref(false);
109+
110+
watch(() => props.modelValue, (val) => {
111+
show.value = val;
112+
if (val) {
113+
// 重置选项
114+
deleteConfig.value = false;
115+
deleteData.value = false;
116+
}
117+
});
118+
119+
watch(show, (val) => {
120+
emit('update:modelValue', val);
121+
});
122+
123+
const handleConfirm = () => {
124+
emit('confirm', {
125+
deleteConfig: deleteConfig.value,
126+
deleteData: deleteData.value,
127+
});
128+
show.value = false;
129+
};
130+
131+
const handleCancel = () => {
132+
emit('cancel');
133+
show.value = false;
134+
};
135+
</script>

dashboard/src/i18n/locales/en-US/features/extension.json

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -97,7 +97,11 @@
9797
},
9898
"uninstall": {
9999
"title": "Confirm Deletion",
100-
"message": "Are you sure you want to delete this extension?"
100+
"message": "Are you sure you want to delete this extension?",
101+
"deleteConfig": "Also delete plugin configuration file",
102+
"deleteData": "Also delete plugin persistent data",
103+
"configHint": "Configuration file located in data/config directory",
104+
"dataHint": "Deletes data in data/plugin_data and data/plugins_data"
101105
},
102106
"install": {
103107
"title": "Install Extension",

dashboard/src/i18n/locales/en-US/messages/validation.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,5 +20,6 @@
2020
"invalid_date": "Please enter a valid date",
2121
"date_range": "Invalid date range",
2222
"upload_failed": "File upload failed",
23-
"network_error": "Network connection error, please try again"
23+
"network_error": "Network connection error, please try again",
24+
"operation_cannot_be_undone": "⚠️ This operation cannot be undone, please choose carefully!"
2425
}

dashboard/src/i18n/locales/zh-CN/features/extension.json

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -97,7 +97,11 @@
9797
},
9898
"uninstall": {
9999
"title": "删除确认",
100-
"message": "你确定要删除当前插件吗?"
100+
"message": "你确定要删除当前插件吗?",
101+
"deleteConfig": "同时删除插件配置文件",
102+
"deleteData": "同时删除插件持久化数据",
103+
"configHint": "配置文件位于 data/config 目录",
104+
"dataHint": "删除 data/plugin_data 和 data/plugins_data 目录下的数据"
101105
},
102106
"install": {
103107
"title": "安装插件",

dashboard/src/i18n/locales/zh-CN/messages/validation.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,5 +20,6 @@
2020
"invalid_date": "请输入有效的日期",
2121
"date_range": "日期范围无效",
2222
"upload_failed": "文件上传失败",
23-
"network_error": "网络连接错误,请重试"
23+
"network_error": "网络连接错误,请重试",
24+
"operation_cannot_be_undone": "⚠️ 此操作无法撤销,请谨慎选择!"
2425
}

0 commit comments

Comments
 (0)