From a1ff4e78a2f4d734a790b5b82c5123bd42c423c9 Mon Sep 17 00:00:00 2001 From: Pujit Mehrotra Date: Wed, 12 Nov 2025 12:02:21 -0500 Subject: [PATCH 1/5] fix(connect): disable api plugin init when unraid plugin is absent --- api/.env.development | 1 + .../unraid-api-plugin-connect/src/index.ts | 36 ++++++++++++++++++- 2 files changed, 36 insertions(+), 1 deletion(-) diff --git a/api/.env.development b/api/.env.development index 3261fcf359..35a4b30d71 100644 --- a/api/.env.development +++ b/api/.env.development @@ -32,3 +32,4 @@ CHOKIDAR_USEPOLLING=true LOG_TRANSPORT=console LOG_LEVEL=trace ENABLE_NEXT_DOCKER_RELEASE=true +SKIP_CONNECT_PLUGIN_CHECK=true diff --git a/packages/unraid-api-plugin-connect/src/index.ts b/packages/unraid-api-plugin-connect/src/index.ts index 21748673f7..afbe70cab8 100644 --- a/packages/unraid-api-plugin-connect/src/index.ts +++ b/packages/unraid-api-plugin-connect/src/index.ts @@ -1,5 +1,6 @@ import { Inject, Logger, Module } from '@nestjs/common'; import { ConfigModule, ConfigService } from '@nestjs/config'; +import { existsSync } from 'node:fs'; import { ConnectConfigPersister } from './config/config.persistence.js'; import { configFeature } from './config/connect.config.js'; @@ -8,6 +9,10 @@ import { ConnectModule } from './unraid-connect/connect.module.js'; export const adapter = 'nestjs'; +/** + * When the plugin is installed we expose the full Nest module graph. + * Configuration and proxy submodules only bootstrap in this branch. + */ @Module({ imports: [ConfigModule.forFeature(configFeature), ConnectModule, MothershipModule], providers: [ConnectConfigPersister], @@ -23,4 +28,33 @@ class ConnectPluginModule { } } -export const ApiModule = ConnectPluginModule; +/** + * Fallback module keeps the export shape intact but only warns operators. + * This makes `ApiModule` safe to import even when the plugin is absent. + */ +@Module({}) +export class DisabledConnectPluginModule { + logger = new Logger(DisabledConnectPluginModule.name); + onModuleInit() { + this.logger.warn( + 'Connect plugin is not installed, but is listed as an API plugin. Please run `unraid-api remove -b unraid-api-plugin-connect` to remove it.' + ); + } +} + +/** + * Local filesystem and env checks stay synchronous so we can branch at module load. + */ +const isConnectPluginInstalled = () => { + if (process.env.SKIP_CONNECT_PLUGIN_CHECK) { + return true; + } + return existsSync('/boot/config/plugins/dynamix.unraid.net.plg'); +}; + +/** + * Downstream code always imports `ApiModule`. We swap the implementation based on availability, + * avoiding dynamic module plumbing while keeping the DI graph predictable. + * Set `SKIP_CONNECT_PLUGIN_CHECK=true` in development to force the connected path. + */ +export const ApiModule = isConnectPluginInstalled() ? ConnectPluginModule : DisabledConnectPluginModule; From 3e438b875d3b21d8aeb9d39a64bcf29ee9d1a32a Mon Sep 17 00:00:00 2001 From: Pujit Mehrotra Date: Wed, 12 Nov 2025 12:12:51 -0500 Subject: [PATCH 2/5] fix: command to remove api plugin --- packages/unraid-api-plugin-connect/src/index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/unraid-api-plugin-connect/src/index.ts b/packages/unraid-api-plugin-connect/src/index.ts index afbe70cab8..767ea529c3 100644 --- a/packages/unraid-api-plugin-connect/src/index.ts +++ b/packages/unraid-api-plugin-connect/src/index.ts @@ -37,7 +37,7 @@ export class DisabledConnectPluginModule { logger = new Logger(DisabledConnectPluginModule.name); onModuleInit() { this.logger.warn( - 'Connect plugin is not installed, but is listed as an API plugin. Please run `unraid-api remove -b unraid-api-plugin-connect` to remove it.' + 'Connect plugin is not installed, but is listed as an API plugin. Please run `unraid-api plugins remove -b unraid-api-plugin-connect` to remove it.' ); } } From e03f163d74914ffe00e52e1432f6f8f17a40e28e Mon Sep 17 00:00:00 2001 From: Pujit Mehrotra Date: Wed, 12 Nov 2025 13:24:23 -0500 Subject: [PATCH 3/5] fix: accomodate setting SKIP_CONNECT_PLUGIN_CHECK=false --- packages/unraid-api-plugin-connect/src/index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/unraid-api-plugin-connect/src/index.ts b/packages/unraid-api-plugin-connect/src/index.ts index 767ea529c3..6936bdc683 100644 --- a/packages/unraid-api-plugin-connect/src/index.ts +++ b/packages/unraid-api-plugin-connect/src/index.ts @@ -46,7 +46,7 @@ export class DisabledConnectPluginModule { * Local filesystem and env checks stay synchronous so we can branch at module load. */ const isConnectPluginInstalled = () => { - if (process.env.SKIP_CONNECT_PLUGIN_CHECK) { + if (process.env.SKIP_CONNECT_PLUGIN_CHECK === 'true') { return true; } return existsSync('/boot/config/plugins/dynamix.unraid.net.plg'); From 26a152627015617cbc00ddc8dfc81a2cf084f72b Mon Sep 17 00:00:00 2001 From: Pujit Mehrotra Date: Thu, 13 Nov 2025 11:17:43 -0500 Subject: [PATCH 4/5] feat: attempt to auto-prune desynced api plugin --- .../unraid-api-plugin-connect/src/index.ts | 38 ++++++++++++++++++- 1 file changed, 36 insertions(+), 2 deletions(-) diff --git a/packages/unraid-api-plugin-connect/src/index.ts b/packages/unraid-api-plugin-connect/src/index.ts index 6936bdc683..1ce692025b 100644 --- a/packages/unraid-api-plugin-connect/src/index.ts +++ b/packages/unraid-api-plugin-connect/src/index.ts @@ -2,6 +2,8 @@ import { Inject, Logger, Module } from '@nestjs/common'; import { ConfigModule, ConfigService } from '@nestjs/config'; import { existsSync } from 'node:fs'; +import { execa } from 'execa'; + import { ConnectConfigPersister } from './config/config.persistence.js'; import { configFeature } from './config/connect.config.js'; import { MothershipModule } from './mothership-proxy/mothership.module.js'; @@ -35,10 +37,42 @@ class ConnectPluginModule { @Module({}) export class DisabledConnectPluginModule { logger = new Logger(DisabledConnectPluginModule.name); - onModuleInit() { + async onModuleInit() { + const removalCommand = 'unraid-api plugins remove -b --no-restart unraid-api-plugin-connect'; + this.logger.warn( - 'Connect plugin is not installed, but is listed as an API plugin. Please run `unraid-api plugins remove -b unraid-api-plugin-connect` to remove it.' + 'Connect plugin is not installed, but is listed as an API plugin. Attempting `%s` automatically.', + removalCommand ); + + try { + const { stdout, stderr } = await execa('unraid-api', [ + 'plugins', + 'remove', + '-b', + '--no-restart', + 'unraid-api-plugin-connect', + ]); + + if (stdout?.trim()) { + this.logger.debug(stdout.trim()); + } + + if (stderr?.trim()) { + this.logger.debug(stderr.trim()); + } + + this.logger.log( + 'Successfully completed `%s` to prune the stale connect plugin entry.', + removalCommand + ); + } catch (error) { + const message = + error instanceof Error + ? error.message + : 'Unknown error while removing stale connect plugin entry.'; + this.logger.error('Failed to run `%s`: %s', removalCommand, message); + } } } From c70bb8eb3ec8e3248de0086ad7ebbf02c7079aa3 Mon Sep 17 00:00:00 2001 From: Pujit Mehrotra Date: Thu, 13 Nov 2025 12:19:37 -0500 Subject: [PATCH 5/5] fix: allow restart to avoid race with api config persistence --- packages/unraid-api-plugin-connect/src/index.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/packages/unraid-api-plugin-connect/src/index.ts b/packages/unraid-api-plugin-connect/src/index.ts index 1ce692025b..d799da9765 100644 --- a/packages/unraid-api-plugin-connect/src/index.ts +++ b/packages/unraid-api-plugin-connect/src/index.ts @@ -38,7 +38,7 @@ class ConnectPluginModule { export class DisabledConnectPluginModule { logger = new Logger(DisabledConnectPluginModule.name); async onModuleInit() { - const removalCommand = 'unraid-api plugins remove -b --no-restart unraid-api-plugin-connect'; + const removalCommand = 'unraid-api plugins remove -b unraid-api-plugin-connect'; this.logger.warn( 'Connect plugin is not installed, but is listed as an API plugin. Attempting `%s` automatically.', @@ -50,7 +50,6 @@ export class DisabledConnectPluginModule { 'plugins', 'remove', '-b', - '--no-restart', 'unraid-api-plugin-connect', ]);