Skip to content
Merged
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,19 @@ describe('PluginManagementService', () => {
if (key === 'api.plugins') {
return configStore ?? defaultValue ?? [];
}
if (key === 'api') {
return { plugins: configStore ?? defaultValue ?? [] };
}
return defaultValue;
}),
set: vi.fn((key: string, value: unknown) => {
if (key === 'api' && typeof value === 'object' && value !== null) {
// @ts-expect-error - value is an object
if (Array.isArray(value.plugins)) {
// @ts-expect-error - value is an object
configStore = [...value.plugins];
}
}
if (key === 'api.plugins' && Array.isArray(value)) {
configStore = [...value];
}
Expand Down
11 changes: 7 additions & 4 deletions api/src/unraid-api/plugin/plugin-management.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,8 +56,7 @@ export class PluginManagementService {
}
pluginSet.add(plugin);
});
// @ts-expect-error - This is a valid config key
this.configService.set<string[]>('api.plugins', Array.from(pluginSet));
this.updatePluginsConfig(Array.from(pluginSet));
return added;
}

Expand All @@ -71,11 +70,15 @@ export class PluginManagementService {
const pluginSet = new Set(this.plugins);
const removed = plugins.filter((plugin) => pluginSet.delete(plugin));
const pluginsArray = Array.from(pluginSet);
// @ts-expect-error - This is a valid config key
this.configService.set('api.plugins', pluginsArray);
this.updatePluginsConfig(pluginsArray);
return removed;
}

private updatePluginsConfig(plugins: string[]) {
const apiConfig = this.configService.get<ApiConfig>('api');
this.configService.set('api', { ...apiConfig, plugins });
}

/**
* Install bundle / unbundled plugins using npm or direct with the config.
*
Expand Down
33 changes: 21 additions & 12 deletions packages/unraid-api-plugin-connect/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,21 +37,32 @@ class ConnectPluginModule {
@Module({})
export class DisabledConnectPluginModule {
logger = new Logger(DisabledConnectPluginModule.name);
async onModuleInit() {
const removalCommand = 'unraid-api plugins remove -b unraid-api-plugin-connect';

constructor(@Inject(ConfigService) private readonly configService: ConfigService) {}

async onModuleInit() {
const removalCommand = 'unraid-api plugins remove -b unraid-api-plugin-connect --no-restart';
this.logger.warn(
'Connect plugin is not installed, but is listed as an API plugin. Attempting `%s` automatically.',
removalCommand
);

const apiConfig = this.configService.get('api') || {};
const plugins = apiConfig.plugins || [];
const updatedPlugins = plugins.filter((p: string) => p !== 'unraid-api-plugin-connect');
this.configService.set('api', { ...apiConfig, plugins: updatedPlugins });

// Allow config subscription to flush to disk (25ms buffer + margin)
await new Promise((resolve) => setTimeout(resolve, 1000));
const debugPlugins = this.configService.get<string[]>('api.plugins') || [];
this.logger.debug('Plugins before running removal command: %o', debugPlugins);

try {
const { stdout, stderr } = await execa('unraid-api', [
'plugins',
'remove',
'-b',
'unraid-api-plugin-connect',
]);
const { stdout, stderr } = await execa(
'unraid-api',
['plugins', 'remove', '-b', 'unraid-api-plugin-connect', '--no-restart'],
{ shell: 'bash', extendEnv: true }
);

if (stdout?.trim()) {
this.logger.debug(stdout.trim());
Expand All @@ -67,10 +78,8 @@ export class DisabledConnectPluginModule {
);
} 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);
error instanceof Error ? error.message : 'Unknown error while restarting API.';
this.logger.error('Failed to restart API: %s', message);
}
}
}
Expand Down
Loading