const fs = require('fs-extra'); const express = require('express') const request = require('request') const unzipper = require('unzipper') const fetch = require("node-fetch") const spawn = require('child_process').spawn module.exports = async (s,config,lang,app,io) => { const modulesBasePath = s.mainDirectory + '/libs/customAutoLoad/' const searchText = function(searchFor,searchIn){ return searchIn.indexOf(searchFor) > -1 } const extractNameFromPackage = (filePath) => { const filePathParts = filePath.split('/') const packageName = filePathParts[filePathParts.length - 1].split('.')[0] return packageName } const getModulePath = (name) => { return modulesBasePath + name + '/' } const getModule = (moduleName) => { const modulePath = modulesBasePath + moduleName const isDirectory = fs.lstatSync(modulePath).isDirectory() const newModule = { name: moduleName, path: modulePath + '/', isDirectory: isDirectory, } if(isDirectory){ var hasInstaller = false if(!fs.existsSync(modulePath + '/index.js')){ hasInstaller = true newModule.noIndex = true } if(fs.existsSync(modulePath + '/package.json')){ hasInstaller = true newModule.properties = getModuleProperties(moduleName) }else{ newModule.properties = { name: moduleName } } newModule.hasInstaller = hasInstaller }else{ newModule.isIgnitor = (moduleName.indexOf('.js') > -1) newModule.properties = { name: moduleName } } return newModule } const getModules = (asArray) => { const foundModules = {} fs.readdirSync(modulesBasePath).forEach((moduleName) => { foundModules[moduleName] = getModule(moduleName) }) return asArray ? Object.values(foundModules) : foundModules } const downloadModule = (downloadUrl,packageName) => { const downloadPath = modulesBasePath + packageName fs.mkdirSync(downloadPath) return new Promise(async (resolve, reject) => { fs.mkdir(downloadPath, () => { request(downloadUrl).pipe(fs.createWriteStream(downloadPath + '.zip')) .on('finish',() => { zip = fs.createReadStream(downloadPath + '.zip') .pipe(unzipper.Parse()) .on('entry', async (file) => { if(file.type === 'Directory'){ try{ fs.mkdirSync(modulesBasePath + file.path, { recursive: true }) }catch(err){ } }else{ const content = await file.buffer(); fs.writeFile(modulesBasePath + file.path,content,(err) => { if(err)console.log(err) }) } }) .promise() .then(() => { fs.remove(downloadPath + '.zip', () => {}) resolve() }) }) }) }) } const getModuleProperties = (name) => { const modulePath = getModulePath(name) const propertiesPath = modulePath + 'package.json' const properties = fs.existsSync(propertiesPath) ? s.parseJSON(fs.readFileSync(propertiesPath)) : { name: name } return properties } const installModule = (name) => { return new Promise((resolve, reject) => { //depending on module this may only work for Ubuntu const modulePath = getModulePath(name) const properties = getModuleProperties(name); const installerPath = modulePath + `INSTALL.sh` const propertiesPath = modulePath + 'package.json' // check for INSTALL.sh (ubuntu only) console.log(name) console.log(modulePath) console.log(installerPath) console.log(propertiesPath) if(fs.existsSync(installerPath)){ const installProcess = spawn(`sh`,[installerPath]) installProcess.stderr.on('data',(data) => { console.log(data.toString()) }) installProcess.stdout.on('data',(data) => { console.log(data.toString()) }) installProcess.on('exit',(data) => { resolve() }) }else if(fs.existsSync(propertiesPath)){ // no INSTALL.sh found, check for package.json and do `npm install --unsafe-perm` const installProcess = spawn(`npm`,['install','--unsafe-perm']) installProcess.stderr.on('data',(data) => { console.log(data.toString()) }) installProcess.stdout.on('data',(data) => { console.log(data.toString()) }) installProcess.on('exit',(data) => { resolve() }) }else{ resolve() } }) } const disableModule = (name,status) => { // set status to `false` to enable const modulePath = getModulePath(name) const properties = getModuleProperties(name); const propertiesPath = modulePath + 'package.json' var packageJson = { name: name } try{ packageJson = JSON.parse(fs.readFileSync(propertiesPath)) }catch(err){ } packageJson.disabled = status; fs.writeFileSync(propertiesPath,s.prettyPrint(packageJson)) } const deleteModule = (name) => { // requires restart for changes to take effect try{ const modulePath = modulesBasePath + name fs.remove(modulePath, (err) => { console.log(err) }) return true }catch(err){ console.log(err) return false } } const loadModule = (shinobiModule) => { const moduleName = shinobiModule.name s.customAutoLoadModules[moduleName] = {} var customModulePath = modulesBasePath + '/' + moduleName if(shinobiModule.isIgnitor){ s.customAutoLoadModules[moduleName].type = 'file' try{ require(customModulePath)(s,config,lang,app,io) }catch(err){ s.systemLog('Failed to Load Module : ' + moduleName) s.systemLog(err) } }else if(shinobiModule.isDirectory){ s.customAutoLoadModules[moduleName].type = 'folder' try{ require(customModulePath)(s,config,lang,app,io) fs.readdir(customModulePath,function(err,folderContents){ folderContents.forEach(function(name){ switch(name){ case'web': var webFolder = s.checkCorrectPathEnding(customModulePath) + 'web/' fs.readdir(webFolder,function(err,webFolderContents){ webFolderContents.forEach(function(name){ switch(name){ case'libs': case'pages': if(name === 'libs'){ if(config.webPaths.home !== '/'){ app.use('/libs',express.static(webFolder + '/libs')) } app.use(s.checkCorrectPathEnding(config.webPaths.home)+'libs',express.static(webFolder + '/libs')) app.use(s.checkCorrectPathEnding(config.webPaths.admin)+'libs',express.static(webFolder + '/libs')) app.use(s.checkCorrectPathEnding(config.webPaths.super)+'libs',express.static(webFolder + '/libs')) } var libFolder = webFolder + name + '/' fs.readdir(libFolder,function(err,webFolderContents){ webFolderContents.forEach(function(libName){ var thirdLevelName = libFolder + libName switch(libName){ case'js': case'css': case'blocks': fs.readdir(thirdLevelName,function(err,webFolderContents){ webFolderContents.forEach(function(filename){ var fullPath = thirdLevelName + '/' + filename var blockPrefix = '' switch(true){ case searchText('super.',filename): blockPrefix = 'super' break; case searchText('admin.',filename): blockPrefix = 'admin' break; } switch(libName){ case'js': s.customAutoLoadTree[blockPrefix + 'LibsJs'].push(filename) break; case'css': s.customAutoLoadTree[blockPrefix + 'LibsCss'].push(filename) break; case'blocks': s.customAutoLoadTree[blockPrefix + 'PageBlocks'].push(fullPath) break; } }) }) break; default: if(libName.indexOf('.ejs') > -1){ s.customAutoLoadTree.pages.push(thirdLevelName) } break; } }) }) break; } }) }) break; case'languages': var languagesFolder = s.checkCorrectPathEnding(customModulePath) + 'languages/' fs.readdir(languagesFolder,function(err,files){ if(err)return console.log(err); files.forEach(function(filename){ var fileData = require(languagesFolder + filename) var rule = filename.replace('.json','') if(config.language === rule){ lang = Object.assign(lang,fileData) } if(s.loadedLanguages[rule]){ s.loadedLanguages[rule] = Object.assign(s.loadedLanguages[rule],fileData) }else{ s.loadedLanguages[rule] = Object.assign(s.copySystemDefaultLanguage(),fileData) } }) }) break; case'definitions': var definitionsFolder = s.checkCorrectPathEnding(customModulePath) + 'definitions/' fs.readdir(definitionsFolder,function(err,files){ if(err)return console.log(err); files.forEach(function(filename){ var fileData = require(definitionsFolder + filename) var rule = filename.replace('.json','').replace('.js','') if(config.language === rule){ s.definitions = s.mergeDeep(s.definitions,fileData) } if(s.loadedDefinitons[rule]){ s.loadedDefinitons[rule] = s.mergeDeep(s.loadedDefinitons[rule],fileData) }else{ s.loadedDefinitons[rule] = s.mergeDeep(s.copySystemDefaultDefinitions(),fileData) } }) }) break; } }) }) }catch(err){ s.systemLog('Failed to Load Module : ' + moduleName) s.systemLog(err) } } } const moveModuleToNameInProperties = (modulePath,packageRoot,properties) => { return new Promise((resolve,reject) => { const packageRootParts = packageRoot.split('/') const filename = packageRootParts[packageRootParts.length - 1] fs.move(modulePath + packageRoot,modulesBasePath + filename,(err) => { if(packageRoot){ fs.remove(modulePath, (err) => { if(err)console.log(err) resolve(filename) }) }else{ resolve(filename) } }) }) } const initializeAllModules = async () => { s.customAutoLoadModules = {} s.customAutoLoadTree = { pages: [], PageBlocks: [], LibsJs: [], LibsCss: [], adminPageBlocks: [], adminLibsJs: [], adminLibsCss: [], superPageBlocks: [], superLibsJs: [], superLibsCss: [] } fs.readdir(modulesBasePath,function(err,folderContents){ if(!err && folderContents.length > 0){ getModules(true).forEach((shinobiModule) => { if(shinobiModule.properties.disabled){ return; } loadModule(shinobiModule) }) }else{ fs.mkdir(modulesBasePath,() => {}) } }) } /** * API : Superuser : Custom Auto Load Package Download. */ app.get(config.webPaths.superApiPrefix+':auth/package/list', async (req,res) => { s.superAuth(req.params, async (resp) => { s.closeJsonResponse(res,{ ok: true, modules: getModules() }) },res,req) }) /** * API : Superuser : Custom Auto Load Package Download. */ app.post(config.webPaths.superApiPrefix+':auth/package/download', async (req,res) => { s.superAuth(req.params, async (resp) => { try{ const url = req.body.downloadUrl const packageRoot = req.body.packageRoot || '' const packageName = req.body.packageName || extractNameFromPackage(url) const modulePath = getModulePath(packageName) await downloadModule(url,packageName) const properties = getModuleProperties(packageName) const newName = await moveModuleToNameInProperties(modulePath,packageRoot,properties) const chosenName = newName ? newName : packageName disableModule(chosenName,true) s.closeJsonResponse(res,{ ok: true, moduleName: chosenName, newModule: getModule(chosenName) }) }catch(err){ console.log(err) s.closeJsonResponse(res,{ ok: false }) } },res,req) }) /** * API : Superuser : Custom Auto Load Package Install. */ app.post(config.webPaths.superApiPrefix+':auth/package/install', (req,res) => { s.superAuth(req.params, async (resp) => { const packageName = req.body.packageName await installModule(packageName) s.closeJsonResponse(res,{ok: true}) },res,req) }) /** * API : Superuser : Custom Auto Load Package set Status (Enabled or Disabled). */ app.post(config.webPaths.superApiPrefix+':auth/package/status', (req,res) => { s.superAuth(req.params, async (resp) => { const status = req.body.status const packageName = req.body.packageName disableModule(packageName,status == 'true' ? true : false) s.closeJsonResponse(res,{ok: true}) },res,req) }) /** * API : Superuser : Custom Auto Load Package Delete */ app.post(config.webPaths.superApiPrefix+':auth/package/delete', async (req,res) => { s.superAuth(req.params, async (resp) => { const packageName = req.body.packageName const response = deleteModule(packageName) s.closeJsonResponse(res,{ok: response}) },res,req) }) /** * API : Superuser : Custom Auto Load Package Reload All */ app.post(config.webPaths.superApiPrefix+':auth/package/reloadAll', async (req,res) => { s.superAuth(req.params, async (resp) => { await initializeAllModules(); s.closeJsonResponse(res,{ok: true}) },res,req) }) // Initialize Modules on Start await initializeAllModules(); }