From da6df4169ca1431e5943e85a5429a92f126dd000 Mon Sep 17 00:00:00 2001 From: Its4Nik Date: Tue, 6 Jan 2026 18:59:51 +0100 Subject: [PATCH] refactor(plugins): use Blob URLs for plugin loading and eager initialization - Refactors plugin loading mechanism to utilize in-memory Blob objects and `URL.createObjectURL` instead of writing to temporary files. This improves security, performance, and resource management by eliminating filesystem I/O. - Updates the `PluginHandler` constructor to directly accept a `Logger` instance, simplifying logger configuration and promoting consistency. - Implements eager loading of all registered plugins during API startup, ensuring plugins are available immediately upon service initialization. --- apps/api/src/plugins/index.ts | 4 +++- packages/plugin-handler/src/index.ts | 22 +++++++++++++++------- 2 files changed, 18 insertions(+), 8 deletions(-) diff --git a/apps/api/src/plugins/index.ts b/apps/api/src/plugins/index.ts index 01d42c7d..5c1530e5 100644 --- a/apps/api/src/plugins/index.ts +++ b/apps/api/src/plugins/index.ts @@ -4,7 +4,9 @@ import BaseLogger from "../logger" const PluginHandler = new PluginHandlerFactory( DockStatDB._sqliteWrapper, - BaseLogger.getParentsForLoggerChaining() + BaseLogger.spawn("PluginHandler") ) +await PluginHandler.loadAllPlugins() + export default PluginHandler diff --git a/packages/plugin-handler/src/index.ts b/packages/plugin-handler/src/index.ts index 905108cc..a64f4e0d 100644 --- a/packages/plugin-handler/src/index.ts +++ b/packages/plugin-handler/src/index.ts @@ -33,8 +33,8 @@ class PluginHandler { private frontendHandler: PluginFrontendHandler private actionsHandler: FrontendActionsHandler - constructor(db: DB, loggerParents: string[] = []) { - this.logger = new Logger("PluginHandler", loggerParents) + constructor(db: DB, logger: Logger) { + this.logger = logger this.logger.debug("Initializing...") this.DB = db @@ -175,9 +175,14 @@ class PluginHandler { } public async loadAllPlugins() { + this.logger.info("Loading all plugins") const plugins = this.table.select(["*"]).all() const loadedPlugins = this.loadedPluginsMap + this.logger.debug( + `Found ${plugins.length} Plugins in DB & ${loadedPlugins.size} already loaded` + ) + const validPlugins = plugins.filter((p): p is DBPluginShemaT => { if (loadedPlugins.get(p.id as number)) { return false @@ -189,11 +194,12 @@ class PluginHandler { const imports = await Promise.allSettled( validPlugins.map(async (plugin) => { - const tempPath = join(tmpdir(), `/dockstat-plugins/plugin-${plugin.id}-${Date.now()}.js`) - this.logger.debug(`Writing plugin ${plugin.id} to ${tempPath}`) + let blobUrl: string | null = null try { - await Bun.write(tempPath, plugin.plugin) - const { default: mod } = await import(/* @vite-ignore */ tempPath) + const blob = new Blob([plugin.plugin], { type: "text/javascript" }) + blobUrl = URL.createObjectURL(blob) + + const { default: mod } = await import(/* @vite-ignore */ blobUrl) mod.id = plugin.id as number @@ -232,7 +238,9 @@ class PluginHandler { this.logger.error(`Failed to import plugin ${plugin.id}: ${err}`) return null } finally { - await unlink(tempPath).catch(() => {}) + if (blobUrl) { + URL.revokeObjectURL(blobUrl) + } } }) )