From ca87d95bcbfec9241e5840267862bace8b0197fa Mon Sep 17 00:00:00 2001 From: Chocobozzz Date: Fri, 3 Dec 2021 09:52:03 +0100 Subject: Fix plugin upgrade Correctly decache all plugin paths --- server/helpers/decache.ts | 78 ++++++++++++++++++++++++++++++++++++ server/initializers/config.ts | 4 +- server/lib/plugins/plugin-manager.ts | 8 ++-- 3 files changed, 84 insertions(+), 6 deletions(-) create mode 100644 server/helpers/decache.ts (limited to 'server') diff --git a/server/helpers/decache.ts b/server/helpers/decache.ts new file mode 100644 index 000000000..e31973b7a --- /dev/null +++ b/server/helpers/decache.ts @@ -0,0 +1,78 @@ +// Thanks: https://github.com/dwyl/decache +// We reuse this file to also uncache plugin base path + +import { extname } from 'path' + +function decachePlugin (pluginPath: string, libraryPath: string) { + const moduleName = find(libraryPath) + + if (!moduleName) return + + searchCache(moduleName, function (mod) { + delete require.cache[mod.id] + }) + + removeCachedPath(pluginPath) +} + +function decacheModule (name: string) { + const moduleName = find(name) + + if (!moduleName) return + + searchCache(moduleName, function (mod) { + delete require.cache[mod.id] + }) + + removeCachedPath(moduleName) +} + +// --------------------------------------------------------------------------- + +export { + decacheModule, + decachePlugin +} + +// --------------------------------------------------------------------------- + +function find (moduleName: string) { + try { + return require.resolve(moduleName) + } catch { + return '' + } +} + +function searchCache (moduleName: string, callback: (current: NodeModule) => void) { + const resolvedModule = require.resolve(moduleName) + let mod: NodeModule + const visited = {} + + if (resolvedModule && ((mod = require.cache[resolvedModule]) !== undefined)) { + // Recursively go over the results + (function run (current) { + visited[current.id] = true + + current.children.forEach(function (child) { + if (extname(child.filename) !== '.node' && !visited[child.id]) { + run(child) + } + }) + + // Call the specified callback providing the + // found module + callback(current) + })(mod) + } +}; + +function removeCachedPath (pluginPath: string) { + const pathCache = (module.constructor as any)._pathCache + + Object.keys(pathCache).forEach(function (cacheKey) { + if (cacheKey.includes(pluginPath)) { + delete pathCache[cacheKey] + } + }) +} diff --git a/server/initializers/config.ts b/server/initializers/config.ts index dadda2a77..f3a7c6b6b 100644 --- a/server/initializers/config.ts +++ b/server/initializers/config.ts @@ -1,7 +1,7 @@ import bytes from 'bytes' import { IConfig } from 'config' -import decache from 'decache' import { dirname, join } from 'path' +import { decacheModule } from '@server/helpers/decache' import { VideoRedundancyConfigFilter } from '@shared/models/redundancy/video-redundancy-config-filter.type' import { BroadcastMessageLevel } from '@shared/models/server' import { VideosRedundancyStrategy } from '../../shared/models' @@ -497,7 +497,7 @@ export function reloadConfig () { delete require.cache[fileName] } - decache('config') + decacheModule('config') } purge() diff --git a/server/lib/plugins/plugin-manager.ts b/server/lib/plugins/plugin-manager.ts index d4d2a7edc..8add72a85 100644 --- a/server/lib/plugins/plugin-manager.ts +++ b/server/lib/plugins/plugin-manager.ts @@ -1,8 +1,8 @@ -import decache from 'decache' import express from 'express' import { createReadStream, createWriteStream } from 'fs' import { ensureDir, outputFile, readJSON } from 'fs-extra' import { basename, join } from 'path' +import { decachePlugin } from '@server/helpers/decache' import { MOAuthTokenUser, MUser } from '@server/types/models' import { getCompleteLocale } from '@shared/core-utils' import { ClientScript, PluginPackageJson, PluginTranslation, PluginTranslationPaths, RegisterServerHookOptions } from '@shared/models' @@ -312,12 +312,12 @@ export class PluginManager implements ServerHook { logger.error('Cannot install plugin %s, removing it...', toInstall, { err: rootErr }) try { - await this.uninstall(npmName) + // await this.uninstall(npmName) } catch (err) { logger.error('Cannot uninstall plugin %s after failed installation.', toInstall, { err }) try { - await removeNpmPlugin(npmName) + // await removeNpmPlugin(npmName) } catch (err) { logger.error('Cannot remove plugin %s after failed installation.', toInstall, { err }) } @@ -420,7 +420,7 @@ export class PluginManager implements ServerHook { // Delete cache if needed const modulePath = join(pluginPath, packageJSON.library) - decache(modulePath) + decachePlugin(pluginPath, modulePath) const library: PluginLibrary = require(modulePath) if (!isLibraryCodeValid(library)) { -- cgit v1.2.3