From 61e944b061d0ff4e7b6a8ee7bec2ece60756cbd3 Mon Sep 17 00:00:00 2001 From: Danielv123 Date: Thu, 26 Apr 2018 18:53:06 +0200 Subject: [PATCH 01/93] Attempted to add https support --- master.js | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/master.js b/master.js index 4c110aeb..3d9376ae 100644 --- a/master.js +++ b/master.js @@ -40,6 +40,7 @@ const deepmerge = require("deepmerge"); const path = require("path"); const fs = require("fs"); const nedb = require("nedb"); +const https = require("https"); const jwt = require('jsonwebtoken'); const bcrypt = require('bcryptjs'); @@ -840,10 +841,21 @@ app.get("/api/getFactorioLocale", function(req,res){ httpRequestDurationMilliseconds.labels(req.route.path).observe(Date.now()-reqStartTime); }); }); -var server = require("http").Server(app); -server.listen(config.masterPort || 8080, function () { - console.log("Listening on port %s...", server.address().port); -}); +if(config.masterPort == 443){ + var privateKey = fs.readFileSync( 'privatekey.pem' ); + var certificate = fs.readFileSync( 'certificate.pem' ); + + server = https.createServer({ + key: privateKey, + cert: certificate + }, app).listen(443); +} else { + server = require("http").Server(app); + server.listen(config.masterPort || 8080, function () { + console.log("Listening on port %s...", server.address().port); + }); +} + /* Websockets for remoteMap */ var io = require("socket.io")(server); var slaveMappers = {}; From c02bb6d58ffa4ceb9e070dd1c4df2c0e9f4c1bd1 Mon Sep 17 00:00:00 2001 From: Danielv123 Date: Fri, 4 May 2018 17:50:09 +0200 Subject: [PATCH 02/93] #85 made instance directory configurable in config.json --- client.js | 24 ++++++++++++------------ config.json | 1 + lib/getInstances.js | 3 ++- lib/manager/modManager.js | 6 +++--- 4 files changed, 18 insertions(+), 16 deletions(-) diff --git a/client.js b/client.js index 7f66dfdd..4e5b15ac 100644 --- a/client.js +++ b/client.js @@ -25,8 +25,8 @@ const configManager = require("./lib/manager/configManager.js"); var config = require('./config'); var global = {}; -if (!fs.existsSync("./instances/")) { - fs.mkdirSync("instances"); +if (!fs.existsSync(config.instanceDirectory)) { + fs.mkdirSync(config.instanceDirectory); } if (!fs.existsSync("./sharedPlugins/")) { fs.mkdirSync("sharedPlugins"); @@ -35,7 +35,7 @@ if (!fs.existsSync("./sharedMods/")) { fs.mkdirSync("sharedMods"); } const instance = process.argv[3]; -const instancedirectory = './instances/' + instance; +const instancedirectory = config.instanceDirectory + '/' + instance; const command = process.argv[2]; // Set the process title, shows up as the title of the CMD window on windows @@ -99,7 +99,7 @@ if (!command || command == "help" || command == "--help") { console.error("node client.js manage"); process.exit(1); } else if (command == "list") { - let instanceNames = fileOps.getDirectoriesSync("./instances/"); + let instanceNames = fileOps.getDirectoriesSync(config.instanceDirectory); instanceNames.unshift("Name:"); let longestInstanceName = 0; // determine longest instance name @@ -122,7 +122,7 @@ if (!command || command == "help" || command == "--help") { if(instance.includes("Name:")){ factorioPort = "Port:" } else { - factorioPort = require("./instances/"+instance+"/config").factorioPort; + factorioPort = require(config.instanceDirectory+"/"+instance+"/config").factorioPort; } factorioPorts.push(factorioPort); }); @@ -201,8 +201,8 @@ if (!command || command == "help" || command == "--help") { if (!process.argv[3]) { console.error("Usage: node client.js delete [instance]"); process.exit(1); - } else if (typeof process.argv[3] == "string" && fs.existsSync("./instances/" + process.argv[3]) && process.argv[3] != "/" && process.argv[3] != "") { - fileOps.deleteFolderRecursiveSync("./instances/" + process.argv[3]); + } else if (typeof process.argv[3] == "string" && fs.existsSync(config.instanceDirectory+"/" + process.argv[3]) && process.argv[3] != "/" && process.argv[3] != "") { + fileOps.deleteFolderRecursiveSync(config.instanceDirectory+"/" + process.argv[3]); console.log("Deleted instance " + process.argv[3]); process.exit(1); } else { @@ -241,7 +241,7 @@ if (!command || command == "help" || command == "--help") { // fs.symlinkSync('../../../sharedMods', instancedirectory + "/mods", 'junction') // This is broken because it can only take a file as first argument, not a folder fs.writeFileSync(instancedirectory + "/config.ini", "[path]\r\n\ read-data=__PATH__executable__/../../data\r\n\ -write-data=__PATH__executable__/../../../instances/" + instance + "\r\n\ +write-data=__PATH__executable__/../../../"+config.instanceDirectory+"/" + instance + "\r\n\ "); // this line is probably not needed anymore but Im not gonna remove it @@ -288,7 +288,7 @@ write-data=__PATH__executable__/../../../instances/" + instance + "\r\n\ console.log("Instance created!"); } else if (command == "start" && typeof instance == "string" && instance != "/" && fs.existsSync(instancedirectory)) { // Exit if no instance specified (it should be, just a safeguard); - if(instancedirectory != "./instances/undefined"){ + if(instancedirectory != config.instanceDirectory+"/undefined"){ var instanceconfig = require(instancedirectory + '/config'); instanceconfig.unique = stringUtils.hashCode(instanceconfig.clientPassword); if(process.env.FACTORIOPORT){ @@ -682,7 +682,7 @@ function instanceManagement() { } }); var form = req.form(); - form.append('file', fs.createReadStream("./instances/"+instance+"/mods/"+mod)); + form.append('file', fs.createReadStream(config.instanceDirectory+"/"+instance+"/mods/"+mod)); } else { console.log("Not sending mod: " + mod + " to master because config.uploadModsToMaster is not enabled") } @@ -967,7 +967,7 @@ function hashMods(instanceName, callback) { } let hashedMods = []; /*let mods = fs.readdirSync("./sharedMods/")*/ - let instanceMods = fs.readdirSync("./instances/"+instanceName+"/mods/"); + let instanceMods = fs.readdirSync(config.instanceDirectory+"/"+instanceName+"/mods/"); if(instanceMods.length == 0){ callback({}); // there are no mods installed, return an empty object } @@ -977,7 +977,7 @@ function hashMods(instanceName, callback) { } } for(let i=0; i 0){ if(term.includes("mods.factorio.com/mods/")){ // https://mods.factorio.com/mods/Danielv123/ChooChoo @@ -186,7 +186,7 @@ module.exports.removeMod = function removeMod(term, instance, force = false){ if(instance == "shared"){ // modDirectory has to end in / var modDirectory = "sharedMods/"; } else { - var modDirectory = "instances/"+instance+"/instanceMods/"; + var modDirectory = config.instanceDirectory+"/"+instance+"/instanceMods/"; } if(term && typeof term == "string" && term.length > 0){ let mods = fs.readdirSync(modDirectory); From e87c702cf238c247e436d937b94b52712f2a6948 Mon Sep 17 00:00:00 2001 From: Danielv123 Date: Sat, 19 May 2018 13:59:57 +0200 Subject: [PATCH 03/93] Added plugin functionality to master --- master.js | 59 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 59 insertions(+) diff --git a/master.js b/master.js index ae3ec2a4..c6756f6d 100644 --- a/master.js +++ b/master.js @@ -27,6 +27,7 @@ var config = require('./config'); // homebrew modules const getFactorioLocale = require("./lib/getFactorioLocale"); const stringUtils = require("./lib/stringUtils"); +const fileOps = require("_app/fileOps"); // homemade express middleware for token auth const authenticate = require("./lib/authenticate"); @@ -1080,4 +1081,62 @@ io.on('connection', function (socket) { }); }); +// handle plugins on the master +pluginManagement(); +async function pluginManagement(){ + let startPluginLoad = Date.now(); + let plugins = await getPlugins("sharedPlugins"); + console.log("All plugins loaded in "+(Date.now() - startPluginLoad)+"ms"); +} +function getPlugins(pluginDirectory){ + return new Promise((resolve, reject) => { + let filesLeft = 0; + let plugins = []; + + function checkLoadComplete(plugin){ + if(!filesLeft){ + resolve(plugins); + } + } + fs.readdir(pluginDirectory, function(err, files){ + if(err){ + reject(err); + } else { + files.forEach(file => { + let pluginStartedLoading = Date.now(); // just for logging + filesLeft++; + let pluginPath = path.join(pluginDirectory, file); + fs.stat(pluginPath, function(err, stats){ + if(err){ + reject(err); + checkLoadComplete(); + } else if(stats.isDirectory()){ + let pluginConfig = require(path.resolve(pluginPath, "config")); + --filesLeft; + if(pluginConfig.masterPlugin){ + let masterPlugin = require(path.resolve(pluginPath, pluginConfig.masterPlugin)); + plugins.push(new masterPlugin({ + config, + pluginConfig, + path: pluginPath, + socketio: io, + express: app, + })); + + console.log("Loaded plugin "+pluginConfig.name+" in "+(Date.now() - pluginStartedLoading)+"ms"); + checkLoadComplete(); + } else { + checkLoadComplete(); + } + } else { + checkLoadComplete(); + } + }); + }); + } + }); + }); +} + + module.exports = app; From 88e8ab454fa42c034ea4b79ba8fef15636a433e1 Mon Sep 17 00:00:00 2001 From: Danielv123 Date: Sat, 19 May 2018 20:23:30 +0200 Subject: [PATCH 04/93] Possibility of having master-only plugins --- client.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/client.js b/client.js index 3f050152..a4862c5d 100644 --- a/client.js +++ b/client.js @@ -522,7 +522,7 @@ function instanceManagement() { } } console.log("Clusterio | Loaded plugin " + pluginDirectories[i]); - } else if(pluginConfig.binary != "nodePackage"){ + } else if(pluginConfig.binary != "nodePackage" && pluginConfig.binary){ // handle as fragile executable plugin let args = pluginConfig.args || []; plugins[I]=child_process.spawn(pluginConfig.binary, args, { @@ -583,6 +583,8 @@ function instanceManagement() { plugins[i].on('close', (code) => { log(`child process exited with code ${code}`); }); + } else { + // This plugin doesn't have a client portion } } From da6f96a34bd447814ce8562a615b52c34107a368 Mon Sep 17 00:00:00 2001 From: Danielv123 Date: Thu, 31 May 2018 21:15:34 +0200 Subject: [PATCH 05/93] UPSdisplay now uses rcon return value instead of game.write_file --- client.js | 4 ++-- sharedPlugins/UPSdisplay/index.js | 25 ++++++++++++------------- 2 files changed, 14 insertions(+), 15 deletions(-) diff --git a/client.js b/client.js index a4862c5d..126074c1 100644 --- a/client.js +++ b/client.js @@ -290,8 +290,8 @@ write-data=__PATH__executable__/../../../instances/" + instance + "\r\n\ console.log("Instance created!"); } else if (command == "start" && typeof instance == "string" && instance != "/" && fs.existsSync(instancedirectory)) { // Exit if no instance specified (it should be, just a safeguard); - if(instancedirectory != "./instances/undefined"){ - var instanceconfig = require(instancedirectory + '/config'); + if(instancedirectory != config.instanceDirectory+"/undefined"){ + var instanceconfig = require(path.resolve(instancedirectory,'config')); instanceconfig.unique = stringUtils.hashCode(instanceconfig.clientPassword); if(process.env.FACTORIOPORT){ instanceconfig.factorioPort = process.env.FACTORIOPORT; diff --git a/sharedPlugins/UPSdisplay/index.js b/sharedPlugins/UPSdisplay/index.js index 5618783e..e8928abb 100644 --- a/sharedPlugins/UPSdisplay/index.js +++ b/sharedPlugins/UPSdisplay/index.js @@ -3,12 +3,20 @@ const needle = require("needle"); class UPSdisplay { - constructor(slaveConfig, msgInterface){ + constructor(slaveConfig, msgInterface, extras){ this.config = slaveConfig; this.messageInterface = msgInterface; + this.socket = extras.socket; + this.historicalTicks = []; setInterval(() => { - this.messageInterface("/silent-command game.write_file('UPSdisplay.txt', game.tick, true, 0)"); + this.messageInterface("/silent-command rcon.print(game.tick)", data => { + this.tick = Number(data); + this.historicalTicks[this.historicalTicks.length] = {tick:Number(data), timestamp:Date.now()}; + if(this.historicalTicks.length > 30){ + this.historicalTicks.shift(); // delete last element in array (position 0); + } + }); },1000); setInterval(() => { if(this.historicalTicks && this.historicalTicks[0]){ @@ -17,24 +25,15 @@ class UPSdisplay { let UPS = Math.round(ticksInPeriod / (timePeriod/1000)); // console.log("UPS: " + UPS); try{ - needle.post(this.config.masterIP+':'+this.config.masterPort+'/api/editSlaveMeta', {instanceID: this.config.unique, password: this.config.clientPassword, meta: {UPS:UPS}}, {headers: {'x-access-token': this.config.masterAuthToken}}, function(err, resp) { + needle.post(this.config.masterIP+':'+this.config.masterPort+'/api/editSlaveMeta', {instanceID: this.config.unique, password: this.config.clientPassword, meta: {UPS:UPS, tick:this.tick}}, {headers: {'x-access-token': this.config.masterAuthToken}}, function(err, resp) { // success? }); } catch (err){ console.log(err); } } - }, 10000); + }, 5000); this.messageInterface("/silent-command game.print('UPSdisplay enabled')"); } - scriptOutput(data){ - if (data !== null && !isNaN(data)){ - // console.log("Tick: " + data); - this.historicalTicks[this.historicalTicks.length] = {tick:Number(data), timestamp:Date.now()}; - if(this.historicalTicks.length > 30){ - this.historicalTicks.shift(); // delete last element in array (position 0); - } - } - } } module.exports = UPSdisplay; \ No newline at end of file From 812265d0fdbe7ca98c99b6df5c774e2fe83260fc Mon Sep 17 00:00:00 2001 From: Danielv123 Date: Tue, 22 May 2018 08:15:03 +0200 Subject: [PATCH 06/93] messageInterface now returns a promise as well --- client.js | 35 +++++++++++++++++++++++------------ 1 file changed, 23 insertions(+), 12 deletions(-) diff --git a/client.js b/client.js index 126074c1..9f6750cc 100644 --- a/client.js +++ b/client.js @@ -57,8 +57,16 @@ const needleOptionsWithTokenAuthHeader = { var instanceInfo = {}; var commandBuffer=[]; +// messageInterface Management +setInterval(function(){ + let command=commandBuffer.shift(); + if(command){ + messageInterfaceInternal(command[0], command[1], command[2], command[3]); + } +},config.msBetweenCommands || 50); + // function to handle sending commands into the game -function messageInterfaceInternal(command, callback) { +async function messageInterfaceInternal(command, callback, resolve, reject) { // try to save us if you send a buffer instead of string if(typeof command == "object") { command = command.toString('utf8'); @@ -73,20 +81,26 @@ function messageInterfaceInternal(command, callback) { if(typeof callback == "function"){ callback(); } + resolve(); } else if(typeof command == "string" && client && client.send && typeof client.send == "function") { try { - client.send(command+"\n").then(str => { - if(typeof callback == "function") callback(str) - }); + let str = await client.send(command+"\n"); + if(typeof callback == "function") callback(str) + resolve(str) } catch (err) { console.log(err); if(typeof callback == "function"){ callback(); } + reject(err) } } } -function messageInterface(command, callback) {commandBuffer.push([command,callback]);} +function messageInterface(command, callback) { + return new Promise((resolve,reject) => { + commandBuffer.push([command,callback, resolve, reject]); + }); +} // handle commandline parameters @@ -466,11 +480,12 @@ function instanceManagement() { if(pluginConfig.binary == "nodePackage"){ // require index.js.main() of plugin and execute it as a class let pluginClass = require("./sharedPlugins/" + pluginDirectories[I] + "/index.js"); - plugins[I] = new pluginClass(combinedConfig, function(data, callback){ + plugins[I] = new pluginClass(combinedConfig, async function(data, callback){ if(data.toString('utf8')[0] != "/") { log("Stdout: " + data.toString('utf8')); + return true; } else { - messageInterface(data.toString('utf8'), callback); + return messageInterface(data.toString('utf8'), callback); } }, { // extra functions to pass in object. Should have done it like this from the start, but won't break backwards compat. socket, // socket.io connection to master (and ES6 destructuring, yay) @@ -631,10 +646,6 @@ function instanceManagement() { }); } }); - - // messageInterface Management - setInterval(function(){let command=commandBuffer.pop();if(command){messageInterfaceInternal(command[0],command[1])}},config.msBetweenCommands || 50); - // Mod uploading and management ----------------------------------------------- // get mod names and hashes // string: instance, function: callback @@ -650,7 +661,7 @@ function instanceManagement() { needle.post(config.masterIP + ":" + config.masterPort + '/api/checkMod', payload, needleOptionsWithTokenAuthHeader, function (err, response, body) { if(err) console.log(new Error("Unable to contact master server! Please check your config.json.")); if(response && body && body == "found") { - console.log("master has mod"); + console.log("master has mod "+modHashes[i].modName); } else if (response && body && typeof body == "string") { let mod = response.body; if(config.uploadModsToMaster){ From f72ea7357e8629a5c2060c53c1cc170cd8c72afb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Vest=C3=B8l?= Date: Sun, 3 Jun 2018 17:52:21 +0200 Subject: [PATCH 07/93] Removed debug line that put auth info in the logs --- sharedPlugins/globalChat/index.js | 1 - 1 file changed, 1 deletion(-) diff --git a/sharedPlugins/globalChat/index.js b/sharedPlugins/globalChat/index.js index 52de1c11..29c33a7a 100644 --- a/sharedPlugins/globalChat/index.js +++ b/sharedPlugins/globalChat/index.js @@ -5,7 +5,6 @@ module.exports = class remoteCommands { this.messageInterface = messageInterface; this.config = mergedConfig; this.socket = extras.socket; - console.log(mergedConfig) this.instances = {}; this.socket.on("hello", () => this.socket.emit("registerChatReciever")); From 226e157f690fa79d97faaed7897b684d93ae90ca Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Vest=C3=B8l?= Date: Wed, 6 Jun 2018 12:54:11 +0200 Subject: [PATCH 08/93] Added onExit handler to masterPlugins (see description) To use the onExit handler, simply add a method called onExit to your exported class. If it returns a promise, the master will wait for it to resolve. --- master.js | 31 +++++++++++++++++++++++-------- package.json | 4 ++-- 2 files changed, 25 insertions(+), 10 deletions(-) diff --git a/master.js b/master.js index c6756f6d..6037a450 100644 --- a/master.js +++ b/master.js @@ -242,8 +242,9 @@ db.items.removeItem = function(object) { } // store slaves and inventory in a .json full of JSON data -process.on('SIGINT', function () { +process.on('SIGINT', async function () { console.log('Ctrl-C...'); + let exitStartTime = Date.now(); // set insane limit to slave length, if its longer than this we are probably being ddosed or something if(slaves && Object.keys(slaves).length < 2000000){ console.log("saving to slaves.json"); @@ -257,6 +258,15 @@ process.on('SIGINT', function () { } else if(slaves) { console.log("Item database too large, not saving ("+Object.keys(slaves).length+")"); } + for(let i in masterPlugins){ + let plugin = masterPlugins[i]; + if(plugin.main && plugin.main.onExit && typeof plugin.main.onExit == "function"){ + let startTime = Date.now(); + await plugin.main.onExit(); + console.log("Plugin "+plugin.pluginConfig.name+" exited in "+(Date.now()-startTime)+"ms"); + } + } + console.log("Exited in "+(Date.now()-exitStartTime)+"ms"); process.exit(2); }); @@ -1083,11 +1093,13 @@ io.on('connection', function (socket) { // handle plugins on the master pluginManagement(); +masterPlugins = []; async function pluginManagement(){ let startPluginLoad = Date.now(); - let plugins = await getPlugins("sharedPlugins"); + masterPlugins = await getPlugins("sharedPlugins"); console.log("All plugins loaded in "+(Date.now() - startPluginLoad)+"ms"); } + function getPlugins(pluginDirectory){ return new Promise((resolve, reject) => { let filesLeft = 0; @@ -1115,13 +1127,16 @@ function getPlugins(pluginDirectory){ --filesLeft; if(pluginConfig.masterPlugin){ let masterPlugin = require(path.resolve(pluginPath, pluginConfig.masterPlugin)); - plugins.push(new masterPlugin({ - config, + plugins.push({ + main:new masterPlugin({ + config, + pluginConfig, + path: pluginPath, + socketio: io, + express: app, + }), pluginConfig, - path: pluginPath, - socketio: io, - express: app, - })); + }); console.log("Loaded plugin "+pluginConfig.name+" in "+(Date.now() - pluginStartedLoading)+"ms"); checkLoadComplete(); diff --git a/package.json b/package.json index 98fdc942..f40f7cb2 100644 --- a/package.json +++ b/package.json @@ -30,8 +30,8 @@ "ejs": "^2.5.7", "express": "^4.16.2", "express-fileupload": "0.0.6", - "follow-redirects": "^1.4.1", - "fs-extra": "^1.0.0", + "follow-redirects": "^1.5.0", + "fs-extra": "^6.0.1", "getmac": "^1.4.0", "hash-files": "^1.1.1", "html5-validator": "^1.0.7", From 17b5967b0f9d3aadc0d522417c2f2ad5f382d830 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Vest=C3=B8l?= Date: Wed, 6 Jun 2018 12:57:17 +0200 Subject: [PATCH 09/93] Added mention about masterAuthToken in the readme --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index 2c50731b..3c95657a 100644 --- a/README.md +++ b/README.md @@ -90,6 +90,8 @@ downloads and installs nodejs, pm2, git and clusterio. To specify a version, cha To download the mod for all its non vanilla features and items, (optional) node client.js download + +Add the master API key, found in the masters install directory (secret-api-token.txt) to your config.json masterAuthToken field To create a new instance (its own save, set of mods and config files) From 034db541529f28d75b2ee3227dc4b4f41e6dba53 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Vest=C3=B8l?= Date: Wed, 6 Jun 2018 21:33:08 +0200 Subject: [PATCH 10/93] Added lots of web interface related stuff for external plugin --- master.js | 11 +++++--- static/head.html | 16 ++++++------ static/main.js | 63 +++++++++++++++++++++++++++++++-------------- static/sidebar.html | 9 +++++-- 4 files changed, 66 insertions(+), 33 deletions(-) diff --git a/master.js b/master.js index 6037a450..e9f18df0 100644 --- a/master.js +++ b/master.js @@ -83,10 +83,15 @@ app.set('views', path.join(__dirname, 'static')); app.set('view engine', 'html'); app.engine('html', ejs.renderFile); -var routes = require("./routes.js"); -routes(app); - +// give ejs access to some interesting information +app.use(function(req, res, next){ + res.locals.res = res; + res.locals.req = req; + res.locals.masterPlugins = masterPlugins; + next(); +}); +require("./routes.js")(app); // Set folder to serve static content from (the website) app.use(express.static('static')); // mod downloads diff --git a/static/head.html b/static/head.html index c503fe6b..943c3324 100644 --- a/static/head.html +++ b/static/head.html @@ -1,9 +1,9 @@ Clusterio - - - - - - - - \ No newline at end of file + + + + + + + + diff --git a/static/main.js b/static/main.js index a1b805f1..0f3d43bd 100644 --- a/static/main.js +++ b/static/main.js @@ -49,6 +49,24 @@ debugTools = { } } +let cachedInstanceNames = {}; +async function getInstanceName(instanceID){ + let instance = cachedInstanceNames[instanceID]; + if(!instance){ + let slaves = await getJSON("/api/slaves"); + try { + for(id in slaves){ + cachedInstanceNames[id] = slaves[id].instanceName; + } + } catch(e){ + console.log("Error while looking for instance name!"); + console.log(e); + } + return cachedInstanceNames[instanceID] || instanceID; + } else { + return cachedInstanceNames[instanceID]; + } +} var g = {} contents = { "iron-plate":100, @@ -65,22 +83,27 @@ function djb2(str){ } // callback(err, json) function getJSON(url, callback) { - var xhr = new XMLHttpRequest(); - xhr.open('GET', url, true); - xhr.responseType = 'json'; - xhr.onload = function() { - var status = xhr.status; - if (status == 200) { - callback(null, xhr.response); - } else { - callback(status); - } - }; - // triggers if connection is refused - xhr.onerror = function(e){ - callback(e); - }; - xhr.send(); + return new Promise((resolve, reject) => { + var xhr = new XMLHttpRequest(); + xhr.open('GET', url, true); + xhr.responseType = 'json'; + xhr.onload = function() { + var status = xhr.status; + if (status == 200) { + if(callback) callback(null, xhr.response); + resolve(xhr.response); + } else { + if(callback) callback(status); + resolve(status); + } + }; + // triggers if connection is refused + xhr.onerror = function(e){ + if(callback) callback(e); + reject(e); + }; + xhr.send(); + }); }; // callback(err, json) function postJSON(url, data, callback) { @@ -89,12 +112,12 @@ function postJSON(url, data, callback) { xhr.setRequestHeader('Content-Type', 'application/json; charset=UTF-8'); xhr.responseType = 'json'; xhr.onload = function() { - var status = xhr.status; - if (status == 200) { + var status = xhr.status; + if (status == 200) { callback(null, xhr.response); - } else { + } else { callback(status); - } + } }; // triggers if connection is refused xhr.onerror = function(e){ diff --git a/static/sidebar.html b/static/sidebar.html index b24a4d2e..84f60cde 100644 --- a/static/sidebar.html +++ b/static/sidebar.html @@ -8,10 +8,15 @@ -