aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--package-lock.json410
-rw-r--r--package.json3
-rw-r--r--src/server/ApiManagers/SessionManager.ts29
-rw-r--r--src/server/DashSession/DashSessionAgent.ts56
-rw-r--r--src/server/index.ts2
-rw-r--r--src/server/session/README.txt11
-rw-r--r--src/server/session/agents/applied_session_agent.ts58
-rw-r--r--src/server/session/agents/monitor.ts302
-rw-r--r--src/server/session/agents/process_message_router.ts46
-rw-r--r--src/server/session/agents/promisified_ipc_manager.ts97
-rw-r--r--src/server/session/agents/server_worker.ts160
-rw-r--r--src/server/session/utilities/repl.ts128
-rw-r--r--src/server/session/utilities/session_config.ts129
-rw-r--r--src/server/session/utilities/utilities.ts31
-rw-r--r--src/typings/index.d.ts2
15 files changed, 378 insertions, 1086 deletions
diff --git a/package-lock.json b/package-lock.json
index 1200dc26c..3b4fbcc29 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -374,8 +374,7 @@
"@types/chai": {
"version": "4.2.7",
"resolved": "https://registry.npmjs.org/@types/chai/-/chai-4.2.7.tgz",
- "integrity": "sha512-luq8meHGYwvky0O7u0eQZdA7B4Wd9owUCqvbw2m3XCrCU8mplYOujMBbvyS547AxJkC+pGnd0Cm15eNxEUNU8g==",
- "dev": true
+ "integrity": "sha512-luq8meHGYwvky0O7u0eQZdA7B4Wd9owUCqvbw2m3XCrCU8mplYOujMBbvyS547AxJkC+pGnd0Cm15eNxEUNU8g=="
},
"@types/classnames": {
"version": "2.2.9",
@@ -595,8 +594,7 @@
"@types/mocha": {
"version": "5.2.7",
"resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-5.2.7.tgz",
- "integrity": "sha512-NYrtPht0wGzhwe9+/idPaBB+TqkY9AhTvOLMkThm0IoEfLaiVQZwBwyJ5puCkO3AUCWrmcoePjp2mbFocKy4SQ==",
- "dev": true
+ "integrity": "sha512-NYrtPht0wGzhwe9+/idPaBB+TqkY9AhTvOLMkThm0IoEfLaiVQZwBwyJ5puCkO3AUCWrmcoePjp2mbFocKy4SQ=="
},
"@types/mongodb": {
"version": "3.3.14",
@@ -2444,21 +2442,6 @@
"resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz",
"integrity": "sha1-aN/1++YMUes3cl6p4+0xDcwed24="
},
- "boolify-string": {
- "version": "2.0.2",
- "resolved": "https://registry.npmjs.org/boolify-string/-/boolify-string-2.0.2.tgz",
- "integrity": "sha1-n4m9l9YKFEijlAF8SjuaPSQNRY4=",
- "requires": {
- "type-detect": "^1.0.0"
- },
- "dependencies": {
- "type-detect": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-1.0.0.tgz",
- "integrity": "sha1-diIXzAbbJY7EiQihKY6LlRIejqI="
- }
- }
- },
"bootstrap": {
"version": "4.4.1",
"resolved": "https://registry.npmjs.org/bootstrap/-/bootstrap-4.4.1.tgz",
@@ -2678,7 +2661,8 @@
"buffer-from": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz",
- "integrity": "sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A=="
+ "integrity": "sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==",
+ "dev": true
},
"buffer-indexof": {
"version": "1.1.1",
@@ -4303,14 +4287,6 @@
"minimalistic-crypto-utils": "^1.0.0"
}
},
- "emit-logger": {
- "version": "github:chocolateboy/emit-logger#b9d25a2d939e42f29c940861e9648bd0fb810070",
- "from": "github:chocolateboy/emit-logger#better-emitter-name",
- "requires": {
- "chalk": "^1.1.1",
- "moment": "^2.10.6"
- }
- },
"emoji-regex": {
"version": "7.0.3",
"resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz",
@@ -5947,11 +5923,6 @@
"resolved": "https://registry.npmjs.org/get-stream/-/get-stream-3.0.0.tgz",
"integrity": "sha1-jpQ9E1jcN1VQVOy+LtsFqhdO3hQ="
},
- "get-them-args": {
- "version": "1.3.2",
- "resolved": "https://registry.npmjs.org/get-them-args/-/get-them-args-1.3.2.tgz",
- "integrity": "sha1-dKILqKSr7OWuGZrQPyvMaP38m6U="
- },
"get-value": {
"version": "2.0.6",
"resolved": "https://registry.npmjs.org/get-value/-/get-value-2.0.6.tgz",
@@ -6917,20 +6888,6 @@
"resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.0.tgz",
"integrity": "sha512-M4Sjn6N/+O6/IXSJseKqHoFc+5FdGJ22sXqnjTpdZweHK64MzEPAyQZyEU3R/KRv2GLoa7nNtg/C2Ev6m7z+eA=="
},
- "ipc-event-emitter": {
- "version": "2.0.2",
- "resolved": "https://registry.npmjs.org/ipc-event-emitter/-/ipc-event-emitter-2.0.2.tgz",
- "integrity": "sha512-hJsN8zCg8MZwl5nbTutqINDO4pJPbKwmCfrTJaRLNE+5H15mJx7Mxo3pXIAi8zlh+N5xpf+PdMOQ0pbtZQvRKA==",
- "requires": {
- "babel-runtime": "^6.22.0",
- "bluebird": "^3.4.7",
- "boolify-string": "^2.0.2",
- "emit-logger": "github:chocolateboy/emit-logger#better-emitter-name",
- "lodash": "^4.17.4",
- "semver": "^5.0.3",
- "source-map-support": "^0.5.9"
- }
- },
"is-absolute-url": {
"version": "3.0.3",
"resolved": "https://registry.npmjs.org/is-absolute-url/-/is-absolute-url-3.0.3.tgz",
@@ -7596,15 +7553,6 @@
"resolved": "https://registry.npmjs.org/keygrip/-/keygrip-1.0.3.tgz",
"integrity": "sha512-/PpesirAIfaklxUzp4Yb7xBper9MwP6hNRA6BGGUFCgbJ+BM5CKBtsoxinNXkLHAr+GXS1/lSlF2rP7cv5Fl+g=="
},
- "kill-port": {
- "version": "1.6.0",
- "resolved": "https://registry.npmjs.org/kill-port/-/kill-port-1.6.0.tgz",
- "integrity": "sha512-gwHRBZ3OLBcupsOJZlIt2Xvf6QqFH3lfdpGnmonXJnJrqq819UXtItGEU1rCMXHK6sXFlxdpkw8ka56rtWw/eQ==",
- "requires": {
- "get-them-args": "^1.3.1",
- "shell-exec": "^1.0.2"
- }
- },
"killable": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/killable/-/killable-1.0.1.tgz",
@@ -8290,11 +8238,6 @@
}
}
},
- "moment": {
- "version": "2.24.0",
- "resolved": "https://registry.npmjs.org/moment/-/moment-2.24.0.tgz",
- "integrity": "sha512-bV7f+6l2QigeBBZSM/6yTNq4P2fNpSWj/0e7jQcy87A8e7o2nAfP/34/2ky5Vw4B9S446EtIhodAzkFCcR4dQg=="
- },
"mongodb": {
"version": "3.4.1",
"resolved": "https://registry.npmjs.org/mongodb/-/mongodb-3.4.1.tgz",
@@ -12494,6 +12437,11 @@
"resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz",
"integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns="
},
+ "picomatch": {
+ "version": "2.2.1",
+ "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.2.1.tgz",
+ "integrity": "sha512-ISBaA8xQNmwELC7eOjqFKMESB2VIqt4PPDD0nsS95b/9dZXvVKOlz9keMSnoGGKcOHXfTvDD6WMaRoSc9UuhRA=="
+ },
"pify": {
"version": "2.3.0",
"resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz",
@@ -13767,7 +13715,7 @@
},
"readable-stream": {
"version": "2.3.6",
- "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz",
+ "resolved": "http://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz",
"integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==",
"requires": {
"core-util-is": "~1.0.0",
@@ -14088,6 +14036,331 @@
"integrity": "sha1-kl0mAdOaxIXgkc8NpcbmlNw9yv8=",
"dev": true
},
+ "resilient-server-session": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/resilient-server-session/-/resilient-server-session-1.1.2.tgz",
+ "integrity": "sha512-eLoXxTc5bFOYH2JejSCYc2O8emoo80p2zOuwVVWVoK6/2NJBzLP8Yl7kU8m7tJDrOoqDSZGghsVD5ob9BbUgAQ==",
+ "requires": {
+ "@types/chai": "^4.2.7",
+ "@types/mocha": "^5.2.7",
+ "@types/node": "^10.12.30",
+ "@types/request-promise": "^4.1.42",
+ "@types/uuid": "^3.4.6",
+ "chai": "^4.2.0",
+ "colors": "^1.4.0",
+ "jsonschema": "^1.2.5",
+ "mocha": "^7.0.0",
+ "request-promise": "^4.2.5",
+ "typescript": "^3.7.4",
+ "uuid": "^3.3.3"
+ },
+ "dependencies": {
+ "ansi-colors": {
+ "version": "3.2.3",
+ "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-3.2.3.tgz",
+ "integrity": "sha512-LEHHyuhlPY3TmuUYMh2oz89lTShfvgbmzaBcxve9t/9Wuy7Dwf4yoAKcND7KFT1HAQfqZ12qtc+DUrBMeKF9nw=="
+ },
+ "ansi-regex": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz",
+ "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg=="
+ },
+ "ansi-styles": {
+ "version": "3.2.1",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz",
+ "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==",
+ "requires": {
+ "color-convert": "^1.9.0"
+ }
+ },
+ "anymatch": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.1.tgz",
+ "integrity": "sha512-mM8522psRCqzV+6LhomX5wgp25YVibjh8Wj23I5RPkPppSVSjyKD2A2mBJmWGa+KN7f2D6LNh9jkBCeyLktzjg==",
+ "requires": {
+ "normalize-path": "^3.0.0",
+ "picomatch": "^2.0.4"
+ }
+ },
+ "binary-extensions": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.0.0.tgz",
+ "integrity": "sha512-Phlt0plgpIIBOGTT/ehfFnbNlfsDEiqmzE2KRXoX1bLIlir4X/MR+zSyBEkL05ffWgnRSf/DXv+WrUAVr93/ow=="
+ },
+ "braces": {
+ "version": "3.0.2",
+ "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz",
+ "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==",
+ "requires": {
+ "fill-range": "^7.0.1"
+ }
+ },
+ "camelcase": {
+ "version": "5.3.1",
+ "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz",
+ "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg=="
+ },
+ "chokidar": {
+ "version": "3.3.0",
+ "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.3.0.tgz",
+ "integrity": "sha512-dGmKLDdT3Gdl7fBUe8XK+gAtGmzy5Fn0XkkWQuYxGIgWVPPse2CxFA5mtrlD0TOHaHjEUqkWNyP1XdHoJES/4A==",
+ "requires": {
+ "anymatch": "~3.1.1",
+ "braces": "~3.0.2",
+ "fsevents": "~2.1.1",
+ "glob-parent": "~5.1.0",
+ "is-binary-path": "~2.1.0",
+ "is-glob": "~4.0.1",
+ "normalize-path": "~3.0.0",
+ "readdirp": "~3.2.0"
+ }
+ },
+ "cliui": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/cliui/-/cliui-5.0.0.tgz",
+ "integrity": "sha512-PYeGSEmmHM6zvoef2w8TPzlrnNpXIjTipYK780YswmIP9vjxmd6Y2a3CB2Ks6/AU8NHjZugXvo8w3oWM2qnwXA==",
+ "requires": {
+ "string-width": "^3.1.0",
+ "strip-ansi": "^5.2.0",
+ "wrap-ansi": "^5.1.0"
+ }
+ },
+ "debug": {
+ "version": "3.2.6",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz",
+ "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==",
+ "requires": {
+ "ms": "^2.1.1"
+ }
+ },
+ "fill-range": {
+ "version": "7.0.1",
+ "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz",
+ "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==",
+ "requires": {
+ "to-regex-range": "^5.0.1"
+ }
+ },
+ "find-up": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz",
+ "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==",
+ "requires": {
+ "locate-path": "^3.0.0"
+ }
+ },
+ "fsevents": {
+ "version": "2.1.2",
+ "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.1.2.tgz",
+ "integrity": "sha512-R4wDiBwZ0KzpgOWetKDug1FZcYhqYnUYKtfZYt4mD5SBz76q0KR4Q9o7GIPamsVPGmW3EYPPJ0dOOjvx32ldZA==",
+ "optional": true
+ },
+ "get-caller-file": {
+ "version": "2.0.5",
+ "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz",
+ "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg=="
+ },
+ "glob": {
+ "version": "7.1.3",
+ "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.3.tgz",
+ "integrity": "sha512-vcfuiIxogLV4DlGBHIUOwI0IbrJ8HWPc4MU7HzviGeNho/UJDfi6B5p3sHeWIQ0KGIU0Jpxi5ZHxemQfLkkAwQ==",
+ "requires": {
+ "fs.realpath": "^1.0.0",
+ "inflight": "^1.0.4",
+ "inherits": "2",
+ "minimatch": "^3.0.4",
+ "once": "^1.3.0",
+ "path-is-absolute": "^1.0.0"
+ }
+ },
+ "glob-parent": {
+ "version": "5.1.0",
+ "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.0.tgz",
+ "integrity": "sha512-qjtRgnIVmOfnKUE3NJAQEdk+lKrxfw8t5ke7SXtfMTHcjsBfOfWXCQfdb30zfDoZQ2IRSIiidmjtbHZPZ++Ihw==",
+ "requires": {
+ "is-glob": "^4.0.1"
+ }
+ },
+ "he": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz",
+ "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw=="
+ },
+ "is-binary-path": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz",
+ "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==",
+ "requires": {
+ "binary-extensions": "^2.0.0"
+ }
+ },
+ "is-fullwidth-code-point": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz",
+ "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8="
+ },
+ "is-number": {
+ "version": "7.0.0",
+ "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz",
+ "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng=="
+ },
+ "locate-path": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz",
+ "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==",
+ "requires": {
+ "p-locate": "^3.0.0",
+ "path-exists": "^3.0.0"
+ }
+ },
+ "mocha": {
+ "version": "7.0.0",
+ "resolved": "https://registry.npmjs.org/mocha/-/mocha-7.0.0.tgz",
+ "integrity": "sha512-CirsOPbO3jU86YKjjMzFLcXIb5YiGLUrjrXFHoJ3e2z9vWiaZVCZQ2+gtRGMPWF+nFhN6AWwLM/juzAQ6KRkbA==",
+ "requires": {
+ "ansi-colors": "3.2.3",
+ "browser-stdout": "1.3.1",
+ "chokidar": "3.3.0",
+ "debug": "3.2.6",
+ "diff": "3.5.0",
+ "escape-string-regexp": "1.0.5",
+ "find-up": "3.0.0",
+ "glob": "7.1.3",
+ "growl": "1.10.5",
+ "he": "1.2.0",
+ "js-yaml": "3.13.1",
+ "log-symbols": "2.2.0",
+ "minimatch": "3.0.4",
+ "mkdirp": "0.5.1",
+ "ms": "2.1.1",
+ "node-environment-flags": "1.0.6",
+ "object.assign": "4.1.0",
+ "strip-json-comments": "2.0.1",
+ "supports-color": "6.0.0",
+ "which": "1.3.1",
+ "wide-align": "1.1.3",
+ "yargs": "13.3.0",
+ "yargs-parser": "13.1.1",
+ "yargs-unparser": "1.6.0"
+ }
+ },
+ "ms": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz",
+ "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg=="
+ },
+ "node-environment-flags": {
+ "version": "1.0.6",
+ "resolved": "https://registry.npmjs.org/node-environment-flags/-/node-environment-flags-1.0.6.tgz",
+ "integrity": "sha512-5Evy2epuL+6TM0lCQGpFIj6KwiEsGh1SrHUhTbNX+sLbBtjidPZFAnVK9y5yU1+h//RitLbRHTIMyxQPtxMdHw==",
+ "requires": {
+ "object.getownpropertydescriptors": "^2.0.3",
+ "semver": "^5.7.0"
+ }
+ },
+ "p-locate": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz",
+ "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==",
+ "requires": {
+ "p-limit": "^2.0.0"
+ }
+ },
+ "readdirp": {
+ "version": "3.2.0",
+ "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.2.0.tgz",
+ "integrity": "sha512-crk4Qu3pmXwgxdSgGhgA/eXiJAPQiX4GMOZZMXnqKxHX7TaoL+3gQVo/WeuAiogr07DpnfjIMpXXa+PAIvwPGQ==",
+ "requires": {
+ "picomatch": "^2.0.4"
+ }
+ },
+ "require-main-filename": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz",
+ "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg=="
+ },
+ "string-width": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz",
+ "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==",
+ "requires": {
+ "emoji-regex": "^7.0.1",
+ "is-fullwidth-code-point": "^2.0.0",
+ "strip-ansi": "^5.1.0"
+ }
+ },
+ "strip-ansi": {
+ "version": "5.2.0",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz",
+ "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==",
+ "requires": {
+ "ansi-regex": "^4.1.0"
+ }
+ },
+ "supports-color": {
+ "version": "6.0.0",
+ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.0.0.tgz",
+ "integrity": "sha512-on9Kwidc1IUQo+bQdhi8+Tijpo0e1SS6RoGo2guUwn5vdaxw8RXOF9Vb2ws+ihWOmh4JnCJOvaziZWP1VABaLg==",
+ "requires": {
+ "has-flag": "^3.0.0"
+ }
+ },
+ "to-regex-range": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",
+ "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==",
+ "requires": {
+ "is-number": "^7.0.0"
+ }
+ },
+ "which-module": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz",
+ "integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho="
+ },
+ "wrap-ansi": {
+ "version": "5.1.0",
+ "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-5.1.0.tgz",
+ "integrity": "sha512-QC1/iN/2/RPVJ5jYK8BGttj5z83LmSKmvbvrXPNCLZSEb32KKVDJDl/MOt2N01qU2H/FkzEa9PKto1BqDjtd7Q==",
+ "requires": {
+ "ansi-styles": "^3.2.0",
+ "string-width": "^3.0.0",
+ "strip-ansi": "^5.0.0"
+ }
+ },
+ "y18n": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.0.tgz",
+ "integrity": "sha512-r9S/ZyXu/Xu9q1tYlpsLIsa3EeLXXk0VwlxqTcFRfg9EhMW+17kbt9G0NrgCmhGb5vT2hyhJZLfDGx+7+5Uj/w=="
+ },
+ "yargs": {
+ "version": "13.3.0",
+ "resolved": "https://registry.npmjs.org/yargs/-/yargs-13.3.0.tgz",
+ "integrity": "sha512-2eehun/8ALW8TLoIl7MVaRUrg+yCnenu8B4kBlRxj3GJGDKU1Og7sMXPNm1BYyM1DOJmTZ4YeN/Nwxv+8XJsUA==",
+ "requires": {
+ "cliui": "^5.0.0",
+ "find-up": "^3.0.0",
+ "get-caller-file": "^2.0.1",
+ "require-directory": "^2.1.1",
+ "require-main-filename": "^2.0.0",
+ "set-blocking": "^2.0.0",
+ "string-width": "^3.0.0",
+ "which-module": "^2.0.0",
+ "y18n": "^4.0.0",
+ "yargs-parser": "^13.1.1"
+ }
+ },
+ "yargs-parser": {
+ "version": "13.1.1",
+ "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-13.1.1.tgz",
+ "integrity": "sha512-oVAVsHz6uFrg3XQheFII8ESO2ssAf9luWuAd6Wexsu4F3OtIW0o8IribPXYrD4WC24LWtPrJlGy87y5udK+dxQ==",
+ "requires": {
+ "camelcase": "^5.0.0",
+ "decamelize": "^1.2.0"
+ }
+ }
+ }
+ },
"resize-observer-polyfill": {
"version": "1.5.1",
"resolved": "https://registry.npmjs.org/resize-observer-polyfill/-/resize-observer-polyfill-1.5.1.tgz",
@@ -14599,11 +14872,6 @@
"resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz",
"integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM="
},
- "shell-exec": {
- "version": "1.0.2",
- "resolved": "https://registry.npmjs.org/shell-exec/-/shell-exec-1.0.2.tgz",
- "integrity": "sha512-jyVd+kU2X+mWKMmGhx4fpWbPsjvD53k9ivqetutVW/BQ+WIZoDoP4d8vUMGezV6saZsiNoW2f9GIhg9Dondohg=="
- },
"shelljs": {
"version": "0.8.3",
"resolved": "https://registry.npmjs.org/shelljs/-/shelljs-0.8.3.tgz",
@@ -15001,6 +15269,7 @@
"version": "0.5.12",
"resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.12.tgz",
"integrity": "sha512-4h2Pbvyy15EE02G+JOZpUCmqWJuqrs+sEkzewTm++BPi7Hvn/HwcqLAcNxYAyI0x13CpPPn+kMjl+hplXMHITQ==",
+ "dev": true,
"requires": {
"buffer-from": "^1.0.0",
"source-map": "^0.6.0"
@@ -15009,7 +15278,8 @@
"source-map": {
"version": "0.6.1",
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
- "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g=="
+ "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
+ "dev": true
}
}
},
@@ -15344,7 +15614,7 @@
},
"strip-ansi": {
"version": "3.0.1",
- "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz",
+ "resolved": "http://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz",
"integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=",
"requires": {
"ansi-regex": "^2.0.0"
@@ -17549,7 +17819,7 @@
},
"wrap-ansi": {
"version": "2.1.0",
- "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-2.1.0.tgz",
+ "resolved": "http://registry.npmjs.org/wrap-ansi/-/wrap-ansi-2.1.0.tgz",
"integrity": "sha1-2Pw9KE3QV5T+hJc8rs3Rz4JP3YU=",
"requires": {
"string-width": "^1.0.1",
diff --git a/package.json b/package.json
index 0a6cada2b..be1d3a9fe 100644
--- a/package.json
+++ b/package.json
@@ -157,13 +157,11 @@
"image-data-uri": "^2.0.1",
"image-size": "^0.7.5",
"imagesloaded": "^4.1.4",
- "ipc-event-emitter": "^2.0.2",
"jquery-awesome-cursor": "^0.3.1",
"js-datepicker": "^4.6.6",
"jsonschema": "^1.2.5",
"jsonwebtoken": "^8.5.0",
"jsx-to-string": "^1.4.0",
- "kill-port": "^1.6.0",
"lodash": "^4.17.15",
"mobile-detect": "^1.4.4",
"mobx": "^5.15.1",
@@ -218,6 +216,7 @@
"readline": "^1.3.0",
"request": "^2.88.0",
"request-promise": "^4.2.5",
+ "resilient-server-session": "^1.1.2",
"rimraf": "^3.0.0",
"serializr": "^1.5.4",
"sharp": "^0.23.4",
diff --git a/src/server/ApiManagers/SessionManager.ts b/src/server/ApiManagers/SessionManager.ts
index d989d8d1b..a99aa05e0 100644
--- a/src/server/ApiManagers/SessionManager.ts
+++ b/src/server/ApiManagers/SessionManager.ts
@@ -28,22 +28,11 @@ export default class SessionManager extends ApiManager {
register({
method: Method.GET,
- subscription: this.secureSubscriber("debug", "mode?", "recipient?"),
- secureHandler: this.authorizedAction(async ({ req, res }) => {
- const { mode, recipient } = req.params;
- if (mode && !["passive", "active"].includes(mode)) {
- res.send(`Your request failed. '${mode}' is not a valid mode: please choose either 'active' or 'passive'`);
- } else {
- const response = await sessionAgent.serverWorker.emitToMonitorPromise("debug", {
- mode: mode || "active",
- recipient: recipient || DashSessionAgent.notificationRecipient
- });
- if (response instanceof Error) {
- res.send(response);
- } else {
- res.send(`Your request was successful: the server ${mode === "active" ? "created and compressed a new" : "retrieved and compressed the most recent"} back up. It was sent to ${recipient}.`);
- }
- }
+ subscription: this.secureSubscriber("debug", "to?"),
+ secureHandler: this.authorizedAction(async ({ req: { params }, res }) => {
+ const to = params.to || DashSessionAgent.notificationRecipient;
+ const { error } = await sessionAgent.serverWorker.emit("debug", { to });
+ res.send(error ? error.message : `Your request was successful: the server captured and compressed (but did not save) a new back up. It was sent to ${to}.`);
})
});
@@ -51,12 +40,8 @@ export default class SessionManager extends ApiManager {
method: Method.GET,
subscription: this.secureSubscriber("backup"),
secureHandler: this.authorizedAction(async ({ res }) => {
- const response = await sessionAgent.serverWorker.emitToMonitor("backup");
- if (response instanceof Error) {
- res.send(response);
- } else {
- res.send("Your request was successful: the server successfully created a new back up.");
- }
+ const { error } = await sessionAgent.serverWorker.emit("backup");
+ res.send(error ? error.message : "Your request was successful: the server successfully created a new back up.");
})
});
diff --git a/src/server/DashSession/DashSessionAgent.ts b/src/server/DashSession/DashSessionAgent.ts
index fe7cdae88..c55e01243 100644
--- a/src/server/DashSession/DashSessionAgent.ts
+++ b/src/server/DashSession/DashSessionAgent.ts
@@ -5,13 +5,11 @@ import { Utils } from "../../Utils";
import { WebSocket } from "../Websocket/Websocket";
import { MessageStore } from "../Message";
import { launchServer, onWindows } from "..";
-import { existsSync, mkdirSync, readdirSync, statSync, createWriteStream, readFileSync } from "fs";
+import { readdirSync, statSync, createWriteStream, readFileSync, unlinkSync } from "fs";
import * as Archiver from "archiver";
import { resolve } from "path";
-import { AppliedSessionAgent, ExitHandler } from "../session/agents/applied_session_agent";
-import { Monitor } from "../session/agents/monitor";
-import { ServerWorker } from "../session/agents/server_worker";
-import { MessageHandler } from "../session/agents/promisified_ipc_manager";
+import { AppliedSessionAgent, MessageHandler, ExitHandler, Monitor, ServerWorker } from "resilient-server-session";
+import rimraf = require("rimraf");
/**
* If we're the monitor (master) thread, we should launch the monitor logic for the session.
@@ -27,14 +25,14 @@ export class DashSessionAgent extends AppliedSessionAgent {
* The core method invoked when the single master thread is initialized.
* Installs event hooks, repl commands and additional IPC listeners.
*/
- protected async initializeMonitor(monitor: Monitor, sessionKey: string) {
+ protected async initializeMonitor(monitor: Monitor, sessionKey: string): Promise<void> {
await this.dispatchSessionPassword(sessionKey);
monitor.addReplCommand("pull", [], () => monitor.exec("git pull"));
monitor.addReplCommand("solr", [/start|stop|index/], this.executeSolrCommand);
monitor.addReplCommand("backup", [], this.backup);
- monitor.addReplCommand("debug", [/active|passive/, /\S+\@\S+/], async ([mode, recipient]) => this.dispatchZippedDebugBackup(mode, recipient));
+ monitor.addReplCommand("debug", [/\S+\@\S+/], async ([to]) => this.dispatchZippedDebugBackup(to));
monitor.on("backup", this.backup);
- monitor.on("debug", ({ mode, recipient }) => this.dispatchZippedDebugBackup(mode, recipient));
+ monitor.on("debug", async ({ to }) => this.dispatchZippedDebugBackup(to));
monitor.coreHooks.onCrashDetected(this.dispatchCrashReport);
}
@@ -42,7 +40,7 @@ export class DashSessionAgent extends AppliedSessionAgent {
* The core method invoked when a server worker thread is initialized.
* Installs logic to be executed when the server worker dies.
*/
- protected async initializeServerWorker() {
+ protected async initializeServerWorker(): Promise<ServerWorker> {
const worker = ServerWorker.Create(launchServer); // server initialization delegated to worker
worker.addExitHandler(this.notifyClient);
return worker;
@@ -52,7 +50,7 @@ export class DashSessionAgent extends AppliedSessionAgent {
* Prepares the body of the email with instructions on restoring the transmitted remote database backup locally.
*/
private _remoteDebugInstructions: string | undefined;
- private generateDebugInstructions = (zipName: string, target: string) => {
+ private generateDebugInstructions = (zipName: string, target: string): string => {
if (!this._remoteDebugInstructions) {
this._remoteDebugInstructions = readFileSync(resolve(__dirname, "./templates/remote_debug_instructions.txt"), { encoding: "utf8" });
}
@@ -66,7 +64,7 @@ export class DashSessionAgent extends AppliedSessionAgent {
* Prepares the body of the email with information regarding a crash event.
*/
private _crashInstructions: string | undefined;
- private generateCrashInstructions({ name, message, stack }: Error) {
+ private generateCrashInstructions({ name, message, stack }: Error): string {
if (!this._crashInstructions) {
this._crashInstructions = readFileSync(resolve(__dirname, "./templates/crash_instructions.txt"), { encoding: "utf8" });
}
@@ -81,14 +79,18 @@ export class DashSessionAgent extends AppliedSessionAgent {
* This sends a pseudorandomly generated guid to the configuration's recipients, allowing them alone
* to kill the server via the /kill/:key route.
*/
- private dispatchSessionPassword = async (sessionKey: string) => {
+ private dispatchSessionPassword = async (sessionKey: string): Promise<void> => {
const { mainLog } = this.sessionMonitor;
const { notificationRecipient } = DashSessionAgent;
mainLog(green("dispatching session key..."));
const error = await Email.dispatch({
to: notificationRecipient,
subject: "Dash Release Session Admin Authentication Key",
- content: `Here's the key for this session (started @ ${new Date().toUTCString()}):\n\n${sessionKey}.\n\n${this.signature}`
+ content: [
+ `Here's the key for this session (started @ ${new Date().toUTCString()}):`,
+ sessionKey,
+ this.signature
+ ].join("\n\n")
});
if (error) {
this.sessionMonitor.mainLog(red(`dispatch failure @ ${notificationRecipient} (${yellow(error.message)})`));
@@ -121,7 +123,7 @@ export class DashSessionAgent extends AppliedSessionAgent {
* Logic for interfacing with Solr. Either starts it,
* stops it, or rebuilds its indicies.
*/
- private executeSolrCommand = async (args: string[]) => {
+ private executeSolrCommand = async (args: string[]): Promise<void> => {
const { exec, mainLog } = this.sessionMonitor;
const action = args[0];
if (action === "index") {
@@ -154,7 +156,7 @@ export class DashSessionAgent extends AppliedSessionAgent {
* Performs a backup of the database, saved to the desktop subdirectory.
* This should work as is only on our specific release server.
*/
- private backup = async () => this.sessionMonitor.exec("backup.bat", { cwd: this.releaseDesktop });
+ private backup = async (): Promise<void> => this.sessionMonitor.exec("backup.bat", { cwd: this.releaseDesktop });
/**
* Compress either a brand new backup or the most recent backup and send it
@@ -162,21 +164,14 @@ export class DashSessionAgent extends AppliedSessionAgent {
* @param mode specifies whether or not to make a new backup before exporting
* @param to the recipient of the email
*/
- private async dispatchZippedDebugBackup(mode: string, to: string) {
+ private async dispatchZippedDebugBackup(to: string): Promise<void> {
const { mainLog } = this.sessionMonitor;
try {
// if desired, complete an immediate backup to send
- if (mode === "active") {
- await this.backup();
- mainLog("backup complete");
- }
+ await this.backup();
+ mainLog("backup complete");
- // ensure the directory for compressed backups exists
const backupsDirectory = `${this.releaseDesktop}/backups`;
- const compressedDirectory = `${this.releaseDesktop}/compressed`;
- if (!existsSync(compressedDirectory)) {
- mkdirSync(compressedDirectory);
- }
// sort all backups by their modified time, and choose the most recent one
const target = readdirSync(backupsDirectory).map(filename => ({
@@ -187,11 +182,12 @@ export class DashSessionAgent extends AppliedSessionAgent {
// create a zip file and to it, write the contents of the backup directory
const zipName = `${target}.zip`;
- const zipPath = `${compressedDirectory}/${zipName}`;
+ const zipPath = `${this.releaseDesktop}/${zipName}`;
+ const targetPath = `${backupsDirectory}/${target}`;
const output = createWriteStream(zipPath);
const zip = Archiver('zip');
zip.pipe(output);
- zip.directory(`${backupsDirectory}/${target}/Dash`, false);
+ zip.directory(`${targetPath}/Dash`, false);
await zip.finalize();
mainLog(`zip finalized with size ${statSync(zipPath).size} bytes, saved to ${zipPath}`);
@@ -203,6 +199,12 @@ export class DashSessionAgent extends AppliedSessionAgent {
attachments: [{ filename: zipName, path: zipPath }]
});
+ // since this is intended to be a zero-footprint operation, clean up
+ // by unlinking both the backup generated earlier in the function and the compressed zip file.
+ // to generate a persistent backup, just run backup.
+ unlinkSync(zipPath);
+ rimraf.sync(targetPath);
+
// indicate success or failure
mainLog(`${error === null ? green("successfully dispatched") : red("failed to dispatch")} ${zipName} to ${cyan(to)}`);
error && mainLog(red(error.message));
diff --git a/src/server/index.ts b/src/server/index.ts
index ef8ed9700..313a2f0e2 100644
--- a/src/server/index.ts
+++ b/src/server/index.ts
@@ -24,7 +24,7 @@ import { Logger } from "./ProcessFactory";
import { yellow } from "colors";
import { DashSessionAgent } from "./DashSession/DashSessionAgent";
import SessionManager from "./ApiManagers/SessionManager";
-import { AppliedSessionAgent } from "./session/agents/applied_session_agent";
+import { AppliedSessionAgent } from "resilient-server-session";
export const onWindows = process.platform === "win32";
export let sessionAgent: AppliedSessionAgent;
diff --git a/src/server/session/README.txt b/src/server/session/README.txt
deleted file mode 100644
index ac7d3d4e7..000000000
--- a/src/server/session/README.txt
+++ /dev/null
@@ -1,11 +0,0 @@
-/**
- * These abstractions rely on NodeJS's cluster module, which allows a parent (master) process to share
- * code with its children (workers). A simple `isMaster` flag indicates who is trying to access
- * the code, and thus determines the functionality that actually gets invoked (checked by the caller, not internally).
- *
- * Think of the master thread as a factory, and the workers as the helpers that actually run the server.
- *
- * So, when we run `npm start`, given the appropriate check, initializeMaster() is called in the parent process
- * This will spawn off its own child process (by default, mirrors the execution path of its parent),
- * in which initializeWorker() is invoked.
- */ \ No newline at end of file
diff --git a/src/server/session/agents/applied_session_agent.ts b/src/server/session/agents/applied_session_agent.ts
deleted file mode 100644
index 48226dab6..000000000
--- a/src/server/session/agents/applied_session_agent.ts
+++ /dev/null
@@ -1,58 +0,0 @@
-import { isMaster } from "cluster";
-import { Monitor } from "./monitor";
-import { ServerWorker } from "./server_worker";
-import { Utils } from "../../../Utils";
-
-export type ExitHandler = (reason: Error | boolean) => void | Promise<void>;
-
-export abstract class AppliedSessionAgent {
-
- // the following two methods allow the developer to create a custom
- // session and use the built in customization options for each thread
- protected abstract async initializeMonitor(monitor: Monitor, key: string): Promise<void>;
- protected abstract async initializeServerWorker(): Promise<ServerWorker>;
-
- private launched = false;
-
- public killSession = (reason: string, graceful = true, errorCode = 0) => {
- const target = isMaster ? this.sessionMonitor : this.serverWorker;
- target.killSession(reason, graceful, errorCode);
- }
-
- private sessionMonitorRef: Monitor | undefined;
- public get sessionMonitor(): Monitor {
- if (!isMaster) {
- this.serverWorker.emitToMonitor("kill", {
- graceful: false,
- reason: "Cannot access the session monitor directly from the server worker thread.",
- errorCode: 1
- });
- throw new Error();
- }
- return this.sessionMonitorRef!;
- }
-
- private serverWorkerRef: ServerWorker | undefined;
- public get serverWorker(): ServerWorker {
- if (isMaster) {
- throw new Error("Cannot access the server worker directly from the session monitor thread");
- }
- return this.serverWorkerRef!;
- }
-
- public async launch(): Promise<void> {
- if (!this.launched) {
- this.launched = true;
- if (isMaster) {
- const sessionKey = Utils.GenerateGuid();
- await this.initializeMonitor(this.sessionMonitorRef = Monitor.Create(sessionKey), sessionKey);
- this.sessionMonitorRef.finalize();
- } else {
- this.serverWorkerRef = await this.initializeServerWorker();
- }
- } else {
- throw new Error("Cannot launch a session thread more than once per process.");
- }
- }
-
-} \ No newline at end of file
diff --git a/src/server/session/agents/monitor.ts b/src/server/session/agents/monitor.ts
deleted file mode 100644
index 5ea950b2b..000000000
--- a/src/server/session/agents/monitor.ts
+++ /dev/null
@@ -1,302 +0,0 @@
-import { ExitHandler } from "./applied_session_agent";
-import { Configuration, configurationSchema, defaultConfig, Identifiers, colorMapping } from "../utilities/session_config";
-import Repl, { ReplAction } from "../utilities/repl";
-import { isWorker, setupMaster, on, Worker, fork } from "cluster";
-import { IPC_Promisify, MessageHandler } from "./promisified_ipc_manager";
-import { red, cyan, white, yellow, blue } from "colors";
-import { exec, ExecOptions } from "child_process";
-import { validate, ValidationError } from "jsonschema";
-import { Utilities } from "../utilities/utilities";
-import { readFileSync } from "fs";
-import ProcessMessageRouter from "./process_message_router";
-import { ServerWorker } from "./server_worker";
-
-/**
- * Validates and reads the configuration file, accordingly builds a child process factory
- * and spawns off an initial process that will respawn as predecessors die.
- */
-export class Monitor extends ProcessMessageRouter {
- private static count = 0;
- private finalized = false;
- private exitHandlers: ExitHandler[] = [];
- private readonly config: Configuration;
- private activeWorker: Worker | undefined;
- private key: string | undefined;
- private repl: Repl;
-
- public static Create(sessionKey: string) {
- if (isWorker) {
- ServerWorker.IPCManager.emit("kill", {
- reason: "cannot create a monitor on the worker process.",
- graceful: false,
- errorCode: 1
- });
- process.exit(1);
- } else if (++Monitor.count > 1) {
- console.error(red("cannot create more than one monitor."));
- process.exit(1);
- } else {
- return new Monitor(sessionKey);
- }
- }
-
- private constructor(sessionKey: string) {
- super();
- this.config = this.loadAndValidateConfiguration();
- this.initialize(sessionKey);
- this.repl = this.initializeRepl();
- }
-
- private initialize = (sessionKey: string) => {
- console.log(this.timestamp(), cyan("initializing session..."));
- this.key = sessionKey;
-
- // determines whether or not we see the compilation / initialization / runtime output of each child server process
- const output = this.config.showServerOutput ? "inherit" : "ignore";
- setupMaster({ stdio: ["ignore", output, output, "ipc"] });
-
- // handle exceptions in the master thread - there shouldn't be many of these
- // the IPC (inter process communication) channel closed exception can't seem
- // to be caught in a try catch, and is inconsequential, so it is ignored
- process.on("uncaughtException", ({ message, stack }): void => {
- if (message !== "Channel closed") {
- this.mainLog(red(message));
- if (stack) {
- this.mainLog(`uncaught exception\n${red(stack)}`);
- }
- }
- });
-
- // a helpful cluster event called on the master thread each time a child process exits
- on("exit", ({ process: { pid } }, code, signal) => {
- const prompt = `server worker with process id ${pid} has exited with code ${code}${signal === null ? "" : `, having encountered signal ${signal}`}.`;
- this.mainLog(cyan(prompt));
- // to make this a robust, continuous session, every time a child process dies, we immediately spawn a new one
- this.spawn();
- });
- }
-
- public finalize = (): void => {
- if (this.finalized) {
- throw new Error("Session monitor is already finalized");
- }
- this.finalized = true;
- this.spawn();
- }
-
- public readonly coreHooks = Object.freeze({
- onCrashDetected: (listener: MessageHandler<{ error: Error }>) => this.on(Monitor.IntrinsicEvents.CrashDetected, listener),
- onServerRunning: (listener: MessageHandler<{ isFirstTime: boolean }>) => this.on(Monitor.IntrinsicEvents.ServerRunning, listener)
- });
-
- /**
- * Kill this session and its active child
- * server process, either gracefully (may wait
- * indefinitely, but at least allows active networking
- * requests to complete) or immediately.
- */
- public killSession = async (reason: string, graceful = true, errorCode = 0) => {
- this.mainLog(cyan(`exiting session ${graceful ? "clean" : "immediate"}ly`));
- this.mainLog(`session exit reason: ${(red(reason))}`);
- await this.executeExitHandlers(true);
- this.killActiveWorker(graceful, true);
- process.exit(errorCode);
- }
-
- /**
- * Execute the list of functions registered to be called
- * whenever the process exits.
- */
- public addExitHandler = (handler: ExitHandler) => this.exitHandlers.push(handler);
-
- /**
- * Extend the default repl by adding in custom commands
- * that can invoke application logic external to this module
- */
- public addReplCommand = (basename: string, argPatterns: (RegExp | string)[], action: ReplAction) => {
- this.repl.registerCommand(basename, argPatterns, action);
- }
-
- public exec = (command: string, options?: ExecOptions) => {
- return new Promise<void>(resolve => {
- exec(command, { ...options, encoding: "utf8" }, (error, stdout, stderr) => {
- if (error) {
- this.execLog(red(`unable to execute ${white(command)}`));
- error.message.split("\n").forEach(line => line.length && this.execLog(red(`(error) ${line}`)));
- } else {
- let outLines: string[], errorLines: string[];
- if ((outLines = stdout.split("\n").filter(line => line.length)).length) {
- outLines.forEach(line => line.length && this.execLog(cyan(`(stdout) ${line}`)));
- }
- if ((errorLines = stderr.split("\n").filter(line => line.length)).length) {
- errorLines.forEach(line => line.length && this.execLog(yellow(`(stderr) ${line}`)));
- }
- }
- resolve();
- });
- });
- }
-
- /**
- * Generates a blue UTC string associated with the time
- * of invocation.
- */
- private timestamp = () => blue(`[${new Date().toUTCString()}]`);
-
- /**
- * A formatted, identified and timestamped log in color
- */
- public mainLog = (...optionalParams: any[]) => {
- console.log(this.timestamp(), this.config.identifiers.master.text, ...optionalParams);
- }
-
- /**
- * A formatted, identified and timestamped log in color for non-
- */
- private execLog = (...optionalParams: any[]) => {
- console.log(this.timestamp(), this.config.identifiers.exec.text, ...optionalParams);
- }
-
- /**
- * Reads in configuration .json file only once, in the master thread
- * and pass down any variables the pertinent to the child processes as environment variables.
- */
- private loadAndValidateConfiguration = (): Configuration => {
- let config: Configuration;
- try {
- console.log(this.timestamp(), cyan("validating configuration..."));
- config = JSON.parse(readFileSync('./session.config.json', 'utf8'));
- const options = {
- throwError: true,
- allowUnknownAttributes: false
- };
- // ensure all necessary and no excess information is specified by the configuration file
- validate(config, configurationSchema, options);
- config = Utilities.preciseAssign({}, defaultConfig, config);
- } catch (error) {
- if (error instanceof ValidationError) {
- console.log(red("\nSession configuration failed."));
- console.log("The given session.config.json configuration file is invalid.");
- console.log(`${error.instance}: ${error.stack}`);
- process.exit(0);
- } else if (error.code === "ENOENT" && error.path === "./session.config.json") {
- console.log(cyan("Loading default session parameters..."));
- console.log("Consider including a session.config.json configuration file in your project root for customization.");
- config = Utilities.preciseAssign({}, defaultConfig);
- } else {
- console.log(red("\nSession configuration failed."));
- console.log("The following unknown error occurred during configuration.");
- console.log(error.stack);
- process.exit(0);
- }
- } finally {
- const { identifiers } = config!;
- Object.keys(identifiers).forEach(key => {
- const resolved = key as keyof Identifiers;
- const { text, color } = identifiers[resolved];
- identifiers[resolved].text = (colorMapping.get(color) || white)(`${text}:`);
- });
- return config!;
- }
- }
-
- /**
- * Builds the repl that allows the following commands to be typed into stdin of the master thread.
- */
- private initializeRepl = (): Repl => {
- const repl = new Repl({ identifier: () => `${this.timestamp()} ${this.config.identifiers.master.text}` });
- const boolean = /true|false/;
- const number = /\d+/;
- const letters = /[a-zA-Z]+/;
- repl.registerCommand("exit", [/clean|force/], args => this.killSession("manual exit requested by repl", args[0] === "clean", 0));
- repl.registerCommand("restart", [/clean|force/], args => this.killActiveWorker(args[0] === "clean"));
- repl.registerCommand("set", [letters, "port", number, boolean], args => this.setPort(args[0], Number(args[2]), args[3] === "true"));
- repl.registerCommand("set", [/polling/, number, boolean], args => {
- const newPollingIntervalSeconds = Math.floor(Number(args[1]));
- if (newPollingIntervalSeconds < 0) {
- this.mainLog(red("the polling interval must be a non-negative integer"));
- } else {
- if (newPollingIntervalSeconds !== this.config.polling.intervalSeconds) {
- this.config.polling.intervalSeconds = newPollingIntervalSeconds;
- if (args[2] === "true") {
- Monitor.IPCManager.emit("updatePollingInterval", { newPollingIntervalSeconds });
- }
- }
- }
- });
- return repl;
- }
-
- private executeExitHandlers = async (reason: Error | boolean) => Promise.all(this.exitHandlers.map(handler => handler(reason)));
-
- /**
- * Attempts to kill the active worker gracefully, unless otherwise specified.
- */
- private killActiveWorker = (graceful = true, isSessionEnd = false): void => {
- if (this.activeWorker && !this.activeWorker.isDead()) {
- if (graceful) {
- Monitor.IPCManager.emit("manualExit", { isSessionEnd });
- } else {
- this.activeWorker.process.kill();
- }
- }
- }
-
- /**
- * Allows the caller to set the port at which the target (be it the server,
- * the websocket, some other custom port) is listening. If an immediate restart
- * is specified, this monitor will kill the active child and re-launch the server
- * at the port. Otherwise, the updated port won't be used until / unless the child
- * dies on its own and triggers a restart.
- */
- private setPort = (port: "server" | "socket" | string, value: number, immediateRestart: boolean): void => {
- if (value > 1023 && value < 65536) {
- this.config.ports[port] = value;
- if (immediateRestart) {
- this.killActiveWorker();
- }
- } else {
- this.mainLog(red(`${port} is an invalid port number`));
- }
- }
-
- /**
- * Kills the current active worker and proceeds to spawn a new worker,
- * feeding in configuration information as environment variables.
- */
- private spawn = (): void => {
- const {
- polling: {
- route,
- failureTolerance,
- intervalSeconds
- },
- ports
- } = this.config;
- this.killActiveWorker();
- this.activeWorker = fork({
- pollingRoute: route,
- pollingFailureTolerance: failureTolerance,
- serverPort: ports.server,
- socketPort: ports.socket,
- pollingIntervalSeconds: intervalSeconds,
- session_key: this.key
- });
- Monitor.IPCManager = IPC_Promisify(this.activeWorker, this.route);
- this.mainLog(cyan(`spawned new server worker with process id ${this.activeWorker?.process.pid}`));
-
- this.on("kill", ({ reason, graceful, errorCode }) => this.killSession(reason, graceful, errorCode), true);
- this.on("lifecycle", ({ event }) => console.log(this.timestamp(), `${this.config.identifiers.worker.text} lifecycle phase (${event})`), true);
- }
-
-}
-
-export namespace Monitor {
-
- export enum IntrinsicEvents {
- KeyGenerated = "key_generated",
- CrashDetected = "crash_detected",
- ServerRunning = "server_running"
- }
-
-} \ No newline at end of file
diff --git a/src/server/session/agents/process_message_router.ts b/src/server/session/agents/process_message_router.ts
deleted file mode 100644
index d359e97c3..000000000
--- a/src/server/session/agents/process_message_router.ts
+++ /dev/null
@@ -1,46 +0,0 @@
-import { MessageHandler, PromisifiedIPCManager } from "./promisified_ipc_manager";
-
-export default abstract class ProcessMessageRouter {
-
- protected static IPCManager: PromisifiedIPCManager;
- private onMessage: { [name: string]: MessageHandler[] | undefined } = {};
-
- /**
- * Add a listener at this message. When the monitor process
- * receives a message, it will invoke all registered functions.
- */
- public on = (name: string, handler: MessageHandler, exclusive = false) => {
- const handlers = this.onMessage[name];
- if (exclusive || !handlers) {
- this.onMessage[name] = [handler];
- } else {
- handlers.push(handler);
- }
- }
-
- /**
- * Unregister a given listener at this message.
- */
- public off = (name: string, handler: MessageHandler) => {
- const handlers = this.onMessage[name];
- if (handlers) {
- const index = handlers.indexOf(handler);
- if (index > -1) {
- handlers.splice(index, 1);
- }
- }
- }
-
- /**
- * Unregister all listeners at this message.
- */
- public clearMessageListeners = (...names: string[]) => names.map(name => this.onMessage[name] = undefined);
-
- protected route: MessageHandler = async ({ name, args }) => {
- const handlers = this.onMessage[name];
- if (handlers) {
- await Promise.all(handlers.map(handler => handler(args)));
- }
- }
-
-} \ No newline at end of file
diff --git a/src/server/session/agents/promisified_ipc_manager.ts b/src/server/session/agents/promisified_ipc_manager.ts
deleted file mode 100644
index 216e9be44..000000000
--- a/src/server/session/agents/promisified_ipc_manager.ts
+++ /dev/null
@@ -1,97 +0,0 @@
-import { Utils } from "../../../Utils";
-import { isMaster } from "cluster";
-
-/**
- * Convenience constructor
- * @param target the process / worker to which to attach the specialized listeners
- */
-export function IPC_Promisify(target: IPCTarget, router: Router) {
- return new PromisifiedIPCManager(target, router);
-}
-
-/**
- * Essentially, a node process or node cluster worker
- */
-export type IPCTarget = NodeJS.EventEmitter & { send?: Function };
-
-/**
- * Some external code that maps the name of incoming messages to registered handlers, if any
- * when this returns, the message is assumed to have been handled in its entirety by the process, so
- * await any asynchronous code inside this router.
- */
-export type Router = (message: Message) => void | Promise<void>;
-
-/**
- * Specifies a general message format for this API
- */
-export type Message<T = any> = { name: string; args: T; };
-export type MessageHandler<T = any> = (args: T) => any | Promise<any>;
-
-/**
- * When a message is emitted, it
- */
-type InternalMessage = Message & { metadata: any };
-type InternalMessageHandler = (message: InternalMessage) => any | Promise<any>;
-
-/**
- * This is a wrapper utility class that allows the caller process
- * to emit an event and return a promise that resolves when it and all
- * other processes listening to its emission of this event have completed.
- */
-export class PromisifiedIPCManager {
- private readonly target: IPCTarget;
-
- constructor(target: IPCTarget, router: Router) {
- this.target = target;
- this.target.addListener("message", this.internalHandler(router));
- }
-
- /**
- * A convenience wrapper around the standard process emission.
- * Does not wait for a response.
- */
- public emit = async (name: string, args?: any) => this.target.send?.({ name, args });
-
- /**
- * This routine uniquely identifies each message, then adds a general
- * message listener that waits for a response with the same id before resolving
- * the promise.
- */
- public emitPromise = async (name: string, args?: any) => {
- return new Promise(resolve => {
- const messageId = Utils.GenerateGuid();
- const responseHandler: InternalMessageHandler = ({ metadata: { id, isResponse }, args, name }) => {
- if (isResponse && id === messageId) {
- this.target.removeListener("message", responseHandler);
- resolve(args?.error as Error | undefined);
- }
- };
- this.target.addListener("message", responseHandler);
- const message = { name, args, metadata: { id: messageId } };
- this.target.send?.(message);
- });
- }
-
- /**
- * This routine receives a uniquely identified message. If the message is itself a response,
- * it is ignored to avoid infinite mutual responses. Otherwise, the routine awaits its completion using whatever
- * router the caller has installed, and then sends a response containing the original message id,
- * which will ultimately invoke the responseHandler of the original emission and resolve the
- * sender's promise.
- */
- private internalHandler = (router: Router) => async ({ name, args, metadata }: InternalMessage) => {
- if (name && (!metadata || !metadata.isResponse)) {
- let error: Error | undefined;
- try {
- await router({ name, args });
- } catch (e) {
- error = e;
- }
- if (metadata && this.target.send) {
- metadata.isResponse = true;
- this.target.send({ name, args: { error }, metadata });
- }
- }
- }
-
-} \ No newline at end of file
diff --git a/src/server/session/agents/server_worker.ts b/src/server/session/agents/server_worker.ts
deleted file mode 100644
index 705307030..000000000
--- a/src/server/session/agents/server_worker.ts
+++ /dev/null
@@ -1,160 +0,0 @@
-import { ExitHandler } from "./applied_session_agent";
-import { isMaster } from "cluster";
-import { PromisifiedIPCManager } from "./promisified_ipc_manager";
-import ProcessMessageRouter from "./process_message_router";
-import { red, green, white, yellow } from "colors";
-import { get } from "request-promise";
-import { Monitor } from "./monitor";
-
-/**
- * Effectively, each worker repairs the connection to the server by reintroducing a consistent state
- * if its predecessor has died. It itself also polls the server heartbeat, and exits with a notification
- * email if the server encounters an uncaught exception or if the server cannot be reached.
- */
-export class ServerWorker extends ProcessMessageRouter {
- private static count = 0;
- private shouldServerBeResponsive = false;
- private exitHandlers: ExitHandler[] = [];
- private pollingFailureCount = 0;
- private pollingIntervalSeconds: number;
- private pollingFailureTolerance: number;
- private pollTarget: string;
- private serverPort: number;
- private isInitialized = false;
-
- public static Create(work: Function) {
- if (isMaster) {
- console.error(red("cannot create a worker on the monitor process."));
- process.exit(1);
- } else if (++ServerWorker.count > 1) {
- ServerWorker.IPCManager.emit("kill", {
- reason: "cannot create more than one worker on a given worker process.",
- graceful: false,
- errorCode: 1
- });
- process.exit(1);
- } else {
- return new ServerWorker(work);
- }
- }
-
- /**
- * Allows developers to invoke application specific logic
- * by hooking into the exiting of the server process.
- */
- public addExitHandler = (handler: ExitHandler) => this.exitHandlers.push(handler);
-
- /**
- * Kill the session monitor (parent process) from this
- * server worker (child process). This will also kill
- * this process (child process).
- */
- public killSession = (reason: string, graceful = true, errorCode = 0) => this.emitToMonitor("kill", { reason, graceful, errorCode });
-
- /**
- * A convenience wrapper to tell the session monitor (parent process)
- * to carry out the action with the specified message and arguments.
- */
- public emitToMonitor = (name: string, args?: any) => ServerWorker.IPCManager.emit(name, args);
-
- public emitToMonitorPromise = (name: string, args?: any) => ServerWorker.IPCManager.emitPromise(name, args);
-
- private constructor(work: Function) {
- super();
- ServerWorker.IPCManager = new PromisifiedIPCManager(process, this.route);
- this.lifecycleNotification(green(`initializing process... ${white(`[${process.execPath} ${process.execArgv.join(" ")}]`)}`));
-
- const { pollingRoute, serverPort, pollingIntervalSeconds, pollingFailureTolerance } = process.env;
- this.serverPort = Number(serverPort);
- this.pollingIntervalSeconds = Number(pollingIntervalSeconds);
- this.pollingFailureTolerance = Number(pollingFailureTolerance);
- this.pollTarget = `http://localhost:${serverPort}${pollingRoute}`;
-
- this.configureProcess();
- work();
- this.pollServer();
- }
-
- /**
- * Set up message and uncaught exception handlers for this
- * server process.
- */
- private configureProcess = () => {
- // updates the local values of variables to the those sent from master
- this.on("updatePollingInterval", ({ newPollingIntervalSeconds }) => this.pollingIntervalSeconds = newPollingIntervalSeconds);
- this.on("manualExit", async ({ isSessionEnd }) => {
- await this.executeExitHandlers(isSessionEnd);
- process.exit(0);
- });
-
- // one reason to exit, as the process might be in an inconsistent state after such an exception
- process.on('uncaughtException', this.proactiveUnplannedExit);
- process.on('unhandledRejection', reason => {
- const appropriateError = reason instanceof Error ? reason : new Error(`unhandled rejection: ${reason}`);
- this.proactiveUnplannedExit(appropriateError);
- });
- }
-
- /**
- * Execute the list of functions registered to be called
- * whenever the process exits.
- */
- private executeExitHandlers = async (reason: Error | boolean) => Promise.all(this.exitHandlers.map(handler => handler(reason)));
-
- /**
- * Notify master thread (which will log update in the console) of initialization via IPC.
- */
- public lifecycleNotification = (event: string) => ServerWorker.IPCManager.emit("lifecycle", { event });
-
- /**
- * Called whenever the process has a reason to terminate, either through an uncaught exception
- * in the process (potentially inconsistent state) or the server cannot be reached.
- */
- private proactiveUnplannedExit = async (error: Error): Promise<void> => {
- this.shouldServerBeResponsive = false;
- // communicates via IPC to the master thread that it should dispatch a crash notification email
- this.emitToMonitor(Monitor.IntrinsicEvents.CrashDetected, { error });
- await this.executeExitHandlers(error);
- // notify master thread (which will log update in the console) of crash event via IPC
- this.lifecycleNotification(red(`crash event detected @ ${new Date().toUTCString()}`));
- this.lifecycleNotification(red(error.message));
- process.exit(1);
- }
-
- /**
- * This monitors the health of the server by submitting a get request to whatever port / route specified
- * by the configuration every n seconds, where n is also given by the configuration.
- */
- private pollServer = async (): Promise<void> => {
- await new Promise<void>(resolve => {
- setTimeout(async () => {
- try {
- await get(this.pollTarget);
- if (!this.shouldServerBeResponsive) {
- // notify monitor thread that the server is up and running
- this.lifecycleNotification(green(`listening on ${this.serverPort}...`));
- this.emitToMonitor(Monitor.IntrinsicEvents.ServerRunning, { isFirstTime: !this.isInitialized });
- this.isInitialized = true;
- }
- this.shouldServerBeResponsive = true;
- } catch (error) {
- // if we expect the server to be unavailable, i.e. during compilation,
- // the listening variable is false, activeExit will return early and the child
- // process will continue
- if (this.shouldServerBeResponsive) {
- if (++this.pollingFailureCount > this.pollingFailureTolerance) {
- this.proactiveUnplannedExit(error);
- } else {
- this.lifecycleNotification(yellow(`the server has encountered ${this.pollingFailureCount} of ${this.pollingFailureTolerance} tolerable failures`));
- }
- }
- } finally {
- resolve();
- }
- }, 1000 * this.pollingIntervalSeconds);
- });
- // controlled, asynchronous infinite recursion achieves a persistent poll that does not submit a new request until the previous has completed
- this.pollServer();
- }
-
-} \ No newline at end of file
diff --git a/src/server/session/utilities/repl.ts b/src/server/session/utilities/repl.ts
deleted file mode 100644
index 643141286..000000000
--- a/src/server/session/utilities/repl.ts
+++ /dev/null
@@ -1,128 +0,0 @@
-import { createInterface, Interface } from "readline";
-import { red, green, white } from "colors";
-
-export interface Configuration {
- identifier: () => string | string;
- onInvalid?: (command: string, validCommand: boolean) => string | string;
- onValid?: (success?: string) => string | string;
- isCaseSensitive?: boolean;
-}
-
-export type ReplAction = (parsedArgs: Array<string>) => any | Promise<any>;
-export interface Registration {
- argPatterns: RegExp[];
- action: ReplAction;
-}
-
-export default class Repl {
- private identifier: () => string | string;
- private onInvalid: ((command: string, validCommand: boolean) => string) | string;
- private onValid: ((success: string) => string) | string;
- private isCaseSensitive: boolean;
- private commandMap = new Map<string, Registration[]>();
- public interface: Interface;
- private busy = false;
- private keys: string | undefined;
-
- constructor({ identifier: prompt, onInvalid, onValid, isCaseSensitive }: Configuration) {
- this.identifier = prompt;
- this.onInvalid = onInvalid || this.usage;
- this.onValid = onValid || this.success;
- this.isCaseSensitive = isCaseSensitive ?? true;
- this.interface = createInterface(process.stdin, process.stdout).on('line', this.considerInput);
- }
-
- private resolvedIdentifier = () => typeof this.identifier === "string" ? this.identifier : this.identifier();
-
- private usage = (command: string, validCommand: boolean) => {
- if (validCommand) {
- const formatted = white(command);
- const patterns = green(this.commandMap.get(command)!.map(({ argPatterns }) => `${formatted} ${argPatterns.join(" ")}`).join('\n'));
- return `${this.resolvedIdentifier()}\nthe given arguments do not match any registered patterns for ${formatted}\nthe list of valid argument patterns is given by:\n${patterns}`;
- } else {
- const resolved = this.keys;
- if (resolved) {
- return resolved;
- }
- const members: string[] = [];
- const keys = this.commandMap.keys();
- let next: IteratorResult<string>;
- while (!(next = keys.next()).done) {
- members.push(next.value);
- }
- return `${this.resolvedIdentifier()} commands: { ${members.sort().join(", ")} }`;
- }
- }
-
- private success = (command: string) => `${this.resolvedIdentifier()} completed local execution of ${white(command)}`;
-
- public registerCommand = (basename: string, argPatterns: (RegExp | string)[], action: ReplAction) => {
- const existing = this.commandMap.get(basename);
- const converted = argPatterns.map(input => input instanceof RegExp ? input : new RegExp(input));
- const registration = { argPatterns: converted, action };
- if (existing) {
- existing.push(registration);
- } else {
- this.commandMap.set(basename, [registration]);
- }
- }
-
- private invalid = (command: string, validCommand: boolean) => {
- console.log(red(typeof this.onInvalid === "string" ? this.onInvalid : this.onInvalid(command, validCommand)));
- this.busy = false;
- }
-
- private valid = (command: string) => {
- console.log(green(typeof this.onValid === "string" ? this.onValid : this.onValid(command)));
- this.busy = false;
- }
-
- private considerInput = async (line: string) => {
- if (this.busy) {
- console.log(red("Busy"));
- return;
- }
- this.busy = true;
- line = line.trim();
- if (this.isCaseSensitive) {
- line = line.toLowerCase();
- }
- const [command, ...args] = line.split(/\s+/g);
- if (!command) {
- return this.invalid(command, false);
- }
- const registered = this.commandMap.get(command);
- if (registered) {
- const { length } = args;
- const candidates = registered.filter(({ argPatterns: { length: count } }) => count === length);
- for (const { argPatterns, action } of candidates) {
- const parsed: string[] = [];
- let matched = true;
- if (length) {
- for (let i = 0; i < length; i++) {
- let matches: RegExpExecArray | null;
- if ((matches = argPatterns[i].exec(args[i])) === null) {
- matched = false;
- break;
- }
- parsed.push(matches[0]);
- }
- }
- if (!length || matched) {
- const result = action(parsed);
- const resolve = () => this.valid(`${command} ${parsed.join(" ")}`);
- if (result instanceof Promise) {
- result.then(resolve);
- } else {
- resolve();
- }
- return;
- }
- }
- this.invalid(command, true);
- } else {
- this.invalid(command, false);
- }
- }
-
-} \ No newline at end of file
diff --git a/src/server/session/utilities/session_config.ts b/src/server/session/utilities/session_config.ts
deleted file mode 100644
index b0e65dde4..000000000
--- a/src/server/session/utilities/session_config.ts
+++ /dev/null
@@ -1,129 +0,0 @@
-import { Schema } from "jsonschema";
-import { yellow, red, cyan, green, blue, magenta, Color, grey, gray, white, black } from "colors";
-
-const colorPattern = /black|red|green|yellow|blue|magenta|cyan|white|gray|grey/;
-
-const identifierProperties: Schema = {
- type: "object",
- properties: {
- text: {
- type: "string",
- minLength: 1
- },
- color: {
- type: "string",
- pattern: colorPattern
- }
- }
-};
-
-const portProperties: Schema = {
- type: "number",
- minimum: 1024,
- maximum: 65535
-};
-
-export const configurationSchema: Schema = {
- id: "/configuration",
- type: "object",
- properties: {
- showServerOutput: { type: "boolean" },
- ports: {
- type: "object",
- properties: {
- server: portProperties,
- socket: portProperties
- },
- required: ["server"],
- additionalProperties: true
- },
- identifiers: {
- type: "object",
- properties: {
- master: identifierProperties,
- worker: identifierProperties,
- exec: identifierProperties
- }
- },
- polling: {
- type: "object",
- additionalProperties: false,
- properties: {
- intervalSeconds: {
- type: "number",
- minimum: 1,
- maximum: 86400
- },
- route: {
- type: "string",
- pattern: /\/[a-zA-Z]*/g
- },
- failureTolerance: {
- type: "number",
- minimum: 0,
- }
- }
- },
- }
-};
-
-type ColorLabel = "yellow" | "red" | "cyan" | "green" | "blue" | "magenta" | "grey" | "gray" | "white" | "black";
-
-export const colorMapping: Map<ColorLabel, Color> = new Map([
- ["yellow", yellow],
- ["red", red],
- ["cyan", cyan],
- ["green", green],
- ["blue", blue],
- ["magenta", magenta],
- ["grey", grey],
- ["gray", gray],
- ["white", white],
- ["black", black]
-]);
-
-interface Identifier {
- text: string;
- color: ColorLabel;
-}
-
-export interface Identifiers {
- master: Identifier;
- worker: Identifier;
- exec: Identifier;
-}
-
-export interface Configuration {
- showServerOutput: boolean;
- identifiers: Identifiers;
- ports: { [description: string]: number };
- polling: {
- route: string;
- intervalSeconds: number;
- failureTolerance: number;
- };
-}
-
-export const defaultConfig: Configuration = {
- showServerOutput: false,
- identifiers: {
- master: {
- text: "__monitor__",
- color: "yellow"
- },
- worker: {
- text: "__server__",
- color: "magenta"
- },
- exec: {
- text: "__exec__",
- color: "green"
- }
- },
- ports: { server: 3000 },
- polling: {
- route: "/",
- intervalSeconds: 30,
- failureTolerance: 0
- }
-}; \ No newline at end of file
diff --git a/src/server/session/utilities/utilities.ts b/src/server/session/utilities/utilities.ts
deleted file mode 100644
index ac8a6590a..000000000
--- a/src/server/session/utilities/utilities.ts
+++ /dev/null
@@ -1,31 +0,0 @@
-export namespace Utilities {
-
- /**
- * At any arbitrary layer of nesting within the configuration objects, any single value that
- * is not specified by the configuration is given the default counterpart. If, within an object,
- * one peer is given by configuration and two are not, the one is preserved while the two are given
- * the default value.
- * @returns the composition of all of the assigned objects, much like Object.assign(), but with more
- * granularity in the overwriting of nested objects
- */
- export function preciseAssign(target: any, ...sources: any[]): any {
- for (const source of sources) {
- preciseAssignHelper(target, source);
- }
- return target;
- }
-
- export function preciseAssignHelper(target: any, source: any) {
- Array.from(new Set([...Object.keys(target), ...Object.keys(source)])).map(property => {
- let targetValue: any, sourceValue: any;
- if (sourceValue = source[property]) {
- if (typeof sourceValue === "object" && typeof (targetValue = target[property]) === "object") {
- preciseAssignHelper(targetValue, sourceValue);
- } else {
- target[property] = sourceValue;
- }
- }
- });
- }
-
-} \ No newline at end of file
diff --git a/src/typings/index.d.ts b/src/typings/index.d.ts
index cc68e8a4d..281bb3217 100644
--- a/src/typings/index.d.ts
+++ b/src/typings/index.d.ts
@@ -2,8 +2,6 @@
declare module 'googlephotos';
declare module 'react-image-lightbox-with-rotate';
-declare module 'kill-port';
-declare module 'ipc-event-emitter';
declare module 'cors';
declare module '@react-pdf/renderer' {