From e825afcb1bea6abb121f847eaa6d3009d9ae3cce Mon Sep 17 00:00:00 2001 From: Ashish Prasad Date: Fri, 7 Aug 2026 16:47:12 +0530 Subject: [PATCH] settings: expose plugin settings to CommonJS plugins Synchronize CommonJS Settings getters after each settings reload so plugin-specific ep_* hashes loaded from configuration are accessible directly to plugins. --- src/node/utils/Settings.ts | 13 +++++++-- src/tests/backend/specs/settings.ts | 45 +++++++++++++++++++++++++++++ 2 files changed, 55 insertions(+), 3 deletions(-) diff --git a/src/node/utils/Settings.ts b/src/node/utils/Settings.ts index f1ed4e4e085..477dc0100a8 100644 --- a/src/node/utils/Settings.ts +++ b/src/node/utils/Settings.ts @@ -909,9 +909,14 @@ export const getPublicPrivacyBanner = () => ({ }); export default settings; + // CJS compatibility: plugins use require('ep_etherpad-lite/node/utils/Settings') -// and expect settings properties directly on the module object, not under .default -if (typeof module !== 'undefined' && module.exports) { +// and expect settings properties directly on the module object, not under .default. +// Getter/setter pairs are (re)synchronized whenever settings are loaded so keys +// added later — especially plugin-specific ep_* hashes from settings.json — are +// reachable via require(...).ep_myplugin. See issue #8109. +const syncCjsModuleExports = () => { + if (typeof module === 'undefined' || !module.exports) return; const currentExports = module.exports; for (const key of Object.keys(settings)) { if (!(key in currentExports)) { @@ -923,7 +928,7 @@ if (typeof module !== 'undefined' && module.exports) { }); } } -} +}; /** * This setting is passed with dbType to ueberDB to set up the database @@ -1454,6 +1459,8 @@ export const reloadSettings = () => { .slice(0, 8); } logger.info(`String used for versioning assets: ${settings.randomVersionString}`); + + syncCjsModuleExports(); }; export const exportedForTestingOnly = { diff --git a/src/tests/backend/specs/settings.ts b/src/tests/backend/specs/settings.ts index 4409d0910b4..82e064eadd5 100644 --- a/src/tests/backend/specs/settings.ts +++ b/src/tests/backend/specs/settings.ts @@ -146,6 +146,51 @@ describe(__filename, function () { cjs.title = original; } }); + + // Regression test for https://github.com/ether/etherpad/issues/8109. + // Plugin ep_* hashes are added by storeSettings() inside reloadSettings(), + // after the module's initial evaluation. The CJS mirror must be refreshed + // on each reload so require(...).ep_myplugin resolves the loaded hash. + it('exposes ep_* plugin settings on CJS require after reloadSettings()', function () { + const settingsMod = require('../../../node/utils/Settings'); + const cjs = require('../../../node/utils/Settings'); + const settingsSingleton = settingsMod.default ?? settingsMod; + const envKey = 'EP__ep_test_plugin__allowFeature'; + const hadEnv = Object.prototype.hasOwnProperty.call(process.env, envKey); + const savedEnv = process.env[envKey]; + const hadEpTestPlugin = + Object.prototype.hasOwnProperty.call(settingsSingleton, 'ep_test_plugin'); + const savedEpTestPlugin = settingsSingleton.ep_test_plugin; + const savedSettingsFile = settingsMod.settingsFilename; + const savedCredsFile = settingsMod.credentialsFilename; + try { + process.env[envKey] = 'true'; + settingsMod.settingsFilename = path.join(__dirname, 'settings.json'); + settingsMod.credentialsFilename = path.join(__dirname, 'credentials.json'); + settingsMod.reloadSettings(); + + assert.notStrictEqual(cjs.ep_test_plugin, undefined, + 'ep_test_plugin must be reachable via CJS require after reloadSettings'); + assert.deepEqual(cjs.ep_test_plugin, {allowFeature: true}); + assert.ok('ep_test_plugin' in cjs, + 'ep_test_plugin key must appear on the CJS module export'); + if (cjs.default != null) { + assert.strictEqual(cjs.ep_test_plugin, cjs.default.ep_test_plugin, + 'CJS ep_test_plugin must reference the same object as default.ep_test_plugin'); + } + } finally { + if (hadEnv) process.env[envKey] = savedEnv; + else delete process.env[envKey]; + if (hadEpTestPlugin) { + settingsSingleton.ep_test_plugin = savedEpTestPlugin; + } else { + delete settingsSingleton.ep_test_plugin; + } + settingsMod.settingsFilename = savedSettingsFile; + settingsMod.credentialsFilename = savedCredsFile; + settingsMod.reloadSettings(); + } + }); }); // Regression test for https://github.com/ether/etherpad/issues/7213.