mirror of
https://github.com/NapNeko/NapCatQQ.git
synced 2026-03-01 16:20:25 +00:00
Move plugin data to config path; improve uninstall UI
Change plugin data storage to use core.context.pathWrapper.configPath/plugins/<id> instead of pluginPath/data across OB11 plugin managers. Ensure plugin config directory is created when building the plugin context, use the central path for cleanup/uninstall, and update getPluginDataPath accordingly. Update web UI uninstall flow to prompt for cleaning configuration files using dialog.confirm (showing the config path) and performUninstall helper instead of window.confirm. Also include rebuilt native binaries (napi2native) for Linux x64 and arm64.
This commit is contained in:
Binary file not shown.
Binary file not shown.
@@ -196,9 +196,14 @@ export class OB11PluginMangerAdapter extends IOB11NetworkAdapter<PluginConfig> i
|
|||||||
* 创建插件上下文
|
* 创建插件上下文
|
||||||
*/
|
*/
|
||||||
private createPluginContext (entry: PluginEntry): NapCatPluginContext {
|
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');
|
const configPath = path.join(dataPath, 'config.json');
|
||||||
|
|
||||||
|
// 确保插件配置目录存在
|
||||||
|
if (!fs.existsSync(dataPath)) {
|
||||||
|
fs.mkdirSync(dataPath, { recursive: true });
|
||||||
|
}
|
||||||
|
|
||||||
// 创建插件专用日志器
|
// 创建插件专用日志器
|
||||||
const pluginPrefix = `[Plugin: ${entry.id}]`;
|
const pluginPrefix = `[Plugin: ${entry.id}]`;
|
||||||
const coreLogger = this.logger;
|
const coreLogger = this.logger;
|
||||||
@@ -358,7 +363,7 @@ export class OB11PluginMangerAdapter extends IOB11NetworkAdapter<PluginConfig> i
|
|||||||
}
|
}
|
||||||
|
|
||||||
const pluginPath = entry.pluginPath;
|
const pluginPath = entry.pluginPath;
|
||||||
const dataPath = path.join(pluginPath, 'data');
|
const dataPath = path.join(this.core.context.pathWrapper.configPath, 'plugins', pluginId);
|
||||||
|
|
||||||
if (entry.loaded) {
|
if (entry.loaded) {
|
||||||
await this.unloadPlugin(entry);
|
await this.unloadPlugin(entry);
|
||||||
@@ -372,7 +377,7 @@ export class OB11PluginMangerAdapter extends IOB11NetworkAdapter<PluginConfig> i
|
|||||||
fs.rmSync(pluginPath, { recursive: true, force: true });
|
fs.rmSync(pluginPath, { recursive: true, force: true });
|
||||||
}
|
}
|
||||||
|
|
||||||
// 清理数据
|
// 清理插件配置数据
|
||||||
if (cleanData && fs.existsSync(dataPath)) {
|
if (cleanData && fs.existsSync(dataPath)) {
|
||||||
fs.rmSync(dataPath, { recursive: true, force: true });
|
fs.rmSync(dataPath, { recursive: true, force: true });
|
||||||
}
|
}
|
||||||
@@ -440,11 +445,7 @@ export class OB11PluginMangerAdapter extends IOB11NetworkAdapter<PluginConfig> i
|
|||||||
* 获取插件数据目录路径
|
* 获取插件数据目录路径
|
||||||
*/
|
*/
|
||||||
public getPluginDataPath (pluginId: string): string {
|
public getPluginDataPath (pluginId: string): string {
|
||||||
const entry = this.plugins.get(pluginId);
|
return path.join(this.core.context.pathWrapper.configPath, 'plugins', pluginId);
|
||||||
if (!entry) {
|
|
||||||
throw new Error(`Plugin ${pluginId} not found`);
|
|
||||||
}
|
|
||||||
return path.join(entry.pluginPath, 'data');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -173,9 +173,14 @@ export class OB11PluginManager extends IOB11NetworkAdapter<PluginConfig> impleme
|
|||||||
* 创建插件上下文
|
* 创建插件上下文
|
||||||
*/
|
*/
|
||||||
private createPluginContext (entry: PluginEntry): NapCatPluginContext {
|
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');
|
const configPath = path.join(dataPath, 'config.json');
|
||||||
|
|
||||||
|
// 确保插件配置目录存在
|
||||||
|
if (!fs.existsSync(dataPath)) {
|
||||||
|
fs.mkdirSync(dataPath, { recursive: true });
|
||||||
|
}
|
||||||
|
|
||||||
// 创建插件专用日志器
|
// 创建插件专用日志器
|
||||||
const pluginPrefix = `[Plugin: ${entry.id}]`;
|
const pluginPrefix = `[Plugin: ${entry.id}]`;
|
||||||
const coreLogger = this.logger;
|
const coreLogger = this.logger;
|
||||||
@@ -323,7 +328,7 @@ export class OB11PluginManager extends IOB11NetworkAdapter<PluginConfig> impleme
|
|||||||
}
|
}
|
||||||
|
|
||||||
const pluginPath = entry.pluginPath;
|
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);
|
await this.unloadPlugin(entry);
|
||||||
@@ -336,7 +341,7 @@ export class OB11PluginManager extends IOB11NetworkAdapter<PluginConfig> impleme
|
|||||||
fs.rmSync(pluginPath, { recursive: true, force: true });
|
fs.rmSync(pluginPath, { recursive: true, force: true });
|
||||||
}
|
}
|
||||||
|
|
||||||
// 清理数据
|
// 清理插件配置数据
|
||||||
if (cleanData && fs.existsSync(dataPath)) {
|
if (cleanData && fs.existsSync(dataPath)) {
|
||||||
fs.rmSync(dataPath, { recursive: true, force: true });
|
fs.rmSync(dataPath, { recursive: true, force: true });
|
||||||
}
|
}
|
||||||
@@ -404,11 +409,7 @@ export class OB11PluginManager extends IOB11NetworkAdapter<PluginConfig> impleme
|
|||||||
* 获取插件数据目录路径
|
* 获取插件数据目录路径
|
||||||
*/
|
*/
|
||||||
public getPluginDataPath (pluginId: string): string {
|
public getPluginDataPath (pluginId: string): string {
|
||||||
const entry = this.plugins.get(pluginId);
|
return path.join(this.core.context.pathWrapper.configPath, 'plugins', pluginId);
|
||||||
if (!entry) {
|
|
||||||
throw new Error(`Plugin ${pluginId} not found`);
|
|
||||||
}
|
|
||||||
return path.join(entry.pluginPath, 'data');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -66,40 +66,49 @@ export default function PluginPage () {
|
|||||||
content: (
|
content: (
|
||||||
<div className="flex flex-col gap-2">
|
<div className="flex flex-col gap-2">
|
||||||
<p>确定要卸载插件「{plugin.name}」吗? 此操作不可恢复。</p>
|
<p>确定要卸载插件「{plugin.name}」吗? 此操作不可恢复。</p>
|
||||||
<p className="text-small text-default-500">如果插件创建了数据文件,是否一并删除?</p>
|
<p className="text-small text-default-500">如果插件创建了配置文件,是否一并删除?</p>
|
||||||
</div>
|
</div>
|
||||||
),
|
),
|
||||||
// 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 () => {
|
onConfirm: async () => {
|
||||||
// Ask for data cleanup
|
// Ask for data cleanup
|
||||||
// Since we are in an async callback, we can use another dialog or confirm.
|
dialog.confirm({
|
||||||
// Native confirm is ugly but works reliably for logic:
|
title: '删除配置',
|
||||||
const cleanData = window.confirm(`是否同时清理插件「${plugin.name}」的数据文件?\n点击“确定”清理数据,点击“取消”仅卸载插件。`);
|
content: (
|
||||||
|
<div className="flex flex-col gap-2">
|
||||||
const loadingToast = toast.loading('卸载中...');
|
<p>是否同时清理插件「{plugin.name}」的配置文件?</p>
|
||||||
try {
|
<div className="text-small text-default-500">
|
||||||
await PluginManager.uninstallPlugin(plugin.id, cleanData);
|
<p>配置目录: config/plugins/{plugin.id}</p>
|
||||||
toast.success('卸载成功', { id: loadingToast });
|
<p>点击"确定"清理配置,点击"取消"仅卸载插件。</p>
|
||||||
loadPlugins();
|
</div>
|
||||||
resolve();
|
</div>
|
||||||
} catch (e: any) {
|
),
|
||||||
toast.error(e.message, { id: loadingToast });
|
confirmText: '清理并卸载',
|
||||||
reject(e);
|
cancelText: '仅卸载',
|
||||||
}
|
onConfirm: async () => {
|
||||||
|
await performUninstall(true);
|
||||||
|
},
|
||||||
|
onCancel: async () => {
|
||||||
|
await performUninstall(false);
|
||||||
|
}
|
||||||
|
});
|
||||||
},
|
},
|
||||||
onCancel: () => {
|
onCancel: () => {
|
||||||
resolve();
|
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);
|
||||||
|
}
|
||||||
|
};
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user