From 723f0928df51ed2c9ff72fb823b353c6bcc82cbb Mon Sep 17 00:00:00 2001 From: fablgen-agent Date: Sun, 23 Aug 2026 02:44:14 +0000 Subject: [PATCH] fix: clean site configs when uninstalling plugins --- app/back-end/plugins.js | 24 +++++++++++ app/back-end/plugins.spec.js | 78 ++++++++++++++++++++++++++++++++++++ 2 files changed, 102 insertions(+) create mode 100644 app/back-end/plugins.spec.js diff --git a/app/back-end/plugins.js b/app/back-end/plugins.js index 35c90c81..634485de 100644 --- a/app/back-end/plugins.js +++ b/app/back-end/plugins.js @@ -211,6 +211,30 @@ class Plugins { } UtilsHelper.removePathRecursively(target); + this.removePluginFromSiteConfigs(directory); + } + + /** + * Remove an uninstalled plugin from every site's activation config + */ + removePluginFromSiteConfigs (directory) { + if (!PathValidator.isValidDirSegment(directory) || !UtilsHelper.dirExists(this.sitesDir)) { + return; + } + + let sites = fs.readdirSync(this.sitesDir); + + for (let i = 0; i < sites.length; i++) { + let siteName = sites[i]; + + if (!PathValidator.isValidDirSegment(siteName) || + !UtilsHelper.dirExists(path.join(this.sitesDir, siteName))) { + continue; + } + + // Loading validates the saved list against the plugins that still exist. + this.loadSitePluginsConfig(siteName); + } } getPluginConfig (siteName, pluginName) { diff --git a/app/back-end/plugins.spec.js b/app/back-end/plugins.spec.js new file mode 100644 index 00000000..e9277a23 --- /dev/null +++ b/app/back-end/plugins.spec.js @@ -0,0 +1,78 @@ +const assert = require('assert'); +const fs = require('fs-extra'); +const os = require('os'); +const path = require('path'); +const Plugins = require('./plugins.js'); + +describe('Plugins', function () { + let tempDir; + let appDir; + let sitesDir; + let originalConsoleLog; + + beforeEach(function () { + originalConsoleLog = console.log; + console.log = function () {}; + tempDir = fs.mkdtempSync(path.join(os.tmpdir(), 'publii-plugins-')); + appDir = path.join(tempDir, 'app'); + sitesDir = path.join(tempDir, 'sites'); + fs.ensureDirSync(path.join(appDir, 'plugins', 'removed-plugin')); + fs.ensureDirSync(path.join(appDir, 'plugins', 'retained-plugin')); + fs.ensureDirSync(sitesDir); + }); + + afterEach(function () { + console.log = originalConsoleLog; + fs.removeSync(tempDir); + }); + + function writeSiteConfig (siteName, config) { + let configPath = path.join(sitesDir, siteName, 'input', 'config', 'site.plugins.json'); + fs.ensureDirSync(path.dirname(configPath)); + fs.writeFileSync(configPath, JSON.stringify(config, null, 4)); + return configPath; + } + + it('removes an uninstalled plugin from every site config immediately', function () { + let activeConfigPath = writeSiteConfig('active-site', { + 'removed-plugin': true, + 'retained-plugin': true + }); + let inactiveConfigPath = writeSiteConfig('inactive-site', { + 'removed-plugin': false, + 'retained-plugin': false + }); + let unrelatedConfigPath = writeSiteConfig('unrelated-site', { + 'retained-plugin': true + }); + let plugins = new Plugins(appDir, sitesDir); + + plugins.removePlugin('removed-plugin'); + + assert.strictEqual(fs.existsSync(path.join(appDir, 'plugins', 'removed-plugin')), false); + assert.deepStrictEqual(fs.readJsonSync(activeConfigPath), { + 'retained-plugin': true + }); + assert.deepStrictEqual(fs.readJsonSync(inactiveConfigPath), { + 'retained-plugin': false + }); + assert.deepStrictEqual(fs.readJsonSync(unrelatedConfigPath), { + 'retained-plugin': true + }); + }); + + it('leaves an invalid site config untouched while cleaning valid sites', function () { + let invalidConfigPath = path.join(sitesDir, 'invalid-site', 'input', 'config', 'site.plugins.json'); + fs.ensureDirSync(path.dirname(invalidConfigPath)); + fs.writeFileSync(invalidConfigPath, '{invalid json'); + let validConfigPath = writeSiteConfig('valid-site', { + 'removed-plugin': true + }); + let plugins = new Plugins(appDir, sitesDir); + + plugins.removePlugin('removed-plugin'); + + assert.strictEqual(fs.readFileSync(invalidConfigPath, 'utf8'), '{invalid json'); + assert.strictEqual(fs.readJsonSync(validConfigPath)['removed-plugin'], undefined); + }); +});