From 7bdce37fd3f18e2712e18c4e2c64cac69af0aca1 Mon Sep 17 00:00:00 2001 From: Jakob Stendahl Date: Sun, 19 Sep 2021 19:43:11 +0200 Subject: :boom: Introduce new UI based on svelte, and rewrite a lot of the node app and the NeoRuntime --- src/SocketIO/index.js | 353 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 353 insertions(+) create mode 100644 src/SocketIO/index.js (limited to 'src/SocketIO/index.js') diff --git a/src/SocketIO/index.js b/src/SocketIO/index.js new file mode 100644 index 0000000..fdaad56 --- /dev/null +++ b/src/SocketIO/index.js @@ -0,0 +1,353 @@ +/** + * This module contains code for handling socketIO clients. + * + * There are two classes, one is a SocketIO controller module. + * The other one is a authorizedclient. + * + * @author jakobst1n. + * @since 19.12.2019 + */ + +let logger = require(__basedir + "/src/logger"); +var exec = require('child_process').exec; +var CryptoJS = require("crypto-js"); +let fs = require("fs"); + +let neoModules; + +const sanitizePath = (path) => path.match(/(user|remote|builtin\/[a-zA-Z0-9-_\/]{1,200})(\.[a-zA-Z0-9]{1,10})?/)[0]; + +/** + * Create the open socketio namespace and setup all listeners. + * + * @param {io} socketio + */ +function createOpenSocketNamespace(io) { + const openNamespace = io.of("/open") + + openNamespace.on("connection", (socket) => { + logger.access(`SOCKET:open Client (${socket.id}@${socket.handshake.headers.host}) connected.`); + + socket.on("name:get", () => { + socket.emit("name", neoModules.userData.config.instanceName); + }); + socket.on("mode:set", (modeId) => { + neoModules.neoRuntimeManager.mode.set(modeId); + }); + socket.on("mode:get", () => { + socket.emit("mode", neoModules.neoRuntimeManager.mode.current()); + }); + socket.on("modelist:get", () => { + socket.emit("modelist", neoModules.neoRuntimeManager.modes()) + }); + socket.on("brightness:set", (brightness) => { + neoModules.neoRuntimeManager.mode.globvars.set("brightness", brightness); + }); + socket.on("brightness:get", () => { + socket.emit("brightness", neoModules.neoRuntimeManager.mode.globvars.get().brightness); + }); + socket.on("power:set", (power) => { + neoModules.neoRuntimeManager.mode.globvars.set("power_on", power); + }); + socket.on("power:get", () => { + socket.emit("power", neoModules.neoRuntimeManager.mode.globvars.get().power_on); + }); + socket.on("var:set", (name, value) => { + neoModules.neoRuntimeManager.mode.variables.set(name, value); + }); + socket.on("vars:get", () => { + socket.emit("vars", neoModules.neoRuntimeManager.mode.variables.get()); + }); + socket.on("modeinfo:get", () => { + socket.emit("modeinfo", { + mode: neoModules.neoRuntimeManager.mode.current(), + brightness: neoModules.neoRuntimeManager.mode.globvars.get().brightness, + power: neoModules.neoRuntimeManager.mode.globvars.get().power_on, + vars: neoModules.neoRuntimeManager.mode.variables.get() + }); + }); + socket.on("authenticate:user", (username, password, callback) => { + let user = neoModules.userData.user.get(username); + if (user == null) { + callback({success: false, reason: "Invalid username/password"}) + logger.access(`SOCKET:open Client (${socket.id}@${socket.handshake.headers.host}) tried to log in as '${username}', wrong username and/or password.`); + return; + } + + let providedHash = hashPassword(password, user.salt); + if (providedHash.hash == user.password) { + let token = createToken(socket); + while (session_tokens.hasOwnProperty(token)) { + token = createToken(socket); + } + + session_tokens[token] = { + expire: (~~(Date.now()) / 1000)+(86400), + host: socket.handshake.headers.host, + user: {username: user.username} + }; + + callback({success: true, user: {username: username}, token: token}) + logger.access(`SOCKET:open Client (${socket.id}@${socket.handshake.headers.host}) authenticated as user '${username}'`); + return; + } + + callback({success: false, reason: "Invalid username/password"}) + logger.access(`SOCKET:open Client (${socket.id}@${socket.handshake.headers.host}) tried to log in as '${username}', wrong username and/or password.`); + }); + + socket.on("disconnect", () => { + logger.access(`SOCKET:open Client (${socket.id}@${socket.handshake.headers.host}) disconnected.`); + }); + }); + + neoModules.neoRuntimeManager.event.on("change", (name, value) => { + if (name == "modelist") { + openNamespace.emit("modelist", neoModules.neoRuntimeManager.modes()); + } else if (["mode", "power_on", "brightness"].includes(name)) { + if (name == "power_on") { name = "power"; } + openNamespace.emit(name, value); + } else { + openNamespace.emit("var", name, value); + } + }); +} + +/** + * @type {object} This is the collection of valid session tokens. + */ +let session_tokens = {}; + +/** + * Middleware that will stop the request if the client does not have a valid + * session token. + * + * @param {object} socket - The socket instance of the connected client + * @param {function} next - The callback to continue the middleware chain + */ +function authorize_middleware(socket, next) { + const token = socket.handshake.auth.token; + + if (session_tokens.hasOwnProperty(token) && + session_tokens[token].host === socket.handshake.headers.host && + session_tokens[token].expire > (~~(Date.now()))) { + socket.data.user = session_tokens[token].user; + next(); + } else { + const err = new Error("not authorized"); + err.data = { content: "invalid session token" }; + next(err); + } +} + +/** + * Create the open socketio namespace and setup all listeners. + * A valid session token is required to connect to this namespace. + * + * @param {io} socetio + */ +function createAuthorizedNamespace(io) { + const authorizedNamespace = io.of("/authed"); + authorizedNamespace.use(authorize_middleware); + authorizedNamespace.on("connection", (socket) => { + logger.access(`SOCKET:authed Client (${socket.id}@${socket.handshake.headers.host}) connected.`); + let debuggerOpen = false; + + socket.emit("user", socket.data.user); + + /* InstanceName */ + socket.on("name:set", (name, fn) => { + neoModules.userData.config.instanceName = name; + fn({success: true}); + io.emit("name", neoModules.userData.config.instanceName); + }); + + /* UserData */ + socket.on("mode:create", (name, template, fn) => { + fn(neoModules.userData.mode.create(name, template)); + }); + socket.on("mode:delete", (modeid, fn) => { + fn(neoModules.userData.mode.delete(modeid)); + }); + + /* LED Config */ + socket.on("led_config:get", () => { + socket.emit("led_config", neoModules.userData.strip.get()); + }); + socket.on("led_config:set", (config) => { + neoModules.userData.strip.set(config); + }); + + /* SelfUpdater */ + socket.on("version:current_number", () => { + socket.emit("version:current_number", neoModules.selfUpdater.version); + }); + socket.on("version:branch", (fn) => { + socket.emit("version:branch", neoModules.selfUpdater.repoBranch); + }); + socket.on("version:newest_number", (fn) => { + socket.emit("version:newest_number", neoModules.selfUpdater.newestVersion); + }); + socket.on("version:check_for_update", (fn) => { + neoModules.selfUpdater.checkVersion(); + socket.emit("version:newest_number", neoModules.selfUpdater.newestVersion); + fn({success: true}); + }); + socket.on("system:update_version", () => { + }); + + /* SSLCert */ + socket.on("sslcert:info", (fn) => { + socket.emit("sslcert:info", {...neoModules.SSLCert.getConfig(), "isValid": neoModules.SSLCert.checkValidity()}); + }); + socket.on("sslcert:generate_new", (fn) => { + neoModules.SSLCert.generateCert(); + fn({success: true}); + }); + + /* System actions */ + socket.on("restart:system", () => { + exec('shutdown -r now', function(error, stdout, stderr){ callback(stdout); }); + }); + socket.on("restart:service", () => { + let p = exec('systemctl restart luxcena-neo'); + p.unref(); + }); + + /* Users */ + socket.on("users:get", () => { + socket.emit("users", neoModules.userData.users()) + }); + socket.on("user:delete", (username, fn) => { + if (username == socket.data.user.username) { fn({success: false, reason: "cannot delete logged in account"}); return; } + fn(neoModules.userData.user.delete(username)); + socket.emit("users", neoModules.userData.users()) + }); + socket.on("user:changeusername", (oldusername, newusername, fn) => { + if (oldusername == socket.data.user.username) { fn({success: false, reason: "cannot change username of logged in account"}); return; } + let user = neoModules.userData.user.get(oldUserName); + if (user == null) { fn({success: false, reason: "unknown username", detail: oldusername}); return; } + user.username = newusername; + let res = neoModules.userData.user.save(user); + if (!res.success) { fn(res); return; } + res = neoModules.userData.user.delete(oldusername) + if (!res.success) { fn(res); return; } + fn({success: true}); + socket.emit("users", neoModules.userData.users()) + }); + socket.on("user:newpassword", (username, newPassword, fn) => { + let user = neoModules.userData.user.get(username); + if (user == null) { fn({success: false, reason: "unknown username", detail: username}); return; } + let newHash = hashPassword(newPassword); + fn(neoModules.userData.user.save(username, newHash.salt, newHash.hash)); + socket.emit("users", neoModules.userData.users()) + }); + socket.on("user:create", (username, newPassword, fn) => { + let user = neoModules.userData.user.get(username); + if (user != null) { fn({success: false, reason: "user already exists", detail: username}); return; } + if (username.length < 1) { fn({success: false, reason: "no username provided"}); return; } + let newHash = hashPassword(newPassword); + fn(neoModules.userData.user.save(username, newHash.salt, newHash.hash)); + socket.emit("users", neoModules.userData.users()) + }); + + /* Editor/debugger */ + let onProcStart = () => socket.emit("editor:proc:start"); + let onProcStop = (code) => socket.emit("editor:proc:exit", code); + let onProcStdout = (stdout) => socket.emit("editor:proc:stdout", stdout); + let onProcStderr = (stderr) => socket.emit("editor:proc:stderr", stderr); + let closeDebugger = () => { + debuggerOpen = false; + neoModules.neoRuntimeManager.event.removeListener("proc:start", onProcStart); + neoModules.neoRuntimeManager.event.removeListener("proc:stop", onProcStop); + neoModules.neoRuntimeManager.event.removeListener("proc:stdout", onProcStdout); + neoModules.neoRuntimeManager.event.removeListener("proc:stderr", onProcStderr); + return neoModules.neoRuntimeManager.stopDebugger(); + }; + socket.on("editor:open", (modeId, fn) => { + let res = neoModules.neoRuntimeManager.startDebugger(modeId); + if (!res.success) { fn(res); return; } + logger.info(`Starting debugger for ${modeId}.`) + debuggerOpen = true; + fn({success: true}) + socket.emit("editor:code", modeId, res.code); + + neoModules.neoRuntimeManager.event.on("proc:start", onProcStart); + neoModules.neoRuntimeManager.event.on("proc:exit", onProcStop); + neoModules.neoRuntimeManager.event.on("proc:stdout", onProcStdout); + neoModules.neoRuntimeManager.event.on("proc:stderr", onProcStderr); + if (neoModules.neoRuntimeManager.modeRunning()) { + socket.emit("editor:proc:start"); + } + }); + socket.on("editor:save", (modeId, code, fn) => { + if (!debuggerOpen) { fn({success: false, reason: "Debugger not open"}); return; }; + fn(neoModules.neoRuntimeManager.saveModeCode(modeId, code)); + }); + socket.on("editor:startmode", (fn) => { + fn(neoModules.neoRuntimeManager.startMode()); + }); + socket.on("editor:stopmode", (fn) => { + fn(neoModules.neoRuntimeManager.stopMode()); + }); + socket.on("editor:restartmode", (fn) => { + fn(neoModules.neoRuntimeManager.restartMode()); + }); + socket.on("editor:close", (fn) => { + fn(closeDebugger()); + logger.info("Stopped debugger"); + }); + + socket.on("disconnect", () => { + logger.access(`SOCKET:authed Client (${socket.id}@${socket.handshake.headers.host}) disconnected.`); + if (debuggerOpen) { + closeDebugger(); + logger.info("Stopped debugger because client disconnected") + } + }); + }); +} + +/** + * Creates an access-token from the clients host-name and the current EPOCH. + * + * @param {client} + * + * @return {string} - The access-token. + */ + function createToken(client) { + let time = Date.now().toString(); + let host = client.handshake.headers.host; + return (CryptoJS.SHA256(`${host}${time}`).toString()); +} + +/** + * Create a new salt and hash from a password. + * + * @param {string} password - The password to hash. + * @param {string} salt - If set, this salt will be used, else a new salt is generated. + * + * @return {object} A object containing a password and a salt property. + */ +function hashPassword(password, salt = null) { + if (salt == null) { + salt = CryptoJS.lib.WordArray.random(128 / 2); + } else { + salt = CryptoJS.enc.Hex.parse(salt); + } + let hash = CryptoJS.PBKDF2(password, salt, { + keySize: 512 / 32, + iterations: 1000, + hasher: CryptoJS.algo.SHA512 + }); + return {hash: hash.toString(), salt: salt.toString()} +} + +module.exports = (_neoModules, io) => { + neoModules = _neoModules; + return { + openNamespace: createOpenSocketNamespace(io), + authorizedNamespace: createAuthorizedNamespace(io) + } +}; + \ No newline at end of file -- cgit v1.2.3 From 950d093ca54366ce49164370325062afd061cf9d Mon Sep 17 00:00:00 2001 From: Jakob Stendahl Date: Sun, 3 Oct 2021 17:06:28 +0200 Subject: :bug: Fix small auth bug --- src/SocketIO/index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/SocketIO/index.js') diff --git a/src/SocketIO/index.js b/src/SocketIO/index.js index fdaad56..8d06947 100644 --- a/src/SocketIO/index.js +++ b/src/SocketIO/index.js @@ -82,7 +82,7 @@ function createOpenSocketNamespace(io) { } session_tokens[token] = { - expire: (~~(Date.now()) / 1000)+(86400), + expire: (~~Date.now())+(86400), host: socket.handshake.headers.host, user: {username: user.username} }; -- cgit v1.2.3 From 8fe3b246a12d409b0819b574a050b64ca96ce251 Mon Sep 17 00:00:00 2001 From: jakobst1n Date: Sun, 3 Oct 2021 18:36:12 +0200 Subject: :boom: Change paths to be in normal linux locations --- app.js | 27 +++--- bin/install.sh | 142 +++++++------------------------- bin/luxcena-neo-cli.sh | 41 ++++++++- bin/luxcena-neo.service | 6 +- bin/luxcena-neo.sh | 4 +- src/NeoRuntimeManager/RuntimeProcess.js | 4 +- src/NeoRuntimeManager/index.js | 8 +- src/SSLCert/index.js | 6 +- src/SelfUpdater/index.js | 4 +- src/SocketIO/index.js | 4 +- src/UserData/index.js | 48 +++++------ 11 files changed, 124 insertions(+), 170 deletions(-) (limited to 'src/SocketIO/index.js') diff --git a/app.js b/app.js index 0196645..30146ea 100644 --- a/app.js +++ b/app.js @@ -2,22 +2,27 @@ let fse = require("fs-extra"); let events = require('events'); // Firstly we set up all globals, check that the usrData dir exists, if not, we run the setup -let userDir = "/home/lux-neo"; -if (process.argv.length >= 3) { userDir = process.argv[2]; } -if (!fse.existsSync(userDir + "/userdata/")) { +global.__appdir = "/opt/luxcena-neo"; +global.__configdir = "/etc/luxcena-neo"; +global.__datadir = "/var/luxcena-neo"; +global.__logdir = "/var/log/luxcena-neo"; + +if ((process.argv.length >= 3) && (process.argv[2] == "dev")) { + global.__appdir = __dirname; + global.__configdir = __dirname + "/tmp/config"; + global.__datadir = __dirname + "/tmp/userdata"; + global.__logdir = __dirname + "/tmp/logs"; +} +if (!fse.existsSync(global.__datadir)) { console.log(`CRITICAL UserDir not found '${userDir}'! Exiting...`); process.exit(1); } -// Global path variables -global.__basedir = __dirname + ""; -global.__datadir = userDir + "/userdata"; -global.__logdir = userDir + "/logs"; // global eventEmitter global.__event = new events.EventEmitter(); // Secondly we setup the logger, -let logger = require("./src/logger"); +let logger = require("./src/Logger"); logger.info("Starting Luxcena-Neo..."); let neoModules = {}; @@ -33,14 +38,14 @@ let express = require("express"); let https = require("https"); let app = express(); let server = https.createServer({ - key: fse.readFileSync(__datadir + "/config/certs/privkey.pem"), - cert: fse.readFileSync(__datadir + "/config/certs/cert.pem") + key: fse.readFileSync(__configdir + "/certs/privkey.pem"), + cert: fse.readFileSync(__configdir + "/certs/cert.pem") }, app ); let io = require("socket.io")(server); require("./src/SocketIO")(neoModules, io); -app.use("/", express.static(__basedir + "/public")); +app.use("/", express.static(__appdir + "/public")); server.listen(neoModules.userData.config.HTTP.port, () => { let host = server.address().address; diff --git a/bin/install.sh b/bin/install.sh index cd4f07b..a7b811b 100755 --- a/bin/install.sh +++ b/bin/install.sh @@ -5,9 +5,6 @@ printf '%s\n' "Luxcena-neo Installer" tput sgr0 printf '\e[93m%s\e[0m\n\n' "---------------------" -LOG="/tmp/luxcena-neo.install.log" -echo "Starting Luxcena-neo installer..." > $LOG - if [ "$EUID" -ne 0 ]; then echo "You need to run this script as root." echo "Try running with 'sudo ./bin/install.sh'" @@ -16,139 +13,56 @@ fi function die() { tput setaf 1 - printf "\n\nInstall failed.\n" - printf "Check the logfile at '/tmp/lucxena-neo.install.log'.\n" - printf "Use this command to see the last 30 lines of the file;\n" - printf " tail -n 30 /tmp/luxcena-neo.install.log" + printf "\n\nInstall failed, successfull steps not reversed.\n" tput sgr0 exit 1 } -function dlgYN() { - tput sc - tput setaf 4 - printf "$1 (y/n)? " - while : - do - read -n 1 -p "" YNQuestionAnswer - if [[ $YNQuestionAnswer == "y" ]]; then - tput rc; tput el - printf ". $1?: \e[0;32mYes\e[0m\n" - tput sc - eval $2=1 # Set parameter 2 of input to the return value - break - elif [[ $YNQuestionAnswer == "n" ]]; then - tput rc; tput el - printf ". $1?: \e[0;31mNo\e[0m\n" - eval $2=0 # Set parameter 2 of input to the return value - break - fi - done -} - -# Update system -dlgYN ". Update your system" res -if [ $res -eq 1 ]; then - tput sc - apt-get -y update &>> $LOG || die - apt-get -y upgrade &>> $LOG || die - tput rc; tput ed -fi - -# Install packages -dlgYN ". Install required packages" res -if [ $res -eq 1 ]; then - tput sc - apt-get -y install nodejs scons python-dev swig &>> $LOG || die - if [ $? -eq 0 ]; then - tput rc; tput ed - printf "✓" - else - printf "\nInstall failed.\n" - exit 1 - fi -else - tput setaf 2 - printf " We are now assuming that all the following packages exists on your system:\n" - printf " nodejs scons python-dev swig\n" - tput sgr0 -fi - -# Install led-library -dlgYN ". Install jgarff's rpi_ws281x library" res -if [ $res -eq 1 ]; then - tput sc - git clone https://github.com/jgarff/rpi_ws281x /tmp/rpi_ws281x # TODO CHANGE PATH - python /tmp/rpi_ws281x/python/setup.py install # TODO CHANGE PAHT - if [ $? -eq 0 ]; then - tput rc; tput ed - printf "✓" - else - printf "\nInstall failed.\n" - exit 1 - fi -fi - -tput setaf 4 -printf ". Installing the app itself...\n" -tput sgr0 - # Create user 'luxcena-neo' tput setaf 8 -printf '%s\n' " - Creating user 'lux-neo'..." +printf '%s\n' "- Creating user 'lux-neo'..." tput sgr0 username="lux-neo" egrep "^$username" /etc/passwd >/dev/null if [ $? -eq 0 ]; then - echo "User already exists, continuing..." + echo "User already exists, continuing..." else - #pass=$(perl -e 'print crypt($ARGV[0], "password")' $password) - useradd -m $username &>> $LOG || die + useradd -m $username || die fi +usermod -a -G gpio $username +usermod -a -G spi $username # First we make our directories tput setaf 8 -printf '%s\n' " - Making app-dir (/bin/luxcena-neo)..." -tput sgr0 -userDir=$(eval echo "~$username") -#mkdir -p "$userDir/install" &>> $LOG || die -#chown $username:$username "$userDir/install" &>> $LOG || die -mkdir -p "$userDir/src" &>> $LOG || die -chown $username:$username "$userDir/src" &>> $LOG || die -mkdir -p "$userDir/userdata" &>> $LOG || die -chown $username:$username "$userDir/userdata" &>> $LOG || die - -# Third we copy the source into the correct swap-folder -tput setaf 8 -printf '%s\n' " - Copying sourceCode to app-dir..." +printf '%s\n' "- Making directories..." tput sgr0 -cp -r . "$userDir/src" &>> $LOG || die -chown -R $username:$username "$userDir/src" &>> $LOG || die - -# fourth we run npm i -tput setaf 8 -printf '%s\n' " - Running npm i..." -tput sgr0 -tput sc -export NODE_ENV=production &>> $LOG || die -runuser -l $username -c 'npm --prefix ~/src install ~/src --only=production' &>> $LOG || die # This is probably a bit overkill to have --only=... but better safe than sorry? -tput rc; tput ed +[ -d "/opt/luxcena-neo/" ] && echo "Seems like luxcena-neo is already installed, please do update instead" && die +mkdir -p "/opt/luxcena-neo" || die +chown $username:$username "/opt/luxcena-neo" || die +mkdir -p "/var/luxcena-neo" || die +chown $username:$username "/var/luxcena-neo" || die +mkdir -p "/etc/luxcena-neo" || die +chown $username:$username "/etc/luxcena-neo" || die +mkdir -p "/var/log/luxcena-neo" || die +chown $username:$username "/var/log/luxcena-neo" || die + +printf '%s' "Which branch do you want to install (default: master)? " +read BRANCH +if [ -z "$BRANCH" ]; then + BRANCH="master" +fi -# fourth we copy the cli to our bin folder +# Get source code tput setaf 8 -printf '%s\n' " - Adding cli-script..." +printf '%s\n' "- Fetch source code..." tput sgr0 -cp bin/luxcena-neo-cli.sh /usr/bin/luxcena-neo-cli.sh &>> $LOG || die -ln -sf /usr/bin/luxcena-neo-cli.sh /usr/bin/lux-neo &>> $LOG || die -tput rc; tput ed +runuser -l $username -c "git clone -b $BRANCH https://github.com/jakobst1n/luxcena-neo /opt/luxcena-neo/" || die -# Fifth we add the service files +# Install all packages, build the app, and prepare everything tput setaf 8 -printf '%s\n' " - Adding service-file to systemd..." +printf '%s\n' "- Running installer (updater) from newly fetched source code..." tput sgr0 -cp bin/luxcena-neo.service /etc/systemd/system/luxcena-neo.service &>> $LOG || die -systemctl daemon-reload &>> $LOG || die +/opt/luxcena-neo/bin/luxcena-neo-cli.sh update || die # Installation is done! printf '\n\e[5m%s\e[0m\n' "🎉Luxcena-Neo is now installed🎉" -printf 'You can now delete this folder' diff --git a/bin/luxcena-neo-cli.sh b/bin/luxcena-neo-cli.sh index defb766..b3c6553 100755 --- a/bin/luxcena-neo-cli.sh +++ b/bin/luxcena-neo-cli.sh @@ -65,14 +65,49 @@ if [ "$action" == "update" ]; then exit 1 fi + # Stop the service if it is running already systemctl stop luxcena-neo - runuser -l 'lux-neo' -c 'git -C ~/src pull' + # Go to source code directory + WDIR="/opt/luxcena-neo" + #cd "$WDIR" + + # Fetch newest changes on branch + runuser -l 'lux-neo' -c "git -C $WDIR pull" || die + + # Add node repo + curl -fsSL https://deb.nodesource.com/setup_14.x | bash - || die + + # Make sure nodejs and prerequisites is installed + apt install nodejs python-pip || die + + # Make sure we have python virtualenv installed + pip3 install virtualenv || die + + # Create and configure python virtualenv + runuser -l 'lux-neo' -c "rm -rf $WDIR/NeoRuntime/Runtime/venv" || die + runuser -l 'lux-neo' -c "virtualenv -p /usr/bin/python3 $WDIR/NeoRuntime/Runtime/venv" || die + runuser -l 'lux-neo' -c "source $WDIR/NeoRuntime/Runtime/venv/bin/activate && pip install rpi_ws281x" || die + + # Build and run all npm scripts if [ "$2" != "skipNode" ]; then - runuser -l 'lux-neo' -c 'export NODE_ENV=production; npm --prefix ~/src install ~/src --only=production' + runuser -l 'lux-neo' -c "export NODE_ENV=development; npm --prefix $WDIR install $WDIR" || die fi + ##runuser -l 'lux-neo' -c "cd $WDIR && npm run build:frontend" || die + ##runuser -l 'lux-neo' -c "cd $WDIR && npm run build:fontawesome" || die + ##runuser -l 'lux-neo' -c "cd $WDIR && npm run build:dialog-polyfill" || die + runuser -l 'lux-neo' -c "npm --prefix \"$WDIR\" run build:frontend" || die + runuser -l 'lux-neo' -c "npm --prefix \"$WDIR\" run build:fontawesome" || die + runuser -l 'lux-neo' -c "npm --prefix \"$WDIR\" run build:dialog-polyfill" || die + + + # Install new cli script + cp /opt/luxcena-neo/bin/luxcena-neo-cli.sh /usr/bin/luxcena-neo-cli.sh || die + + # Install updated systemd script + cp /opt/luxcena-neo/bin/luxcena-neo.service /etc/systemd/system/luxcena-neo.service || die + systemctl daemon-reload || die - cp /home/lux-neo/src/bin/luxcena-neo-cli.sh /usr/bin/luxcena-neo-cli.sh printf "Update complete.\n" systemctl start luxcena-neo exit 0 diff --git a/bin/luxcena-neo.service b/bin/luxcena-neo.service index efea1ad..b4115be 100644 --- a/bin/luxcena-neo.service +++ b/bin/luxcena-neo.service @@ -2,13 +2,13 @@ Description=Luxcena Neo [Service] -ExecStart=/home/lux-neo/src/bin/luxcena-neo.sh +ExecStart=/opt/luxcena-neo/bin/luxcena-neo.sh Restart=always RestartSec=10 Environment=PATH=/usr/bin:/usr/local/bin -Environment=NODE_ENV=production -WorkingDirectory=/home/lux-neo/src/ +Environment=NODE_ENV=development +WorkingDirectory=/opt/luxcena-neo/ [Install] WantedBy=multi-user.target diff --git a/bin/luxcena-neo.sh b/bin/luxcena-neo.sh index fc41f75..a861d87 100755 --- a/bin/luxcena-neo.sh +++ b/bin/luxcena-neo.sh @@ -5,5 +5,5 @@ # the server needs root as well. #runuser -l pi -c "export NODE_ENV=production; node ~/luxcena-neo-install/src/app.js" -export NODE_ENV=production -node /home/lux-neo/src/app.js >> /home/lux-neo/logs/service.log +export NODE_ENV=development +node /opt/luxcena-neo/app.js >> /var/log/luxcena-neo/service.log diff --git a/src/NeoRuntimeManager/RuntimeProcess.js b/src/NeoRuntimeManager/RuntimeProcess.js index 60f6a28..0058382 100644 --- a/src/NeoRuntimeManager/RuntimeProcess.js +++ b/src/NeoRuntimeManager/RuntimeProcess.js @@ -32,8 +32,8 @@ class RuntimeProcess { "python3", [ "-u", // This makes us able to get real-time output - `${__basedir}/NeoRuntime/Runtime/neo_runtime.py`, - `--strip-config="${__datadir}/config/strip.ini"`, + `${__appdir}/NeoRuntime/Runtime/neo_runtime.py`, + `--strip-config="${__configdir}/strip.ini"`, `--mode-path="${this.modePath}"`, `--mode-entry=script` ] diff --git a/src/NeoRuntimeManager/index.js b/src/NeoRuntimeManager/index.js index 62acb8a..e4718e4 100644 --- a/src/NeoRuntimeManager/index.js +++ b/src/NeoRuntimeManager/index.js @@ -8,7 +8,7 @@ const fs = require("fs"); const fsPromises = fs.promises; const RuntimeProcess = require("./RuntimeProcess"); -let logger = require(__basedir + "/src/logger"); +let logger = require(__appdir + "/src/Logger"); const EventEmitter = require('events'); /** @type {object} this should be a pointer to a object referencing all neoModules (see app.js) */ @@ -51,7 +51,7 @@ function isMode(path) { */ function listModes() { let modeDirs = [ - ["builtin/", fs.readdirSync(__basedir + "/NeoRuntime/builtin")], + ["builtin/", fs.readdirSync(__appdir + "/NeoRuntime/builtin")], ["remote/", fs.readdirSync(__datadir + "/remoteCode")], ["user/", fs.readdirSync(__datadir + "/userCode")] ] @@ -167,7 +167,7 @@ function getModePath(modeId) { let location = path.splice(0, 1).toString(); if (location === "user") { path = __datadir + "/userCode/" + path.join("/"); } if (location === "remote") { path = __datadir + "/remoteCode/" + path.join("/"); } - if (location === "builtin") { path = __basedir + "/NeoRuntime/builtin/" + path.join("/"); } + if (location === "builtin") { path = __appdir + "/NeoRuntime/builtin/" + path.join("/"); } return path; } @@ -306,4 +306,4 @@ module.exports = (_neoModules) => { startDebugger, stopDebugger, saveModeCode, startMode, stopMode, restartMode } -}; \ No newline at end of file +}; diff --git a/src/SSLCert/index.js b/src/SSLCert/index.js index 6dad579..d235c9b 100644 --- a/src/SSLCert/index.js +++ b/src/SSLCert/index.js @@ -7,7 +7,7 @@ * @author jakobst1n. * @since 14.16.2019 */ - let logger = require(__basedir + "/src/logger"); + let logger = require(__appdir + "/src/Logger"); const fs = require("fs"); const { execSync } = require("child_process"); @@ -20,7 +20,7 @@ var neoModules; class CertMon { constructor(configPath, certPath, httpsConfig) { - this.certPath = __datadir + "/config/certs/"; + this.certPath = __configdir + "/certs/"; let valid = this.checkValidity(); if (!valid) { @@ -141,4 +141,4 @@ module.exports = (_neoModules) => { neoModules = _neoModules; return new CertMon(); }; - \ No newline at end of file + diff --git a/src/SelfUpdater/index.js b/src/SelfUpdater/index.js index 3c5546b..5a9baa3 100644 --- a/src/SelfUpdater/index.js +++ b/src/SelfUpdater/index.js @@ -2,14 +2,14 @@ let fs = require("fs-extra"); let url = require("url"); let request = require('request'); let exec = require("child_process").exec; -let logger = require(__basedir + "/src/logger"); +let logger = require(__appdir + "/src/Logger"); let neoModules; class VersionChecker { constructor() { - this.CPackageJson = JSON.parse(fs.readFileSync(__basedir + "/package.json")); + this.CPackageJson = JSON.parse(fs.readFileSync(__appdir + "/package.json")); this.version = this.CPackageJson["version"]; this.repoLink = this.CPackageJson["repository"]["url"]; diff --git a/src/SocketIO/index.js b/src/SocketIO/index.js index fdaad56..e20460d 100644 --- a/src/SocketIO/index.js +++ b/src/SocketIO/index.js @@ -8,7 +8,7 @@ * @since 19.12.2019 */ -let logger = require(__basedir + "/src/logger"); +let logger = require(__appdir + "/src/Logger"); var exec = require('child_process').exec; var CryptoJS = require("crypto-js"); let fs = require("fs"); @@ -350,4 +350,4 @@ module.exports = (_neoModules, io) => { authorizedNamespace: createAuthorizedNamespace(io) } }; - \ No newline at end of file + diff --git a/src/UserData/index.js b/src/UserData/index.js index e5318c9..4684a75 100644 --- a/src/UserData/index.js +++ b/src/UserData/index.js @@ -6,7 +6,7 @@ * @since 19.12.2019 */ -let logger = require(__basedir + "/src/logger"); +let logger = require(__appdir + "/src/Logger"); let fse = require("fs-extra"); let ini = require('ini'); @@ -16,7 +16,7 @@ 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')) + var config = ini.decode(fse.readFileSync(__configdir + "/config.ini", 'utf-8')) if (config.instanceName == null) { config.instanceName = "neoStrip"; } if (config.activeMode == null) { config.activeMode = "builtin/static"; } @@ -40,14 +40,14 @@ function ensureMainConfig() { 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)) + fse.writeFileSync(__configdir + "/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')) + var config = ini.decode(fse.readFileSync(__configdir + "/strip.ini", 'utf-8')) if (config.DEFAULT == null) { config.DEFAULT = {}; } if (config.DEFAULT.led_pin == null) { config.DEFAULT.led_pin = 18; } @@ -58,7 +58,7 @@ function ensureStripConfig() { 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)) + fse.writeFileSync(__configdir + "/strip.ini", ini.encode(config)) } /** @@ -70,24 +70,24 @@ function init() { logger.info("Ensuring all folder in UserDir exists..."); fse.ensureDirSync(__datadir + "/"); - fse.ensureDirSync(__datadir + "/config/"); - fse.ensureDirSync(__datadir + "/config/certs"); + fse.ensureDirSync(__configdir); + fse.ensureDirSync(__configdir + "/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')); + if (!fse.existsSync(__configdir + "/config.ini")) { + fse.closeSync(fse.openSync(__configdir + "/config.ini", 'w')); } ensureMainConfig(); - if (!fse.existsSync(__datadir + "/config/strip.ini")) { - fse.closeSync(fse.openSync(__datadir + "/config/strip.ini", 'w')); + if (!fse.existsSync(__configdir + "/strip.ini")) { + fse.closeSync(fse.openSync(__configdir + "/strip.ini", 'w')); } ensureStripConfig(); - if (!fse.existsSync(__datadir + "/config/users.ini")) { - fse.writeFileSync(__datadir + "/config/users.ini", ini.encode({ + if (!fse.existsSync(__configdir + "/users.ini")) { + fse.writeFileSync(__configdir + "/users.ini", ini.encode({ "neo": { "password": "5adbc90fb4716fff62d9cf634837e22f29b011803ba29cee51f921b920fa941651737bd15d00dc72e4cbeee5e64e06ec99cc50ea917285a029797a98740cce0f", "salt": "59b6de1040f3ae3c63de984ca5d61ef46f41dc6ecead3a9d5dab69f0bb3636aa49017e179b74dbcdb407f62bc139a7d55aa78fe2bbdd5327609ea124b2fa03b1" @@ -193,11 +193,11 @@ function getFullConfig(file, addSetters=true) { * @return {object} Standardform return object */ function saveUser(username, salt, password) { - let config = ini.decode(fse.readFileSync(__datadir + "/config/users.ini", 'utf-8')) + let config = ini.decode(fse.readFileSync(__configdir + "/users.ini", 'utf-8')) config[username] = {} config[username].salt = salt config[username].password = password - fse.writeFileSync(__datadir + "/config/users.ini", ini.encode(config)) + fse.writeFileSync(__configdir + "/users.ini", ini.encode(config)) return {success: true} } @@ -207,7 +207,7 @@ function getFullConfig(file, addSetters=true) { * @return {object} with username, salt and hash properties. */ function getUser(username) { - let config = ini.decode(fse.readFileSync(__datadir + "/config/users.ini", 'utf-8')) + let config = ini.decode(fse.readFileSync(__configdir + "/users.ini", 'utf-8')) if (Object.prototype.hasOwnProperty.call(config, username)) { return {...config[username], username: username} } @@ -220,7 +220,7 @@ function getUser(username) { * @return {array} usernames */ function getUsers() { - let config = ini.decode(fse.readFileSync(__datadir + "/config/users.ini", "utf-8")); + let config = ini.decode(fse.readFileSync(__configdir + "/users.ini", "utf-8")); let users = []; for (const username of Object.keys(config)) { users.push(username); @@ -234,11 +234,11 @@ function getUsers() { * @return {object} Standardform success object. */ function deleteUser(username) { - let config = ini.decode(fse.readFileSync(__datadir + "/config/users.ini", 'utf-8')) + let config = ini.decode(fse.readFileSync(__configdir + "/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)); + fse.writeFileSync(__configdir + "/users.ini", ini.encode(config)); return {success: true} } @@ -254,7 +254,7 @@ function deleteUser(username) { function createNewUserMode(name, template) { source_script = null; if ((template === "template/base") || (template === "") || (template == null)) { - source_script = __basedir + "/NeoRuntime/special/template_base/"; + source_script = __appdir + "/NeoRuntime/special/template_base/"; } else { source_script = neoModules.neoRuntimeManager.getModePath(template); } @@ -310,7 +310,7 @@ module.exports = (_neoModules) => { }, strip: { get: () => { - let c = getFullConfig(`${__datadir}/config/strip.ini`, addSetters=false); + let c = getFullConfig(`${__configdir}/strip.ini`, addSetters=false); c.DEFAULT.matrix = JSON.parse(c.DEFAULT.matrix); c.DEFAULT.segments = c.DEFAULT.segments.split(" "); return c.DEFAULT; @@ -318,13 +318,13 @@ module.exports = (_neoModules) => { set: (c) => { c.segments = c.segments.join(" "); c.matrix = JSON.stringify(c.matrix); - return saveConfig(`${__datadir}/config/strip.ini`, {DEFAULT: c}, removeSetters=false); + return saveConfig(`${__configdir}/strip.ini`, {DEFAULT: c}, removeSetters=false); }, }, - config: getFullConfig(`${__datadir}/config/config.ini`), + config: getFullConfig(`${__configdir}/config.ini`), mode: { create: createNewUserMode, delete: deleteUserMode } } -}; \ No newline at end of file +}; -- cgit v1.2.3 From d962cdaa317b384b2e82d0f9dc5b9d15a5733869 Mon Sep 17 00:00:00 2001 From: Jakob Stendahl Date: Wed, 6 Oct 2021 17:24:43 +0200 Subject: :children_crossing: Add update button to ui --- src/SocketIO/index.js | 2 ++ src_frontend/Components/Settings/Version.svelte | 6 ++++++ 2 files changed, 8 insertions(+) (limited to 'src/SocketIO/index.js') diff --git a/src/SocketIO/index.js b/src/SocketIO/index.js index 2a625ed..1803845 100644 --- a/src/SocketIO/index.js +++ b/src/SocketIO/index.js @@ -194,6 +194,8 @@ function createAuthorizedNamespace(io) { fn({success: true}); }); socket.on("system:update_version", () => { + let p = exec('luxcena-neo-cli.sh update'); + p.unref(); }); /* SSLCert */ diff --git a/src_frontend/Components/Settings/Version.svelte b/src_frontend/Components/Settings/Version.svelte index 29d04ae..35c8f96 100644 --- a/src_frontend/Components/Settings/Version.svelte +++ b/src_frontend/Components/Settings/Version.svelte @@ -20,6 +20,11 @@ }); }); } + + let updateVersionPromise; + function doUpdate() { + authorizedSocket.emit("system:update_version"); + } onMount(async() => { authorizedSocket.emit("version:branch"); @@ -53,6 +58,7 @@

Current branch

{#if newVer != version}

Version available.

+ Update luxcena-neo {/if}
Check for updates -- cgit v1.2.3