From 38a6349735899a76c4c82afb0eaf36f11a2ea4f4 Mon Sep 17 00:00:00 2001 From: Matheus Sampaio Queiroga Date: Tue, 25 Oct 2022 02:39:02 +0000 Subject: [PATCH] update all files --- example/linux/systemd/bdsd.service | 10 +- example/linux/sysv/bdsd | 149 ------- package-lock.json | 656 ++++++++++++++++++++++++++++- package.json | 6 +- src/api/v2.ts | 76 ---- src/{ => cmd/daemon}/api/v1.ts | 0 src/cmd/daemon/api/v2.ts | 92 ++++ src/{ => cmd/daemon}/daemon.ts | 103 +++-- src/cmd/daemon/main.ts | 140 ++++++ src/cmd/local/main.ts | 136 ++++++ src/index.ts | 305 +------------- src/libs/daemon_request.ts | 68 +++ src/libs/messages.ts | 7 + 13 files changed, 1174 insertions(+), 574 deletions(-) delete mode 100755 example/linux/sysv/bdsd delete mode 100644 src/api/v2.ts rename src/{ => cmd/daemon}/api/v1.ts (100%) create mode 100644 src/cmd/daemon/api/v2.ts rename src/{ => cmd/daemon}/daemon.ts (59%) create mode 100644 src/cmd/daemon/main.ts create mode 100644 src/cmd/local/main.ts create mode 100644 src/libs/daemon_request.ts create mode 100644 src/libs/messages.ts diff --git a/example/linux/systemd/bdsd.service b/example/linux/systemd/bdsd.service index 598f18a..d222da0 100644 --- a/example/linux/systemd/bdsd.service +++ b/example/linux/systemd/bdsd.service @@ -1,12 +1,12 @@ [Unit] -Description=Daemon and CLI to maneger You Minecraft Server After=network.target +Description=Daemon and CLI to maneger You Minecraft Server [Install] WantedBy=multi-user.target [Service] -ExecStart=/bin/bdsd daemon -ExecStop=/bin/bdsd server stop -Restart=on-failure -Type=simple \ No newline at end of file +Type=exec +Restart=always +ExecStart=/bin/bdsd server daemon --auth_key --port 39074 +ExecStop=/bin/bdsd server stop \ No newline at end of file diff --git a/example/linux/sysv/bdsd b/example/linux/sysv/bdsd deleted file mode 100755 index 5d1ef01..0000000 --- a/example/linux/sysv/bdsd +++ /dev/null @@ -1,149 +0,0 @@ -#!/bin/sh -### BEGIN INIT INFO -# Provides: bdsd -# Required-Start: $local_fs $remote_fs $network $syslog $named -# Required-Stop: $local_fs $remote_fs $network $syslog $named -# Default-Start: 2 3 4 5 -# Default-Stop: 0 1 6 -# X-Interactive: true -# Short-Description: BDSd Daemon -# Description: Daemon and CLI to maneger You Minecraft Server -# This script will start the bdsd daemon -### END INIT INFO - -DESC="Daemon and CLI to maneger You Minecraft Server" -NAME=bdsd -DAEMON=/bin/$NAME -PATH=/sbin:/usr/sbin:/bin:/usr/bin -DAEMON_ARGS="daemon --socket /var/run/$NAME.sock" -PIDFILE=/var/run/$NAME.pid -SCRIPTNAME=/etc/init.d/$NAME - -# Exit if the package is not installed -[ -x "$DAEMON" ] || exit 0 - -# Read configuration variable file if it is present -[ -r /etc/default/$NAME ] && . /etc/default/$NAME - -# Load the VERBOSE setting and other rcS variables -. /lib/init/vars.sh - -# Define LSB log_* functions. -# Depend on lsb-base (>= 3.2-14) to ensure that this file is present -# and status_of_proc is working. -. /lib/lsb/init-functions - -# -# Function that starts the daemon/service -# -do_start() -{ - # Return - # 0 if daemon has been started - # 1 if daemon was already running - # 2 if daemon could not be started - start-stop-daemon --start --quiet --background --make-pidfile --pidfile $PIDFILE --exec $DAEMON --test > /dev/null || return 1 - start-stop-daemon --start --quiet --background --make-pidfile --pidfile $PIDFILE --exec $DAEMON -- $DAEMON_ARGS || return 2 - # Add code here, if necessary, that waits for the process to be ready - # to handle requests from services started subsequently which depend - # on this one. As a last resort, sleep for some time. -} - -# -# Function that stops the daemon/service -# -do_stop() -{ - # Return - # 0 if daemon has been stopped - # 1 if daemon was already stopped - # 2 if daemon could not be stopped - # other if a failure occurred - start-stop-daemon --stop --quiet --retry=TERM/30/KILL/5 --pidfile $PIDFILE --name $NAME - RETVAL="$?" - [ "$RETVAL" = 2 ] && return 2 - # Wait for children to finish too if this is a daemon that forks - # and if the daemon is only ever run from this initscript. - # If the above conditions are not satisfied then add some other code - # that waits for the process to drop all resources that could be - # needed by services started subsequently. A last resort is to - # sleep for some time. - start-stop-daemon --stop --quiet --oknodo --retry=0/30/KILL/5 --exec $DAEMON - [ "$?" = 2 ] && return 2 - # Many daemons don't delete their pidfiles when they exit. - rm -f $PIDFILE - return "$RETVAL" -} - -# -# Function that sends a SIGHUP to the daemon/service -# -do_reload() { - # - # If the daemon can reload its configuration without - # restarting (for example, when it is sent a SIGHUP), - # then implement that here. - # - start-stop-daemon --stop --signal 1 --quiet --pidfile $PIDFILE --name $NAME - return 0 -} - -case "$1" in - start) - [ "$VERBOSE" != no ] && log_daemon_msg "Starting $DESC" "$NAME" - do_start - case "$?" in - 0|1) [ "$VERBOSE" != no ] && log_end_msg 0 ;; - 2) [ "$VERBOSE" != no ] && log_end_msg 1 ;; - esac - ;; - stop) - [ "$VERBOSE" != no ] && log_daemon_msg "Stopping $DESC" "$NAME" - do_stop - case "$?" in - 0|1) [ "$VERBOSE" != no ] && log_end_msg 0 ;; - 2) [ "$VERBOSE" != no ] && log_end_msg 1 ;; - esac - ;; - status) - status_of_proc "$DAEMON" "$NAME" && exit 0 || exit $? - ;; - #reload|force-reload) - # - # If do_reload() is not implemented then leave this commented out - # and leave 'force-reload' as an alias for 'restart'. - # - #log_daemon_msg "Reloading $DESC" "$NAME" - #do_reload - #log_end_msg $? - #;; - restart|force-reload) - # - # If the "reload" option is implemented then remove the - # 'force-reload' alias - # - log_daemon_msg "Restarting $DESC" "$NAME" - do_stop - case "$?" in - 0|1) - do_start - case "$?" in - 0) log_end_msg 0 ;; - 1) log_end_msg 1 ;; # Old process is still running - *) log_end_msg 1 ;; # Failed to start - esac - ;; - *) - # Failed to stop - log_end_msg 1 - ;; - esac - ;; - *) - #echo "Usage: $SCRIPTNAME {start|stop|restart|reload|force-reload}" >&2 - echo "Usage: $SCRIPTNAME {start|stop|status|restart|force-reload}" >&2 - exit 3 - ;; -esac - -: \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index 9a872ac..5f44bd7 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,19 +1,22 @@ { "name": "@the-bds-maneger/bdsd", - "version": "0.1.3", + "version": "0.1.4", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@the-bds-maneger/bdsd", - "version": "0.1.3", + "version": "0.1.4", "license": "GPL-2.0-only", "dependencies": { "@the-bds-maneger/core": "^5.1.8", "cli-color": "^2.0.3", "express": "^4.18.2", "express-rate-limit": "^6.6.0", + "node-notifier": "^10.0.1", "prom-client": "^14.1.0", + "socket.io": "^4.5.3", + "socket.io-client": "^4.5.3", "yargs": "^17.6.0" }, "bin": { @@ -23,6 +26,7 @@ "@types/cli-color": "^2.0.2", "@types/express": "^4.17.14", "@types/node": "^18.11.4", + "@types/node-notifier": "^8.0.2", "@types/yargs": "^17.0.13", "ts-node": "^10.9.1", "typescript": "^4.8.4" @@ -79,6 +83,11 @@ "url": "https://github.com/sindresorhus/is?sponsor=1" } }, + "node_modules/@socket.io/component-emitter": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@socket.io/component-emitter/-/component-emitter-3.1.0.tgz", + "integrity": "sha512-+9jVqKhRSpsc591z5vX+X5Yyw+he/HCB4iQ/RYxw35CEPaY1gnsNE43nf9n9AaYjAQrTiI/mOwKUKdUs9vf7Xg==" + }, "node_modules/@szmarczak/http-timer": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/@szmarczak/http-timer/-/http-timer-5.0.1.tgz", @@ -180,6 +189,16 @@ "@types/node": "*" } }, + "node_modules/@types/cookie": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/@types/cookie/-/cookie-0.4.1.tgz", + "integrity": "sha512-XW/Aa8APYr6jSVVA1y/DEIZX0/GMKLEVekNG727R8cs56ahETkRAy/3DR7+fJyh7oUgGwNQaRfXCun0+KbWY7Q==" + }, + "node_modules/@types/cors": { + "version": "2.8.12", + "resolved": "https://registry.npmjs.org/@types/cors/-/cors-2.8.12.tgz", + "integrity": "sha512-vt+kDhq/M2ayberEtJcIN/hxXy1Pk+59g2FV/ZQceeaTyCtCucjL2Q7FXlFjtWn4n15KCr1NE2lNNFhp0lEThw==" + }, "node_modules/@types/express": { "version": "4.17.14", "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.14.tgz", @@ -219,6 +238,15 @@ "resolved": "https://registry.npmjs.org/@types/node/-/node-18.11.4.tgz", "integrity": "sha512-BxcJpBu8D3kv/GZkx/gSMz6VnTJREBj/4lbzYOQueUOELkt8WrO6zAcSPmp9uRPEW/d+lUO8QK0W2xnS1hEU0A==" }, + "node_modules/@types/node-notifier": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/@types/node-notifier/-/node-notifier-8.0.2.tgz", + "integrity": "sha512-5v0PhPv0AManpxT7W25Zipmj/Lxp1WqfkcpZHyqSloB+gGoAHRBuzhrCelFKrPvNF5ki3gAcO4kxaGO2/21u8g==", + "dev": true, + "dependencies": { + "@types/node": "*" + } + }, "node_modules/@types/qs": { "version": "6.9.7", "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.7.tgz", @@ -436,6 +464,14 @@ } ] }, + "node_modules/base64id": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/base64id/-/base64id-2.0.0.tgz", + "integrity": "sha512-lGe34o6EHj9y3Kts9R4ZYs/Gr+6N7MCaMlIFA3F1R2O5/m7K06AxfSeO5530PEERE6/WyEg3lsuyw4GHlPZHog==", + "engines": { + "node": "^4.5.0 || >= 5.9" + } + }, "node_modules/bintrees": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/bintrees/-/bintrees-1.0.2.tgz", @@ -829,6 +865,136 @@ "node": ">= 0.8" } }, + "node_modules/engine.io": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/engine.io/-/engine.io-6.2.0.tgz", + "integrity": "sha512-4KzwW3F3bk+KlzSOY57fj/Jx6LyRQ1nbcyIadehl+AnXjKT7gDO0ORdRi/84ixvMKTym6ZKuxvbzN62HDDU1Lg==", + "dependencies": { + "@types/cookie": "^0.4.1", + "@types/cors": "^2.8.12", + "@types/node": ">=10.0.0", + "accepts": "~1.3.4", + "base64id": "2.0.0", + "cookie": "~0.4.1", + "cors": "~2.8.5", + "debug": "~4.3.1", + "engine.io-parser": "~5.0.3", + "ws": "~8.2.3" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/engine.io-client": { + "version": "6.2.3", + "resolved": "https://registry.npmjs.org/engine.io-client/-/engine.io-client-6.2.3.tgz", + "integrity": "sha512-aXPtgF1JS3RuuKcpSrBtimSjYvrbhKW9froICH4s0F3XQWLxsKNxqzG39nnvQZQnva4CMvUK63T7shevxRyYHw==", + "dependencies": { + "@socket.io/component-emitter": "~3.1.0", + "debug": "~4.3.1", + "engine.io-parser": "~5.0.3", + "ws": "~8.2.3", + "xmlhttprequest-ssl": "~2.0.0" + } + }, + "node_modules/engine.io-client/node_modules/debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/engine.io-client/node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + }, + "node_modules/engine.io-client/node_modules/ws": { + "version": "8.2.3", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.2.3.tgz", + "integrity": "sha512-wBuoj1BDpC6ZQ1B7DWQBYVLphPWkm8i9Y0/3YdHjHKHiohOJ1ws+3OccDWtH+PoC9DZD5WOTrJvNbWvjS6JWaA==", + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": "^5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, + "node_modules/engine.io-parser": { + "version": "5.0.4", + "resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-5.0.4.tgz", + "integrity": "sha512-+nVFp+5z1E3HcToEnO7ZIj3g+3k9389DvWtvJZz0T6/eOCPIyyxehFcedoYrZQrp0LgQbD9pPXhpMBKMd5QURg==", + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/engine.io/node_modules/cookie": { + "version": "0.4.2", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.2.tgz", + "integrity": "sha512-aSWTXFzaKWkvHO1Ny/s+ePFpvKsPnjc551iI41v3ny/ow6tBG5Vd+FuqGNhh1LxOmVzOlGUriIlOaokOvhaStA==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/engine.io/node_modules/debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/engine.io/node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + }, + "node_modules/engine.io/node_modules/ws": { + "version": "8.2.3", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.2.3.tgz", + "integrity": "sha512-wBuoj1BDpC6ZQ1B7DWQBYVLphPWkm8i9Y0/3YdHjHKHiohOJ1ws+3OccDWtH+PoC9DZD5WOTrJvNbWvjS6JWaA==", + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": "^5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, "node_modules/entities": { "version": "4.4.0", "resolved": "https://registry.npmjs.org/entities/-/entities-4.4.0.tgz", @@ -1188,6 +1354,11 @@ "url": "https://github.com/sindresorhus/got?sponsor=1" } }, + "node_modules/growly": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/growly/-/growly-1.3.0.tgz", + "integrity": "sha512-+xGQY0YyAWCnqy7Cd++hc2JqMYzlm0dG30Jd0beaA64sROr8C4nt8Yc9V5Ro3avlSUDTN0ulqP/VBKi1/lLygw==" + }, "node_modules/has": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", @@ -1368,6 +1539,20 @@ "node": ">= 0.10" } }, + "node_modules/is-docker": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.2.1.tgz", + "integrity": "sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==", + "bin": { + "is-docker": "cli.js" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/is-fullwidth-code-point": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", @@ -1386,6 +1571,22 @@ "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-2.2.2.tgz", "integrity": "sha512-+lP4/6lKUBfQjZ2pdxThZvLUAafmZb8OAxFb8XXtiQmS35INgr85hdOGoEs124ez1FCnZJt6jau/T+alh58QFQ==" }, + "node_modules/is-wsl": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz", + "integrity": "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==", + "dependencies": { + "is-docker": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==" + }, "node_modules/jsdom": { "version": "20.0.1", "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-20.0.1.tgz", @@ -1486,6 +1687,17 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/lru-queue": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/lru-queue/-/lru-queue-0.1.0.tgz", @@ -1735,6 +1947,19 @@ "resolved": "https://registry.npmjs.org/next-tick/-/next-tick-1.1.0.tgz", "integrity": "sha512-CXdUiJembsNjuToQvxayPZF9Vqht7hewsvy2sOWafLvi2awflj9mOC6bHIg50orX8IJvWKY9wYQ/zB2kogPslQ==" }, + "node_modules/node-notifier": { + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/node-notifier/-/node-notifier-10.0.1.tgz", + "integrity": "sha512-YX7TSyDukOZ0g+gmzjB6abKu+hTGvO8+8+gIFDsRCU2t8fLV/P2unmt+LGFaIa4y64aX98Qksa97rgz4vMNeLQ==", + "dependencies": { + "growly": "^1.3.0", + "is-wsl": "^2.2.0", + "semver": "^7.3.5", + "shellwords": "^0.1.1", + "uuid": "^8.3.2", + "which": "^2.0.2" + } + }, "node_modules/normalize-url": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-7.2.0.tgz", @@ -2047,6 +2272,20 @@ "node": ">=v12.22.7" } }, + "node_modules/semver": { + "version": "7.3.8", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz", + "integrity": "sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==", + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/send": { "version": "0.18.0", "resolved": "https://registry.npmjs.org/send/-/send-0.18.0.tgz", @@ -2094,6 +2333,11 @@ "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==" }, + "node_modules/shellwords": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/shellwords/-/shellwords-0.1.1.tgz", + "integrity": "sha512-vFwSUfQvqybiICwZY5+DAWIPLKsWO31Q91JSKl3UYv+K5c2QRPzn0qzec6QPu1Qc9eHYItiP3NdJqNVqetYAww==" + }, "node_modules/side-channel": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz", @@ -2121,6 +2365,116 @@ "npm": ">= 3.0.0" } }, + "node_modules/socket.io": { + "version": "4.5.3", + "resolved": "https://registry.npmjs.org/socket.io/-/socket.io-4.5.3.tgz", + "integrity": "sha512-zdpnnKU+H6mOp7nYRXH4GNv1ux6HL6+lHL8g7Ds7Lj8CkdK1jJK/dlwsKDculbyOHifcJ0Pr/yeXnZQ5GeFrcg==", + "dependencies": { + "accepts": "~1.3.4", + "base64id": "~2.0.0", + "debug": "~4.3.2", + "engine.io": "~6.2.0", + "socket.io-adapter": "~2.4.0", + "socket.io-parser": "~4.2.0" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/socket.io-adapter": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/socket.io-adapter/-/socket.io-adapter-2.4.0.tgz", + "integrity": "sha512-W4N+o69rkMEGVuk2D/cvca3uYsvGlMwsySWV447y99gUPghxq42BxqLNMndb+a1mm/5/7NeXVQS7RLa2XyXvYg==" + }, + "node_modules/socket.io-client": { + "version": "4.5.3", + "resolved": "https://registry.npmjs.org/socket.io-client/-/socket.io-client-4.5.3.tgz", + "integrity": "sha512-I/hqDYpQ6JKwtJOf5ikM+Qz+YujZPMEl6qBLhxiP0nX+TfXKhW4KZZG8lamrD6Y5ngjmYHreESVasVCgi5Kl3A==", + "dependencies": { + "@socket.io/component-emitter": "~3.1.0", + "debug": "~4.3.2", + "engine.io-client": "~6.2.3", + "socket.io-parser": "~4.2.0" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/socket.io-client/node_modules/debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/socket.io-client/node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + }, + "node_modules/socket.io-parser": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-4.2.1.tgz", + "integrity": "sha512-V4GrkLy+HeF1F/en3SpUaM+7XxYXpuMUWLGde1kSSh5nQMN4hLrbPIkD+otwh6q9R6NOQBN4AMaOZ2zVjui82g==", + "dependencies": { + "@socket.io/component-emitter": "~3.1.0", + "debug": "~4.3.1" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/socket.io-parser/node_modules/debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/socket.io-parser/node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + }, + "node_modules/socket.io/node_modules/debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/socket.io/node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + }, "node_modules/socks": { "version": "2.7.1", "resolved": "https://registry.npmjs.org/socks/-/socks-2.7.1.tgz", @@ -2393,6 +2747,14 @@ "node": ">= 0.4.0" } }, + "node_modules/uuid": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", + "bin": { + "uuid": "dist/bin/uuid" + } + }, "node_modules/v8-compile-cache-lib": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz", @@ -2468,6 +2830,20 @@ "node": ">=12" } }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, "node_modules/word-wrap": { "version": "1.2.3", "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz", @@ -2525,6 +2901,14 @@ "resolved": "https://registry.npmjs.org/xmlchars/-/xmlchars-2.2.0.tgz", "integrity": "sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==" }, + "node_modules/xmlhttprequest-ssl": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/xmlhttprequest-ssl/-/xmlhttprequest-ssl-2.0.0.tgz", + "integrity": "sha512-QKxVRxiRACQcVuQEYFsI1hhkrMlrXHPegbbd1yn9UHOmRxY+si12nQYzri3vbzt8VdTTRviqcKxcyllFas5z2A==", + "engines": { + "node": ">=0.4.0" + } + }, "node_modules/y18n": { "version": "5.0.8", "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", @@ -2618,6 +3002,11 @@ "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-5.3.0.tgz", "integrity": "sha512-CX6t4SYQ37lzxicAqsBtxA3OseeoVrh9cSJ5PFYam0GksYlupRfy1A+Q4aYD3zvcfECLc0zO2u+ZnR2UYKvCrw==" }, + "@socket.io/component-emitter": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@socket.io/component-emitter/-/component-emitter-3.1.0.tgz", + "integrity": "sha512-+9jVqKhRSpsc591z5vX+X5Yyw+he/HCB4iQ/RYxw35CEPaY1gnsNE43nf9n9AaYjAQrTiI/mOwKUKdUs9vf7Xg==" + }, "@szmarczak/http-timer": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/@szmarczak/http-timer/-/http-timer-5.0.1.tgz", @@ -2707,6 +3096,16 @@ "@types/node": "*" } }, + "@types/cookie": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/@types/cookie/-/cookie-0.4.1.tgz", + "integrity": "sha512-XW/Aa8APYr6jSVVA1y/DEIZX0/GMKLEVekNG727R8cs56ahETkRAy/3DR7+fJyh7oUgGwNQaRfXCun0+KbWY7Q==" + }, + "@types/cors": { + "version": "2.8.12", + "resolved": "https://registry.npmjs.org/@types/cors/-/cors-2.8.12.tgz", + "integrity": "sha512-vt+kDhq/M2ayberEtJcIN/hxXy1Pk+59g2FV/ZQceeaTyCtCucjL2Q7FXlFjtWn4n15KCr1NE2lNNFhp0lEThw==" + }, "@types/express": { "version": "4.17.14", "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.14.tgz", @@ -2746,6 +3145,15 @@ "resolved": "https://registry.npmjs.org/@types/node/-/node-18.11.4.tgz", "integrity": "sha512-BxcJpBu8D3kv/GZkx/gSMz6VnTJREBj/4lbzYOQueUOELkt8WrO6zAcSPmp9uRPEW/d+lUO8QK0W2xnS1hEU0A==" }, + "@types/node-notifier": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/@types/node-notifier/-/node-notifier-8.0.2.tgz", + "integrity": "sha512-5v0PhPv0AManpxT7W25Zipmj/Lxp1WqfkcpZHyqSloB+gGoAHRBuzhrCelFKrPvNF5ki3gAcO4kxaGO2/21u8g==", + "dev": true, + "requires": { + "@types/node": "*" + } + }, "@types/qs": { "version": "6.9.7", "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.7.tgz", @@ -2912,6 +3320,11 @@ "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==" }, + "base64id": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/base64id/-/base64id-2.0.0.tgz", + "integrity": "sha512-lGe34o6EHj9y3Kts9R4ZYs/Gr+6N7MCaMlIFA3F1R2O5/m7K06AxfSeO5530PEERE6/WyEg3lsuyw4GHlPZHog==" + }, "bintrees": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/bintrees/-/bintrees-1.0.2.tgz", @@ -3206,6 +3619,87 @@ "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==" }, + "engine.io": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/engine.io/-/engine.io-6.2.0.tgz", + "integrity": "sha512-4KzwW3F3bk+KlzSOY57fj/Jx6LyRQ1nbcyIadehl+AnXjKT7gDO0ORdRi/84ixvMKTym6ZKuxvbzN62HDDU1Lg==", + "requires": { + "@types/cookie": "^0.4.1", + "@types/cors": "^2.8.12", + "@types/node": ">=10.0.0", + "accepts": "~1.3.4", + "base64id": "2.0.0", + "cookie": "~0.4.1", + "cors": "~2.8.5", + "debug": "~4.3.1", + "engine.io-parser": "~5.0.3", + "ws": "~8.2.3" + }, + "dependencies": { + "cookie": { + "version": "0.4.2", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.2.tgz", + "integrity": "sha512-aSWTXFzaKWkvHO1Ny/s+ePFpvKsPnjc551iI41v3ny/ow6tBG5Vd+FuqGNhh1LxOmVzOlGUriIlOaokOvhaStA==" + }, + "debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "requires": { + "ms": "2.1.2" + } + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + }, + "ws": { + "version": "8.2.3", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.2.3.tgz", + "integrity": "sha512-wBuoj1BDpC6ZQ1B7DWQBYVLphPWkm8i9Y0/3YdHjHKHiohOJ1ws+3OccDWtH+PoC9DZD5WOTrJvNbWvjS6JWaA==", + "requires": {} + } + } + }, + "engine.io-client": { + "version": "6.2.3", + "resolved": "https://registry.npmjs.org/engine.io-client/-/engine.io-client-6.2.3.tgz", + "integrity": "sha512-aXPtgF1JS3RuuKcpSrBtimSjYvrbhKW9froICH4s0F3XQWLxsKNxqzG39nnvQZQnva4CMvUK63T7shevxRyYHw==", + "requires": { + "@socket.io/component-emitter": "~3.1.0", + "debug": "~4.3.1", + "engine.io-parser": "~5.0.3", + "ws": "~8.2.3", + "xmlhttprequest-ssl": "~2.0.0" + }, + "dependencies": { + "debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "requires": { + "ms": "2.1.2" + } + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + }, + "ws": { + "version": "8.2.3", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.2.3.tgz", + "integrity": "sha512-wBuoj1BDpC6ZQ1B7DWQBYVLphPWkm8i9Y0/3YdHjHKHiohOJ1ws+3OccDWtH+PoC9DZD5WOTrJvNbWvjS6JWaA==", + "requires": {} + } + } + }, + "engine.io-parser": { + "version": "5.0.4", + "resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-5.0.4.tgz", + "integrity": "sha512-+nVFp+5z1E3HcToEnO7ZIj3g+3k9389DvWtvJZz0T6/eOCPIyyxehFcedoYrZQrp0LgQbD9pPXhpMBKMd5QURg==" + }, "entities": { "version": "4.4.0", "resolved": "https://registry.npmjs.org/entities/-/entities-4.4.0.tgz", @@ -3471,6 +3965,11 @@ "responselike": "^3.0.0" } }, + "growly": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/growly/-/growly-1.3.0.tgz", + "integrity": "sha512-+xGQY0YyAWCnqy7Cd++hc2JqMYzlm0dG30Jd0beaA64sROr8C4nt8Yc9V5Ro3avlSUDTN0ulqP/VBKi1/lLygw==" + }, "has": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", @@ -3595,6 +4094,11 @@ "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==" }, + "is-docker": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.2.1.tgz", + "integrity": "sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==" + }, "is-fullwidth-code-point": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", @@ -3610,6 +4114,19 @@ "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-2.2.2.tgz", "integrity": "sha512-+lP4/6lKUBfQjZ2pdxThZvLUAafmZb8OAxFb8XXtiQmS35INgr85hdOGoEs124ez1FCnZJt6jau/T+alh58QFQ==" }, + "is-wsl": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz", + "integrity": "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==", + "requires": { + "is-docker": "^2.0.0" + } + }, + "isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==" + }, "jsdom": { "version": "20.0.1", "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-20.0.1.tgz", @@ -3690,6 +4207,14 @@ "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-3.0.0.tgz", "integrity": "sha512-ozCC6gdQ+glXOQsveKD0YsDy8DSQFjDTz4zyzEHNV5+JP5D62LmfDZ6o1cycFx9ouG940M5dE8C8CTewdj2YWQ==" }, + "lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "requires": { + "yallist": "^4.0.0" + } + }, "lru-queue": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/lru-queue/-/lru-queue-0.1.0.tgz", @@ -3875,6 +4400,19 @@ "resolved": "https://registry.npmjs.org/next-tick/-/next-tick-1.1.0.tgz", "integrity": "sha512-CXdUiJembsNjuToQvxayPZF9Vqht7hewsvy2sOWafLvi2awflj9mOC6bHIg50orX8IJvWKY9wYQ/zB2kogPslQ==" }, + "node-notifier": { + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/node-notifier/-/node-notifier-10.0.1.tgz", + "integrity": "sha512-YX7TSyDukOZ0g+gmzjB6abKu+hTGvO8+8+gIFDsRCU2t8fLV/P2unmt+LGFaIa4y64aX98Qksa97rgz4vMNeLQ==", + "requires": { + "growly": "^1.3.0", + "is-wsl": "^2.2.0", + "semver": "^7.3.5", + "shellwords": "^0.1.1", + "uuid": "^8.3.2", + "which": "^2.0.2" + } + }, "normalize-url": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-7.2.0.tgz", @@ -4092,6 +4630,14 @@ "xmlchars": "^2.2.0" } }, + "semver": { + "version": "7.3.8", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz", + "integrity": "sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==", + "requires": { + "lru-cache": "^6.0.0" + } + }, "send": { "version": "0.18.0", "resolved": "https://registry.npmjs.org/send/-/send-0.18.0.tgz", @@ -4135,6 +4681,11 @@ "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==" }, + "shellwords": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/shellwords/-/shellwords-0.1.1.tgz", + "integrity": "sha512-vFwSUfQvqybiICwZY5+DAWIPLKsWO31Q91JSKl3UYv+K5c2QRPzn0qzec6QPu1Qc9eHYItiP3NdJqNVqetYAww==" + }, "side-channel": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz", @@ -4155,6 +4706,89 @@ "resolved": "https://registry.npmjs.org/smart-buffer/-/smart-buffer-4.2.0.tgz", "integrity": "sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==" }, + "socket.io": { + "version": "4.5.3", + "resolved": "https://registry.npmjs.org/socket.io/-/socket.io-4.5.3.tgz", + "integrity": "sha512-zdpnnKU+H6mOp7nYRXH4GNv1ux6HL6+lHL8g7Ds7Lj8CkdK1jJK/dlwsKDculbyOHifcJ0Pr/yeXnZQ5GeFrcg==", + "requires": { + "accepts": "~1.3.4", + "base64id": "~2.0.0", + "debug": "~4.3.2", + "engine.io": "~6.2.0", + "socket.io-adapter": "~2.4.0", + "socket.io-parser": "~4.2.0" + }, + "dependencies": { + "debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "requires": { + "ms": "2.1.2" + } + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + } + } + }, + "socket.io-adapter": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/socket.io-adapter/-/socket.io-adapter-2.4.0.tgz", + "integrity": "sha512-W4N+o69rkMEGVuk2D/cvca3uYsvGlMwsySWV447y99gUPghxq42BxqLNMndb+a1mm/5/7NeXVQS7RLa2XyXvYg==" + }, + "socket.io-client": { + "version": "4.5.3", + "resolved": "https://registry.npmjs.org/socket.io-client/-/socket.io-client-4.5.3.tgz", + "integrity": "sha512-I/hqDYpQ6JKwtJOf5ikM+Qz+YujZPMEl6qBLhxiP0nX+TfXKhW4KZZG8lamrD6Y5ngjmYHreESVasVCgi5Kl3A==", + "requires": { + "@socket.io/component-emitter": "~3.1.0", + "debug": "~4.3.2", + "engine.io-client": "~6.2.3", + "socket.io-parser": "~4.2.0" + }, + "dependencies": { + "debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "requires": { + "ms": "2.1.2" + } + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + } + } + }, + "socket.io-parser": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-4.2.1.tgz", + "integrity": "sha512-V4GrkLy+HeF1F/en3SpUaM+7XxYXpuMUWLGde1kSSh5nQMN4hLrbPIkD+otwh6q9R6NOQBN4AMaOZ2zVjui82g==", + "requires": { + "@socket.io/component-emitter": "~3.1.0", + "debug": "~4.3.1" + }, + "dependencies": { + "debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "requires": { + "ms": "2.1.2" + } + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + } + } + }, "socks": { "version": "2.7.1", "resolved": "https://registry.npmjs.org/socks/-/socks-2.7.1.tgz", @@ -4355,6 +4989,11 @@ "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", "integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==" }, + "uuid": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==" + }, "v8-compile-cache-lib": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz", @@ -4411,6 +5050,14 @@ "webidl-conversions": "^7.0.0" } }, + "which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "requires": { + "isexe": "^2.0.0" + } + }, "word-wrap": { "version": "1.2.3", "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz", @@ -4442,6 +5089,11 @@ "resolved": "https://registry.npmjs.org/xmlchars/-/xmlchars-2.2.0.tgz", "integrity": "sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==" }, + "xmlhttprequest-ssl": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/xmlhttprequest-ssl/-/xmlhttprequest-ssl-2.0.0.tgz", + "integrity": "sha512-QKxVRxiRACQcVuQEYFsI1hhkrMlrXHPegbbd1yn9UHOmRxY+si12nQYzri3vbzt8VdTTRviqcKxcyllFas5z2A==" + }, "y18n": { "version": "5.0.8", "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", diff --git a/package.json b/package.json index 9b641e3..0bccf62 100644 --- a/package.json +++ b/package.json @@ -38,6 +38,7 @@ "@types/cli-color": "^2.0.2", "@types/express": "^4.17.14", "@types/node": "^18.11.4", + "@types/node-notifier": "^8.0.2", "@types/yargs": "^17.0.13", "ts-node": "^10.9.1", "typescript": "^4.8.4" @@ -47,7 +48,10 @@ "cli-color": "^2.0.3", "express": "^4.18.2", "express-rate-limit": "^6.6.0", + "node-notifier": "^10.0.1", "prom-client": "^14.1.0", + "socket.io": "^4.5.3", + "socket.io-client": "^4.5.3", "yargs": "^17.6.0" } -} \ No newline at end of file +} diff --git a/src/api/v2.ts b/src/api/v2.ts deleted file mode 100644 index dfbe4ba..0000000 --- a/src/api/v2.ts +++ /dev/null @@ -1,76 +0,0 @@ -import fs from "node:fs"; -import express from "express"; -import * as bdsCore from "@the-bds-maneger/core"; -const app = express.Router(); -export default app; - -app.get("/", ({res}) => res.json(Object.keys(bdsCore.globalPlatfroms.internalSessions).reduce((sessionRes, sessionID) => { - sessionRes[sessionID] = { - platform: bdsCore.globalPlatfroms.internalSessions[sessionID].platform, - ports: bdsCore.globalPlatfroms.internalSessions[sessionID].portListening, - player: bdsCore.globalPlatfroms.internalSessions[sessionID].playerActions - }; - - return sessionRes -}, {}))); - -app.post("/", (req, res, next) => { - if (!req.body.platform) return res.status(400).json({error: "Informs a platform to start the server"}); - else if (req.body.platform === "bedrock"||req.body.platform === "Bedrock") return bdsCore.Bedrock.startServer(req.body.platformOptions).then(data => res.status(200).json({id: data.id, platform: data.platform})).catch(err => next(err)); - else if (req.body.platform === "java"||req.body.platform === "Java") return bdsCore.Java.startServer(req.body.platformOptions).then(data => res.status(200).json({id: data.id, platform: data.platform})).catch(err => next(err)); - else if (req.body.platform === "spigot"||req.body.platform === "Spigot") return bdsCore.Spigot.startServer(req.body.platformOptions).then(data => res.status(200).json({id: data.id, platform: data.platform})).catch(err => next(err)); - else if (req.body.platform === "powernukkit"||req.body.platform === "Powernukkit") return bdsCore.PocketmineMP.startServer(req.body.platformOptions).then(data => res.status(200).json({id: data.id, platform: data.platform})).catch(err => next(err)); - else if (req.body.platform === "paper"||req.body.platform === "Paper"||req.body.platform === "papermc"||req.body.platform === "PaperMC") return bdsCore.PaperMC.startServer(req.body.platformOptions).then(data => res.status(200).json({id: data.id, platform: data.platform})).catch(err => next(err)); - else if (req.body.platform === "pocketmine"||req.body.platform === "Pocketmine"||req.body.platform === "PocketmineMP"||req.body.platform === "PocketmineMP") return bdsCore.PocketmineMP.startServer(req.body.platformOptions).then(data => res.status(200).json({id: data.id, platform: data.platform})).catch(err => next(err)); - else return res.status(400).json({ - error: "Invalid platform" - }); -}); - -app.post("/install", (req, res, next) => { - if (!req.body) req.body = {}; - if (!req.body.platform) return res.status(400).json({error: "Informs a platform to start the server"}); - if (!req.body.version) req.body.version = "latest"; - - if (req.body.platform === "bedrock"||req.body.platform === "Bedrock") return bdsCore.Bedrock.installServer(req.body.version, req.body.platformOptions).then(data => res.status(200).json(data)).catch(err => next(err)); - else if (req.body.platform === "java"||req.body.platform === "Java") return bdsCore.Java.installServer(req.body.version, req.body.platformOptions).then(data => res.status(200).json(data)).catch(err => next(err)); - else if (req.body.platform === "spigot"||req.body.platform === "Spigot") return bdsCore.Spigot.installServer(req.body.version, req.body.platformOptions).then(data => res.status(200).json(data)).catch(err => next(err)); - else if (req.body.platform === "powernukkit"||req.body.platform === "Powernukkit") return bdsCore.PocketmineMP.installServer(req.body.version, req.body.platformOptions).then(data => res.status(200).json(data)).catch(err => next(err)); - else if (req.body.platform === "paper"||req.body.platform === "Paper"||req.body.platform === "papermc"||req.body.platform === "PaperMC") return bdsCore.PaperMC.installServer(req.body.version, req.body.platformOptions).then(data => res.status(200).json(data)).catch(err => next(err)); - else if (req.body.platform === "pocketmine"||req.body.platform === "Pocketmine"||req.body.platform === "PocketmineMP"||req.body.platform === "PocketmineMP") return bdsCore.PocketmineMP.installServer(req.body.version, req.body.platformOptions).then(data => res.status(200).json(data)).catch(err => next(err)); - else return res.status(400).json({ - error: "Invalid platform" - }); -}); - -app.post("/stop", (req, res, next) => { - const session = bdsCore.globalPlatfroms.internalSessions[req.body?.id]; - if (!session) return res.status(404).json({error: "Session not exists"}); - return session.stopServer().then(exitCode => res.status(200).json({exitCode})).catch(err => next(err)); -}); - -app.post("/stop/all", (_req, res, next) => Promise.all(Object.keys(bdsCore.globalPlatfroms.internalSessions).map(id => bdsCore.globalPlatfroms.internalSessions[id].stopServer().then(exitCode => ({id, exitCode})).catch((err: Error) => err))).then(data => res.json(data)).catch(err => next(err))); - -app.put("/", (req, res) => { - const session = bdsCore.globalPlatfroms.internalSessions[req.body?.id]; - if (!session) return res.status(404).json({error: "Session not exists"}); - session.runCommand(...(Array.isArray(req.body?.commands)?req.body?.commands:[req.body?.commands])); - return res.status(200).json({ok: true}); -}); - -app.get("/log/:id", (req, res) => { - const session = bdsCore.globalPlatfroms.internalSessions[req.params.id]; - if (!session) return res.status(404).json({error: "Session not exists"}); - if (!session.serverCommand?.options?.logPath?.stdout) return res.status(404).json({error: "Not exists log file to session id"}); - - const logPipe = fs.createReadStream(session.serverCommand?.options?.logPath?.stdout); - if (req.query.noClose !== "true") logPipe.pipe(res); - else { - function logEmit(data: string|Buffer) {res.write(typeof data === "string"?(data+"\n"):data);} - logPipe.on("data", logEmit); - session.events.on("log", logEmit); - req.socket.once("close", () => session.events.removeListener("log", logEmit)); - session.events.once("exit", () => res.socket.closed?null:res.end("")); - } - return res; -}); \ No newline at end of file diff --git a/src/api/v1.ts b/src/cmd/daemon/api/v1.ts similarity index 100% rename from src/api/v1.ts rename to src/cmd/daemon/api/v1.ts diff --git a/src/cmd/daemon/api/v2.ts b/src/cmd/daemon/api/v2.ts new file mode 100644 index 0000000..4971897 --- /dev/null +++ b/src/cmd/daemon/api/v2.ts @@ -0,0 +1,92 @@ +import fs from "node:fs"; +import express from "express"; +import * as bdsCore from "@the-bds-maneger/core"; +const app = express.Router(); +export default app; + +const bedrock = ["bedrock", "Bedrock"]; +const java = ["java", "Java"]; +const spigot = ["spigot", "Spigot"]; +const powernukkit = ["powernukkit", "Powernukkit"]; +const paper = ["paper", "Paper", "papermc", "PaperMC"]; +const pocketmine = ["pocketmine", "Pocketmine", "pocketminemp", "PocketmineMP"]; + +app.post("/stop/all", (_req, res, next) => Promise.all(Object.keys(bdsCore.globalPlatfroms.internalSessions).map(id => bdsCore.globalPlatfroms.internalSessions[id].stopServer().then(exitCode => ({id, exitCode})).catch((err: Error) => err))).then(data => res.json(data)).catch(err => next(err))); +app.get("/", ({res}) => res.json(Object.keys(bdsCore.globalPlatfroms.internalSessions).reduce((sessionRes, sessionID) => { + sessionRes[sessionID] = { + platform: bdsCore.globalPlatfroms.internalSessions[sessionID].platform, + ports: bdsCore.globalPlatfroms.internalSessions[sessionID].portListening, + player: bdsCore.globalPlatfroms.internalSessions[sessionID].playerActions + }; + return sessionRes; +}, {}))); + +app.get("/:id", (req, res) => { + const session = bdsCore.globalPlatfroms.internalSessions[req.params.id]; + if (!session) return res.status(400).json({error: "Session ID not exists"}); + return res.status(200).json({ + platform: session.platform, + ports: session.portListening, + player: session.playerActions + }); +}); + +app.get("/log/:id", (req, res) => { + const session = bdsCore.globalPlatfroms.internalSessions[req.params.id]; + if (!session) return res.status(404).json({error: "Session not exists"}); + if (!session.serverCommand?.options?.logPath?.stdout) return res.status(404).json({error: "Not exists log file to session id"}); + + const logPipe = fs.createReadStream(session.serverCommand?.options?.logPath?.stdout); + if (req.query.noClose !== "true") logPipe.pipe(res); + else { + function logEmit(data: string|Buffer) {res.write(typeof data === "string"?(data+"\n"):data);} + logPipe.on("data", logEmit); + session.events.on("log", logEmit); + req.socket.once("close", () => session.events.removeListener("log", logEmit)); + session.events.once("exit", () => res.socket.closed?null:res.end("")); + } + return res; +}); + +app.post("/install", (req, res, next) => { + if (!req.body) req.body = {}; + if (!req.body.platform) return res.status(400).json({error: "Informs a platform to start the server"}); + if (!req.body.version) req.body.version = "latest"; + + if (bedrock.includes(req.body.platform)) return bdsCore.Bedrock.installServer(req.body.version, req.body.platformOptions).then(data => res.status(200).json(data)).catch(err => next(err)); + else if (java.includes(req.body.platform)) return bdsCore.Java.installServer(req.body.version, req.body.platformOptions).then(data => res.status(200).json(data)).catch(err => next(err)); + else if (spigot.includes(req.body.platform)) return bdsCore.Spigot.installServer(req.body.version, req.body.platformOptions).then(data => res.status(200).json(data)).catch(err => next(err)); + else if (powernukkit.includes(req.body.platform)) return bdsCore.PocketmineMP.installServer(req.body.version, req.body.platformOptions).then(data => res.status(200).json(data)).catch(err => next(err)); + else if (paper.includes(req.body.platform)) return bdsCore.PaperMC.installServer(req.body.version, req.body.platformOptions).then(data => res.status(200).json(data)).catch(err => next(err)); + else if (pocketmine.includes(req.body.platform)) return bdsCore.PocketmineMP.installServer(req.body.version, req.body.platformOptions).then(data => res.status(200).json(data)).catch(err => next(err)); + else return res.status(400).json({ + error: "Invalid platform" + }); +}); + +app.post("/", (req, res, next) => { + if (!req.body) req.body = {}; + if (!req.body.platform) return res.status(400).json({error: "Informs a platform to start the server"}); + else if (bedrock.includes(req.body.platform)) return bdsCore.Bedrock.startServer(req.body.platformOptions).then(data => res.status(200).json({id: data.id, platform: data.platform})).catch(err => next(err)); + else if (java.includes(req.body.platform)) return bdsCore.Java.startServer(req.body.platformOptions).then(data => res.status(200).json({id: data.id, platform: data.platform})).catch(err => next(err)); + else if (spigot.includes(req.body.platform)) return bdsCore.Spigot.startServer(req.body.platformOptions).then(data => res.status(200).json({id: data.id, platform: data.platform})).catch(err => next(err)); + else if (powernukkit.includes(req.body.platform)) return bdsCore.PocketmineMP.startServer(req.body.platformOptions).then(data => res.status(200).json({id: data.id, platform: data.platform})).catch(err => next(err)); + else if (paper.includes(req.body.platform)) return bdsCore.PaperMC.startServer(req.body.platformOptions).then(data => res.status(200).json({id: data.id, platform: data.platform})).catch(err => next(err)); + else if (pocketmine.includes(req.body.platform)) return bdsCore.PocketmineMP.startServer(req.body.platformOptions).then(data => res.status(200).json({id: data.id, platform: data.platform})).catch(err => next(err)); + else return res.status(400).json({ + error: "Invalid platform" + }); +}); + +app.put("/", (req, res) => { + const session = bdsCore.globalPlatfroms.internalSessions[req.body?.id]; + if (!session) return res.status(404).json({error: "Session not exists"}); + session.runCommand(...(Array.isArray(req.body?.commands)?req.body?.commands:[req.body?.commands])); + return res.status(200).json({ok: true}); +}); + +app.post("/stop", (req, res, next) => { + const session = bdsCore.globalPlatfroms.internalSessions[req.body?.id]; + if (!session) return res.status(404).json({error: "Session not exists"}); + return session.stopServer().then(exitCode => res.status(200).json({exitCode})).catch(err => next(err)); +}); diff --git a/src/daemon.ts b/src/cmd/daemon/daemon.ts similarity index 59% rename from src/daemon.ts rename to src/cmd/daemon/daemon.ts index 8db9a35..bc149b6 100644 --- a/src/daemon.ts +++ b/src/cmd/daemon/daemon.ts @@ -1,14 +1,16 @@ import os from "node:os"; +import http from "node:http"; import express from "express"; import expressRateLimit from "express-rate-limit"; import fs, { Mode } from "node:fs"; import fsPromise from "node:fs/promises"; import path from "node:path"; import crypto from "node:crypto"; +import app_v2 from "./api/v2"; +import { app_v1 } from "./api/v1"; +import { Server } from "socket.io"; import * as bdsCore from "@the-bds-maneger/core"; import * as Prometheus from "prom-client"; -import { app_v1 } from "./api/v1"; -import app_v2 from "./api/v2"; Prometheus.collectDefaultMetrics({prefix: "bdsd"}); const requests = new Prometheus.Counter({ @@ -17,11 +19,63 @@ const requests = new Prometheus.Counter({ labelNames: ["method", "from", "path"] }); +export const bdsdAuth = path.join(bdsCore.platformPathManeger.bdsRoot, "bdsd_auth.json"); +async function auth(Auth: string) { + // External requests + if (!fs.existsSync(bdsdAuth)) { + if (!fs.existsSync(bdsCore.platformPathManeger.bdsRoot)) await fsPromise.mkdir(bdsCore.platformPathManeger.bdsRoot, {recursive: true}); + const keys = crypto.generateKeyPairSync("rsa", {modulusLength: 4096, publicKeyEncoding: {type: "spki", format: "pem"}, privateKeyEncoding: {type: "pkcs8", format: "pem", cipher: "aes-256-cbc", passphrase: crypto.randomBytes(128).toString("hex")}}); + await fsPromise.writeFile(bdsdAuth, JSON.stringify(keys, null, 2)); + console.log("Bdsd Keys\nPublic base64: '%s'\n\npublic:\n%s", Buffer.from(keys.publicKey).toString("base64"), keys.publicKey); + return false; + } + + if (!Auth) throw new Error("Auth required"); + const authorizationPub = Buffer.from(Auth, "base64").toString("utf8").trim(); + const publicKey = (JSON.parse(await fsPromise.readFile(bdsdAuth, "utf8")) as crypto.KeyPairSyncResult).publicKey.trim(); + if (publicKey === authorizationPub) return true; + return false; +} + export default function app(options: {socket: string, port?: number, auth_key: boolean, chmod?: Mode}) { const app = express(); - app.disable("x-powered-by").disable("etag"); - app.use(express.json()); - app.use(express.urlencoded({extended: true})); + const httpServer = http.createServer(app); + const socket = http.createServer(app); + const io = new Server(httpServer); + + if (options.port) { + httpServer.listen(options.port, () => console.info("HTTP listen on http://127.0.0.1:%s", options.port)); + // Socket.io + io.use(async (socket, next) => { + if (!options.auth_key) {console.warn("Bdsd ignore auth key!"); return next();} + const { handshake } = socket; + console.log(handshake.address, handshake.auth); + if (await auth(handshake.auth.pub)) return next(); + return next(new Error("Auth invalid")); + }); + + // Express + app.use(async (req, res, next) => { + if (!options.auth_key) {console.warn("Bdsd ignore auth key!"); return next();} + // Allow by default socket + if (!req.socket.remoteAddress && !req.socket.remotePort) return next(); + + if (await auth(req.headers.authorization?.replace(/^.*\s+/, ""))) return next(); + return res.status(400).json({ + error: "Invalid auth or incorret public key" + }); + }); + } + + // Listen socks + if (fs.existsSync(options.socket)) fs.rmSync(options.socket, {force: true}); + socket.listen(options.socket, async function () { + // if (options.chmod) await fsPromise.chmod(options.socket, options.chmod); + console.info("Socket listen on '%s'", this.address()); + }); + + // App Route + app.disable("x-powered-by").disable("etag").use(express.json()).use(express.urlencoded({extended: true})); app.use(({ res, next }) => { res.json = (body: any) => res.setHeader("Content-Type", "application/json").send(JSON.stringify(body, null, 2)); return next(); }); app.get("/metrics", async ({res, next}) => Prometheus.register.metrics().then(data => res.set("Content-Type", Prometheus.register.contentType).send(data)).catch(err => next(err))); app.use((req, _, next) => {requests.inc({method: req.method, path: req.path, from: !(req.socket.remoteAddress&&req.socket.remotePort)?"socket":req.protocol});next();}); @@ -54,45 +108,6 @@ export default function app(options: {socket: string, port?: number, auth_key: b }); }); - // Listen socks - if (fs.existsSync(options.socket)) fs.rmSync(options.socket, {force: true}); - app.listen(options.socket, async function () { - // if (options.chmod) await fsPromise.chmod(options.socket, options.chmod); - console.info("Socket listen on '%s'", this.address()); - }); - if (options.port) { - const bdsdAuth = path.join(bdsCore.platformPathManeger.bdsRoot, "bdsd_auth.json"); - app.use(async (req, res, next) => { - if (!options.auth_key) { - console.warn("Bdsd ignore auth key!"); - return next(); - } - // Allow by default socket - if (!req.socket.remoteAddress && !req.socket.remotePort) { - res.setHeader("AuthSocket", "true"); - return next(); - } - - // External requests - if (!fs.existsSync(bdsdAuth)) { - if (!fs.existsSync(bdsCore.platformPathManeger.bdsRoot)) await fsPromise.mkdir(bdsCore.platformPathManeger.bdsRoot, {recursive: true}); - const keys = crypto.generateKeyPairSync("rsa", {modulusLength: 4096, publicKeyEncoding: {type: "spki", format: "pem"}, privateKeyEncoding: {type: "pkcs8", format: "pem", cipher: "aes-256-cbc", passphrase: crypto.randomBytes(128).toString("hex")}}); - await fsPromise.writeFile(bdsdAuth, JSON.stringify(keys, null, 2)); - console.log("Bdsd Keys\nPublic base64: '%s'\n\npublic:\n%s", Buffer.from(keys.publicKey).toString("base64"), keys.publicKey); - return res.redirect("/"); - } - - if (!req.headers.authorization) return res.status(400).json({error: "Send authorization with public key!"}); - const authorizationPub = Buffer.from(req.headers.authorization.replace(/^.*\s+/, ""), "base64").toString("utf8").trim(); - const publicKey = (JSON.parse(await fsPromise.readFile(bdsdAuth, "utf8")) as crypto.KeyPairSyncResult).publicKey.trim(); - if (publicKey === authorizationPub) return next(); - return res.status(400).json({ - error: "Invalid auth or incorret public key" - }); - }); - app.listen(options.port, () => console.info("HTTP listen on http://127.0.0.1:%s", options.port)); - } - // API Routes app.use("/v1", app_v1); app.use("/v2", app_v2); diff --git a/src/cmd/daemon/main.ts b/src/cmd/daemon/main.ts new file mode 100644 index 0000000..2df611b --- /dev/null +++ b/src/cmd/daemon/main.ts @@ -0,0 +1,140 @@ +import path from "node:path"; +import os from "node:os"; +import fs from "node:fs/promises"; +import utils from "node:util"; +import Yargs from "yargs"; +import daemonRequest from "../../libs/daemon_request"; +import { createInterface as readline } from "node:readline"; +import { installUpdateMessage } from "../../libs/messages"; +import daemon, {bdsdAuth} from "./daemon"; +import { bdsPlatform } from "@the-bds-maneger/core/src/platformPathManeger"; +import { playerHistoric, portListen } from "@the-bds-maneger/core/src/globalPlatfroms"; + +export async function daemon_main(Yargs: Yargs.Argv) { + const keyFile = await fs.readFile(bdsdAuth, "base64").catch(() => ""); + return Yargs.demandCommand().command("daemon", "Start daemon and listen port and socket", yargs => { + const options = yargs.option("port", { + alias: "p", + type: "number", + description: "Port listen HTTP/HTTPs API" + }).option("socket", { + alias: "S", + type: "string", + description: "unix socket listen", + default: process.env.BDSD_SOCKET?path.resolve(process.env.BDSD_SOCKET):path.join(os.tmpdir(), "bdsd.sock"), + }).option("auth_key", { + alias: "a", + type: "boolean", + description: "Enable auth to HTTP/HTTPs API", + default: false, + }).option("chmod", { + alias: "c", + type: "number", + default: 7777 + }).parseSync(); + process.title = `Minecraft Server Deamon, socket: ${options.socket}${options.port?", Port: "+options.port:""}`; + return daemon({ + socket: options.socket, + auth_key: options.auth_key, + port: options.port, + chmod: options.chmod + }); + }).option("socket", { + alias: "S", + type: "string", + description: "unix socket listen", + default: path.join(os.tmpdir(), "bdsd.sock"), + }).option("host", { + alias: "H", + type: "string", + description: "HTTP/HTTPs host if unix socket not exists", + default: "http://localhost:39074", + }).command("install", "Download and Install server to folder", async yargs => { + const opts = yargs.option("version", {alias: "v", default: "latest"}).option("id", {alias: "i", type: "string", default: "default"}).option("platform", {demandOption: true}).parseSync(); + const { post } = daemonRequest(opts.host, opts.socket, keyFile); + return post("/install", {platform: opts.platform, version: opts.version, platformOptions: {id: opts.id}}).then((data: {id: string, version: string, date: string, url: string}) => installUpdateMessage({ + date: new Date(data.date), + id: data.id, + version: data.version, + url: data.url + })) + }).command("ps", "List Minecraft servers IDs", yargs => { + const options = yargs.parseSync(); + const { get } = daemonRequest(options.host, options.socket, keyFile); + return get("/").then(data => { + const keysData = Object.keys(data).map(id => { + const a: {platform: bdsPlatform, player: playerHistoric, ports: {[key: string]: portListen}} = data[id]; + return utils.format( + "Platform: %s, ID: %s\n Players connected: %f\n Ports: %s", + a.platform, id, + Object.keys(a.player).filter(b => a.player[b].action === "connect").length, + Object.keys(a.ports).map(b => `${a.ports[b].port} (${a.ports[b].type}/${a.ports[b].protocol})`).join(", ") + ); + }); + return console.log(keysData.join("\n\n"), "\n"); + }); + }).command("start", "Start minecraft server", async yargs => { + const options = yargs.option("id", {type: "string", default: "default"}).option("platform", {demandOption: true, type: "string"}).option("interactive", { + alias: "i", + type: "boolean", + default: false, + description: "Send commands to server" + }).option("tty", { + alias: "t", + type: "boolean", + default: false, + description: "Show log" + }).option("stopOnClose", { + alias: "n", + default: true, + type: "boolean", + description: "If interactive (-i) stop server if press ctrl+c (close server)" + }).parseSync(); + const { post, put, stream } = daemonRequest(options.host, options.socket, keyFile); + return post("/", {platform: options.platform, version: options.version, platformOptions: {id: options.id}}).then(({id}) => { + console.log("Server ID: %s", id); + if (options.interactive) { + const line = readline({input: process.stdin, output: process.stdout}); + line.on("line", data => put("/", {id, commands: data})); + if (options.stopOnClose) { + line.once("SIGINT", () => post("/stop", {id}).catch(() => null).then(() => line.close())); + line.once("SIGCONT", () => post("/stop", {id}).catch(() => null).then(() => line.close())); + line.once("SIGTSTP", () => post("/stop", {id}).catch(() => null).then(() => line.close())); + } + if (options.tty) stream(`/log/${id}?noClose=true`, process.stdout).catch(() => undefined).then(() => line.emit("SIGINT")); + } + }); + }).command("attach", "attach to runnnig server", async yargs => { + const options = yargs.option("stopOnClose", { + alias: "n", + default: false, + type: "boolean", + description: "Stop server if press ctrl+c (close server)" + }).option("id", { + alias: "i", + type: "string", + demandOption: true, + description: "Target server id" + }).parseSync(); + const { post, put, stream } = daemonRequest(options.host, options.socket, keyFile); + const line = readline({input: process.stdin, output: process.stdout}); + line.on("line", data => put("/", {id: options.id, commands: data})); + stream(`/log/${options.id}?noClose=true`, process.stdout).catch(() => undefined).then(() => line.emit("SIGINT")); + const close = async () => { + if (options.stopOnClose) await post("/stop", {id: options.id}).catch(() => null); + return line.close(); + } + line.once("SIGINT", close); + line.once("SIGCONT", close); + line.once("SIGTSTP", close); + }).command("stop", "Stop server if not infomed IDs stop all", async yargs => { + const options = yargs.parseSync(); + const { post } = daemonRequest(options.host, options.socket, keyFile); + const [,, ...ids] = options._; + if (ids.length >= 1) return Promise.all(ids.map((id: string) => post("/stop", {id}).then(data => console.log("Server ID: %s\n\tCode Exit: %f", id, data.exitCode)).catch(console.error))); + return post("/stop/all").then((data: {id: string, exitCode: number}[]) => data.forEach(data => console.log("Server ID: %s\n\tCode Exit: %f", data.id, data.exitCode))); + }).command("migrate", "Migrate your existing servers to the daemon", async yargs => { + yargs.parseSync(); + throw new Error("Under construction, wait for the next version"); + }); +} \ No newline at end of file diff --git a/src/cmd/local/main.ts b/src/cmd/local/main.ts new file mode 100644 index 0000000..8513b08 --- /dev/null +++ b/src/cmd/local/main.ts @@ -0,0 +1,136 @@ +import yargs from "yargs"; +import { createInterface as readline } from "node:readline"; +import { installUpdateMessage } from "../../libs/messages"; +import utils from "node:util"; +import bdsCore from "@the-bds-maneger/core"; +import cliColors from "cli-color"; +import notifier from "node-notifier"; + +export function local_main(Yargs: yargs.Argv) { + return Yargs.demandCommand().command("install", "Install server", async yargs => { + const options = yargs.option("platform", { + alias: "p", + type: "string", + demandOption: true + }).option("version", { + alias: "v", + type: "string", + default: "latest" + }).option("id", { + alias: "I", + type: "string", + default: "default" + }).option("newId", { + alias: "n", + type: "boolean", + default: false + }).parseSync(); + + return (async function(): Promise { + // Bedrock + if (options.platform?.toLocaleLowerCase() === "bedrock") return bdsCore.Bedrock.installServer(options.version, {id: options.id, newId: options.newId}); + // Java + else if (options.platform?.toLocaleLowerCase() === "java") return bdsCore.Java.installServer(options.version, {id: options.id, newId: options.newId}); + // Pocketmine-MP + else if ((["pocketmine", "pocketminemp"]).includes(options.platform?.toLocaleLowerCase())) return bdsCore.PocketmineMP.installServer(options.version, {id: options.id, newId: options.newId}); + // Spigot + else if (options.platform?.toLocaleLowerCase() === "spigot") return bdsCore.Spigot.installServer(options.version, {id: options.id, newId: options.newId}); + // Powernukkit + else if (options.platform?.toLocaleLowerCase() === "powernukkit") return bdsCore.Powernukkit.installServer(options.version, {id: options.id, newId: options.newId}); + // PaperMC + else if ((["paper", "papermc"]).includes(options.platform?.toLocaleLowerCase())) return bdsCore.PaperMC.installServer(options.version, {id: options.id, newId: options.newId}); + // Throw + else throw new Error("Invalid platform!"); + })().then(data => installUpdateMessage(data)); + }).command("start", "Start server", async yargs => { + const options = yargs.option("platform", { + alias: "p", + type: "string", + demandOption: true, + }).option("id", { + alias: "I", + type: "string", + default: "default" + }).option("maxMemory", { + alias: "m", + type: "boolean", + default: true, + description: "On java severs, use all free memory to run server" + }).option("update", { + alias: "u", + type: "string", + description: "Update Server after start Server" + }).parseSync(); + return (async function(){ + // Bedrock + if (options.platform?.toLocaleLowerCase() === "bedrock") { + if (options.update) await bdsCore.Bedrock.installServer(options.update, {id: options.id}).then(data => installUpdateMessage({...data, isUpdate: true})); + return bdsCore.Bedrock.startServer({id: options.id}); + } + // Pocketmine-MP + else if ((["pocketmine", "pocketminemp"]).includes(options.platform?.toLocaleLowerCase())) { + if (options.update) await bdsCore.PocketmineMP.installServer(options.update, {id: options.id}).then(data => installUpdateMessage({...data, isUpdate: true})); + return bdsCore.PocketmineMP.startServer({id: options.id}); + } + // Java + else if (options.platform?.toLocaleLowerCase() === "java") { + if (options.update) await bdsCore.Java.installServer(options.update, {id: options.id}).then(data => installUpdateMessage({...data, isUpdate: true})); + return bdsCore.Java.startServer({platformOptions: {id: options.id}, maxFreeMemory: options.maxMemory}); + } + // Spigot + else if (options.platform?.toLocaleLowerCase() === "spigot") { + if (options.update) await bdsCore.Spigot.installServer(options.update, {id: options.id}).then((data: any) => installUpdateMessage({...data, isUpdate: true})); + return bdsCore.Spigot.startServer({platformOptions: {id: options.id}, maxFreeMemory: options.maxMemory}); + } + // Powernukkit + else if (options.platform?.toLocaleLowerCase() === "powernukkit") { + if (options.update) await bdsCore.Powernukkit.installServer(options.update, {id: options.id}).then(data => installUpdateMessage({...data, isUpdate: true})); + return bdsCore.Powernukkit.startServer({platformOptions: {id: options.id}, maxFreeMemory: options.maxMemory}); + } + // PaperMC + else if ((["paper", "papermc"]).includes(options.platform?.toLocaleLowerCase())) { + if (options.update) await bdsCore.PaperMC.installServer(options.update, {id: options.id}).then(data => installUpdateMessage({...data, isUpdate: true})); + return bdsCore.PaperMC.startServer({platformOptions: {id: options.id}, maxFreeMemory: options.maxMemory}); + } + // Throw + else throw new Error("Invalid platform!"); + })().then(server => { + const line = readline({input: process.stdin, output: process.stdout}); + server.events.on("log_stderr", data => console.log(cliColors.redBright(data))); + server.events.on("log_stdout", data => console.log(cliColors.greenBright(data))); + server.events.on("exit", data => console.log(...(data.signal?["Server exit with %s, signal: %s", data.code, data.signal]:["Server exit with %s", data.code]))); + line.on("line", line => server.runCommand(line)); + line.once("SIGINT", () => server.stopServer()); + line.once("SIGCONT", () => server.stopServer()); + line.once("SIGTSTP", () => server.stopServer()); + server.events.once("exit", () => line.close()); + server.events.on("playerConnect", (data) => { + return notifier.notify({ + title: `Player connect (${server.id})`, + message: data.playerName + }); + }); + server.events.on("playerDisconnect", (data) => { + return notifier.notify({ + title: `Player disconnect (${server.id})`, + message: data.playerName + }); + }); + server.events.on("portListening", (data) => { + return notifier.notify({ + title: `Server port listen (${server.id})`, + message: `${data.port}` + }); + }); + return server.waitExit(); + }); + }).command("ls", "List IDs and Platforms installed", async () => { + const ids = await bdsCore.utils.platformPathManeger.getIds(); + const data = Object.keys(ids).map(key => { + let text = cliColors.redBright("No Installs"); + if (ids[key].length > 0) text = cliColors.greenBright("ID: "+ids[key].map(a => a.id === "default" ? `${a.id} -> ${a.realID}` : a.id).join("\n ID: ")); + return utils.format(cliColors.blueBright("Platform: %s\n %s"), cliColors.yellow(key.charAt(0).toUpperCase() + key.slice(1)), text); + }); + console.log(data.join("\n\n"), "\n"); + }); +} \ No newline at end of file diff --git a/src/index.ts b/src/index.ts index 56cc873..b0741f4 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,303 +1,14 @@ #!/usr/bin/env node -import { createInterface as readline } from "node:readline"; -import * as bdsCore from "@the-bds-maneger/core"; -import utils from "node:util"; -import path from "node:path"; -import os from "node:os"; import Yargs from "yargs"; -import daemon from "./daemon"; -import cliColors from "cli-color"; +import { daemon_main } from "./cmd/daemon/main"; +import { local_main } from "./cmd/local/main"; -function installUpdateMessage(data: {date: Date, id?: string, version: string, url?: string, isUpdate?: boolean}) { - const releaseDate = new Date(data?.date), day = releaseDate.getDay()>9?releaseDate.getDay().toFixed(0):"0"+releaseDate.getDay(), month = (releaseDate.getMonth()+1)>9?(releaseDate.getMonth()+1).toFixed(0):"0"+(releaseDate.getMonth()+1); - if (data?.isUpdate) console.log("Update Platform ID: %s\n\tVersion: %s, Release date: %s/%s/%s", data?.id, data?.version, day, month, releaseDate.getFullYear()); - else console.log("Install Platform ID: %s\n\tVersion: %s, Release date: %s/%s/%s", data?.id, data?.version, day, month, releaseDate.getFullYear()); -} - -function daemonRequest(host: string, socketPath: string) { - async function post(requestPath: string, body?: any) { - return bdsCore.httpRequest.getJSON({ - method: "POST", - socket: { - socketPath, - path: requestPath - }, - headers: {"Content-Type": "application/json"}, - body: body||{} - }).catch(() => bdsCore.httpRequest.getJSON({ - method: "POST", - url: host+requestPath, - headers: {"Content-Type": "application/json"}, - body: body||{} - })); - } - - async function get(requestPath: string, body?: any) { - return bdsCore.httpRequest.getJSON({ - socket: { - socketPath, - path: requestPath - }, - headers: {"Content-Type": "application/json"}, - body: body||{} - }).catch(() => bdsCore.httpRequest.getJSON({ - url: host+requestPath, - headers: {"Content-Type": "application/json"}, - body: body||{} - })); - } - - async function put(requestPath: string, body?: any) { - return bdsCore.httpRequest.getJSON({ - method: "PUT", - socket: { - socketPath, - path: requestPath - }, - headers: {"Content-Type": "application/json"}, - body: body||{} - }).catch(() => bdsCore.httpRequest.getJSON({ - method: "PUT", - url: host+requestPath, - headers: {"Content-Type": "application/json"}, - body: body||{} - })); - } - - async function stream(requestPath: string, stream?: any, method: bdsCore.httpRequest.requestOptions["method"] = "GET") { - return bdsCore.httpRequest.pipeFetch({ - stream, method, - socket: { - socketPath, - path: requestPath - }, - headers: {"Content-Type": "application/json"} - }).catch(() => bdsCore.httpRequest.pipeFetch({ - url: host+requestPath, - stream, method, - headers: {"Content-Type": "application/json"} - })); - } - - return {post, get, put, stream}; -} - -const yargs = Yargs(process.argv.slice(2)).help().version(false).alias("h", "help").wrap(Yargs.terminalWidth()).command("daemon", "Start daemon and listen port and socket", yargs => { - const options = yargs.option("port", { - alias: "p", - type: "number", - description: "Port listen HTTP/HTTPs API" - }).option("socket", { - alias: "S", - type: "string", - description: "unix socket listen", - default: process.env.BDSD_SOCKET?path.resolve(process.env.BDSD_SOCKET):path.join(os.tmpdir(), "bdsd.sock"), - }).option("auth_key", { - alias: "a", - type: "boolean", - description: "Enable auth to HTTP/HTTPs API", - default: false, - }).option("chmod", { - alias: "c", - type: "string", - default: "a+rw" - }).parseSync(); - process.title = `Minecraft Server Deamon, socket: ${options.socket}${options.port?", Port: "+options.port:""}`; - return daemon({ - socket: options.socket, - auth_key: options.auth_key, - port: options.port, - chmod: options.chmod - }); -}).command("server", "Maneger server in daemon", yargs => { - return yargs.option("socket", { - alias: "S", - type: "string", - description: "unix socket listen", - default: path.join(os.tmpdir(), "bdsd.sock"), - }).option("host", { - alias: "H", - type: "string", - description: "HTTP/HTTPs host if unix socket not exists", - default: "http://localhost:9074", - }).command("install", "Download and Install server to folder", async yargs => { - const opts = yargs.option("version", {alias: "v", default: "latest"}).option("id", {alias: "i", type: "string", default: "default"}).option("platform", {demandOption: true}).parseSync(); - const { post } = daemonRequest(opts.host, opts.socket); - return post("/install", {platform: opts.platform, version: opts.version, platformOptions: {id: opts.id}}).then((data: {id: string, version: string, date: string, url: string}) => installUpdateMessage({ - date: new Date(data.date), - id: data.id, - version: data.version, - url: data.url - })); - }).command("start", "Start minecraft server", async yargs => { - const options = yargs.option("id", {type: "string", default: "default"}).option("platform", {demandOption: true, type: "string"}).option("interactive", { - alias: "i", - type: "boolean", - default: false, - description: "Send commands to server" - }).option("tty", { - alias: "t", - type: "boolean", - default: false, - description: "Show log" - }).option("stopOnClose", { - alias: "n", - default: true, - type: "boolean", - description: "If interactive (-i) stop server if press ctrl+c (close server)" - }).parseSync(); - const { post, put, stream } = daemonRequest(options.host, options.socket); - return post("/", {platform: options.platform, version: options.version, platformOptions: {id: options.id}}).then(({id}) => { - console.log("Server ID: %s", id); - if (options.interactive) { - const line = readline({input: process.stdin, output: process.stdout}); - line.on("line", data => put("/", {id, commands: data})); - if (options.stopOnClose) { - line.once("SIGINT", () => post("/stop", {id}).catch(() => null).then(() => line.close())); - line.once("SIGCONT", () => post("/stop", {id}).catch(() => null).then(() => line.close())); - line.once("SIGTSTP", () => post("/stop", {id}).catch(() => null).then(() => line.close())); - } - if (options.tty) stream(`/log/${id}?noClose=true`, process.stdout).catch(() => undefined).then(() => line.emit("SIGINT")); - } - }); - }).command("attach", "attach to runnnig server", async yargs => { - const options = yargs.option("stopOnClose", { - alias: "n", - default: false, - type: "boolean", - description: "Stop server if press ctrl+c (close server)" - }).option("id", { - alias: "i", - type: "string", - demandOption: true, - description: "Target server id" - }).parseSync(); - const { post, put, stream } = daemonRequest(options.host, options.socket); - const line = readline({input: process.stdin, output: process.stdout}); - line.on("line", data => put("/", {id: options.id, commands: data})); - stream(`/log/${options.id}?noClose=true`, process.stdout).catch(() => undefined).then(() => line.emit("SIGINT")); - const close = async () => { - if (options.stopOnClose) await post("/stop", {id: options.id}).catch(() => null); - return line.close(); - } - line.once("SIGINT", close); - line.once("SIGCONT", close); - line.once("SIGTSTP", close); - }).command("stop", "Stop server if not infomed IDs stop all", async yargs => { - const options = yargs.parseSync(); - const { post } = daemonRequest(options.host, options.socket); - const [,, ...ids] = options._; - if (ids.length >= 1) return Promise.all(ids.map((id: string) => post("/stop", {id}).then(data => console.log("Server ID: %s\n\tCode Exit: %f", id, data.exitCode)).catch(console.error))); - return post("/stop/all").then((data: {id: string, exitCode: number}[]) => data.forEach(data => console.log("Server ID: %s\n\tCode Exit: %f", data.id, data.exitCode))); - }).command("migrate", "Migrate your existing servers to the daemon", async yargs => { - yargs.parseSync(); - throw new Error("Under construction, wait for the next version"); - }).parseAsync(); -}).command("local", "Run server localy", async yargs => { - return yargs.command("install", "Install server", async yargs => { - const options = yargs.option("platform", { - alias: "p", - type: "string", - demandOption: true - }).option("version", { - alias: "v", - type: "string", - default: "latest" - }).option("id", { - alias: "I", - type: "string", - default: "default" - }).option("newId", { - alias: "n", - type: "boolean", - default: false - }).parseSync(); - - return (async function(): Promise { - // Bedrock - if (options.platform?.toLocaleLowerCase() === "bedrock") return bdsCore.Bedrock.installServer(options.version, {id: options.id, newId: options.newId}); - // Java - else if (options.platform?.toLocaleLowerCase() === "java") return bdsCore.Java.installServer(options.version, {id: options.id, newId: options.newId}); - // Pocketmine-MP - else if ((["pocketmine", "pocketminemp"]).includes(options.platform?.toLocaleLowerCase())) return bdsCore.PocketmineMP.installServer(options.version, {id: options.id, newId: options.newId}); - // Spigot - else if (options.platform?.toLocaleLowerCase() === "spigot") return bdsCore.Spigot.installServer(options.version, {id: options.id, newId: options.newId}); - // Powernukkit - else if (options.platform?.toLocaleLowerCase() === "powernukkit") return bdsCore.Powernukkit.installServer(options.version, {id: options.id, newId: options.newId}); - // PaperMC - else if ((["paper", "papermc"]).includes(options.platform?.toLocaleLowerCase())) return bdsCore.PaperMC.installServer(options.version, {id: options.id, newId: options.newId}); - // Throw - else throw new Error("Invalid platform!"); - })().then(data => installUpdateMessage(data)); - }).command("start", "Start server", async yargs => { - const options = yargs.option("platform", { - alias: "p", - type: "string", - demandOption: true, - }).option("id", { - alias: "I", - type: "string", - default: "default" - }).option("maxMemory", { - alias: "m", - type: "boolean", - default: true, - description: "On java severs, use all free memory to run server" - }).option("update", { - alias: "u", - type: "string", - description: "Update Server after start Server" - }).parseSync(); - return (async function(){ - // Bedrock - if (options.platform?.toLocaleLowerCase() === "bedrock") { - if (options.update) await bdsCore.Bedrock.installServer(options.update, {id: options.id}).then(data => installUpdateMessage({...data, isUpdate: true})); - return bdsCore.Bedrock.startServer({id: options.id}); - } - // Pocketmine-MP - else if ((["pocketmine", "pocketminemp"]).includes(options.platform?.toLocaleLowerCase())) { - if (options.update) await bdsCore.PocketmineMP.installServer(options.update, {id: options.id}).then(data => installUpdateMessage({...data, isUpdate: true})); - return bdsCore.PocketmineMP.startServer({id: options.id}); - } - // Java - else if (options.platform?.toLocaleLowerCase() === "java") { - if (options.update) await bdsCore.Java.installServer(options.update, {id: options.id}).then(data => installUpdateMessage({...data, isUpdate: true})); - return bdsCore.Java.startServer({platformOptions: {id: options.id}, maxFreeMemory: options.maxMemory}); - } - // Spigot - else if (options.platform?.toLocaleLowerCase() === "spigot") { - if (options.update) await bdsCore.Spigot.installServer(options.update, {id: options.id}).then((data: any) => installUpdateMessage({...data, isUpdate: true})); - return bdsCore.Spigot.startServer({platformOptions: {id: options.id}, maxFreeMemory: options.maxMemory}); - } - // Powernukkit - else if (options.platform?.toLocaleLowerCase() === "powernukkit") { - if (options.update) await bdsCore.Powernukkit.installServer(options.update, {id: options.id}).then(data => installUpdateMessage({...data, isUpdate: true})); - return bdsCore.Powernukkit.startServer({platformOptions: {id: options.id}, maxFreeMemory: options.maxMemory}); - } - // PaperMC - else if ((["paper", "papermc"]).includes(options.platform?.toLocaleLowerCase())) { - if (options.update) await bdsCore.PaperMC.installServer(options.update, {id: options.id}).then(data => installUpdateMessage({...data, isUpdate: true})); - return bdsCore.PaperMC.startServer({platformOptions: {id: options.id}, maxFreeMemory: options.maxMemory}); - } - // Throw - else throw new Error("Invalid platform!"); - })().then(server => { - const line = readline({input: process.stdin, output: process.stdout}); - server.events.on("log_stderr", data => console.log(cliColors.redBright(data))); - server.events.on("log_stdout", data => console.log(cliColors.greenBright(data))); - server.events.on("exit", data => console.info("Server exit with %s, signal: %s", data.code, data.signal)); - line.on("line", line => server.runCommand(line)); - line.once("SIGINT", () => server.stopServer()); - line.once("SIGCONT", () => server.stopServer()); - line.once("SIGTSTP", () => server.stopServer()); - server.events.once("exit", () => line.close()); - return server.waitExit(); - }) - }).command("ls", "List IDs and Platforms installed", () => bdsCore.platformPathManeger.getIds().then(data => Object.keys(data).map(key => utils.format(cliColors.blueBright("Platform: %s\n %s"), key.charAt(0).toUpperCase() + key.slice(1), data[key].length===0?cliColors.redBright("No Installs"):cliColors.greenBright("ID: "+data[key].join("\n ID: "))))).then(Print => console.log(Print.join("\n\n"), "\n"))).parseAsync(); -}); - -yargs.command({command: "*", handler: () => {yargs.showHelp();}}).parseAsync().catch((err) => { +Yargs(process.argv.slice(2)).help().version(false).alias("h", "help").wrap(Yargs.terminalWidth()).demandCommand() +.command("server", "Maneger server in daemon", daemon_main) +.command("local", "Run server localy", local_main).parseAsync().catch((err) => { console.error("Error: %s", err?.message||err); - console.error("Error:", err?.options?.body); + if (err?.options?.body) { + console.error("Error:", err?.options?.body); + } process.exit(1); }); \ No newline at end of file diff --git a/src/libs/daemon_request.ts b/src/libs/daemon_request.ts new file mode 100644 index 0000000..3c8a4e4 --- /dev/null +++ b/src/libs/daemon_request.ts @@ -0,0 +1,68 @@ +import bdsCore, { httpRequest } from "@the-bds-maneger/core"; + +export default daemonRequest; +export function daemonRequest(host: string, socketPath: string, authKey?: string) { + async function post(requestPath: string, body?: any) { + return bdsCore.utils.httpRequest.getJSON({ + method: "POST", + socket: { + socketPath, + path: requestPath + }, + headers: {"Content-Type": "application/json", authorization: `basic ${authKey}`}, + body: body||{} + }).catch(() => bdsCore.utils.httpRequest.getJSON({ + method: "POST", + url: host+requestPath, + headers: {"Content-Type": "application/json"}, + body: body||{} + })); + } + + async function get(requestPath: string, body?: any) { + return bdsCore.utils.httpRequest.getJSON({ + socket: { + socketPath, + path: requestPath + }, + headers: {"Content-Type": "application/json"} + }).catch(() => bdsCore.utils.httpRequest.getJSON({ + url: host+requestPath, + headers: {"Content-Type": "application/json"} + })); + } + + async function put(requestPath: string, body?: any) { + return bdsCore.utils.httpRequest.getJSON({ + method: "PUT", + socket: { + socketPath, + path: requestPath + }, + headers: {"Content-Type": "application/json"}, + body: body||{} + }).catch(() => bdsCore.utils.httpRequest.getJSON({ + method: "PUT", + url: host+requestPath, + headers: {"Content-Type": "application/json"}, + body: body||{} + })); + } + + async function stream(requestPath: string, stream?: any, method: httpRequest.requestOptions["method"] = "GET") { + return bdsCore.utils.httpRequest.pipeFetch({ + stream, method, + socket: { + socketPath, + path: requestPath + }, + headers: {"Content-Type": "application/json"} + }).catch(() => bdsCore.utils.httpRequest.pipeFetch({ + url: host+requestPath, + stream, method, + headers: {"Content-Type": "application/json"} + })); + } + + return {post, get, put, stream}; +} diff --git a/src/libs/messages.ts b/src/libs/messages.ts new file mode 100644 index 0000000..812adea --- /dev/null +++ b/src/libs/messages.ts @@ -0,0 +1,7 @@ +import cliColor from "cli-color"; + +export function installUpdateMessage(data: {date: Date, id?: string, version: string, url?: string, isUpdate?: boolean}) { + const releaseDate = new Date(data?.date), day = releaseDate.getDay()>9?releaseDate.getDay().toFixed(0):"0"+releaseDate.getDay(), month = (releaseDate.getMonth()+1)>9?(releaseDate.getMonth()+1).toFixed(0):"0"+(releaseDate.getMonth()+1); + if (data?.isUpdate) console.log(cliColor.yellowBright("Update Platform ID: %s\n\tVersion: %s, Release date: %s/%s/%s"), data?.id, data?.version, day, month, releaseDate.getFullYear()); + else console.log(cliColor.greenBright("Install Platform ID: %s\n\tVersion: %s, Release date: %s/%s/%s"), data?.id, data?.version, day, month, releaseDate.getFullYear()); +} \ No newline at end of file