diff --git a/.changeset/yellow-flies-sniff.md b/.changeset/yellow-flies-sniff.md new file mode 100644 index 000000000..4c372bb29 --- /dev/null +++ b/.changeset/yellow-flies-sniff.md @@ -0,0 +1,10 @@ +--- +'@verdaccio/search-indexer': patch +'@verdaccio/server': patch +'@verdaccio/loaders': patch +'@verdaccio/store': patch +'@verdaccio/auth': patch +'@verdaccio/web': patch +--- + +fix: add legacyMergeConfigs legacy plugins diff --git a/packages/auth/src/auth.ts b/packages/auth/src/auth.ts index 277206ffe..9c4ca157a 100644 --- a/packages/auth/src/auth.ts +++ b/packages/auth/src/auth.ts @@ -123,6 +123,7 @@ class Auth implements IAuthMiddleware, TokenEncryption, pluginUtils.IBasicAuth { typeof allow_publish !== 'undefined' ); }, + false, this.config?.serverSettings?.pluginPrefix, PLUGIN_CATEGORY.AUTHENTICATION ); diff --git a/packages/loaders/src/plugin-async-loader.ts b/packages/loaders/src/plugin-async-loader.ts index 691a59f12..4d308038d 100644 --- a/packages/loaders/src/plugin-async-loader.ts +++ b/packages/loaders/src/plugin-async-loader.ts @@ -1,5 +1,6 @@ import buildDebug from 'debug'; import fs from 'fs'; +import _ from 'lodash'; import { dirname, isAbsolute, join, resolve } from 'path'; import { pluginUtils } from '@verdaccio/core'; @@ -15,6 +16,10 @@ async function isDirectory(pathFolder: string) { return stat.isDirectory(); } +function mergeConfig(appConfig: unknown, pluginConfig: unknown) { + return _.merge({}, appConfig, pluginConfig); +} + // type Plugins = // | pluginUtils.Auth // | pluginUtils.Storage @@ -47,6 +52,7 @@ export async function asyncLoadPlugin>( pluginConfigs: any = {}, pluginOptions: pluginUtils.PluginOptions, sanityCheck: (plugin: PluginType) => boolean, + legacyMergeConfigs: boolean = false, prefix: string = 'verdaccio', pluginCategory: string = '' ): Promise[]> { @@ -82,7 +88,12 @@ export async function asyncLoadPlugin>( logger.error(a, b); }); if (plugin && isValid(plugin)) { - plugin = executePlugin(plugin, pluginConfigs[pluginId], pluginOptions); + plugin = executePlugin( + plugin, + pluginConfigs[pluginId], + pluginOptions, + legacyMergeConfigs + ); if (!sanityCheck(plugin)) { logger.error( { content: externalFilePlugin }, @@ -115,7 +126,7 @@ export async function asyncLoadPlugin>( logger.error(a, b); }); if (plugin && isValid(plugin)) { - plugin = executePlugin(plugin, pluginConfigs[pluginId], pluginOptions); + plugin = executePlugin(plugin, pluginConfigs[pluginId], pluginOptions, legacyMergeConfigs); if (!sanityCheck(plugin)) { logger.error({ content: pluginName }, "@{content} doesn't look like a valid plugin"); continue; @@ -143,8 +154,15 @@ export async function asyncLoadPlugin>( export function executePlugin( plugin: PluginType, pluginConfig: unknown, - pluginOptions: pluginUtils.PluginOptions + pluginOptions: pluginUtils.PluginOptions, + legacyMergeConfigs: boolean = false ): PluginType { + // this is a legacy support for plugins that are not using the new API + if (legacyMergeConfigs) { + debug('>>> plugin merge config enabled'); + let originalConfig = pluginOptions.config; + pluginConfig = mergeConfig(originalConfig, pluginConfig); + } if (isES6(plugin)) { debug('plugin is ES6'); // @ts-expect-error no relevant for the code diff --git a/packages/loaders/test/partials/test-plugin-storage/verdaccio-plugin/index.js b/packages/loaders/test/partials/test-plugin-storage/verdaccio-plugin/index.js index c1291e321..92e66cc68 100644 --- a/packages/loaders/test/partials/test-plugin-storage/verdaccio-plugin/index.js +++ b/packages/loaders/test/partials/test-plugin-storage/verdaccio-plugin/index.js @@ -1,7 +1,13 @@ -function ValidVerdaccioPlugin() { - return { - authenticate: function () {}, - }; +class ValidVerdaccioPlugin { + config; + options; + constructor(config, options) { + console.log('ValidVerdaccioPlugin constructor', config); + this.config = config; + this.options = options; + } + + authenticate() {} } -module.exports = ValidVerdaccioPlugin; +module.exports = (...rest) => new ValidVerdaccioPlugin(...rest); diff --git a/packages/loaders/test/plugin_loader_async.spec.ts b/packages/loaders/test/plugin_loader_async.spec.ts index af246beff..a3e1f7854 100644 --- a/packages/loaders/test/plugin_loader_async.spec.ts +++ b/packages/loaders/test/plugin_loader_async.spec.ts @@ -5,7 +5,7 @@ import { Config, parseConfigFile } from '@verdaccio/config'; import { pluginUtils } from '@verdaccio/core'; import { logger, setup } from '@verdaccio/logger'; -import { asyncLoadPlugin } from '../src/plugin-async-loader'; +import { asyncLoadPlugin } from '../src/index'; function getConfig(file: string) { const conPath = path.join(__dirname, './partials/config', file); @@ -71,6 +71,7 @@ describe('plugin loader', () => { expect(plugins).toHaveLength(0); }); }); + describe('relative path', () => { test('should resolve plugin based on relative path', async () => { const config = getConfig('relative-plugins.yaml'); @@ -93,6 +94,7 @@ describe('plugin loader', () => { }); // config.config_path is not considered for loading plugins due legacy support + // @ts-ignore test('should fails if config path is missing (only config_path)', async () => { const config = getConfig('relative-plugins.yaml'); // @ts-expect-error @@ -142,6 +144,7 @@ describe('plugin loader', () => { config.auth, { config, logger }, authSanitize, + false, 'customprefix' ); @@ -172,4 +175,27 @@ describe('plugin loader', () => { expect(plugins).toHaveLength(1); }); }); + + describe('legacy merge configs', () => { + // whenever 6.x and 7.x version are out of support, we can remove this test + test('should merge configuration with plugin configuration', async () => { + const config = getConfig('relative-plugins.yaml'); + // force file instead a folder. + const plugins = await asyncLoadPlugin(config.auth, { config, logger }, authSanitize, true); + + expect(plugins).toHaveLength(1); + const plugin = plugins[0]; + // just check if the plugin has the main config + expect(plugin.config).toHaveProperty('self_path'); + expect(plugin.config).toHaveProperty('storage'); + // assume all config props are merged + // check if the plugin has the auth config + expect(plugin.config).toHaveProperty('auth'); + expect(plugin.config.auth).toEqual({ + plugin: { + enabled: true, + }, + }); + }); + }); }); diff --git a/packages/search-indexer/package.json b/packages/search-indexer/package.json index 54fe4fc41..a0abbd0aa 100644 --- a/packages/search-indexer/package.json +++ b/packages/search-indexer/package.json @@ -38,7 +38,7 @@ "type-check": "tsc --noEmit -p tsconfig.build.json", "build:types": "tsc --emitDeclarationOnly -p tsconfig.build.json", "build:js": "babel src/ --out-dir build/ --copy-files --extensions \".ts,.tsx\" --source-maps", - "build": "esbuild src/index.ts --bundle --outfile=build/dist.js --platform=node --target=node12 && pnpm run build:types" + "build": "esbuild src/index.ts --bundle --outfile=build/dist.js --platform=node --target=node18 && pnpm run build:types" }, "devDependencies": { "@verdaccio/types": "workspace:13.0.0-next-8.4", diff --git a/packages/server/express/src/server.ts b/packages/server/express/src/server.ts index 4dc62ab96..dba5f20f8 100644 --- a/packages/server/express/src/server.ts +++ b/packages/server/express/src/server.ts @@ -22,7 +22,7 @@ import { import { Storage } from '@verdaccio/store'; import { ConfigYaml } from '@verdaccio/types'; import { Config as IConfig } from '@verdaccio/types'; -import webMiddleware from '@verdaccio/web'; +import webMiddleware, { PLUGIN_UI_PREFIX } from '@verdaccio/web'; import { $NextFunctionVer, $RequestExtend, $ResponseExtend } from '../types/custom'; import hookDebug from './debug'; @@ -73,7 +73,8 @@ const defineAPI = async function (config: IConfig, storage: Storage): Promise { return typeof plugin.getPackageStorage !== 'undefined'; }, + false, this.config?.serverSettings?.pluginPrefix, PLUGIN_CATEGORY.STORAGE ); diff --git a/packages/store/src/storage.ts b/packages/store/src/storage.ts index 56586a1c5..6d5de1da6 100644 --- a/packages/store/src/storage.ts +++ b/packages/store/src/storage.ts @@ -686,6 +686,7 @@ class Storage { (plugin: pluginUtils.ManifestFilter) => { return typeof plugin.filter_metadata !== 'undefined'; }, + false, this.config?.serverSettings?.pluginPrefix, PLUGIN_CATEGORY.FILTER ); @@ -1712,7 +1713,7 @@ class Storage { proxy: npmjs A package requires uplinks syncronization if the proxy section is defined. There can be - more than one uplink. The more uplinks are defined, the longer the request will take. + more than one uplink. The more uplinks are defined, the longer the request will take. The requests are made in serial and if 1st call fails, the second will be triggered, otherwise the 1st will reply and others will be discarded. The order is important. diff --git a/packages/web/src/middleware.ts b/packages/web/src/middleware.ts index 429dd7c52..80c893628 100644 --- a/packages/web/src/middleware.ts +++ b/packages/web/src/middleware.ts @@ -26,6 +26,7 @@ export async function loadTheme(config: any) { */ return plugin.staticPath && plugin.manifest && plugin.manifestFiles; }, + false, config?.serverSettings?.pluginPrefix ?? PLUGIN_UI_PREFIX, PLUGIN_CATEGORY.THEME );