فهرست منبع

Add API to get server status information.

Daniel Scalzi 5 سال پیش
والد
کامیت
3838729da7

+ 112 - 135
package-lock.json

@@ -1541,9 +1541,9 @@
       "dev": true
     },
     "@types/react": {
-      "version": "16.9.46",
-      "resolved": "https://registry.npmjs.org/@types/react/-/react-16.9.46.tgz",
-      "integrity": "sha512-dbHzO3aAq1lB3jRQuNpuZ/mnu+CdD3H0WVaaBQA8LTT3S33xhVBUj232T8M3tAhSWJs/D/UqORYUlJNl/8VQZg==",
+      "version": "16.9.48",
+      "resolved": "https://registry.npmjs.org/@types/react/-/react-16.9.48.tgz",
+      "integrity": "sha512-4ykBVswgYitPGMXFRxJCHkxJDU2rjfU3/zw67f8+dB7sNdVJXsrwqoYxz/stkAucymnEEbRPFmX7Ce5Mc/kJCw==",
       "dev": true,
       "requires": {
         "@types/prop-types": "*",
@@ -1551,9 +1551,9 @@
       },
       "dependencies": {
         "csstype": {
-          "version": "3.0.2",
-          "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.0.2.tgz",
-          "integrity": "sha512-ofovWglpqoqbfLNOTBNZLSbMuGrblAf1efvvArGKOZMBrIoJeu5UsAipQolkijtyQx5MtAzT/J9IHj/CEY1mJw==",
+          "version": "3.0.3",
+          "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.0.3.tgz",
+          "integrity": "sha512-jPl+wbWPOWJ7SXsWyqGRk3lGecbar0Cb0OvZF/r/ZU011R4YqiRehgkQ9p4eQfo9DSDLqLL3wHwfxeJiuIsNag==",
           "dev": true
         }
       }
@@ -1745,12 +1745,12 @@
       "dev": true
     },
     "@typescript-eslint/eslint-plugin": {
-      "version": "3.10.0",
-      "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-3.10.0.tgz",
-      "integrity": "sha512-Bbeg9JAnSzZ85Y0gpInZscSpifA6SbEgRryaKdP5ZlUjhTKsvZS4GUIE6xAZCjhNTrf4zXXsySo83ZdHL7it0w==",
+      "version": "3.10.1",
+      "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-3.10.1.tgz",
+      "integrity": "sha512-PQg0emRtzZFWq6PxBcdxRH3QIQiyFO3WCVpRL3fgj5oQS3CDs3AeAKfv4DxNhzn8ITdNJGJ4D3Qw8eAJf3lXeQ==",
       "dev": true,
       "requires": {
-        "@typescript-eslint/experimental-utils": "3.10.0",
+        "@typescript-eslint/experimental-utils": "3.10.1",
         "debug": "^4.1.1",
         "functional-red-black-tree": "^1.0.1",
         "regexpp": "^3.0.0",
@@ -1759,45 +1759,45 @@
       }
     },
     "@typescript-eslint/experimental-utils": {
-      "version": "3.10.0",
-      "resolved": "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-3.10.0.tgz",
-      "integrity": "sha512-e5ZLSTuXgqC/Gq3QzK2orjlhTZVXzwxDujQmTBOM1NIVBZgW3wiIZjaXuVutk9R4UltFlwC9UD2+bdxsA7yyNg==",
+      "version": "3.10.1",
+      "resolved": "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-3.10.1.tgz",
+      "integrity": "sha512-DewqIgscDzmAfd5nOGe4zm6Bl7PKtMG2Ad0KG8CUZAHlXfAKTF9Ol5PXhiMh39yRL2ChRH1cuuUGOcVyyrhQIw==",
       "dev": true,
       "requires": {
         "@types/json-schema": "^7.0.3",
-        "@typescript-eslint/types": "3.10.0",
-        "@typescript-eslint/typescript-estree": "3.10.0",
+        "@typescript-eslint/types": "3.10.1",
+        "@typescript-eslint/typescript-estree": "3.10.1",
         "eslint-scope": "^5.0.0",
         "eslint-utils": "^2.0.0"
       }
     },
     "@typescript-eslint/parser": {
-      "version": "3.10.0",
-      "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-3.10.0.tgz",
-      "integrity": "sha512-iJyf3f2HVwscvJR7ySGMXw2DJgIAPKEz8TeU17XVKzgJRV4/VgCeDFcqLzueRe7iFI2gv+Tln4AV88ZOnsCNXg==",
+      "version": "3.10.1",
+      "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-3.10.1.tgz",
+      "integrity": "sha512-Ug1RcWcrJP02hmtaXVS3axPPTTPnZjupqhgj+NnZ6BCkwSImWk/283347+x9wN+lqOdK9Eo3vsyiyDHgsmiEJw==",
       "dev": true,
       "requires": {
         "@types/eslint-visitor-keys": "^1.0.0",
-        "@typescript-eslint/experimental-utils": "3.10.0",
-        "@typescript-eslint/types": "3.10.0",
-        "@typescript-eslint/typescript-estree": "3.10.0",
+        "@typescript-eslint/experimental-utils": "3.10.1",
+        "@typescript-eslint/types": "3.10.1",
+        "@typescript-eslint/typescript-estree": "3.10.1",
         "eslint-visitor-keys": "^1.1.0"
       }
     },
     "@typescript-eslint/types": {
-      "version": "3.10.0",
-      "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-3.10.0.tgz",
-      "integrity": "sha512-ktUWSa75heQNwH85GRM7qP/UUrXqx9d6yIdw0iLO9/uE1LILW+i+3+B64dUodUS2WFWLzKTlwfi9giqrODibWg==",
+      "version": "3.10.1",
+      "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-3.10.1.tgz",
+      "integrity": "sha512-+3+FCUJIahE9q0lDi1WleYzjCwJs5hIsbugIgnbB+dSCYUxl8L6PwmsyOPFZde2hc1DlTo/xnkOgiTLSyAbHiQ==",
       "dev": true
     },
     "@typescript-eslint/typescript-estree": {
-      "version": "3.10.0",
-      "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-3.10.0.tgz",
-      "integrity": "sha512-yjuY6rmVHRhcUKgXaSPNVloRueGWpFNhxR5EQLzxXfiFSl1U/+FBqHhbaGwtPPEgCSt61QNhZgiFjWT27bgAyw==",
+      "version": "3.10.1",
+      "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-3.10.1.tgz",
+      "integrity": "sha512-QbcXOuq6WYvnB3XPsZpIwztBoquEYLXh2MtwVU+kO8jgYCiv4G5xrSP/1wg4tkvrEE+esZVquIPX/dxPlePk1w==",
       "dev": true,
       "requires": {
-        "@typescript-eslint/types": "3.10.0",
-        "@typescript-eslint/visitor-keys": "3.10.0",
+        "@typescript-eslint/types": "3.10.1",
+        "@typescript-eslint/visitor-keys": "3.10.1",
         "debug": "^4.1.1",
         "glob": "^7.1.6",
         "is-glob": "^4.0.1",
@@ -1807,9 +1807,9 @@
       }
     },
     "@typescript-eslint/visitor-keys": {
-      "version": "3.10.0",
-      "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-3.10.0.tgz",
-      "integrity": "sha512-g4qftk8lWb/rHZe9uEp8oZSvsJhUvR2cfp7F7qE6DyUD2SsovEs8JDQTRP1xHzsD+pERsEpYNqkDgQXW6+ob5A==",
+      "version": "3.10.1",
+      "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-3.10.1.tgz",
+      "integrity": "sha512-9JgC82AaQeglebjZMgYR5wgmfUdUc+EitGUUMW8u2nDckaeimzW+VsoLV6FoimPv2id3VQzfjwBxEMVz08ameQ==",
       "dev": true,
       "requires": {
         "eslint-visitor-keys": "^1.1.0"
@@ -4663,8 +4663,7 @@
       "version": "4.0.0",
       "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz",
       "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==",
-      "dev": true,
-      "optional": true
+      "dev": true
     },
     "eslint": {
       "version": "7.7.0",
@@ -7260,55 +7259,12 @@
       "dev": true
     },
     "log-symbols": {
-      "version": "3.0.0",
-      "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-3.0.0.tgz",
-      "integrity": "sha512-dSkNGuI7iG3mfvDzUuYZyvk5dD9ocYCYzNU6CYDE6+Xqd+gwme6Z00NS3dUh8mq/73HaEtT7m6W+yUPtU6BZnQ==",
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.0.0.tgz",
+      "integrity": "sha512-FN8JBzLx6CzeMrB0tg6pqlGU1wCrXW+ZXGH481kfsBqer0hToTIiHdjH4Mq8xJUbvATujKCvaREGWpGUionraA==",
       "dev": true,
       "requires": {
-        "chalk": "^2.4.2"
-      },
-      "dependencies": {
-        "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==",
-          "dev": true,
-          "requires": {
-            "color-convert": "^1.9.0"
-          }
-        },
-        "chalk": {
-          "version": "2.4.2",
-          "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz",
-          "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==",
-          "dev": true,
-          "requires": {
-            "ansi-styles": "^3.2.1",
-            "escape-string-regexp": "^1.0.5",
-            "supports-color": "^5.3.0"
-          }
-        },
-        "escape-string-regexp": {
-          "version": "1.0.5",
-          "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
-          "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=",
-          "dev": true
-        },
-        "has-flag": {
-          "version": "3.0.0",
-          "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
-          "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=",
-          "dev": true
-        },
-        "supports-color": {
-          "version": "5.5.0",
-          "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz",
-          "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==",
-          "dev": true,
-          "requires": {
-            "has-flag": "^3.0.0"
-          }
-        }
+        "chalk": "^4.0.0"
       }
     },
     "logform": {
@@ -7724,23 +7680,23 @@
       "integrity": "sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A=="
     },
     "mocha": {
-      "version": "8.1.1",
-      "resolved": "https://registry.npmjs.org/mocha/-/mocha-8.1.1.tgz",
-      "integrity": "sha512-p7FuGlYH8t7gaiodlFreseLxEmxTgvyG9RgPHODFPySNhwUehu8NIb0vdSt3WFckSneswZ0Un5typYcWElk7HQ==",
+      "version": "8.1.2",
+      "resolved": "https://registry.npmjs.org/mocha/-/mocha-8.1.2.tgz",
+      "integrity": "sha512-I8FRAcuACNMLQn3lS4qeWLxXqLvGf6r2CaLstDpZmMUUSmvW6Cnm1AuHxgbc7ctZVRcfwspCRbDHymPsi3dkJw==",
       "dev": true,
       "requires": {
         "ansi-colors": "4.1.1",
         "browser-stdout": "1.3.1",
-        "chokidar": "3.3.1",
-        "debug": "3.2.6",
+        "chokidar": "3.4.2",
+        "debug": "4.1.1",
         "diff": "4.0.2",
-        "escape-string-regexp": "1.0.5",
-        "find-up": "4.1.0",
+        "escape-string-regexp": "4.0.0",
+        "find-up": "5.0.0",
         "glob": "7.1.6",
         "growl": "1.10.5",
         "he": "1.2.0",
-        "js-yaml": "3.13.1",
-        "log-symbols": "3.0.0",
+        "js-yaml": "3.14.0",
+        "log-symbols": "4.0.0",
         "minimatch": "3.0.4",
         "ms": "2.1.2",
         "object.assign": "4.1.0",
@@ -7797,9 +7753,9 @@
           }
         },
         "chokidar": {
-          "version": "3.3.1",
-          "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.3.1.tgz",
-          "integrity": "sha512-4QYCEWOcK3OJrxwvyyAOxFuhpvOVCYkr33LPfFNBjAD/w3sEzWsp2BUOkI4l9bHvWioAd0rc6NlHUOEaWkTeqg==",
+          "version": "3.4.2",
+          "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.4.2.tgz",
+          "integrity": "sha512-IZHaDeBeI+sZJRX7lGcXsdzgvZqKv6sECqsbErJA4mHWfpRrD8B97kSFN4cQz6nGBGiuFia1MKR4d6c1o8Cv7A==",
           "dev": true,
           "requires": {
             "anymatch": "~3.1.1",
@@ -7809,7 +7765,7 @@
             "is-binary-path": "~2.1.0",
             "is-glob": "~4.0.1",
             "normalize-path": "~3.0.0",
-            "readdirp": "~3.3.0"
+            "readdirp": "~3.4.0"
           }
         },
         "cliui": {
@@ -7823,21 +7779,6 @@
             "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==",
-          "dev": true,
-          "requires": {
-            "ms": "^2.1.1"
-          }
-        },
-        "escape-string-regexp": {
-          "version": "1.0.5",
-          "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
-          "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=",
-          "dev": true
-        },
         "fill-range": {
           "version": "7.0.1",
           "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz",
@@ -7847,6 +7788,16 @@
             "to-regex-range": "^5.0.1"
           }
         },
+        "find-up": {
+          "version": "5.0.0",
+          "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz",
+          "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==",
+          "dev": true,
+          "requires": {
+            "locate-path": "^6.0.0",
+            "path-exists": "^4.0.0"
+          }
+        },
         "fsevents": {
           "version": "2.1.3",
           "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.1.3.tgz",
@@ -7878,48 +7829,40 @@
           "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==",
           "dev": true
         },
-        "js-yaml": {
-          "version": "3.13.1",
-          "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.13.1.tgz",
-          "integrity": "sha512-YfbcO7jXDdyj0DGxYVSlSeQNHbD7XPWvrVWeVUujrQEoZzWJIRrCPoyk6kL6IAjAG2IolMK4T0hNUe0HOUs5Jw==",
+        "locate-path": {
+          "version": "6.0.0",
+          "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz",
+          "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==",
           "dev": true,
           "requires": {
-            "argparse": "^1.0.7",
-            "esprima": "^4.0.0"
+            "p-locate": "^5.0.0"
           }
         },
-        "locate-path": {
-          "version": "3.0.0",
-          "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz",
-          "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==",
+        "p-limit": {
+          "version": "3.0.2",
+          "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.0.2.tgz",
+          "integrity": "sha512-iwqZSOoWIW+Ew4kAGUlN16J4M7OB3ysMLSZtnhmqx7njIHFPlxWBX8xo3lVTyFVq6mI/lL9qt2IsN1sHwaxJkg==",
           "dev": true,
           "requires": {
-            "p-locate": "^3.0.0",
-            "path-exists": "^3.0.0"
+            "p-try": "^2.0.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==",
+          "version": "5.0.0",
+          "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz",
+          "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==",
           "dev": true,
           "requires": {
-            "p-limit": "^2.0.0"
+            "p-limit": "^3.0.2"
           }
         },
-        "path-exists": {
-          "version": "3.0.0",
-          "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz",
-          "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=",
-          "dev": true
-        },
         "readdirp": {
-          "version": "3.3.0",
-          "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.3.0.tgz",
-          "integrity": "sha512-zz0pAkSPOXXm1viEwygWIPSPkcBYjW1xU5j/JBh5t9bGCJwa6f9+BJa6VaB2g+b55yVrmXzqkyLf4xaWYM0IkQ==",
+          "version": "3.4.0",
+          "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.4.0.tgz",
+          "integrity": "sha512-0xe001vZBnJEK+uKcj8qOhyAKPzIT+gStxWr3LCB0DwcXR5NZJ3IaC+yGnHCYzB/S7ov3m3EEbZI2zeNvX+hGQ==",
           "dev": true,
           "requires": {
-            "picomatch": "^2.0.7"
+            "picomatch": "^2.2.1"
           }
         },
         "serialize-javascript": {
@@ -7994,6 +7937,40 @@
               "requires": {
                 "locate-path": "^3.0.0"
               }
+            },
+            "locate-path": {
+              "version": "3.0.0",
+              "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz",
+              "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==",
+              "dev": true,
+              "requires": {
+                "p-locate": "^3.0.0",
+                "path-exists": "^3.0.0"
+              }
+            },
+            "p-limit": {
+              "version": "2.3.0",
+              "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz",
+              "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==",
+              "dev": true,
+              "requires": {
+                "p-try": "^2.0.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==",
+              "dev": true,
+              "requires": {
+                "p-limit": "^2.0.0"
+              }
+            },
+            "path-exists": {
+              "version": "3.0.0",
+              "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz",
+              "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=",
+              "dev": true
             }
           }
         },

+ 4 - 4
package.json

@@ -57,7 +57,7 @@
     "@types/lodash": "^4.14.160",
     "@types/mocha": "^8.0.3",
     "@types/node": "^12.12.54",
-    "@types/react": "^16.9.46",
+    "@types/react": "^16.9.48",
     "@types/react-dom": "^16.9.8",
     "@types/react-redux": "^7.1.9",
     "@types/react-transition-group": "^4.4.0",
@@ -65,8 +65,8 @@
     "@types/tar-fs": "^2.0.0",
     "@types/triple-beam": "^1.3.2",
     "@types/winreg": "^1.2.30",
-    "@typescript-eslint/eslint-plugin": "^3.10.0",
-    "@typescript-eslint/parser": "^3.10.0",
+    "@typescript-eslint/eslint-plugin": "^3.10.1",
+    "@typescript-eslint/parser": "^3.10.1",
     "chai": "^4.2.0",
     "cross-env": "^7.0.2",
     "electron": "^9.2.1",
@@ -77,7 +77,7 @@
     "eslint": "^7.7.0",
     "eslint-plugin-react": "^7.20.6",
     "helios-distribution-types": "1.0.0-pre.1",
-    "mocha": "^8.1.1",
+    "mocha": "^8.1.2",
     "nock": "^13.0.4",
     "react": "^16.13.0",
     "react-dom": "^16.13.0",

+ 262 - 0
src/common/util/ServerStatusUtil.ts

@@ -0,0 +1,262 @@
+/* eslint-disable no-control-regex */
+import { connect } from 'net'
+import { LoggerUtil } from 'common/logging/loggerutil'
+
+const logger = LoggerUtil.getLogger('ServerStatusUtil')
+
+export interface ServerStatus {
+    version: {
+        name: string
+        protocol: number
+    }
+    players: {
+        max: number
+        online: number
+        sample: {
+            name: string
+            id: string
+        }[]
+    }
+    description: {
+        text: string
+    }
+    favicon: string
+    modinfo?: {             // Only for modded servers
+        type: string        // Ex. FML
+        modList: {
+            modid: string
+            version: string
+        }[]
+    }
+}
+
+/**
+ * Utility Class to construct a packet conforming to Minecraft's
+ * protocol. All data types are BE except VarInt and VarLong.
+ * 
+ * @see https://wiki.vg/Protocol
+ */
+class ServerBoundPacket {
+
+    private buffer: number[]
+
+    protected constructor() {
+        this.buffer = []
+    }
+
+    public static build(): ServerBoundPacket {
+        return new ServerBoundPacket()
+    }
+
+    /**
+     * Packet is prefixed with its data length as a VarInt.
+     * 
+     * @see https://wiki.vg/Protocol#Packet_format
+     */
+    public toBuffer(): Buffer {
+        const finalizedPacket = new ServerBoundPacket()
+        finalizedPacket.writeVarInt(this.buffer.length)
+        finalizedPacket.writeBytes(...this.buffer)
+
+        return Buffer.from(finalizedPacket.buffer)
+    }
+
+    public writeBytes(...bytes: number[]): ServerBoundPacket {
+        this.buffer.push(...bytes)
+        return this
+    }
+
+    /**
+     * @see https://wiki.vg/Protocol#VarInt_and_VarLong
+     */
+    public writeVarInt(value: number): ServerBoundPacket {
+        do {
+            let temp = value & 0b01111111
+
+            value >>>= 7
+
+            if (value != 0) {
+                temp |= 0b10000000
+            }
+
+            this.writeBytes(temp)
+        } while (value != 0)
+
+        return this
+    }
+
+    /**
+     * Strings are prefixed with their length as a VarInt.
+     * 
+     * @see https://wiki.vg/Protocol#Data_types
+     */
+    public writeString(string: string): ServerBoundPacket {
+        this.writeVarInt(string.length)
+        for (let i=0; i<string.length; i++) {
+            this.writeBytes(string.codePointAt(i)!)
+        }
+
+        return this
+    }
+
+    public writeUnsignedShort(short: number): ServerBoundPacket {
+        const buf = Buffer.alloc(2)
+        buf.writeUInt16BE(short, 0)
+        this.writeBytes(...buf)
+
+        return this
+    }
+ 
+}
+
+/**
+ * Utility Class to read a client-bound packet conforming to
+ * Minecraft's protocol. All data types are BE except VarInt
+ * and VarLong.
+ * 
+ * @see https://wiki.vg/Protocol
+ */
+class ClientBoundPacket {
+
+    private buffer: number[]
+
+    constructor(buffer: Buffer) {
+        this.buffer = [...buffer]
+    }
+
+    public readByte(): number {
+        return this.buffer.shift()!
+    }
+
+    public readBytes(length: number): number[] {
+        const value = this.buffer.slice(0, length)
+        this.buffer.splice(0, length)
+        return value
+    }
+
+    public readVarInt(): number {
+
+        let numRead = 0
+        let result = 0
+        let read
+
+        do {
+            read = this.readByte()
+            const value = (read & 0b01111111)
+            result |= (value << (7 * numRead))
+
+            numRead++
+            if (numRead > 5) {
+                throw new Error('VarInt is too big')
+            }
+        } while ((read & 0b10000000) != 0)
+
+        return result
+    }
+
+    public readString(): string {
+        const length = this.readVarInt()
+        const data = this.readBytes(length)
+
+        let value = ''
+
+        for (let i=0; i<data.length; i++) {
+            value += String.fromCharCode(data[i])
+        }
+
+        return value
+    }
+
+}
+
+/**
+ * Get the handshake packet.
+ * 
+ * @param protocol The client's protocol version.
+ * @param address The server address.
+ * @param port The server port.
+ * 
+ * @see https://wiki.vg/Server_List_Ping#Handshake
+ */
+function getHandshakePacket(protocol: number, address: string, port: number): Buffer {
+
+    return ServerBoundPacket.build()
+        .writeVarInt(0x00)         // Packet Id 
+        .writeVarInt(protocol)
+        .writeString(address)
+        .writeUnsignedShort(port)
+        .writeVarInt(1)            // State, 1 = status
+        .toBuffer()
+}
+
+/**
+ * Get the request packet.
+ * 
+ * @see https://wiki.vg/Server_List_Ping#Request
+ */
+function getRequestPacket(): Buffer {
+
+    return ServerBoundPacket.build()
+        .writeVarInt(0x00)
+        .toBuffer()
+}
+
+export function getServerStatus(protocol: number, address: string, port = 25565): Promise<ServerStatus | null> {
+
+    return new Promise((resolve, reject) => {
+
+        const socket = connect(port, address, () => {
+            socket.write(getHandshakePacket(protocol, address, port))
+            socket.write(getRequestPacket())
+        })
+
+        socket.setTimeout(2500, () => {
+            socket.destroy()
+            logger.error(`Server Status Socket timed out (${address}:${port})`)
+            reject(new Error(`Server Status Socket timed out (${address}:${port})`))
+        })
+
+        socket.on('data', (data) => {
+
+            const inboundPacket = new ClientBoundPacket(data)
+
+            // eslint-disable-next-line @typescript-eslint/no-unused-vars
+            const packetLength = inboundPacket.readVarInt() // First VarInt is packet length.
+            const packetType = inboundPacket.readVarInt()   // Second VarInt is packet type.
+
+            if(packetType !== 0x00) {
+                // TODO
+                socket.destroy()
+                reject(new Error(`Invalid response. Expected packet type ${0x00}, received ${packetType}!`))
+                return
+            }
+
+            const res = inboundPacket.readString() // Remainder of Buffer is the server status json.
+
+            socket.end()
+            resolve(JSON.parse(res))
+        })
+
+        socket.on('error', (err: NodeJS.ErrnoException) => {
+            socket.destroy()
+
+            if(err.code === 'ENOTFOUND') {
+                // ENOTFOUND = Unable to resolve.
+                logger.error(`Server ${address}:${port} not found!`)
+                resolve(null)
+                return
+            } else if(err.code === 'ECONNREFUSED') {
+                // ECONNREFUSED = Unable to connect to port.
+                logger.error(`Server ${address}:${port} refused to connect, is the port correct?`)
+                resolve(null)
+                return
+            } else {
+                logger.error(`Error trying to pull server status (${address}:${port}})`, err)
+                resolve(null)
+                return
+            }
+        })
+
+    })
+
+}

+ 3 - 0
src/renderer/components/Application.tsx

@@ -18,6 +18,7 @@ import Overlay from './overlay/Overlay'
 import { OverlayPushAction, OverlayActionDispatch } from '../redux/actions/overlayActions'
 
 import { DistributionAPI } from 'common/distribution/distribution'
+import { getServerStatus } from 'common/util/ServerStatusUtil'
 
 import './Application.css'
 
@@ -129,6 +130,8 @@ class Application extends React.Component<ApplicationProps & typeof mapDispatch,
                             const distro = new DistributionAPI('C:\\Users\\user\\AppData\\Roaming\\Helios Launcher')
                             const x = await distro.testLoad()
                             console.log(x)
+                            const serverStatus = await getServerStatus(47, 'mc.westeroscraft.com', 25565)
+                            console.log(serverStatus)
                         }
                     })
                     this.props.pushGenericOverlay({

+ 14 - 2
src/renderer/components/overlay/generic-overlay/GenericOverlay.tsx

@@ -2,6 +2,8 @@ import * as React from 'react'
 import { connect } from 'react-redux'
 import { OverlayActionDispatch } from '../../../redux/actions/overlayActions'
 
+import { LoggerUtil } from 'common/logging/loggerutil'
+
 import './GenericOverlay.css'
 
 export interface GenericOverlayProps {
@@ -22,6 +24,8 @@ type InternalGenericOverlayProps = GenericOverlayProps & typeof mapDispatch
 
 class GenericOverlay extends React.Component<InternalGenericOverlayProps> {
 
+    private readonly logger = LoggerUtil.getLogger('GenericOverlay')
+
     private getAcknowledgeText = (): string => {
         return this.props.acknowledgeText || 'OK'
     }
@@ -32,14 +36,22 @@ class GenericOverlay extends React.Component<InternalGenericOverlayProps> {
 
     private onAcknowledgeClick = async (event: React.MouseEvent<HTMLButtonElement, MouseEvent>): Promise<void> => {
         if(this.props.acknowledgeCallback) {
-            await this.props.acknowledgeCallback(event)
+            try {
+                await this.props.acknowledgeCallback(event)
+            } catch(err) {
+                this.logger.error('Uncaught error in acknowledgement', err)
+            }
         }
         this.props.popOverlayContent()
     }
 
     private onDismissClick = async (event: React.MouseEvent<HTMLButtonElement, MouseEvent>): Promise<void> => {
         if(this.props.dismissCallback) {
-            await this.props.dismissCallback(event)
+            try {
+                await this.props.dismissCallback(event)
+            } catch(err) {
+                this.logger.error('Uncaught error in dismission', err)
+            }
         }
         this.props.popOverlayContent()
     }