diff options
author | Jakob Stendahl <jakob.stendahl@outlook.com> | 2021-09-19 19:43:11 +0200 |
---|---|---|
committer | Jakob Stendahl <jakob.stendahl@outlook.com> | 2021-09-19 19:43:11 +0200 |
commit | 7bdce37fd3f18e2712e18c4e2c64cac69af0aca1 (patch) | |
tree | b7ad3f1cca92e2dfd2664ae9e65652bd03ff58b2 /src/UserData/index.js | |
parent | e6880cd8ccf82d993f222cb14b4860581654acb8 (diff) | |
download | Luxcena-Neo-7bdce37fd3f18e2712e18c4e2c64cac69af0aca1.tar.gz Luxcena-Neo-7bdce37fd3f18e2712e18c4e2c64cac69af0aca1.zip |
:boom: Introduce new UI based on svelte, and rewrite a lot of the node app and the NeoRuntime
Diffstat (limited to 'src/UserData/index.js')
-rw-r--r-- | src/UserData/index.js | 330 |
1 files changed, 330 insertions, 0 deletions
diff --git a/src/UserData/index.js b/src/UserData/index.js new file mode 100644 index 0000000..e5318c9 --- /dev/null +++ b/src/UserData/index.js @@ -0,0 +1,330 @@ +/** + * This module is the entry of UserData. This will ensure the user-dirs and all config-files. + * Also, it will + * + * @author jakobst1n. + * @since 19.12.2019 + */ + +let logger = require(__basedir + "/src/logger"); +let fse = require("fs-extra"); +let ini = require('ini'); + +let neoModules; + +/** + * This method will ensure that all required fields are in config.ini + */ +function ensureMainConfig() { + var config = ini.decode(fse.readFileSync(__datadir + "/config/config.ini", 'utf-8')) + + if (config.instanceName == null) { config.instanceName = "neoStrip"; } + if (config.activeMode == null) { config.activeMode = "builtin/static"; } + + if (config.HTTP == null) { config.HTTP = {}; } + if (config.HTTP.port == null) { config.HTTP.port = 443; } + + if (config.SelfUpdater == null) { config.SelfUpdater = {}; } + if (config.SelfUpdater.branch == null) { config.SelfUpdater.branch = "master"; } + if (config.SelfUpdater.checkVersionInterval == null) { config.SelfUpdater.checkVersionInterval = 1; } + if (config.SelfUpdater.automaticUpdate == null) { config.SelfUpdater.automaticUpdate = false; } + + if (config.SSLCert == null) { config.SSLCert = {}; } + if (config.SSLCert.CN == null) { config.SSLCert.CN = "localhost"; } + if (config.SSLCert.certMade == null) { config.SSLCert.certMade = false; } + if (config.SSLCert.certDate == null) { config.SSLCert.certDate = 0; } + if (config.SSLCert.certExpire == null) { config.SSLCert.certExpire = 0; } + if (config.SSLCert.certCN == null) { config.SSLCert.certCN = ""; } + + if (config.DiscoveryServer == null) { config.DiscoveryServer = {}; } + if (config.DiscoveryServer.address == null) { config.DiscoveryServer.address = "https://erj46s.deta.dev"; } + if (config.DiscoveryServer.broadcastSelf == null) { config.DiscoveryServer.broadcastSelf = false; } + + fse.writeFileSync(__datadir + "/config/config.ini", ini.encode(config)) +} + +/** + * This method will ensure that all required fields are in config.ini + */ +function ensureStripConfig() { + var config = ini.decode(fse.readFileSync(__datadir + "/config/strip.ini", 'utf-8')) + + if (config.DEFAULT == null) { config.DEFAULT = {}; } + if (config.DEFAULT.led_pin == null) { config.DEFAULT.led_pin = 18; } + if (config.DEFAULT.led_freq_hz == null) { config.DEFAULT.led_freq_hz = 80000; } + if (config.DEFAULT.led_dma == null) { config.DEFAULT.led_dma = 10; } + if (config.DEFAULT.led_invert == null) { config.DEFAULT.led_invert = false; } + if (config.DEFAULT.led_channel == null) { config.DEFAULT.led_channel = 0 } + if (config.DEFAULT.segments == null) { config.DEFAULT.segments = ""; } + if (config.DEFAULT.matrix == null) { config.DEFAULT.matrix = ""; } + + fse.writeFileSync(__datadir + "/config/strip.ini", ini.encode(config)) +} + +/** + * This method will make sure all files and folders needed for the app exists, + * it will also make sure all files contain all needed data. + */ +function init() { + // Generate all user-folders + logger.info("Ensuring all folder in UserDir exists..."); + + fse.ensureDirSync(__datadir + "/"); + fse.ensureDirSync(__datadir + "/config/"); + fse.ensureDirSync(__datadir + "/config/certs"); + fse.ensureDirSync(__datadir + "/userCode/"); + fse.ensureDirSync(__datadir + "/remoteCode/"); + + // Generate config-files + if (!fse.existsSync(__datadir + "/config/config.ini")) { + fse.closeSync(fse.openSync(__datadir + "/config/config.ini", 'w')); + } + ensureMainConfig(); + + if (!fse.existsSync(__datadir + "/config/strip.ini")) { + fse.closeSync(fse.openSync(__datadir + "/config/strip.ini", 'w')); + } + ensureStripConfig(); + + if (!fse.existsSync(__datadir + "/config/users.ini")) { + fse.writeFileSync(__datadir + "/config/users.ini", ini.encode({ + "neo": { + "password": "5adbc90fb4716fff62d9cf634837e22f29b011803ba29cee51f921b920fa941651737bd15d00dc72e4cbeee5e64e06ec99cc50ea917285a029797a98740cce0f", + "salt": "59b6de1040f3ae3c63de984ca5d61ef46f41dc6ecead3a9d5dab69f0bb3636aa49017e179b74dbcdb407f62bc139a7d55aa78fe2bbdd5327609ea124b2fa03b1" + } + })) + } +}; + +/** + * Recursive function which adds setters and getters to all properties + * in a nested object. This will make us able to save config values + * directly without doing anything other that `prop = value`. + * + * @param {object} config - The full config object. + * @param {string} configFile - The path of the configfile. + * + * @return {object} The config object with setters for values. + */ + function withSetters(config, configFile) { + let outConfig = {}; + function iter(inNode, outNode) { + for (const key of Object.keys(inNode)) { + if (typeof(inNode[key]) === "object") { + outNode[key] = {}; + iter(inNode[key], outNode[key]); + } else { + outNode[`_${key}`] = inNode[key]; + Object.defineProperty(outNode, key, { + get: function() { return this[`_${key}`]; }, + set: function(value) { + this[`_${key}`] = value; + saveConfig(configFile, outConfig); + }, + enumerable: true + }); + } + } + } + iter(config, outConfig); + return outConfig +} + +/** + * Returns a object with only the actual values and not setters, this is the + * inverse of withSetters. + * + * @param {object} config - The full config object. + * + * @return {object} The config object without setters. + */ +function withoutSetters(config) { + let out = {}; + function iter(inNode, outNode) { + for (const key of Object.keys(inNode).filter(k => (k.substr(0, 1) != "_"))) { + if (typeof(inNode[key]) === "object") { + outNode[key] = {}; + iter(inNode[key], outNode[key], out); + } else { + outNode[key] = inNode[`_${key}`]; + } + } + } + iter(config, out); + return out; +} + +/** + * Save config object, this will run stripSetters on the object it saves. + * + * @param {string} file - filename to save the config object to. + * @param {object} object - the config object to save. + */ +function saveConfig(file, object, removeSetters=true) { + if (removeSetters) { + object = withoutSetters(object); + } + fse.writeFileSync(file, ini.encode(object)); +} + +/** + * Reads a ini file and add setters to all properties + * + * @param {string} file - filename of file to read. + * + * @return {object} The config in the file. + */ +function getFullConfig(file, addSetters=true) { + let fullConfig = ini.decode(fse.readFileSync(file, "utf-8")); + if (addSetters) { + fullConfig = withSetters(fullConfig, file); + } + return fullConfig; +} + +/** + * Save a user the user config file, this will append if a new user, and + * overwrite if it is a existsing user. + * + * @param {string} username - The username, case-insensitive. + * @param {string} salt - Salt used for password-checking. + * @param {string} password - Hashed password. + * + * @return {object} Standardform return object + */ + function saveUser(username, salt, password) { + let config = ini.decode(fse.readFileSync(__datadir + "/config/users.ini", 'utf-8')) + config[username] = {} + config[username].salt = salt + config[username].password = password + fse.writeFileSync(__datadir + "/config/users.ini", ini.encode(config)) + return {success: true} +} + +/** + * Get a user, this will return null if no user is found. + * + * @return {object} with username, salt and hash properties. + */ +function getUser(username) { + let config = ini.decode(fse.readFileSync(__datadir + "/config/users.ini", 'utf-8')) + if (Object.prototype.hasOwnProperty.call(config, username)) { + return {...config[username], username: username} + } + return null; +} + +/** + * Get all users + * + * @return {array} usernames + */ +function getUsers() { + let config = ini.decode(fse.readFileSync(__datadir + "/config/users.ini", "utf-8")); + let users = []; + for (const username of Object.keys(config)) { + users.push(username); + } + return users; +} + +/** + * Delete a user + * + * @return {object} Standardform success object. + */ +function deleteUser(username) { + let config = ini.decode(fse.readFileSync(__datadir + "/config/users.ini", 'utf-8')) + if (config.length <= 1) { return {success: false, reason: "cannot delete only user"}; } + if (!Object.prototype.hasOwnProperty.call(config, username)) { return {success: false, reason: "user not found", detail: username}; } + delete config[username]; + fse.writeFileSync(__datadir + "/config/users.ini", ini.encode(config)); + return {success: true} +} + +/** + * Create a new mode in the user directory. + * + * @param {string} name - The name of the file to use, a trailing number will + * be added if there are any conflicts. + * @param {string} template - Id of the template, builtin/static, template/base etc... + * + * @return {object} a standard convention result object. + */ +function createNewUserMode(name, template) { + source_script = null; + if ((template === "template/base") || (template === "") || (template == null)) { + source_script = __basedir + "/NeoRuntime/special/template_base/"; + } else { + source_script = neoModules.neoRuntimeManager.getModePath(template); + } + if (!neoModules.neoRuntimeManager.isMode(source_script)) { + return {success: false, reason: "Source script not found"}; + } + + let newModeName = neoModules.neoRuntimeManager.getModePath(`user/${name}`); + let counter = 0; + while (neoModules.neoRuntimeManager.isMode(newModeName)) { + counter += 1; + newModeName = neoModules.neoRuntimeManager.getModePath(`user/${name}_${counter}`); + } + + fse.ensureDirSync(newModeName); + fse.copySync(`${source_script}/script.py`, `${newModeName}/script.py`) + neoModules.neoRuntimeManager.event.emit("change", "modelist"); + return {success: true}; +} + +/** + * Delete a user created mode + * + * @param {string} modeid - modeid to delete + * + * @return {object} a standard convention result object. + */ +function deleteUserMode(modeid) { + if (modeid.substr(0, 5) !== "user/") { + return {success: false, reason: "Not user mode"} + } + let modePath = neoModules.neoRuntimeManager.getModePath(modeid); + if (!neoModules.neoRuntimeManager.isMode(modePath)) { + return {success: false, reason: "Mode does not found"} + } + if (modeid === neoModules.neoRuntimeManager.mode.current()) { + return {success: false, reason: "Cannot delete currently active mode"} + } + fse.removeSync(modePath); + neoModules.neoRuntimeManager.event.emit("change", "modelist"); + return {success: true} +} + +module.exports = (_neoModules) => { + neoModules = _neoModules; + init(); + return { + users: getUsers, + user: { + save: saveUser, + get: getUser, + delete: deleteUser + }, + strip: { + get: () => { + let c = getFullConfig(`${__datadir}/config/strip.ini`, addSetters=false); + c.DEFAULT.matrix = JSON.parse(c.DEFAULT.matrix); + c.DEFAULT.segments = c.DEFAULT.segments.split(" "); + return c.DEFAULT; + }, + set: (c) => { + c.segments = c.segments.join(" "); + c.matrix = JSON.stringify(c.matrix); + return saveConfig(`${__datadir}/config/strip.ini`, {DEFAULT: c}, removeSetters=false); + }, + }, + config: getFullConfig(`${__datadir}/config/config.ini`), + mode: { + create: createNewUserMode, + delete: deleteUserMode + } + } +};
\ No newline at end of file |