diff --git a/src/node/server.js b/src/node/server.js index 04c622197..44f0a1e3d 100755 --- a/src/node/server.js +++ b/src/node/server.js @@ -49,6 +49,7 @@ const express = require('./hooks/express'); const hooks = require('../static/js/pluginfw/hooks'); const pluginDefs = require('../static/js/pluginfw/plugin_defs'); const plugins = require('../static/js/pluginfw/plugins'); +const installer = require('../static/js/pluginfw/installer'); const {Gate} = require('./utils/promises'); const stats = require('./stats'); @@ -139,6 +140,7 @@ exports.start = async () => { } await db.init(); + await installer.checkForMigration(); await plugins.update(); const installedPlugins = Object.values(pluginDefs.plugins) .filter((plugin) => plugin.package.name !== 'ep_etherpad-lite') diff --git a/src/static/js/pluginfw/installer.js b/src/static/js/pluginfw/installer.js index b04964feb..640566ca2 100644 --- a/src/static/js/pluginfw/installer.js +++ b/src/static/js/pluginfw/installer.js @@ -6,11 +6,14 @@ const hooks = require('./hooks'); const runCmd = require('../../../node/utils/run_cmd'); const settings = require('../../../node/utils/Settings'); const axios = require('axios'); -const {PluginManager} = require('live-plugin-manager'); +const {PluginManager} = require("live-plugin-manager"); +const {promises: fs} = require("fs"); +const path = require("path"); const logger = log4js.getLogger('plugins'); exports.manager = new PluginManager(); +const installedPluginsPath = path.join(settings.root, 'var/installed_plugins.json'); const onAllTasksFinished = async () => { settings.reloadSettings(); @@ -34,6 +37,48 @@ const wrapTaskCb = (cb) => { }; }; +const migratePluginsFromNodeModules = async () => { + logger.info('start migration of plugins in node_modules') + // Notes: + // * Do not pass `--prod` otherwise `npm ls` will fail if there is no `package.json`. + // * The `--no-production` flag is required (or the `NODE_ENV` environment variable must be + // unset or set to `development`) because otherwise `npm ls` will not mention any packages + // that are not included in `package.json` (which is expected to not exist). + const cmd = ['npm', 'ls', '--long', '--json', '--depth=0', '--no-production']; + const {dependencies = {}} = JSON.parse(await runCmd(cmd, {stdio: [null, 'string']})); + await Promise.all(Object.entries(dependencies).map(async ([pkg, info]) => { + if (pkg.startsWith(plugins.prefix) && pkg !== 'ep_etherpad-lite') { + await exports.install(pkg) + } + })); +} + +exports.checkForMigration = async () => { + logger.info('check installed plugins for migration') + + try { + await fs.access(installedPluginsPath, fs.constants.F_OK) + } catch (err) { + await migratePluginsFromNodeModules(); + } + + const fileContent = await fs.readFile(installedPluginsPath); + const installedPlugins = JSON.parse(fileContent.toString()); + + for (const plugin of installedPlugins.plugins) { + await exports.manager.install(plugin) + } +}; + +const persistInstalledPlugins = async () => { + let installedPlugins = { plugins: []}; + for (const pkg of Object.keys(await plugins.getPackages())) { + installedPlugins.plugins.push(pkg) + } + installedPlugins.plugins = [...new Set(installedPlugins.plugins)]; + await fs.writeFile(installedPluginsPath, JSON.stringify(installedPlugins)); +} + exports.uninstall = async (pluginName, cb = null) => { cb = wrapTaskCb(cb); logger.info(`Uninstalling plugin ${pluginName}...`); @@ -41,6 +86,7 @@ exports.uninstall = async (pluginName, cb = null) => { logger.info(`Successfully uninstalled plugin ${pluginName}`); await hooks.aCallAll('pluginUninstall', {pluginName}); await plugins.update(); + await persistInstalledPlugins(); cb(null); }; @@ -51,6 +97,7 @@ exports.install = async (pluginName, cb = null) => { logger.info(`Successfully installed plugin ${pluginName}`); await hooks.aCallAll('pluginInstall', {pluginName}); await plugins.update(); + await persistInstalledPlugins(); cb(null); }; diff --git a/src/static/js/pluginfw/plugins.js b/src/static/js/pluginfw/plugins.js index d35d8c97a..179e016f9 100644 --- a/src/static/js/pluginfw/plugins.js +++ b/src/static/js/pluginfw/plugins.js @@ -9,6 +9,7 @@ const tsort = require('./tsort'); const pluginUtils = require('./shared'); const defs = require('./plugin_defs'); const {manager} = require('./installer'); +const settings = require("../../../node/utils/Settings"); const logger = log4js.getLogger('plugins'); @@ -84,8 +85,6 @@ exports.pathNormalization = (part, hookFnName, hookName) => { }; exports.update = async () => { - await manager.install('ep_bookmark') - await manager.install('ep_align') const packages = await exports.getPackages(); const parts = {}; // Key is full name. sortParts converts this into a topologically sorted array. const plugins = {}; @@ -108,31 +107,26 @@ exports.update = async () => { }; exports.getPackages = async () => { - logger.info('Running npm to get a list of installed plugins...'); let plugins = manager.list() let newDependencies = {} + for (const plugin of plugins) { + if (!plugin.name.startsWith(exports.prefix)) { + continue; + } plugin.realPath = await fs.realpath(plugin.location); plugin.path = plugin.realPath; newDependencies[plugin.name] = plugin } - // Notes: - // * Do not pass `--prod` otherwise `npm ls` will fail if there is no `package.json`. - // * The `--no-production` flag is required (or the `NODE_ENV` environment variable must be - // unset or set to `development`) because otherwise `npm ls` will not mention any packages - // that are not included in `package.json` (which is expected to not exist). - const cmd = ['npm', 'ls', '--long', '--json', '--depth=0', '--no-production']; - const {dependencies = {}} = JSON.parse(await runCmd(cmd, {stdio: [null, 'string']})); - await Promise.all(Object.entries(dependencies).map(async ([pkg, info]) => { - if (!pkg.startsWith(exports.prefix)) { - delete dependencies[pkg]; - return; - } - info.realPath = await fs.realpath(info.path); - })); - let newList = Object.assign({}, dependencies, newDependencies) - console.log('blub', newList) - return newList; + + newDependencies['ep_etherpad-lite'] = { + name: 'ep_etherpad-lite', + version: settings.getEpVersion(), + path: path.join(settings.root, 'node_modules/ep_etherpad-lite'), + realPath: path.join(settings.root, 'src'), + } + + return newDependencies; }; const loadPlugin = async (packages, pluginName, plugins, parts) => {