aboutsummaryrefslogtreecommitdiff
path: root/src/UserData
diff options
context:
space:
mode:
authorJakob Stendahl <jakob.stendahl@outlook.com>2021-09-19 19:43:11 +0200
committerJakob Stendahl <jakob.stendahl@outlook.com>2021-09-19 19:43:11 +0200
commit7bdce37fd3f18e2712e18c4e2c64cac69af0aca1 (patch)
treeb7ad3f1cca92e2dfd2664ae9e65652bd03ff58b2 /src/UserData
parente6880cd8ccf82d993f222cb14b4860581654acb8 (diff)
downloadLuxcena-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')
-rw-r--r--src/UserData/index.js330
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