diff --git a/packages/napcat-native/napi2native/napi2native.linux.arm64.node b/packages/napcat-native/napi2native/napi2native.linux.arm64.node index 5c6145eb..cc5821df 100644 Binary files a/packages/napcat-native/napi2native/napi2native.linux.arm64.node and b/packages/napcat-native/napi2native/napi2native.linux.arm64.node differ diff --git a/packages/napcat-native/napi2native/napi2native.linux.x64.node b/packages/napcat-native/napi2native/napi2native.linux.x64.node index f48ccb68..24a666a9 100644 Binary files a/packages/napcat-native/napi2native/napi2native.linux.x64.node and b/packages/napcat-native/napi2native/napi2native.linux.x64.node differ diff --git a/packages/napcat-onebot/network/plugin-manger.ts b/packages/napcat-onebot/network/plugin-manger.ts index 467d0ec0..09efc0bd 100644 --- a/packages/napcat-onebot/network/plugin-manger.ts +++ b/packages/napcat-onebot/network/plugin-manger.ts @@ -196,9 +196,14 @@ export class OB11PluginMangerAdapter extends IOB11NetworkAdapter i * 创建插件上下文 */ private createPluginContext (entry: PluginEntry): NapCatPluginContext { - const dataPath = path.join(entry.pluginPath, 'data'); + const dataPath = path.join(this.core.context.pathWrapper.configPath, 'plugins', entry.id); const configPath = path.join(dataPath, 'config.json'); + // 确保插件配置目录存在 + if (!fs.existsSync(dataPath)) { + fs.mkdirSync(dataPath, { recursive: true }); + } + // 创建插件专用日志器 const pluginPrefix = `[Plugin: ${entry.id}]`; const coreLogger = this.logger; @@ -358,7 +363,7 @@ export class OB11PluginMangerAdapter extends IOB11NetworkAdapter i } const pluginPath = entry.pluginPath; - const dataPath = path.join(pluginPath, 'data'); + const dataPath = path.join(this.core.context.pathWrapper.configPath, 'plugins', pluginId); if (entry.loaded) { await this.unloadPlugin(entry); @@ -372,7 +377,7 @@ export class OB11PluginMangerAdapter extends IOB11NetworkAdapter i fs.rmSync(pluginPath, { recursive: true, force: true }); } - // 清理数据 + // 清理插件配置数据 if (cleanData && fs.existsSync(dataPath)) { fs.rmSync(dataPath, { recursive: true, force: true }); } @@ -440,11 +445,7 @@ export class OB11PluginMangerAdapter extends IOB11NetworkAdapter i * 获取插件数据目录路径 */ public getPluginDataPath (pluginId: string): string { - const entry = this.plugins.get(pluginId); - if (!entry) { - throw new Error(`Plugin ${pluginId} not found`); - } - return path.join(entry.pluginPath, 'data'); + return path.join(this.core.context.pathWrapper.configPath, 'plugins', pluginId); } /** diff --git a/packages/napcat-onebot/network/plugin/manager.ts b/packages/napcat-onebot/network/plugin/manager.ts index 57918e3d..871a7c10 100644 --- a/packages/napcat-onebot/network/plugin/manager.ts +++ b/packages/napcat-onebot/network/plugin/manager.ts @@ -173,9 +173,14 @@ export class OB11PluginManager extends IOB11NetworkAdapter impleme * 创建插件上下文 */ private createPluginContext (entry: PluginEntry): NapCatPluginContext { - const dataPath = path.join(entry.pluginPath, 'data'); + const dataPath = path.join(this.core.context.pathWrapper.configPath, 'plugins', entry.id); const configPath = path.join(dataPath, 'config.json'); + // 确保插件配置目录存在 + if (!fs.existsSync(dataPath)) { + fs.mkdirSync(dataPath, { recursive: true }); + } + // 创建插件专用日志器 const pluginPrefix = `[Plugin: ${entry.id}]`; const coreLogger = this.logger; @@ -323,7 +328,7 @@ export class OB11PluginManager extends IOB11NetworkAdapter impleme } const pluginPath = entry.pluginPath; - const dataPath = path.join(pluginPath, 'data'); + const dataPath = path.join(this.core.context.pathWrapper.configPath, 'plugins', pluginId); // 先卸载插件 await this.unloadPlugin(entry); @@ -336,7 +341,7 @@ export class OB11PluginManager extends IOB11NetworkAdapter impleme fs.rmSync(pluginPath, { recursive: true, force: true }); } - // 清理数据 + // 清理插件配置数据 if (cleanData && fs.existsSync(dataPath)) { fs.rmSync(dataPath, { recursive: true, force: true }); } @@ -404,11 +409,7 @@ export class OB11PluginManager extends IOB11NetworkAdapter impleme * 获取插件数据目录路径 */ public getPluginDataPath (pluginId: string): string { - const entry = this.plugins.get(pluginId); - if (!entry) { - throw new Error(`Plugin ${pluginId} not found`); - } - return path.join(entry.pluginPath, 'data'); + return path.join(this.core.context.pathWrapper.configPath, 'plugins', pluginId); } /** diff --git a/packages/napcat-webui-frontend/src/pages/dashboard/plugin.tsx b/packages/napcat-webui-frontend/src/pages/dashboard/plugin.tsx index 667ab4b6..fc9cff2d 100644 --- a/packages/napcat-webui-frontend/src/pages/dashboard/plugin.tsx +++ b/packages/napcat-webui-frontend/src/pages/dashboard/plugin.tsx @@ -66,40 +66,49 @@ export default function PluginPage () { content: (

确定要卸载插件「{plugin.name}」吗? 此操作不可恢复。

-

如果插件创建了数据文件,是否一并删除?

+

如果插件创建了配置文件,是否一并删除?

), - // This 'dialog' utility might not support returning a value from UI interacting. - // We might need to implement a custom confirmation flow if we want a checkbox. - // Alternatively, use two buttons? "Uninstall & Clean", "Uninstall Only"? - // Standard dialog usually has Confirm/Cancel. - // Let's stick to a simpler "Uninstall" and then maybe a second prompt? Or just clean data? - // User requested: "Uninstall prompts whether to clean data". - // Let's use `window.confirm` for the second step or assume `dialog.confirm` is flexible enough? - // I will implement a two-step confirmation or try to modify the dialog hook if visible (not visible here). - // Let's use a standard `window.confirm` for the data cleanup question if the custom dialog doesn't support complex return. - // Better: Inside onConfirm, ask again? onConfirm: async () => { // Ask for data cleanup - // Since we are in an async callback, we can use another dialog or confirm. - // Native confirm is ugly but works reliably for logic: - const cleanData = window.confirm(`是否同时清理插件「${plugin.name}」的数据文件?\n点击“确定”清理数据,点击“取消”仅卸载插件。`); - - const loadingToast = toast.loading('卸载中...'); - try { - await PluginManager.uninstallPlugin(plugin.id, cleanData); - toast.success('卸载成功', { id: loadingToast }); - loadPlugins(); - resolve(); - } catch (e: any) { - toast.error(e.message, { id: loadingToast }); - reject(e); - } + dialog.confirm({ + title: '删除配置', + content: ( +
+

是否同时清理插件「{plugin.name}」的配置文件?

+
+

配置目录: config/plugins/{plugin.id}

+

点击"确定"清理配置,点击"取消"仅卸载插件。

+
+
+ ), + confirmText: '清理并卸载', + cancelText: '仅卸载', + onConfirm: async () => { + await performUninstall(true); + }, + onCancel: async () => { + await performUninstall(false); + } + }); }, onCancel: () => { resolve(); } }); + + const performUninstall = async (cleanData: boolean) => { + const loadingToast = toast.loading('卸载中...'); + try { + await PluginManager.uninstallPlugin(plugin.id, cleanData); + toast.success('卸载成功', { id: loadingToast }); + loadPlugins(); + resolve(); + } catch (e: any) { + toast.error(e.message, { id: loadingToast }); + reject(e); + } + }; }); };