diff --git a/webui/package-lock.json b/webui/package-lock.json index 1b6c65e..f38f0f9 100644 --- a/webui/package-lock.json +++ b/webui/package-lock.json @@ -885,6 +885,12 @@ "loader-utils": "^1.1.0" } }, + "@types/node": { + "version": "11.9.0", + "resolved": "https://registry.npmjs.org/@types/node/-/node-11.9.0.tgz", + "integrity": "sha512-ry4DOrC+xenhQbzk1iIPzCZGhhPGEFv7ia7Iu6XXSLVluiJIe9FfG7Iu3mObH9mpxEXCWLCMU4JWbCCR9Oy1Zg==", + "dev": true + }, "@types/prop-types": { "version": "15.5.8", "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.5.8.tgz", @@ -906,14 +912,14 @@ "csstype": "^2.2.0" } }, - "@types/react-redux": { - "version": "6.0.13", - "resolved": "https://registry.npmjs.org/@types/react-redux/-/react-redux-6.0.13.tgz", - "integrity": "sha512-69lPYT69CSvzQ75Ut4MfT+oQU9TAjCycdL6BQOrPztqbzdhzSe1d4Qj8vrMkTOm454Xz151zh+YT6S4t2OBopw==", + "@types/reactstrap": { + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/@types/reactstrap/-/reactstrap-7.1.3.tgz", + "integrity": "sha512-WUYfvKLIOfJG/bZI5WmWLbrVk5Q1usloszE/+tmW/B0SQD8ZaiUkT9X1HIY54ysGvt6CTRoqxj5GMlFi5pPWYA==", "dev": true, "requires": { "@types/react": "*", - "redux": "^4.0.0" + "popper.js": "^1.14.1" } }, "@types/tapable": { @@ -6309,6 +6315,28 @@ "resolved": "https://registry.npmjs.org/hex-color-regex/-/hex-color-regex-1.1.0.tgz", "integrity": "sha512-l9sfDFsuqtOqKDsQdqrMRk0U85RZc0RtOR9yPI7mRVOa4FsR/BVnZ0shmQRM96Ji99kYZP/7hn1cedc1+ApsTQ==" }, + "history": { + "version": "4.7.2", + "resolved": "https://registry.npmjs.org/history/-/history-4.7.2.tgz", + "integrity": "sha512-1zkBRWW6XweO0NBcjiphtVJVsIQ+SXF29z9DVkceeaSLVMFXHool+fdCZD4spDCfZJCILPILc3bm7Bc+HRi0nA==", + "requires": { + "invariant": "^2.2.1", + "loose-envify": "^1.2.0", + "resolve-pathname": "^2.2.0", + "value-equal": "^0.4.0", + "warning": "^3.0.0" + }, + "dependencies": { + "warning": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/warning/-/warning-3.0.0.tgz", + "integrity": "sha1-MuU3fLVy3kqwR1O9+IIcAe1gW3w=", + "requires": { + "loose-envify": "^1.0.0" + } + } + } + }, "hmac-drbg": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/hmac-drbg/-/hmac-drbg-1.0.1.tgz", @@ -6325,12 +6353,9 @@ "integrity": "sha512-QLg82fGkfnJ/4iy1xZ81/9SIJiq1NGFUMGs6ParyjBZr6jW2Ufj/snDqTHixNlHdPNwN2RLVD0Pi3igeK9+JfA==" }, "hoist-non-react-statics": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.2.1.tgz", - "integrity": "sha512-TFsu3TV3YLY+zFTZDrN8L2DTFanObwmBLpWvJs1qfUuEQ5bTAdFcwfx2T/bsCXfM9QHSLvjfP+nihEl0yvozxw==", - "requires": { - "react-is": "^16.3.2" - } + "version": "2.5.5", + "resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-2.5.5.tgz", + "integrity": "sha512-rqcy4pJo55FTTLWt+bU8ukscqHeE/e9KWvsOW2b/a3afxQZhwkQdT1rPPCJ0rYXdj4vNcasY8zHTH+jF/qStxw==" }, "home-or-tmp": { "version": "2.0.0", @@ -6507,30 +6532,34 @@ } }, "http-proxy-middleware": { - "version": "0.18.0", - "resolved": "https://registry.npmjs.org/http-proxy-middleware/-/http-proxy-middleware-0.18.0.tgz", - "integrity": "sha512-Fs25KVMPAIIcgjMZkVHJoKg9VcXcC1C8yb9JUgeDvVXY0S/zgVIhMb+qVswDIgtJe2DfckMSY2d6TuTEutlk6Q==", + "version": "0.19.1", + "resolved": "https://registry.npmjs.org/http-proxy-middleware/-/http-proxy-middleware-0.19.1.tgz", + "integrity": "sha512-yHYTgWMQO8VvwNS22eLLloAkvungsKdKTLO8AJlftYIKNfJr3GK3zK0ZCfzDDGUBttdGc8xFy1mCitvNKQtC3Q==", + "dev": true, "requires": { - "http-proxy": "^1.16.2", + "http-proxy": "^1.17.0", "is-glob": "^4.0.0", - "lodash": "^4.17.5", - "micromatch": "^3.1.9" + "lodash": "^4.17.11", + "micromatch": "^3.1.10" }, "dependencies": { "arr-diff": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-4.0.0.tgz", - "integrity": "sha1-1kYQdP6/7HHn4VI1dhoyml3HxSA=" + "integrity": "sha1-1kYQdP6/7HHn4VI1dhoyml3HxSA=", + "dev": true }, "array-unique": { "version": "0.3.2", "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.3.2.tgz", - "integrity": "sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg=" + "integrity": "sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg=", + "dev": true }, "braces": { "version": "2.3.2", "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz", "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==", + "dev": true, "requires": { "arr-flatten": "^1.1.0", "array-unique": "^0.3.2", @@ -6548,6 +6577,7 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, "requires": { "is-extendable": "^0.1.0" } @@ -6558,6 +6588,7 @@ "version": "2.6.9", "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, "requires": { "ms": "2.0.0" } @@ -6566,6 +6597,7 @@ "version": "2.1.4", "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-2.1.4.tgz", "integrity": "sha1-t3c14xXOMPa27/D4OwQVGiJEliI=", + "dev": true, "requires": { "debug": "^2.3.3", "define-property": "^0.2.5", @@ -6580,6 +6612,7 @@ "version": "0.2.5", "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "dev": true, "requires": { "is-descriptor": "^0.1.0" } @@ -6588,6 +6621,7 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, "requires": { "is-extendable": "^0.1.0" } @@ -6596,6 +6630,7 @@ "version": "0.1.6", "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", + "dev": true, "requires": { "kind-of": "^3.0.2" }, @@ -6604,6 +6639,7 @@ "version": "3.2.2", "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, "requires": { "is-buffer": "^1.1.5" } @@ -6614,6 +6650,7 @@ "version": "0.1.4", "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", + "dev": true, "requires": { "kind-of": "^3.0.2" }, @@ -6622,6 +6659,7 @@ "version": "3.2.2", "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, "requires": { "is-buffer": "^1.1.5" } @@ -6632,6 +6670,7 @@ "version": "0.1.6", "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", + "dev": true, "requires": { "is-accessor-descriptor": "^0.1.6", "is-data-descriptor": "^0.1.4", @@ -6641,7 +6680,8 @@ "kind-of": { "version": "5.1.0", "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", - "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==" + "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==", + "dev": true } } }, @@ -6649,6 +6689,7 @@ "version": "2.0.4", "resolved": "https://registry.npmjs.org/extglob/-/extglob-2.0.4.tgz", "integrity": "sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw==", + "dev": true, "requires": { "array-unique": "^0.3.2", "define-property": "^1.0.0", @@ -6664,6 +6705,7 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", + "dev": true, "requires": { "is-descriptor": "^1.0.0" } @@ -6672,6 +6714,7 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, "requires": { "is-extendable": "^0.1.0" } @@ -6682,6 +6725,7 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", "integrity": "sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=", + "dev": true, "requires": { "extend-shallow": "^2.0.1", "is-number": "^3.0.0", @@ -6693,6 +6737,7 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, "requires": { "is-extendable": "^0.1.0" } @@ -6703,6 +6748,7 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", + "dev": true, "requires": { "kind-of": "^6.0.0" } @@ -6711,6 +6757,7 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", + "dev": true, "requires": { "kind-of": "^6.0.0" } @@ -6719,6 +6766,7 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", + "dev": true, "requires": { "is-accessor-descriptor": "^1.0.0", "is-data-descriptor": "^1.0.0", @@ -6728,12 +6776,14 @@ "is-extglob": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", - "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=" + "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=", + "dev": true }, "is-glob": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.0.tgz", "integrity": "sha1-lSHHaEXMJhCoUgPd8ICpWML/q8A=", + "dev": true, "requires": { "is-extglob": "^2.1.1" } @@ -6742,6 +6792,7 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", + "dev": true, "requires": { "kind-of": "^3.0.2" }, @@ -6750,6 +6801,7 @@ "version": "3.2.2", "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, "requires": { "is-buffer": "^1.1.5" } @@ -6759,12 +6811,14 @@ "kind-of": { "version": "6.0.2", "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz", - "integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==" + "integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==", + "dev": true }, "micromatch": { "version": "3.1.10", "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz", "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==", + "dev": true, "requires": { "arr-diff": "^4.0.0", "array-unique": "^0.3.2", @@ -6784,7 +6838,8 @@ "ms": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true } } }, @@ -11542,11 +11597,6 @@ "resolved": "https://registry.npmjs.org/react-error-overlay/-/react-error-overlay-5.1.2.tgz", "integrity": "sha512-7kEBKwU9R8fKnZJBRa5RSIfay4KJwnYvKB6gODGicUmDSAhQJ7Tdnll5S0RLtYrzRfMVXlqYw61rzrSpP4ThLQ==" }, - "react-is": { - "version": "16.7.0", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.7.0.tgz", - "integrity": "sha512-Z0VRQdF4NPDoI0tsXVMLkJLiwEBa+RP66g0xDHxgxysxSoCUccSten4RTF/UFvZF1dZvZ9Zu1sx+MDXwcOR34g==" - }, "react-lifecycles-compat": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/react-lifecycles-compat/-/react-lifecycles-compat-3.0.4.tgz", @@ -11561,34 +11611,48 @@ "prop-types": "^15.6.1" } }, - "react-redux": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/react-redux/-/react-redux-6.0.0.tgz", - "integrity": "sha512-EmbC3uLl60pw2VqSSkj6HpZ6jTk12RMrwXMBdYtM6niq0MdEaRq9KYCwpJflkOZj349BLGQm1MI/JO1W96kLWQ==", + "react-router": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/react-router/-/react-router-4.3.1.tgz", + "integrity": "sha512-yrvL8AogDh2X42Dt9iknk4wF4V8bWREPirFfS9gLU1huk6qK41sg7Z/1S81jjTrGHxa3B8R3J6xIkDAA6CVarg==", "requires": { - "@babel/runtime": "^7.2.0", - "hoist-non-react-statics": "^3.2.1", + "history": "^4.7.2", + "hoist-non-react-statics": "^2.5.0", "invariant": "^2.2.4", - "loose-envify": "^1.4.0", - "prop-types": "^15.6.2", - "react-is": "^16.6.3" + "loose-envify": "^1.3.1", + "path-to-regexp": "^1.7.0", + "prop-types": "^15.6.1", + "warning": "^4.0.1" }, "dependencies": { - "@babel/runtime": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.2.0.tgz", - "integrity": "sha512-oouEibCbHMVdZSDlJBO6bZmID/zA/G/Qx3H1d3rSNPTD+L8UNKvCat7aKWSJ74zYbm5zWGh0GQN0hKj8zYFTCg==", + "isarray": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", + "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=" + }, + "path-to-regexp": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-1.7.0.tgz", + "integrity": "sha1-Wf3g9DW62suhA6hOnTvGTpa5k30=", "requires": { - "regenerator-runtime": "^0.12.0" + "isarray": "0.0.1" } - }, - "regenerator-runtime": { - "version": "0.12.1", - "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.12.1.tgz", - "integrity": "sha512-odxIc1/vDlo4iZcfXqRYFj0vpXFNoGdKMAUieAlFYO6m/nl5e9KR/beGf41z4a1FI+aQgtjhuaSlDxQ0hmkrHg==" } } }, + "react-router-dom": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-4.3.1.tgz", + "integrity": "sha512-c/MlywfxDdCp7EnB7YfPMOfMD3tOtIjrQlj/CKfNMBxdmpJP8xcz5P/UAFn3JbnQCNUxsHyVVqllF9LhgVyFCA==", + "requires": { + "history": "^4.7.2", + "invariant": "^2.2.4", + "loose-envify": "^1.3.1", + "prop-types": "^15.6.1", + "react-router": "^4.3.1", + "warning": "^4.0.1" + } + }, "react-scripts": { "version": "2.1.3", "resolved": "https://registry.npmjs.org/react-scripts/-/react-scripts-2.1.3.tgz", @@ -11645,9 +11709,9 @@ } }, "react-transition-group": { - "version": "2.5.2", - "resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-2.5.2.tgz", - "integrity": "sha512-vwHP++S+f6KL7rg8V1mfs62+MBKtbMeZDR8KiNmD7v98Gs3UPGsDZDahPJH2PVprFW5YHJfh6cbNim3zPndaSQ==", + "version": "2.5.3", + "resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-2.5.3.tgz", + "integrity": "sha512-2DGFck6h99kLNr8pOFk+z4Soq3iISydwOFeeEVPjTN6+Y01CmvbWmnN02VuTWyFdnRtIDPe+wy2q6Ui8snBPZg==", "requires": { "dom-helpers": "^3.3.1", "loose-envify": "^1.4.0", @@ -11655,10 +11719,18 @@ "react-lifecycles-compat": "^3.0.4" } }, + "reactn": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/reactn/-/reactn-0.2.2.tgz", + "integrity": "sha512-8yjBMXg3V1RsIqM9aAHbjRQyL46pwSpd3JgFD5XVr/zsbUommhIMT9HHiDEWhpUTmKriKegy+ZFaKWJRhDjJoQ==", + "requires": { + "use-force-update": "^1.0.0" + } + }, "reactstrap": { - "version": "7.0.2", - "resolved": "https://registry.npmjs.org/reactstrap/-/reactstrap-7.0.2.tgz", - "integrity": "sha512-VVa/1tqCiKa63YPwAIWrn4za806TVva/GEx7yP/2U+xmq1iKIzGo0UHmFL/6XpZKqx/ZzP/olkTorP/2o/2pTg==", + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/reactstrap/-/reactstrap-7.1.0.tgz", + "integrity": "sha512-wtc4RkgnGn1TsZ0AxOZ2OqT+b8YmCWZj/tErPujWLepxzlEEhveZGC+uDerdaHVSAzJUP2DTk605iper7hutQQ==", "requires": { "@babel/runtime": "^7.2.0", "classnames": "^2.2.3", @@ -11672,9 +11744,9 @@ }, "dependencies": { "@babel/runtime": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.2.0.tgz", - "integrity": "sha512-oouEibCbHMVdZSDlJBO6bZmID/zA/G/Qx3H1d3rSNPTD+L8UNKvCat7aKWSJ74zYbm5zWGh0GQN0hKj8zYFTCg==", + "version": "7.3.1", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.3.1.tgz", + "integrity": "sha512-7jGW8ppV0ant637pIqAcFfQDDH1orEPGJb8aXfUozuCU3QqX7rX4DA8iwrbPrR1hcH0FTTHz47yQnk+bl5xHQA==", "requires": { "regenerator-runtime": "^0.12.0" } @@ -12022,15 +12094,6 @@ "minimatch": "3.0.4" } }, - "redux": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/redux/-/redux-4.0.1.tgz", - "integrity": "sha512-R7bAtSkk7nY6O/OYMVR9RiBI+XghjF9rlbl5806HJbQph0LJVHZrU5oaO4q70eUKiqMRqm4y07KLTlMZ2BlVmg==", - "requires": { - "loose-envify": "^1.4.0", - "symbol-observable": "^1.2.0" - } - }, "regenerate": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.4.0.tgz", @@ -12314,6 +12377,11 @@ "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-3.0.0.tgz", "integrity": "sha1-six699nWiBvItuZTM17rywoYh0g=" }, + "resolve-pathname": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/resolve-pathname/-/resolve-pathname-2.2.0.tgz", + "integrity": "sha512-bAFz9ld18RzJfddgrO2e/0S2O81710++chRMUxHjXOYKF6jTAMrUNZrEZ1PvV0zlhfjidm08iRPdTLPno1FuRg==" + }, "resolve-url": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/resolve-url/-/resolve-url-0.2.1.tgz", @@ -13565,11 +13633,6 @@ "util.promisify": "~1.0.0" } }, - "symbol-observable": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/symbol-observable/-/symbol-observable-1.2.0.tgz", - "integrity": "sha512-e900nM8RRtGhlV36KGEU9k65K3mPb1WV70OdjfxlG2EAuM1noi/E/BaW/uMhL7bPEssK8QV57vN3esixjUvcXQ==" - }, "symbol-tree": { "version": "3.2.2", "resolved": "https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.2.tgz", @@ -14218,6 +14281,11 @@ "resolved": "https://registry.npmjs.org/use/-/use-3.1.1.tgz", "integrity": "sha512-cwESVXlO3url9YWlFW/TA9cshCEhtu7IKJ/p5soJ/gGpj7vbvFrAY/eIioQ6Dw23KjZhYgiIo8HOs1nQ2vr/oQ==" }, + "use-force-update": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/use-force-update/-/use-force-update-1.0.2.tgz", + "integrity": "sha512-/DOz91GCBRg/xMWx9+23JJvo4jLUiksnL4d+OFbbeJEm7BV5Ckn+y+1Z527niwhK8xcHh4zFgkPBtzWyrMkEyg==" + }, "util": { "version": "0.10.4", "resolved": "https://registry.npmjs.org/util/-/util-0.10.4.tgz", @@ -14264,6 +14332,11 @@ "spdx-expression-parse": "^3.0.0" } }, + "value-equal": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/value-equal/-/value-equal-0.4.0.tgz", + "integrity": "sha512-x+cYdNnaA3CxvMaTX0INdTCN8m8aF2uY9BvEqmxuYp8bL09cs/kWVQPVGcA35fMktdOsP69IgU7wFj/61dJHEw==" + }, "vary": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", @@ -14318,6 +14391,14 @@ "makeerror": "1.0.x" } }, + "warning": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/warning/-/warning-4.0.3.tgz", + "integrity": "sha512-rpJyN222KWIvHJ/F53XSZv0Zl/accqHR8et1kpaMTD/fLCRxtV8iX8czMzY7sVZupTI3zcUTg8eycS2kNF9l6w==", + "requires": { + "loose-envify": "^1.0.0" + } + }, "watch": { "version": "0.18.0", "resolved": "https://registry.npmjs.org/watch/-/watch-0.18.0.tgz", @@ -14710,6 +14791,43 @@ "yargs": "12.0.2" }, "dependencies": { + "arr-diff": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-4.0.0.tgz", + "integrity": "sha1-1kYQdP6/7HHn4VI1dhoyml3HxSA=" + }, + "array-unique": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.3.2.tgz", + "integrity": "sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg=" + }, + "braces": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz", + "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==", + "requires": { + "arr-flatten": "^1.1.0", + "array-unique": "^0.3.2", + "extend-shallow": "^2.0.1", + "fill-range": "^4.0.0", + "isobject": "^3.0.1", + "repeat-element": "^1.1.2", + "snapdragon": "^0.8.1", + "snapdragon-node": "^2.0.1", + "split-string": "^3.0.2", + "to-regex": "^3.0.1" + }, + "dependencies": { + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "requires": { + "is-extendable": "^0.1.0" + } + } + } + }, "camelcase": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-4.1.0.tgz", @@ -14753,6 +14871,156 @@ "strip-eof": "^1.0.0" } }, + "expand-brackets": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-2.1.4.tgz", + "integrity": "sha1-t3c14xXOMPa27/D4OwQVGiJEliI=", + "requires": { + "debug": "^2.3.3", + "define-property": "^0.2.5", + "extend-shallow": "^2.0.1", + "posix-character-classes": "^0.1.0", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.1" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "requires": { + "ms": "2.0.0" + } + }, + "define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "requires": { + "is-descriptor": "^0.1.0" + } + }, + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "requires": { + "is-extendable": "^0.1.0" + } + }, + "is-accessor-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", + "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", + "requires": { + "kind-of": "^3.0.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "is-data-descriptor": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", + "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", + "requires": { + "kind-of": "^3.0.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "is-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", + "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", + "requires": { + "is-accessor-descriptor": "^0.1.6", + "is-data-descriptor": "^0.1.4", + "kind-of": "^5.0.0" + } + }, + "kind-of": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", + "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==" + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" + } + } + }, + "extglob": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/extglob/-/extglob-2.0.4.tgz", + "integrity": "sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw==", + "requires": { + "array-unique": "^0.3.2", + "define-property": "^1.0.0", + "expand-brackets": "^2.1.4", + "extend-shallow": "^2.0.1", + "fragment-cache": "^0.2.1", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.1" + }, + "dependencies": { + "define-property": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", + "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", + "requires": { + "is-descriptor": "^1.0.0" + } + }, + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "requires": { + "is-extendable": "^0.1.0" + } + } + } + }, + "fill-range": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", + "integrity": "sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=", + "requires": { + "extend-shallow": "^2.0.1", + "is-number": "^3.0.0", + "repeat-string": "^1.6.1", + "to-regex-range": "^2.1.0" + }, + "dependencies": { + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "requires": { + "is-extendable": "^0.1.0" + } + } + } + }, "find-up": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", @@ -14769,6 +15037,17 @@ "pump": "^3.0.0" } }, + "http-proxy-middleware": { + "version": "0.18.0", + "resolved": "https://registry.npmjs.org/http-proxy-middleware/-/http-proxy-middleware-0.18.0.tgz", + "integrity": "sha512-Fs25KVMPAIIcgjMZkVHJoKg9VcXcC1C8yb9JUgeDvVXY0S/zgVIhMb+qVswDIgtJe2DfckMSY2d6TuTEutlk6Q==", + "requires": { + "http-proxy": "^1.16.2", + "is-glob": "^4.0.0", + "lodash": "^4.17.5", + "micromatch": "^3.1.9" + } + }, "import-local": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/import-local/-/import-local-2.0.0.tgz", @@ -14783,6 +15062,68 @@ "resolved": "https://registry.npmjs.org/invert-kv/-/invert-kv-2.0.0.tgz", "integrity": "sha512-wPVv/y/QQ/Uiirj/vh3oP+1Ww+AWehmi1g5fFWGPF6IpCBCDVrhgHRMvrLfdYcwDh3QJbGXDW4JAuzxElLSqKA==" }, + "is-accessor-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", + "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-data-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", + "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-descriptor": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", + "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", + "requires": { + "is-accessor-descriptor": "^1.0.0", + "is-data-descriptor": "^1.0.0", + "kind-of": "^6.0.2" + } + }, + "is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=" + }, + "is-glob": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.0.tgz", + "integrity": "sha1-lSHHaEXMJhCoUgPd8ICpWML/q8A=", + "requires": { + "is-extglob": "^2.1.1" + } + }, + "is-number": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", + "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", + "requires": { + "kind-of": "^3.0.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "kind-of": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz", + "integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==" + }, "lcid": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/lcid/-/lcid-2.0.0.tgz", @@ -14810,6 +15151,26 @@ "p-is-promise": "^1.1.0" } }, + "micromatch": { + "version": "3.1.10", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz", + "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==", + "requires": { + "arr-diff": "^4.0.0", + "array-unique": "^0.3.2", + "braces": "^2.3.1", + "define-property": "^2.0.2", + "extend-shallow": "^3.0.2", + "extglob": "^2.0.4", + "fragment-cache": "^0.2.1", + "kind-of": "^6.0.2", + "nanomatch": "^1.2.9", + "object.pick": "^1.3.0", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.2" + } + }, "os-locale": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/os-locale/-/os-locale-3.1.0.tgz", diff --git a/webui/package.json b/webui/package.json index 08070c4..50c3ccf 100644 --- a/webui/package.json +++ b/webui/package.json @@ -6,15 +6,17 @@ "bootstrap": "^4.2.1", "react": "^16.8.1", "react-dom": "^16.8.1", - "react-redux": "^6.0.0", "react-scripts": "2.1.3", - "reactstrap": "^7.0.2", - "redux": "^4.0.1" + "reactstrap": "^7.1.0", + "reactn": "^0.2.2", + "react-router-dom": "^4.3.1", + "react-router": "^4.3.1" }, "devDependencies": { "@types/react": "^16.8.1", - "@types/react-redux": "^6.0.0", - "@types/reactstrap": "^7.0.2" + "@types/reactstrap": "^7.1.0", + "http-proxy-middleware": "latest", + "@types/node": "^11.9.0" }, "scripts": { "start": "react-scripts start", diff --git a/webui/proxy.json b/webui/proxy.json new file mode 100644 index 0000000..fae496a --- /dev/null +++ b/webui/proxy.json @@ -0,0 +1,3 @@ +{ + "url": "http://10.32.7.1:8000/" +} \ No newline at end of file diff --git a/webui/src/Actions/lightActions.js b/webui/src/Actions/lightActions.js deleted file mode 100644 index 3095c55..0000000 --- a/webui/src/Actions/lightActions.js +++ /dev/null @@ -1,13 +0,0 @@ -import store from "../Reducers"; -import {fetchGet} from "../Helpers/fetcher"; -import {lightsReceivedEvent} from "../Reducers/lightReducer"; - -const dispatch = store.dispatch; - -export function getLights() { - fetchGet("/light/").then(res => { - if (res !== null) { - dispatch(lightsReceivedEvent(res)); - } - }); -} diff --git a/webui/src/App.css b/webui/src/App.css index cc106c7..c8143c2 100755 --- a/webui/src/App.css +++ b/webui/src/App.css @@ -2,6 +2,6 @@ cursor: pointer; } -button { +a, button { cursor: pointer; -} \ No newline at end of file +} diff --git a/webui/src/App.js b/webui/src/App.js index bde5e91..0734bff 100755 --- a/webui/src/App.js +++ b/webui/src/App.js @@ -1,20 +1,44 @@ -import React, {Component} from 'react'; +import React, {useEffect, useState} from 'react'; import './App.css'; import Header from "./Components/Header"; import LoginForm from "./Components/LoginForm"; -import {Container} from "reactstrap"; +import {Container, Spinner} from "reactstrap"; +import {BrowserRouter} from "react-router-dom"; +import {Route} from "react-router"; +import {setGlobal} from "reactn"; +import useAuth from "./Hooks/auth"; +import IndexPage from "./Components/IndexPage"; -class App extends Component { - render() { - return ( -
+setGlobal({ + "auth/login": false, + "auth/checked": false, +}); + +export default function App() { + const [hasStarted, setHasStarted] = useState(false); + const {isLoggedIn, isChecked, verify} = useAuth(); + + useEffect(() => { + if (!hasStarted) { + verify(); + setHasStarted(true); + } + }); + + return ( + + <>
- + {!isChecked && } + {isChecked && !isLoggedIn && } + {isChecked && isLoggedIn && ( + <> + + + )} -
- ); - } + + + ); } - -export default App; diff --git a/webui/src/Components/Header.jsx b/webui/src/Components/Header.jsx index 151cc4b..894d282 100644 --- a/webui/src/Components/Header.jsx +++ b/webui/src/Components/Header.jsx @@ -1,29 +1,31 @@ import React, {useState} from "react" import {Collapse, Nav, Navbar, NavbarBrand, NavbarToggler, NavItem, NavLink} from "reactstrap"; -import {connect} from "react-redux"; +import useAuth from "../Hooks/auth"; +import {Link} from "react-router-dom"; +import {onEnter} from "../Helpers/keys"; -function Header({isLoggedIn}) { +export default function Header() { const [showMenu, setShowMenu] = useState(false); + const {isLoggedIn, logout} = useAuth(); return ( - Lucifer + Lucifer setShowMenu(!showMenu)}/> {isLoggedIn && ( )} ); -} - -const mapStateToProps = (state) => ({ - isLoggedIn: state.auth.isLoggedIn, -}); - -export default connect(mapStateToProps)(Header); +} \ No newline at end of file diff --git a/webui/src/Components/IndexPage.jsx b/webui/src/Components/IndexPage.jsx new file mode 100644 index 0000000..b303fb2 --- /dev/null +++ b/webui/src/Components/IndexPage.jsx @@ -0,0 +1,30 @@ +import React from "react"; +import useGroups from "../Hooks/lights"; +import {Button, Card, CardFooter, CardHeader, Spinner} from "reactstrap"; + +function IndexPage() { + const groups = useGroups(); + + if (groups === null) { + return ( +
+ +
+ ); + } + + return ( +
+ {groups.map(group => ( + + {group.name} + + + + + ))} +
+ ); +} + +export default IndexPage; diff --git a/webui/src/Components/LoginForm.jsx b/webui/src/Components/LoginForm.jsx index 97e0c1f..c6a27a5 100644 --- a/webui/src/Components/LoginForm.jsx +++ b/webui/src/Components/LoginForm.jsx @@ -1,24 +1,27 @@ import React, {useState} from "react"; -import {connect} from "react-redux"; import { Button, Card, CardBody, + CardFooter, CardHeader, Col, Form, FormGroup, Input, Label, + Nav, NavItem, NavLink, - CardFooter, - Nav, TabPane } from "reactstrap"; import TabContent from "reactstrap/es/TabContent"; +import useAuth from "../Hooks/auth"; +import {onEnter} from "../Helpers/keys"; + +export default function LoginForm() { + const {login, register} = useAuth(); -function LoginForm(props) { const [tab, setTab] = useState(1); const [loginUser, setLoginUser] = useState(""); const [loginPass, setLoginPass] = useState(""); @@ -34,6 +37,8 @@ function LoginForm(props) { setTab(1)} + onKeyDown={onEnter(() => setTab(1))} + tabIndex={0} > Innlogging @@ -41,6 +46,8 @@ function LoginForm(props) { setTab(2)} + onKeyDown={onEnter(() => setTab(2))} + tabIndex={0} > Registrering @@ -108,24 +115,27 @@ function LoginForm(props) { - {tab === 1 && } + {tab === 1 && } {tab === 2 && ( <> - + {" "} - + )} ); } - -const mapStateToProps = (state) => ({}); - -export default connect(mapStateToProps)(LoginForm); diff --git a/webui/src/Helpers/fetcher.js b/webui/src/Helpers/fetcher.js index 8e9e30b..13c673c 100644 --- a/webui/src/Helpers/fetcher.js +++ b/webui/src/Helpers/fetcher.js @@ -1,10 +1,5 @@ -import store from "../Reducers"; -import {verificationFailedEvent} from "../Reducers/authReducer"; - const PREFIX = "/api"; -const dispatch = store.dispatch; - function formatUrl(url, params = {}) { const urlWithPrefix = PREFIX + url; @@ -22,22 +17,27 @@ function formatUrl(url, params = {}) { /** * @param {Response} res + * @return {Promise>} */ function authCheck(res) { - if (res.ok) { - return Promise.resolve(res.json()); - } else { - if (res.status === 403) { - dispatch(verificationFailedEvent()); - - // Allow the promise to sort of fizzle out - return Promise.resolve(null); + return res.json().then(json => { + if (typeof json.data === "undefined") { + json.data = null; } - return Promise.reject({ status: res.status, text: res.statusText }); - } + if (typeof json.error === "undefined") { + json.error = null; + } + + return Promise.resolve(json); + }) } +/** + * @param {string} url + * @param {object} params + * @returns {Promise} + */ export function fetchGet(url, params = {}) { return fetch(formatUrl(url, params), { method: "GET", @@ -45,7 +45,12 @@ export function fetchGet(url, params = {}) { }).then(authCheck); } -export function fetchPost(url, data) { +/** + * @param {string} url + * @param {object} data + * @returns {Promise} + */ +export function fetchPost(url, data = {}) { return fetch(formatUrl(url), { method: "POST", credentials: "include", diff --git a/webui/src/Helpers/groups.js b/webui/src/Helpers/groups.js new file mode 100644 index 0000000..74f8a55 --- /dev/null +++ b/webui/src/Helpers/groups.js @@ -0,0 +1,92 @@ +import {randId} from "./random"; +import {fetchGet} from "./fetcher"; +import {notNullish} from "./null"; + +const localData = {}; +const callbacks = []; + +export function subscribeToGroup(groupId, callback) { + const callbackId = randId(); + + callbacks.push({callbackId, groupId, callback}); + + if (groupId >= 0) { + if (notNullish(localData[groupId])) { + dispatch(localData[groupId]); + } + + fetchOne(groupId); + } else { + const list = []; + for (let key in localData) { + if (localData.hasOwnProperty(key) && notNullish(localData[key])) { + list.push(localData[key]); + } + } + dispatch(list); + + fetchAll(); + } + + console.log("subscribed"); + console.dir(callbacks); + + return callbackId; +} + +export function unsubscribeFromGroup(callbackId) { + const callback = callbacks.find(c => c !== null && c.callbackId === callbackId); + const index = callbacks.indexOf(callback); + callbacks[index] = null; + + console.log("unsubscribed"); + console.dir(callbacks); +} + +function fetchAll() { + fetchGet("/group/").then(({data, error}) => { + if (error === null) { + handleGroups(data); + } + }); +} + +function fetchOne(id) { + fetchGet(`/group/${id}`).then(({data, error}) => { + if (error === null) { + handleGroup(data); + } + }); +} + +function handleGroups(groups) { + groups.forEach(g => handleGroup(g)); + + for (let key in localData) { + if (localData.hasOwnProperty(key) && !groups.hasOwnProperty(key)) { + delete localData[key]; + } + } + + dispatch(groups); +} + +function handleGroup(group) { + localData[group.id] = group; + + dispatch(group); +} + +function dispatch(data) { + if (Array.isArray(data)) { + callbacks + .filter(c => c !== null) + .filter(c => c.groupId === -1) + .forEach(c => c.callback(data)); + } else { + callbacks + .filter(c => c !== null) + .filter(c => c.groupId === data.id) + .forEach(c => c.callback(data)); + } +} diff --git a/webui/src/Helpers/keys.js b/webui/src/Helpers/keys.js new file mode 100644 index 0000000..ce8e662 --- /dev/null +++ b/webui/src/Helpers/keys.js @@ -0,0 +1,7 @@ +export function onEnter(callback) { + return event => { + if (event.keyCode === 13) { + callback(); + } + } +} \ No newline at end of file diff --git a/webui/src/Helpers/null.js b/webui/src/Helpers/null.js new file mode 100644 index 0000000..edf07dc --- /dev/null +++ b/webui/src/Helpers/null.js @@ -0,0 +1,7 @@ +export function nullish(value) { + return typeof value === "undefined" || value === null; +} + +export function notNullish(value) { + return !nullish(value); +} \ No newline at end of file diff --git a/webui/src/Helpers/random.js b/webui/src/Helpers/random.js new file mode 100644 index 0000000..b4c48ff --- /dev/null +++ b/webui/src/Helpers/random.js @@ -0,0 +1,12 @@ +const POSSIBLE = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"; +const POSSIBLE_LENGHTH = POSSIBLE.length; + +export function randId() { + let text = ""; + + for (let i = 0; i < 16; i++) { + text += POSSIBLE.charAt(Math.floor(Math.random() * POSSIBLE_LENGHTH)); + } + + return text; +} \ No newline at end of file diff --git a/webui/src/Hooks/auth.js b/webui/src/Hooks/auth.js new file mode 100644 index 0000000..d0364ca --- /dev/null +++ b/webui/src/Hooks/auth.js @@ -0,0 +1,65 @@ +import {useGlobal} from "reactn"; +import {fetchGet, fetchPost} from "../Helpers/fetcher"; + +export default function useAuth() { + const [isLoggedIn, setIsLoggedIn] = useGlobal("auth/login"); + const [isChecked, setIsChecked] = useGlobal("auth/checked"); + + function verify() { + setIsChecked(false); + + fetchGet("/user/session").then(({data, error}) => { + setIsLoggedIn(error === null && data.loggedIn); + setIsChecked(true); + }) + } + + function login(username, password) { + setIsChecked(false); + + fetchPost("/user/login", {username, password}).then(({data, error}) => { + setIsChecked(true); + + console.dir({data, error}); + if (error !== null) { + setIsLoggedIn(false); + return; + } + + setIsLoggedIn(true); + }); + } + + function logout() { + setIsChecked(false); + + fetchPost("/user/logout").then(({data, error}) => { + setIsChecked(true); + + if (error !== null) { + setIsLoggedIn(false); + return; + // TODO: Show errors ¿? + } + + setIsLoggedIn(false); + }); + } + + function register(username, password, repeated) { + setIsChecked(false); + + if (password !== repeated) { + alert("Passordene er ikke like"); + return; + } + + fetchPost("/user/register", {username, password}).then(({data, error}) => { + if (error !== null) { + login(username, password); + } + }) + } + + return {isLoggedIn, isChecked, verify, login, logout, register}; +} diff --git a/webui/src/Hooks/lights.js b/webui/src/Hooks/lights.js new file mode 100644 index 0000000..7025938 --- /dev/null +++ b/webui/src/Hooks/lights.js @@ -0,0 +1,20 @@ +import {useEffect, useState} from "react"; +import {subscribeToGroup, unsubscribeFromGroup} from "../Helpers/groups"; + +export default function useGroups(id = -1) { + const [group, setGroup] = useState(null); + + function onChange(group) { + setGroup(group); + } + + useEffect(() => { + const cbId = subscribeToGroup(id, onChange); + + return function cleanup() { + unsubscribeFromGroup(cbId); + }; + }, []); + + return group; +} diff --git a/webui/src/Reducers/authReducer.js b/webui/src/Reducers/authReducer.js deleted file mode 100644 index 78876de..0000000 --- a/webui/src/Reducers/authReducer.js +++ /dev/null @@ -1,36 +0,0 @@ -const initialState = { - isChecked: false, - isLoggedIn: false, -}; - -const VERIFICATION_STARTED = "auth/verification/started"; -const VERIFICATION_SUCCESS = "auth/verification/changed"; -const VERIFICATION_FAILED = "auth/verification/failed"; - -const authReducer = (state = initialState, {type, payload} = {}) => { - switch (type) { - case VERIFICATION_STARTED: - return { - isChecked: false, - isLoggedIn: false, - }; - case VERIFICATION_SUCCESS: - return { - isChecked: true, - isLoggedIn: true, - }; - case VERIFICATION_FAILED: - return { - isChecked: true, - isLoggedIn: false, - }; - default: - return state; - } -}; - -export const verificationStartedEvent = () => ({ type: VERIFICATION_STARTED }); -export const verificationSucceededEvent = () => ({ type: VERIFICATION_SUCCESS }); -export const verificationFailedEvent = () => ({ type: VERIFICATION_FAILED }); - -export default authReducer; diff --git a/webui/src/Reducers/index.js b/webui/src/Reducers/index.js deleted file mode 100644 index 6b711a8..0000000 --- a/webui/src/Reducers/index.js +++ /dev/null @@ -1,10 +0,0 @@ -import {combineReducers, createStore} from "redux"; -import authReducer from "./authReducer"; - -const rootReducer = combineReducers({ - auth: authReducer, -}); - -const store = createStore(rootReducer); - -export default store; \ No newline at end of file diff --git a/webui/src/Reducers/lightReducer.js b/webui/src/Reducers/lightReducer.js deleted file mode 100644 index 693dc9a..0000000 --- a/webui/src/Reducers/lightReducer.js +++ /dev/null @@ -1,21 +0,0 @@ -const initialState = { - lights: [], -}; - -const LIGHTS_RECEIVED = "LightReducer/Received"; - -const lightReducer = (state = initialState, {type, payload} = {}) => { - switch (type) { - case LIGHTS_RECEIVED: - return { - ...state, - lights: payload, - }; - default: - return state; - } -}; - -export const lightsReceivedEvent = (lights) => ({type: LIGHTS_RECEIVED, payload: lights}); - -export default lightReducer; \ No newline at end of file diff --git a/webui/src/index.js b/webui/src/index.js index 0a61806..9e92ffb 100755 --- a/webui/src/index.js +++ b/webui/src/index.js @@ -3,13 +3,5 @@ import ReactDOM from "react-dom"; import App from "./App"; import "bootstrap/dist/css/bootstrap.min.css"; -import {Provider} from "react-redux"; -import store from "./Reducers"; -ReactDOM.render( - ( - - - - ), - document.getElementById("root")); +ReactDOM.render(, document.getElementById("root")); diff --git a/webui/src/setupProxy.js b/webui/src/setupProxy.js new file mode 100644 index 0000000..f27be7a --- /dev/null +++ b/webui/src/setupProxy.js @@ -0,0 +1,15 @@ +const fs = require("fs"); +const proxy = require("http-proxy-middleware"); + +let config = {url: "http://10.12.121.228:8100/"}; +try { + const data = fs.readFileSync("./proxy.json", "utf-8"); + config = JSON.parse(data) +} catch(err) { + console.error("Failed to load proxy.json in project root"); +} + +module.exports = function(app) { + app.use(proxy('/api/ws', { target: config.url, ws: true })); + app.use(proxy('/api', { target: config.url, ws: false })); +};