| 
									
										
										
										
											2014-12-26 18:54:20 -05:00
										 |  |  | 'use strict'; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-07-22 00:30:47 -04:00
										 |  |  | const winston = require('winston'); | 
					
						
							|  |  |  | const path = require('path'); | 
					
						
							| 
									
										
										
										
											2021-11-22 15:37:32 -05:00
										 |  |  | const fs = require('fs').promises; | 
					
						
							| 
									
										
										
										
											2019-07-22 00:30:47 -04:00
										 |  |  | const nconf = require('nconf'); | 
					
						
							|  |  |  | const os = require('os'); | 
					
						
							|  |  |  | const cproc = require('child_process'); | 
					
						
							|  |  |  | const util = require('util'); | 
					
						
							| 
									
										
										
										
											2020-09-24 15:06:47 -04:00
										 |  |  | const request = require('request-promise-native'); | 
					
						
							| 
									
										
										
										
											2019-07-22 00:30:47 -04:00
										 |  |  | 
 | 
					
						
							|  |  |  | const db = require('../database'); | 
					
						
							|  |  |  | const meta = require('../meta'); | 
					
						
							|  |  |  | const pubsub = require('../pubsub'); | 
					
						
							| 
									
										
										
										
											2020-10-01 21:02:44 -06:00
										 |  |  | const { paths } = require('../constants'); | 
					
						
							| 
									
										
										
										
											2022-02-02 12:46:13 -05:00
										 |  |  | const pkgInstall = require('../cli/package-install'); | 
					
						
							| 
									
										
										
										
											2019-07-22 00:30:47 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-02-02 12:46:13 -05:00
										 |  |  | const packageManager = pkgInstall.getPackageManager(); | 
					
						
							| 
									
										
										
										
											2019-07-22 00:30:47 -04:00
										 |  |  | let packageManagerExecutable = packageManager; | 
					
						
							|  |  |  | const packageManagerCommands = { | 
					
						
							| 
									
										
										
										
											2017-12-20 13:56:14 -07:00
										 |  |  | 	yarn: { | 
					
						
							|  |  |  | 		install: 'add', | 
					
						
							|  |  |  | 		uninstall: 'remove', | 
					
						
							|  |  |  | 	}, | 
					
						
							|  |  |  | 	npm: { | 
					
						
							|  |  |  | 		install: 'install', | 
					
						
							|  |  |  | 		uninstall: 'uninstall', | 
					
						
							|  |  |  | 	}, | 
					
						
							| 
									
										
										
										
											2020-04-13 21:59:52 +08:00
										 |  |  | 	cnpm: { | 
					
						
							|  |  |  | 		install: 'install', | 
					
						
							|  |  |  | 		uninstall: 'uninstall', | 
					
						
							|  |  |  | 	}, | 
					
						
							|  |  |  | 	pnpm: { | 
					
						
							|  |  |  | 		install: 'install', | 
					
						
							|  |  |  | 		uninstall: 'uninstall', | 
					
						
							|  |  |  | 	}, | 
					
						
							| 
									
										
										
										
											2017-12-20 13:56:14 -07:00
										 |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | if (process.platform === 'win32') { | 
					
						
							|  |  |  | 	packageManagerExecutable += '.cmd'; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-10-13 11:43:39 +02:00
										 |  |  | module.exports = function (Plugins) { | 
					
						
							| 
									
										
										
										
											2020-07-07 20:13:14 -04:00
										 |  |  | 	if (nconf.get('isPrimary')) { | 
					
						
							| 
									
										
										
										
											2021-02-04 00:01:39 -07:00
										 |  |  | 		pubsub.on('plugins:toggleInstall', (data) => { | 
					
						
							| 
									
										
										
										
											2014-12-26 20:18:05 -05:00
										 |  |  | 			if (data.hostname !== os.hostname()) { | 
					
						
							|  |  |  | 				toggleInstall(data.id, data.version); | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		}); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-02-04 00:01:39 -07:00
										 |  |  | 		pubsub.on('plugins:upgrade', (data) => { | 
					
						
							| 
									
										
										
										
											2014-12-26 20:18:05 -05:00
										 |  |  | 			if (data.hostname !== os.hostname()) { | 
					
						
							|  |  |  | 				upgrade(data.id, data.version); | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		}); | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-07-22 00:30:47 -04:00
										 |  |  | 	Plugins.toggleActive = async function (id) { | 
					
						
							|  |  |  | 		const isActive = await Plugins.isActive(id); | 
					
						
							|  |  |  | 		if (isActive) { | 
					
						
							|  |  |  | 			await db.sortedSetRemove('plugins:active', id); | 
					
						
							|  |  |  | 		} else { | 
					
						
							|  |  |  | 			const count = await db.sortedSetCard('plugins:active'); | 
					
						
							|  |  |  | 			await db.sortedSetAdd('plugins:active', count, id); | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		meta.reloadRequired = true; | 
					
						
							| 
									
										
										
										
											2021-08-31 10:45:08 -04:00
										 |  |  | 		const hook = isActive ? 'deactivate' : 'activate'; | 
					
						
							| 
									
										
										
										
											2021-08-31 11:33:20 -04:00
										 |  |  | 		Plugins.hooks.fire(`action:plugin.${hook}`, { id: id }); | 
					
						
							| 
									
										
										
										
											2019-07-22 00:30:47 -04:00
										 |  |  | 		return { id: id, active: !isActive }; | 
					
						
							| 
									
										
										
										
											2014-12-26 18:54:20 -05:00
										 |  |  | 	}; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-09-24 15:06:47 -04:00
										 |  |  | 	Plugins.checkWhitelist = async function (id, version) { | 
					
						
							|  |  |  | 		const body = await request({ | 
					
						
							|  |  |  | 			method: 'GET', | 
					
						
							|  |  |  | 			url: `https://packages.nodebb.org/api/v1/plugins/${encodeURIComponent(id)}`, | 
					
						
							|  |  |  | 			json: true, | 
					
						
							|  |  |  | 		}); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		if (body && body.code === 'ok' && (version === 'latest' || body.payload.valid.includes(version))) { | 
					
						
							|  |  |  | 			return; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		throw new Error('[[error:plugin-not-whitelisted]]'); | 
					
						
							|  |  |  | 	}; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-07-22 00:30:47 -04:00
										 |  |  | 	Plugins.toggleInstall = async function (id, version) { | 
					
						
							| 
									
										
										
										
											2017-02-18 12:30:49 -07:00
										 |  |  | 		pubsub.publish('plugins:toggleInstall', { hostname: os.hostname(), id: id, version: version }); | 
					
						
							| 
									
										
										
										
											2019-07-22 00:30:47 -04:00
										 |  |  | 		return await toggleInstall(id, version); | 
					
						
							| 
									
										
										
										
											2014-12-26 20:18:05 -05:00
										 |  |  | 	}; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-07-22 00:30:47 -04:00
										 |  |  | 	const runPackageManagerCommandAsync = util.promisify(runPackageManagerCommand); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	async function toggleInstall(id, version) { | 
					
						
							|  |  |  | 		const [installed, active] = await Promise.all([ | 
					
						
							|  |  |  | 			Plugins.isInstalled(id), | 
					
						
							|  |  |  | 			Plugins.isActive(id), | 
					
						
							|  |  |  | 		]); | 
					
						
							|  |  |  | 		const type = installed ? 'uninstall' : 'install'; | 
					
						
							|  |  |  | 		if (active) { | 
					
						
							|  |  |  | 			await Plugins.toggleActive(id); | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		await runPackageManagerCommandAsync(type, id, version || 'latest'); | 
					
						
							|  |  |  | 		const pluginData = await Plugins.get(id); | 
					
						
							| 
									
										
										
										
											2021-02-03 23:59:08 -07:00
										 |  |  | 		Plugins.hooks.fire(`action:plugin.${type}`, { id: id, version: version }); | 
					
						
							| 
									
										
										
										
											2019-07-22 00:30:47 -04:00
										 |  |  | 		return pluginData; | 
					
						
							| 
									
										
										
										
											2014-12-26 20:18:05 -05:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2014-12-26 18:54:20 -05:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-12-20 13:56:14 -07:00
										 |  |  | 	function runPackageManagerCommand(command, pkgName, version, callback) { | 
					
						
							|  |  |  | 		cproc.execFile(packageManagerExecutable, [ | 
					
						
							|  |  |  | 			packageManagerCommands[packageManager][command], | 
					
						
							| 
									
										
										
										
											2021-02-03 23:59:08 -07:00
										 |  |  | 			pkgName + (command === 'install' ? `@${version}` : ''), | 
					
						
							| 
									
										
										
										
											2017-12-20 13:56:14 -07:00
										 |  |  | 			'--save', | 
					
						
							| 
									
										
										
										
											2021-02-04 00:01:39 -07:00
										 |  |  | 		], (err, stdout) => { | 
					
						
							| 
									
										
										
										
											2016-01-12 13:53:26 +02:00
										 |  |  | 			if (err) { | 
					
						
							|  |  |  | 				return callback(err); | 
					
						
							|  |  |  | 			} | 
					
						
							| 
									
										
										
										
											2016-12-14 09:28:26 -05:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-02-03 23:59:08 -07:00
										 |  |  | 			winston.verbose(`[plugins/${command}] ${stdout}`); | 
					
						
							| 
									
										
										
										
											2016-11-18 14:03:06 +03:00
										 |  |  | 			callback(); | 
					
						
							| 
									
										
										
										
											2016-12-15 16:08:32 +03:00
										 |  |  | 		}); | 
					
						
							| 
									
										
										
										
											2016-01-12 13:53:26 +02:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-07-22 00:30:47 -04:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	Plugins.upgrade = async function (id, version) { | 
					
						
							| 
									
										
										
										
											2017-02-18 12:30:49 -07:00
										 |  |  | 		pubsub.publish('plugins:upgrade', { hostname: os.hostname(), id: id, version: version }); | 
					
						
							| 
									
										
										
										
											2019-07-22 00:30:47 -04:00
										 |  |  | 		return await upgrade(id, version); | 
					
						
							| 
									
										
										
										
											2014-12-26 20:18:05 -05:00
										 |  |  | 	}; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-07-22 00:30:47 -04:00
										 |  |  | 	async function upgrade(id, version) { | 
					
						
							|  |  |  | 		await runPackageManagerCommandAsync('install', id, version || 'latest'); | 
					
						
							|  |  |  | 		const isActive = await Plugins.isActive(id); | 
					
						
							|  |  |  | 		meta.reloadRequired = isActive; | 
					
						
							|  |  |  | 		return isActive; | 
					
						
							| 
									
										
										
										
											2014-12-26 20:18:05 -05:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2014-12-26 18:54:20 -05:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-07-22 00:30:47 -04:00
										 |  |  | 	Plugins.isInstalled = async function (id) { | 
					
						
							| 
									
										
										
										
											2020-10-01 21:02:44 -06:00
										 |  |  | 		const pluginDir = path.join(paths.nodeModules, id); | 
					
						
							| 
									
										
										
										
											2019-07-22 00:30:47 -04:00
										 |  |  | 		try { | 
					
						
							| 
									
										
										
										
											2021-11-22 15:37:32 -05:00
										 |  |  | 			const stats = await fs.stat(pluginDir); | 
					
						
							| 
									
										
										
										
											2019-07-22 00:30:47 -04:00
										 |  |  | 			return stats.isDirectory(); | 
					
						
							|  |  |  | 		} catch (err) { | 
					
						
							|  |  |  | 			return false; | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2014-12-26 18:54:20 -05:00
										 |  |  | 	}; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-07-22 00:30:47 -04:00
										 |  |  | 	Plugins.isActive = async function (id) { | 
					
						
							|  |  |  | 		return await db.isSortedSetMember('plugins:active', id); | 
					
						
							| 
									
										
										
										
											2014-12-26 18:54:20 -05:00
										 |  |  | 	}; | 
					
						
							| 
									
										
										
										
											2015-02-23 15:55:35 -05:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-07-22 00:30:47 -04:00
										 |  |  | 	Plugins.getActive = async function () { | 
					
						
							|  |  |  | 		return await db.getSortedSetRange('plugins:active', 0, -1); | 
					
						
							| 
									
										
										
										
											2015-02-23 15:55:35 -05:00
										 |  |  | 	}; | 
					
						
							| 
									
										
										
										
											2021-11-22 15:37:32 -05:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	Plugins.autocomplete = async (fragment) => { | 
					
						
							|  |  |  | 		const pluginDir = paths.nodeModules; | 
					
						
							|  |  |  | 		const plugins = (await fs.readdir(pluginDir)).filter(filename => filename.startsWith(fragment)); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		// Autocomplete only if single match
 | 
					
						
							|  |  |  | 		return plugins.length === 1 ? plugins.pop() : fragment; | 
					
						
							|  |  |  | 	}; | 
					
						
							| 
									
										
										
										
											2017-02-18 02:30:48 -07:00
										 |  |  | }; |