Refactor plugin manager to support only directory plugins

Removed support for single-file plugins in OB11PluginMangerAdapter, simplifying plugin identification to use directory names as unique IDs. Updated related logic in the backend API to align with this change, ensuring consistent plugin management and status handling.
This commit is contained in:
手瓜一十雪 2026-01-28 14:18:44 +08:00
parent d680328762
commit 574c257591
2 changed files with 28 additions and 108 deletions

View File

@ -167,28 +167,22 @@ export class OB11PluginMangerAdapter extends IOB11NetworkAdapter<PluginConfig> {
const items = fs.readdirSync(this.pluginPath, { withFileTypes: true }); const items = fs.readdirSync(this.pluginPath, { withFileTypes: true });
const pluginConfig = this.loadPluginConfig(); const pluginConfig = this.loadPluginConfig();
// 扫描文件和目录 // 扫描文件和目录 (Only support directories as plugins now)
for (const item of items) { for (const item of items) {
let pluginName = ''; if (!item.isDirectory()) {
if (item.isFile()) {
pluginName = path.parse(item.name).name;
} else if (item.isDirectory()) {
pluginName = item.name;
}
// Check if plugin is disabled in config
if (pluginConfig[pluginName] === false) {
this.logger.log(`[Plugin Adapter] Plugin ${pluginName} is disabled in config, skipping`);
continue; continue;
} }
if (item.isFile()) { const pluginId = item.name;
// 处理单文件插件
await this.loadFilePlugin(item.name); // Check if plugin is disabled in config
} else if (item.isDirectory()) { if (pluginConfig[pluginId] === false) {
// 处理目录插件 this.logger.log(`[Plugin Adapter] Plugin ${pluginId} is disabled in config, skipping`);
await this.loadDirectoryPlugin(item.name); continue;
} }
// 处理目录插件
await this.loadDirectoryPlugin(item.name);
} }
this.logger.log( this.logger.log(
@ -199,49 +193,7 @@ export class OB11PluginMangerAdapter extends IOB11NetworkAdapter<PluginConfig> {
} }
} }
/** // loadFilePlugin removed
* (.mjs, .js)
*/
public async loadFilePlugin (filename: string): Promise<void> {
// 只处理支持的文件类型
if (!this.isSupportedFile(filename)) {
return;
}
const filePath = path.join(this.pluginPath, filename);
const pluginName = path.parse(filename).name;
const pluginConfig = this.loadPluginConfig();
// Check if plugin is disabled in config
if (pluginConfig[pluginName] === false) {
this.logger.log(`[Plugin Adapter] Plugin ${pluginName} is disabled by user`);
return;
}
try {
const module = await this.importModule(filePath);
if (!this.isValidPluginModule(module)) {
this.logger.logWarn(
`[Plugin Adapter] File ${filename} is not a valid plugin (missing plugin methods)`
);
return;
}
const plugin: LoadedPlugin = {
name: pluginName,
pluginPath: this.pluginPath,
entryPath: filePath,
module,
};
await this.registerPlugin(plugin);
} catch (error) {
this.logger.logError(
`[Plugin Adapter] Error loading file plugin ${filename}:`,
error
);
}
}
/** /**
* *
@ -249,16 +201,10 @@ export class OB11PluginMangerAdapter extends IOB11NetworkAdapter<PluginConfig> {
public async loadDirectoryPlugin (dirname: string): Promise<void> { public async loadDirectoryPlugin (dirname: string): Promise<void> {
const pluginDir = path.join(this.pluginPath, dirname); const pluginDir = path.join(this.pluginPath, dirname);
const pluginConfig = this.loadPluginConfig(); const pluginConfig = this.loadPluginConfig();
const pluginId = dirname; // Use directory name as unique ID
// Ideally we'd get the name from package.json first, but we can use dirname as a fallback identifier initially. if (pluginConfig[pluginId] === false) {
// However, the list scan uses item.name (dirname) as the key. Let's stick to using dirname/filename as the config key for simplicity and consistency. this.logger.log(`[Plugin Adapter] Plugin ${pluginId} is disabled by user`);
// Wait, package.json name might override. But for management, consistent ID is better.
// Let's check config after parsing package.json?
// User expects to disable 'plugin-name'. But if multiple folders have same name? Not handled.
// Let's use dirname as the key for config to be consistent with file system.
if (pluginConfig[dirname] === false) {
this.logger.log(`[Plugin Adapter] Plugin ${dirname} is disabled by user`);
return; return;
} }
@ -279,9 +225,6 @@ export class OB11PluginMangerAdapter extends IOB11NetworkAdapter<PluginConfig> {
} }
} }
// Check if disabled by package name IF package.json exists?
// No, file system name is more reliable ID for resource management here.
// 确定入口文件 // 确定入口文件
const entryFile = this.findEntryFile(pluginDir, packageJson); const entryFile = this.findEntryFile(pluginDir, packageJson);
if (!entryFile) { if (!entryFile) {
@ -302,7 +245,7 @@ export class OB11PluginMangerAdapter extends IOB11NetworkAdapter<PluginConfig> {
} }
const plugin: LoadedPlugin = { const plugin: LoadedPlugin = {
name: packageJson?.name || dirname, name: pluginId, // Use Directory Name as Internal Plugin Name/ID
version: packageJson?.version, version: packageJson?.version,
pluginPath: pluginDir, pluginPath: pluginDir,
entryPath, entryPath,
@ -345,13 +288,7 @@ export class OB11PluginMangerAdapter extends IOB11NetworkAdapter<PluginConfig> {
return null; return null;
} }
/**
*
*/
private isSupportedFile (filename: string): boolean {
const ext = path.extname(filename).toLowerCase();
return ['.mjs', '.js'].includes(ext);
}
/** /**
* *
@ -632,18 +569,8 @@ export class OB11PluginMangerAdapter extends IOB11NetworkAdapter<PluginConfig> {
await this.unloadPlugin(pluginName); await this.unloadPlugin(pluginName);
// 重新加载插件 // 重新加载插件
// Use logic to re-determine if it is directory or file based on original paths // We assume pluginName IS the directory name (the ID)
// Note: we can't fully trust fs status if it's gone. await this.loadDirectoryPlugin(pluginName);
const isDirectory =
plugin.pluginPath !== this.pluginPath; // Simple check: if path is nested, it's a dir plugin
if (isDirectory) {
const dirname = path.basename(plugin.pluginPath);
await this.loadDirectoryPlugin(dirname);
} else {
const filename = path.basename(plugin.entryPath);
await this.loadFilePlugin(filename);
}
this.logger.log( this.logger.log(
`[Plugin Adapter] Plugin ${pluginName} reloaded successfully` `[Plugin Adapter] Plugin ${pluginName} reloaded successfully`

View File

@ -132,13 +132,8 @@ export const SetPluginStatusHandler: RequestHandler = async (req, res) => {
return sendError(res, 'Plugin Manager not found'); return sendError(res, 'Plugin Manager not found');
} }
// Calculate ID from filename (remove ext if file) // ID IS the filename (directory name) now
// Or just use the logic consistent with loadPlugins const id = filename;
let id = filename;
// If it has extension .js/.mjs, remove it to get the ID used in config
if (filename.endsWith('.js') || filename.endsWith('.mjs')) {
id = path.parse(filename).name;
}
try { try {
pluginManager.setPluginStatus(id, enable); pluginManager.setPluginStatus(id, enable);
@ -148,19 +143,17 @@ export const SetPluginStatusHandler: RequestHandler = async (req, res) => {
const pluginPath = pluginManager.getPluginPath(); const pluginPath = pluginManager.getPluginPath();
const fullPath = path.join(pluginPath, filename); const fullPath = path.join(pluginPath, filename);
if (fs.statSync(fullPath).isDirectory()) { if (fs.existsSync(fullPath) && fs.statSync(fullPath).isDirectory()) {
await pluginManager.loadDirectoryPlugin(filename); await pluginManager.loadDirectoryPlugin(filename);
} else { } else {
await pluginManager.loadFilePlugin(filename); return sendError(res, 'Plugin directory not found: ' + filename);
} }
} else { } else {
// Disabling is handled inside setPluginStatus usually if implemented, // Disabling behavior is managed by setPluginStatus which updates config
// OR we can explicitly unload here using the loaded name. // If we want to unload immediately?
// The Manager's setPluginStatus implementation (if added) might logic this out. // The config update will prevent load on next startup.
// But our current Manager implementation just saves config. // Does pluginManager.setPluginStatus unload it?
// Wait, I updated Manager to try to unload. // Inspecting pluginManager.setPluginStatus... it iterates and unloads if match found.
// Let's rely on Manager's setPluginStatus or do it here?
// I implemented a basic unload loop in Manager.setPluginStatus.
} }
return sendSuccess(res, { message: 'Status updated successfully' }); return sendSuccess(res, { message: 'Status updated successfully' });