Pull request #730: + client: Add Hot Module Replacement
Merge in DNS/adguard-home from feature/hmr to master
Squashed commit of the following:
commit 952ed1955c2a7a32446d99489f137f02eb47c99e
Merge: 83484931 de92c852
Author: ArtemBaskal <a.baskal@adguard.com>
Date: Thu Aug 13 11:02:10 2020 +0300
Merge branch 'master' into feature/hmr
commit 8348493105d7d63d8b0836a5c272df2b17a6b142
Author: ArtemBaskal <a.baskal@adguard.com>
Date: Wed Aug 5 15:07:31 2020 +0300
Remove empty prop types, remove Services empty container
commit b2fe4a30b79d91e482318ee5deea8e49c7038f7e
Author: ArtemBaskal <a.baskal@adguard.com>
Date: Wed Aug 5 13:56:35 2020 +0300
Move constants
commit f8be4c18c35193ad77bf5e25f311ad834c1d6870
Author: ArtemBaskal <a.baskal@adguard.com>
Date: Wed Aug 5 13:19:02 2020 +0300
Fix Setup bug, update webpack.dev
commit 1d9cc4ddf8af2c979eb707a7f0fc06744eec186c
Author: ArtemBaskal <a.baskal@adguard.com>
Date: Wed Aug 5 12:10:38 2020 +0300
Review changes
commit a1edb21358def21ed1808b081ffc2f0b6755e3da
Author: ArtemBaskal <a.baskal@adguard.com>
Date: Wed Aug 5 11:46:58 2020 +0300
Remove lazy loading, fix updated components
commit 0aa2cf55f8d4206ac9e2f99fc1b990ed8a9c7825
Author: ArtemBaskal <a.baskal@adguard.com>
Date: Tue Aug 4 20:32:19 2020 +0300
Refactor App component, add lazy loading
commit 3c2ba4772a91ff7b06641dba6c6bf3fdcd2fdf7f
Author: ArtemBaskal <a.baskal@adguard.com>
Date: Tue Aug 4 17:12:41 2020 +0300
Simplify App hot loading boilerplate, setup lazy loading, update Header
commit 8df3221f315372b066f2ac0c9a1687f1677b8415
Author: ArtemBaskal <a.baskal@adguard.com>
Date: Tue Aug 4 15:16:06 2020 +0300
+ client: Add Hot Module Replacement
|
@ -11,6 +11,7 @@ module.exports = (api) => {
|
||||||
'@babel/plugin-proposal-object-rest-spread',
|
'@babel/plugin-proposal-object-rest-spread',
|
||||||
'@babel/plugin-proposal-nullish-coalescing-operator',
|
'@babel/plugin-proposal-nullish-coalescing-operator',
|
||||||
'@babel/plugin-proposal-optional-chaining',
|
'@babel/plugin-proposal-optional-chaining',
|
||||||
|
'react-hot-loader/babel',
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
|
@ -0,0 +1,11 @@
|
||||||
|
const BUILD_ENVS = {
|
||||||
|
dev: 'development',
|
||||||
|
prod: 'production',
|
||||||
|
};
|
||||||
|
|
||||||
|
const BASE_URL = '/control';
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
BUILD_ENVS,
|
||||||
|
BASE_URL,
|
||||||
|
};
|
|
@ -1356,6 +1356,17 @@
|
||||||
"resolved": "https://registry.npmjs.org/@emotion/weak-memoize/-/weak-memoize-0.2.5.tgz",
|
"resolved": "https://registry.npmjs.org/@emotion/weak-memoize/-/weak-memoize-0.2.5.tgz",
|
||||||
"integrity": "sha512-6U71C2Wp7r5XtFtQzYrW5iKFT67OixrSxjI4MptCHzdSVlgabczzqLe0ZSgnub/5Kp4hSbpDB1tMytZY9pwxxA=="
|
"integrity": "sha512-6U71C2Wp7r5XtFtQzYrW5iKFT67OixrSxjI4MptCHzdSVlgabczzqLe0ZSgnub/5Kp4hSbpDB1tMytZY9pwxxA=="
|
||||||
},
|
},
|
||||||
|
"@hot-loader/react-dom": {
|
||||||
|
"version": "16.13.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@hot-loader/react-dom/-/react-dom-16.13.0.tgz",
|
||||||
|
"integrity": "sha512-lJZrmkucz2MrQJTQtJobx5MICXcfQvKihszqv655p557HPi0hMOWxrNpiHv3DWD8ugNWjtWcVWqRnFvwsHq1mQ==",
|
||||||
|
"requires": {
|
||||||
|
"loose-envify": "^1.1.0",
|
||||||
|
"object-assign": "^4.1.1",
|
||||||
|
"prop-types": "^15.6.2",
|
||||||
|
"scheduler": "^0.19.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"@istanbuljs/load-nyc-config": {
|
"@istanbuljs/load-nyc-config": {
|
||||||
"version": "1.1.0",
|
"version": "1.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz",
|
||||||
|
@ -2257,6 +2268,12 @@
|
||||||
"@types/istanbul-lib-report": "*"
|
"@types/istanbul-lib-report": "*"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"@types/json-schema": {
|
||||||
|
"version": "7.0.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.5.tgz",
|
||||||
|
"integrity": "sha512-7+2BITlgjgDhH0vvwZU/HZJVyk+2XUlvxXe8dFMedNX/aMkaOq++rMAFXc0tM7ij15QaWlbdQASBR9dihi+bDQ==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
"@types/minimatch": {
|
"@types/minimatch": {
|
||||||
"version": "3.0.3",
|
"version": "3.0.3",
|
||||||
"resolved": "https://registry.npmjs.org/@types/minimatch/-/minimatch-3.0.3.tgz",
|
"resolved": "https://registry.npmjs.org/@types/minimatch/-/minimatch-3.0.3.tgz",
|
||||||
|
@ -2728,7 +2745,6 @@
|
||||||
"version": "1.0.10",
|
"version": "1.0.10",
|
||||||
"resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz",
|
"resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz",
|
||||||
"integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==",
|
"integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==",
|
||||||
"dev": true,
|
|
||||||
"requires": {
|
"requires": {
|
||||||
"sprintf-js": "~1.0.2"
|
"sprintf-js": "~1.0.2"
|
||||||
}
|
}
|
||||||
|
@ -5108,6 +5124,12 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"dom-walk": {
|
||||||
|
"version": "0.1.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/dom-walk/-/dom-walk-0.1.2.tgz",
|
||||||
|
"integrity": "sha512-6QvTW9mrGeIegrFXdtQi9pk7O/nSK6lSdXW2eqUspN5LWD7UTji2Fqw5V2YLjBpHEoU9Xl/eUWNpDeZvoyOv2w==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
"domain-browser": {
|
"domain-browser": {
|
||||||
"version": "1.2.0",
|
"version": "1.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/domain-browser/-/domain-browser-1.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/domain-browser/-/domain-browser-1.2.0.tgz",
|
||||||
|
@ -5201,9 +5223,9 @@
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"elliptic": {
|
"elliptic": {
|
||||||
"version": "6.5.2",
|
"version": "6.5.3",
|
||||||
"resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.5.2.tgz",
|
"resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.5.3.tgz",
|
||||||
"integrity": "sha512-f4x70okzZbIQl/NSRLkI/+tteV/9WqL98zx+SQ69KbXxmVrmjwsNUPn/gYJJ0sHvEak24cZgHIPegRePAtA/xw==",
|
"integrity": "sha512-IMqzv5wNQf+E6aHeIqATs0tOLeOTwj1QKbRcS3jBbYkl5oLAserA8yJTT7/VyHUYG91PRmPyeQDObKLPpeS4dw==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"bn.js": "^4.4.0",
|
"bn.js": "^4.4.0",
|
||||||
|
@ -5216,9 +5238,9 @@
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"bn.js": {
|
"bn.js": {
|
||||||
"version": "4.11.8",
|
"version": "4.11.9",
|
||||||
"resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.8.tgz",
|
"resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.9.tgz",
|
||||||
"integrity": "sha512-ItfYfPLkWHUjckQCk8xC+LwxgK8NYcXywGigJgSwOP8Y2iyWT4f2vsZnoOXTTbo+o5yXmIUJ4gn5538SO5S3gA==",
|
"integrity": "sha512-E6QoYqCKZfgatHTdHzs1RRKP7ip4vvm+EyRUeE2RF0NblwVvb0p6jSVeNTOFxPn26QXN2o6SMfNxKp6kU8zQaw==",
|
||||||
"dev": true
|
"dev": true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -5887,8 +5909,7 @@
|
||||||
"esprima": {
|
"esprima": {
|
||||||
"version": "4.0.1",
|
"version": "4.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz",
|
||||||
"integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==",
|
"integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A=="
|
||||||
"dev": true
|
|
||||||
},
|
},
|
||||||
"esquery": {
|
"esquery": {
|
||||||
"version": "1.3.1",
|
"version": "1.3.1",
|
||||||
|
@ -6844,6 +6865,16 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"global": {
|
||||||
|
"version": "4.4.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/global/-/global-4.4.0.tgz",
|
||||||
|
"integrity": "sha512-wv/LAoHdRE3BeTGz53FAamhGlPLhlssK45usmGFThIi4XqnBmjKQ16u+RNbP7WvigRZDxUsM0J3gcQ5yicaL0w==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"min-document": "^2.19.0",
|
||||||
|
"process": "^0.11.10"
|
||||||
|
}
|
||||||
|
},
|
||||||
"global-modules": {
|
"global-modules": {
|
||||||
"version": "2.0.0",
|
"version": "2.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/global-modules/-/global-modules-2.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/global-modules/-/global-modules-2.0.0.tgz",
|
||||||
|
@ -7357,18 +7388,6 @@
|
||||||
"requires-port": "^1.0.0"
|
"requires-port": "^1.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"http-proxy-middleware": {
|
|
||||||
"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.17.0",
|
|
||||||
"is-glob": "^4.0.0",
|
|
||||||
"lodash": "^4.17.11",
|
|
||||||
"micromatch": "^3.1.10"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"http-signature": {
|
"http-signature": {
|
||||||
"version": "1.2.0",
|
"version": "1.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz",
|
||||||
|
@ -9898,10 +9917,9 @@
|
||||||
"integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ=="
|
"integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ=="
|
||||||
},
|
},
|
||||||
"js-yaml": {
|
"js-yaml": {
|
||||||
"version": "3.13.1",
|
"version": "3.14.0",
|
||||||
"resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.13.1.tgz",
|
"resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.0.tgz",
|
||||||
"integrity": "sha512-YfbcO7jXDdyj0DGxYVSlSeQNHbD7XPWvrVWeVUujrQEoZzWJIRrCPoyk6kL6IAjAG2IolMK4T0hNUe0HOUs5Jw==",
|
"integrity": "sha512-/4IbIeHcD9VMHFqDR/gQ7EdZdLimOvW2DdcxFjdyyZ9NsbS+ccrXqVWDtab/lRl5AlUqmpBx8EhPaWR+OtY17A==",
|
||||||
"dev": true,
|
|
||||||
"requires": {
|
"requires": {
|
||||||
"argparse": "^1.0.7",
|
"argparse": "^1.0.7",
|
||||||
"esprima": "^4.0.0"
|
"esprima": "^4.0.0"
|
||||||
|
@ -10628,6 +10646,15 @@
|
||||||
"integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==",
|
"integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"min-document": {
|
||||||
|
"version": "2.19.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/min-document/-/min-document-2.19.0.tgz",
|
||||||
|
"integrity": "sha1-e9KC4/WELtKVu3SM3Z8f+iyCRoU=",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"dom-walk": "^0.1.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"min-indent": {
|
"min-indent": {
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/min-indent/-/min-indent-1.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/min-indent/-/min-indent-1.0.0.tgz",
|
||||||
|
@ -12287,6 +12314,39 @@
|
||||||
"scheduler": "^0.19.1"
|
"scheduler": "^0.19.1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"react-hot-loader": {
|
||||||
|
"version": "4.12.21",
|
||||||
|
"resolved": "https://registry.npmjs.org/react-hot-loader/-/react-hot-loader-4.12.21.tgz",
|
||||||
|
"integrity": "sha512-Ynxa6ROfWUeKWsTHxsrL2KMzujxJVPjs385lmB2t5cHUxdoRPGind9F00tOkdc1l5WBleOF4XEAMILY1KPIIDA==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"fast-levenshtein": "^2.0.6",
|
||||||
|
"global": "^4.3.0",
|
||||||
|
"hoist-non-react-statics": "^3.3.0",
|
||||||
|
"loader-utils": "^1.1.0",
|
||||||
|
"prop-types": "^15.6.1",
|
||||||
|
"react-lifecycles-compat": "^3.0.4",
|
||||||
|
"shallowequal": "^1.1.0",
|
||||||
|
"source-map": "^0.7.3"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"hoist-non-react-statics": {
|
||||||
|
"version": "3.3.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz",
|
||||||
|
"integrity": "sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"react-is": "^16.7.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"source-map": {
|
||||||
|
"version": "0.7.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.3.tgz",
|
||||||
|
"integrity": "sha512-CkCj6giN3S+n9qrYiBTX5gystlENnRW5jZeNLHpe6aue+SrHcG5VYwujhW9s4dY31mEGsxBDrHR6oI69fTXsaQ==",
|
||||||
|
"dev": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"react-i18next": {
|
"react-i18next": {
|
||||||
"version": "11.4.0",
|
"version": "11.4.0",
|
||||||
"resolved": "https://registry.npmjs.org/react-i18next/-/react-i18next-11.4.0.tgz",
|
"resolved": "https://registry.npmjs.org/react-i18next/-/react-i18next-11.4.0.tgz",
|
||||||
|
@ -13328,6 +13388,12 @@
|
||||||
"safe-buffer": "^5.0.1"
|
"safe-buffer": "^5.0.1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"shallowequal": {
|
||||||
|
"version": "1.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/shallowequal/-/shallowequal-1.1.0.tgz",
|
||||||
|
"integrity": "sha512-y0m1JoUZSlPAjXVtPPW70aZWfIL/dSP7AFkRnniLCrK/8MDKog3TySTBmckD+RObVxH0v4Tox67+F14PdED2oQ==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
"shebang-command": {
|
"shebang-command": {
|
||||||
"version": "2.0.0",
|
"version": "2.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz",
|
||||||
|
@ -13491,7 +13557,8 @@
|
||||||
},
|
},
|
||||||
"kind-of": {
|
"kind-of": {
|
||||||
"version": "6.0.2",
|
"version": "6.0.2",
|
||||||
"resolved": "",
|
"resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz",
|
||||||
|
"integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==",
|
||||||
"dev": true
|
"dev": true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -13751,8 +13818,7 @@
|
||||||
"sprintf-js": {
|
"sprintf-js": {
|
||||||
"version": "1.0.3",
|
"version": "1.0.3",
|
||||||
"resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz",
|
"resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz",
|
||||||
"integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=",
|
"integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw="
|
||||||
"dev": true
|
|
||||||
},
|
},
|
||||||
"sshpk": {
|
"sshpk": {
|
||||||
"version": "1.16.1",
|
"version": "1.16.1",
|
||||||
|
@ -14079,12 +14145,13 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"schema-utils": {
|
"schema-utils": {
|
||||||
"version": "2.6.6",
|
"version": "2.7.0",
|
||||||
"resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-2.6.6.tgz",
|
"resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-2.7.0.tgz",
|
||||||
"integrity": "sha512-wHutF/WPSbIi9x6ctjGGk2Hvl0VOz5l3EKEuKbjPlB30mKZUzb9A5k9yEXRX3pwyqVLPvpfZZEllaFq/M718hA==",
|
"integrity": "sha512-0ilKFI6QQF5nxDZLFn2dMjvc4hjg/Wkg7rHd3jK6/A4a1Hl9VFdQWvgB1UMGoU94pad1P/8N7fMcEnLnSiju8A==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"ajv": "^6.12.0",
|
"@types/json-schema": "^7.0.4",
|
||||||
|
"ajv": "^6.12.2",
|
||||||
"ajv-keywords": "^3.4.1"
|
"ajv-keywords": "^3.4.1"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -15930,6 +15997,18 @@
|
||||||
"ms": "^2.1.1"
|
"ms": "^2.1.1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"http-proxy-middleware": {
|
||||||
|
"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.17.0",
|
||||||
|
"is-glob": "^4.0.0",
|
||||||
|
"lodash": "^4.17.11",
|
||||||
|
"micromatch": "^3.1.10"
|
||||||
|
}
|
||||||
|
},
|
||||||
"ms": {
|
"ms": {
|
||||||
"version": "2.1.2",
|
"version": "2.1.2",
|
||||||
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
|
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
|
||||||
|
|
|
@ -4,14 +4,16 @@
|
||||||
"private": true,
|
"private": true,
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"build-dev": "cross-env BUILD_ENV=dev webpack --config webpack.dev.js",
|
"build-dev": "cross-env BUILD_ENV=dev webpack --config webpack.dev.js",
|
||||||
"watch": "cross-env BUILD_ENV=dev webpack --config webpack.dev.js --watch",
|
|
||||||
"build-prod": "cross-env BUILD_ENV=prod webpack --config webpack.prod.js",
|
"build-prod": "cross-env BUILD_ENV=prod webpack --config webpack.prod.js",
|
||||||
|
"watch": "cross-env BUILD_ENV=dev webpack --config webpack.dev.js --watch",
|
||||||
|
"watch:hot": "cross-env BUILD_ENV=dev webpack-dev-server --config webpack.dev.js",
|
||||||
"lint": "eslint src",
|
"lint": "eslint src",
|
||||||
"lint:fix": "eslint src --fix",
|
"lint:fix": "eslint src --fix",
|
||||||
"test": "jest",
|
"test": "jest",
|
||||||
"test:watch": "jest --watch"
|
"test:watch": "jest --watch"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@hot-loader/react-dom": "^16.13.0",
|
||||||
"@nivo/line": "^0.49.1",
|
"@nivo/line": "^0.49.1",
|
||||||
"axios": "^0.19.2",
|
"axios": "^0.19.2",
|
||||||
"classnames": "^2.2.6",
|
"classnames": "^2.2.6",
|
||||||
|
@ -19,6 +21,7 @@
|
||||||
"i18next": "^19.4.4",
|
"i18next": "^19.4.4",
|
||||||
"i18next-browser-languagedetector": "^4.2.0",
|
"i18next-browser-languagedetector": "^4.2.0",
|
||||||
"ipaddr.js": "^1.9.1",
|
"ipaddr.js": "^1.9.1",
|
||||||
|
"js-yaml": "^3.14.0",
|
||||||
"lodash": "^4.17.19",
|
"lodash": "^4.17.19",
|
||||||
"nanoid": "^3.1.9",
|
"nanoid": "^3.1.9",
|
||||||
"prop-types": "^15.7.2",
|
"prop-types": "^15.7.2",
|
||||||
|
@ -73,6 +76,7 @@
|
||||||
"path": "^0.12.7",
|
"path": "^0.12.7",
|
||||||
"postcss-flexbugs-fixes": "4.2.1",
|
"postcss-flexbugs-fixes": "4.2.1",
|
||||||
"postcss-loader": "^3.0.0",
|
"postcss-loader": "^3.0.0",
|
||||||
|
"react-hot-loader": "^4.12.21",
|
||||||
"style-loader": "^1.2.1",
|
"style-loader": "^1.2.1",
|
||||||
"stylelint": "^13.5.0",
|
"stylelint": "^13.5.0",
|
||||||
"stylelint-webpack-plugin": "2.0.0",
|
"stylelint-webpack-plugin": "2.0.0",
|
||||||
|
|
|
@ -148,7 +148,7 @@ const checkStatus = async (handleRequestSuccess, handleRequestError, attempts =
|
||||||
const rmTimeout = (t) => t && clearTimeout(t);
|
const rmTimeout = (t) => t && clearTimeout(t);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const response = await axios.get('control/status');
|
const response = await axios.get(`${apiClient.baseUrl}/status`);
|
||||||
rmTimeout(timeout);
|
rmTimeout(timeout);
|
||||||
if (response?.status === 200) {
|
if (response?.status === 200) {
|
||||||
handleRequestSuccess(response);
|
handleRequestSuccess(response);
|
||||||
|
|
|
@ -2,9 +2,10 @@ import axios from 'axios';
|
||||||
|
|
||||||
import { getPathWithQueryString } from '../helpers/helpers';
|
import { getPathWithQueryString } from '../helpers/helpers';
|
||||||
import { R_PATH_LAST_PART } from '../helpers/constants';
|
import { R_PATH_LAST_PART } from '../helpers/constants';
|
||||||
|
import { BASE_URL } from '../../constants';
|
||||||
|
|
||||||
class Api {
|
class Api {
|
||||||
baseUrl = 'control';
|
baseUrl = BASE_URL;
|
||||||
|
|
||||||
async makeRequest(path, method = 'POST', config) {
|
async makeRequest(path, method = 'POST', config) {
|
||||||
try {
|
try {
|
||||||
|
@ -26,18 +27,30 @@ class Api {
|
||||||
|
|
||||||
throw new Error(`${errorPath} | ${error.response.data} | ${error.response.status}`);
|
throw new Error(`${errorPath} | ${error.response.data} | ${error.response.status}`);
|
||||||
}
|
}
|
||||||
throw new Error(`${errorPath} | ${error.message ? error.message : error}`);
|
throw new Error(`${errorPath} | ${error.message || error}`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Global methods
|
// Global methods
|
||||||
GLOBAL_STATUS = { path: 'status', method: 'GET' };
|
GLOBAL_STATUS = {
|
||||||
|
path: 'status',
|
||||||
|
method: 'GET',
|
||||||
|
};
|
||||||
|
|
||||||
GLOBAL_TEST_UPSTREAM_DNS = { path: 'test_upstream_dns', method: 'POST' };
|
GLOBAL_TEST_UPSTREAM_DNS = {
|
||||||
|
path: 'test_upstream_dns',
|
||||||
|
method: 'POST',
|
||||||
|
};
|
||||||
|
|
||||||
GLOBAL_VERSION = { path: 'version.json', method: 'POST' };
|
GLOBAL_VERSION = {
|
||||||
|
path: 'version.json',
|
||||||
|
method: 'POST',
|
||||||
|
};
|
||||||
|
|
||||||
GLOBAL_UPDATE = { path: 'update', method: 'POST' };
|
GLOBAL_UPDATE = {
|
||||||
|
path: 'update',
|
||||||
|
method: 'POST',
|
||||||
|
};
|
||||||
|
|
||||||
getGlobalStatus() {
|
getGlobalStatus() {
|
||||||
const { path, method } = this.GLOBAL_STATUS;
|
const { path, method } = this.GLOBAL_STATUS;
|
||||||
|
@ -68,21 +81,45 @@ class Api {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Filtering
|
// Filtering
|
||||||
FILTERING_STATUS = { path: 'filtering/status', method: 'GET' };
|
FILTERING_STATUS = {
|
||||||
|
path: 'filtering/status',
|
||||||
|
method: 'GET',
|
||||||
|
};
|
||||||
|
|
||||||
FILTERING_ADD_FILTER = { path: 'filtering/add_url', method: 'POST' };
|
FILTERING_ADD_FILTER = {
|
||||||
|
path: 'filtering/add_url',
|
||||||
|
method: 'POST',
|
||||||
|
};
|
||||||
|
|
||||||
FILTERING_REMOVE_FILTER = { path: 'filtering/remove_url', method: 'POST' };
|
FILTERING_REMOVE_FILTER = {
|
||||||
|
path: 'filtering/remove_url',
|
||||||
|
method: 'POST',
|
||||||
|
};
|
||||||
|
|
||||||
FILTERING_SET_RULES = { path: 'filtering/set_rules', method: 'POST' };
|
FILTERING_SET_RULES = {
|
||||||
|
path: 'filtering/set_rules',
|
||||||
|
method: 'POST',
|
||||||
|
};
|
||||||
|
|
||||||
FILTERING_REFRESH = { path: 'filtering/refresh', method: 'POST' };
|
FILTERING_REFRESH = {
|
||||||
|
path: 'filtering/refresh',
|
||||||
|
method: 'POST',
|
||||||
|
};
|
||||||
|
|
||||||
FILTERING_SET_URL = { path: 'filtering/set_url', method: 'POST' };
|
FILTERING_SET_URL = {
|
||||||
|
path: 'filtering/set_url',
|
||||||
|
method: 'POST',
|
||||||
|
};
|
||||||
|
|
||||||
FILTERING_CONFIG = { path: 'filtering/config', method: 'POST' };
|
FILTERING_CONFIG = {
|
||||||
|
path: 'filtering/config',
|
||||||
|
method: 'POST',
|
||||||
|
};
|
||||||
|
|
||||||
FILTERING_CHECK_HOST = { path: 'filtering/check_host', method: 'GET' };
|
FILTERING_CHECK_HOST = {
|
||||||
|
path: 'filtering/check_host',
|
||||||
|
method: 'GET',
|
||||||
|
};
|
||||||
|
|
||||||
getFilteringStatus() {
|
getFilteringStatus() {
|
||||||
const { path, method } = this.FILTERING_STATUS;
|
const { path, method } = this.FILTERING_STATUS;
|
||||||
|
@ -153,11 +190,20 @@ class Api {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Parental
|
// Parental
|
||||||
PARENTAL_STATUS = { path: 'parental/status', method: 'GET' };
|
PARENTAL_STATUS = {
|
||||||
|
path: 'parental/status',
|
||||||
|
method: 'GET',
|
||||||
|
};
|
||||||
|
|
||||||
PARENTAL_ENABLE = { path: 'parental/enable', method: 'POST' };
|
PARENTAL_ENABLE = {
|
||||||
|
path: 'parental/enable',
|
||||||
|
method: 'POST',
|
||||||
|
};
|
||||||
|
|
||||||
PARENTAL_DISABLE = { path: 'parental/disable', method: 'POST' };
|
PARENTAL_DISABLE = {
|
||||||
|
path: 'parental/disable',
|
||||||
|
method: 'POST',
|
||||||
|
};
|
||||||
|
|
||||||
getParentalStatus() {
|
getParentalStatus() {
|
||||||
const { path, method } = this.PARENTAL_STATUS;
|
const { path, method } = this.PARENTAL_STATUS;
|
||||||
|
@ -180,11 +226,20 @@ class Api {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Safebrowsing
|
// Safebrowsing
|
||||||
SAFEBROWSING_STATUS = { path: 'safebrowsing/status', method: 'GET' };
|
SAFEBROWSING_STATUS = {
|
||||||
|
path: 'safebrowsing/status',
|
||||||
|
method: 'GET',
|
||||||
|
};
|
||||||
|
|
||||||
SAFEBROWSING_ENABLE = { path: 'safebrowsing/enable', method: 'POST' };
|
SAFEBROWSING_ENABLE = {
|
||||||
|
path: 'safebrowsing/enable',
|
||||||
|
method: 'POST',
|
||||||
|
};
|
||||||
|
|
||||||
SAFEBROWSING_DISABLE = { path: 'safebrowsing/disable', method: 'POST' };
|
SAFEBROWSING_DISABLE = {
|
||||||
|
path: 'safebrowsing/disable',
|
||||||
|
method: 'POST',
|
||||||
|
};
|
||||||
|
|
||||||
getSafebrowsingStatus() {
|
getSafebrowsingStatus() {
|
||||||
const { path, method } = this.SAFEBROWSING_STATUS;
|
const { path, method } = this.SAFEBROWSING_STATUS;
|
||||||
|
@ -202,11 +257,20 @@ class Api {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Safesearch
|
// Safesearch
|
||||||
SAFESEARCH_STATUS = { path: 'safesearch/status', method: 'GET' };
|
SAFESEARCH_STATUS = {
|
||||||
|
path: 'safesearch/status',
|
||||||
|
method: 'GET',
|
||||||
|
};
|
||||||
|
|
||||||
SAFESEARCH_ENABLE = { path: 'safesearch/enable', method: 'POST' };
|
SAFESEARCH_ENABLE = {
|
||||||
|
path: 'safesearch/enable',
|
||||||
|
method: 'POST',
|
||||||
|
};
|
||||||
|
|
||||||
SAFESEARCH_DISABLE = { path: 'safesearch/disable', method: 'POST' };
|
SAFESEARCH_DISABLE = {
|
||||||
|
path: 'safesearch/disable',
|
||||||
|
method: 'POST',
|
||||||
|
};
|
||||||
|
|
||||||
getSafesearchStatus() {
|
getSafesearchStatus() {
|
||||||
const { path, method } = this.SAFESEARCH_STATUS;
|
const { path, method } = this.SAFESEARCH_STATUS;
|
||||||
|
@ -224,9 +288,15 @@ class Api {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Language
|
// Language
|
||||||
CURRENT_LANGUAGE = { path: 'i18n/current_language', method: 'GET' };
|
CURRENT_LANGUAGE = {
|
||||||
|
path: 'i18n/current_language',
|
||||||
|
method: 'GET',
|
||||||
|
};
|
||||||
|
|
||||||
CHANGE_LANGUAGE = { path: 'i18n/change_language', method: 'POST' };
|
CHANGE_LANGUAGE = {
|
||||||
|
path: 'i18n/change_language',
|
||||||
|
method: 'POST',
|
||||||
|
};
|
||||||
|
|
||||||
getCurrentLanguage() {
|
getCurrentLanguage() {
|
||||||
const { path, method } = this.CURRENT_LANGUAGE;
|
const { path, method } = this.CURRENT_LANGUAGE;
|
||||||
|
@ -243,19 +313,40 @@ class Api {
|
||||||
}
|
}
|
||||||
|
|
||||||
// DHCP
|
// DHCP
|
||||||
DHCP_STATUS = { path: 'dhcp/status', method: 'GET' };
|
DHCP_STATUS = {
|
||||||
|
path: 'dhcp/status',
|
||||||
|
method: 'GET',
|
||||||
|
};
|
||||||
|
|
||||||
DHCP_SET_CONFIG = { path: 'dhcp/set_config', method: 'POST' };
|
DHCP_SET_CONFIG = {
|
||||||
|
path: 'dhcp/set_config',
|
||||||
|
method: 'POST',
|
||||||
|
};
|
||||||
|
|
||||||
DHCP_FIND_ACTIVE = { path: 'dhcp/find_active_dhcp', method: 'POST' };
|
DHCP_FIND_ACTIVE = {
|
||||||
|
path: 'dhcp/find_active_dhcp',
|
||||||
|
method: 'POST',
|
||||||
|
};
|
||||||
|
|
||||||
DHCP_INTERFACES = { path: 'dhcp/interfaces', method: 'GET' };
|
DHCP_INTERFACES = {
|
||||||
|
path: 'dhcp/interfaces',
|
||||||
|
method: 'GET',
|
||||||
|
};
|
||||||
|
|
||||||
DHCP_ADD_STATIC_LEASE = { path: 'dhcp/add_static_lease', method: 'POST' };
|
DHCP_ADD_STATIC_LEASE = {
|
||||||
|
path: 'dhcp/add_static_lease',
|
||||||
|
method: 'POST',
|
||||||
|
};
|
||||||
|
|
||||||
DHCP_REMOVE_STATIC_LEASE = { path: 'dhcp/remove_static_lease', method: 'POST' };
|
DHCP_REMOVE_STATIC_LEASE = {
|
||||||
|
path: 'dhcp/remove_static_lease',
|
||||||
|
method: 'POST',
|
||||||
|
};
|
||||||
|
|
||||||
DHCP_RESET = { path: 'dhcp/reset', method: 'POST' };
|
DHCP_RESET = {
|
||||||
|
path: 'dhcp/reset',
|
||||||
|
method: 'POST',
|
||||||
|
};
|
||||||
|
|
||||||
getDhcpStatus() {
|
getDhcpStatus() {
|
||||||
const { path, method } = this.DHCP_STATUS;
|
const { path, method } = this.DHCP_STATUS;
|
||||||
|
@ -309,11 +400,20 @@ class Api {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Installation
|
// Installation
|
||||||
INSTALL_GET_ADDRESSES = { path: 'install/get_addresses', method: 'GET' };
|
INSTALL_GET_ADDRESSES = {
|
||||||
|
path: 'install/get_addresses',
|
||||||
|
method: 'GET',
|
||||||
|
};
|
||||||
|
|
||||||
INSTALL_CONFIGURE = { path: 'install/configure', method: 'POST' };
|
INSTALL_CONFIGURE = {
|
||||||
|
path: 'install/configure',
|
||||||
|
method: 'POST',
|
||||||
|
};
|
||||||
|
|
||||||
INSTALL_CHECK_CONFIG = { path: 'install/check_config', method: 'POST' };
|
INSTALL_CHECK_CONFIG = {
|
||||||
|
path: 'install/check_config',
|
||||||
|
method: 'POST',
|
||||||
|
};
|
||||||
|
|
||||||
getDefaultAddresses() {
|
getDefaultAddresses() {
|
||||||
const { path, method } = this.INSTALL_GET_ADDRESSES;
|
const { path, method } = this.INSTALL_GET_ADDRESSES;
|
||||||
|
@ -339,11 +439,20 @@ class Api {
|
||||||
}
|
}
|
||||||
|
|
||||||
// DNS-over-HTTPS and DNS-over-TLS
|
// DNS-over-HTTPS and DNS-over-TLS
|
||||||
TLS_STATUS = { path: 'tls/status', method: 'GET' };
|
TLS_STATUS = {
|
||||||
|
path: 'tls/status',
|
||||||
|
method: 'GET',
|
||||||
|
};
|
||||||
|
|
||||||
TLS_CONFIG = { path: 'tls/configure', method: 'POST' };
|
TLS_CONFIG = {
|
||||||
|
path: 'tls/configure',
|
||||||
|
method: 'POST',
|
||||||
|
};
|
||||||
|
|
||||||
TLS_VALIDATE = { path: 'tls/validate', method: 'POST' };
|
TLS_VALIDATE = {
|
||||||
|
path: 'tls/validate',
|
||||||
|
method: 'POST',
|
||||||
|
};
|
||||||
|
|
||||||
getTlsStatus() {
|
getTlsStatus() {
|
||||||
const { path, method } = this.TLS_STATUS;
|
const { path, method } = this.TLS_STATUS;
|
||||||
|
@ -369,15 +478,30 @@ class Api {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Per-client settings
|
// Per-client settings
|
||||||
GET_CLIENTS = { path: 'clients', method: 'GET' };
|
GET_CLIENTS = {
|
||||||
|
path: 'clients',
|
||||||
|
method: 'GET',
|
||||||
|
};
|
||||||
|
|
||||||
FIND_CLIENTS = { path: 'clients/find', method: 'GET' };
|
FIND_CLIENTS = {
|
||||||
|
path: 'clients/find',
|
||||||
|
method: 'GET',
|
||||||
|
};
|
||||||
|
|
||||||
ADD_CLIENT = { path: 'clients/add', method: 'POST' };
|
ADD_CLIENT = {
|
||||||
|
path: 'clients/add',
|
||||||
|
method: 'POST',
|
||||||
|
};
|
||||||
|
|
||||||
DELETE_CLIENT = { path: 'clients/delete', method: 'POST' };
|
DELETE_CLIENT = {
|
||||||
|
path: 'clients/delete',
|
||||||
|
method: 'POST',
|
||||||
|
};
|
||||||
|
|
||||||
UPDATE_CLIENT = { path: 'clients/update', method: 'POST' };
|
UPDATE_CLIENT = {
|
||||||
|
path: 'clients/update',
|
||||||
|
method: 'POST',
|
||||||
|
};
|
||||||
|
|
||||||
getClients() {
|
getClients() {
|
||||||
const { path, method } = this.GET_CLIENTS;
|
const { path, method } = this.GET_CLIENTS;
|
||||||
|
@ -418,9 +542,15 @@ class Api {
|
||||||
}
|
}
|
||||||
|
|
||||||
// DNS access settings
|
// DNS access settings
|
||||||
ACCESS_LIST = { path: 'access/list', method: 'GET' };
|
ACCESS_LIST = {
|
||||||
|
path: 'access/list',
|
||||||
|
method: 'GET',
|
||||||
|
};
|
||||||
|
|
||||||
ACCESS_SET = { path: 'access/set', method: 'POST' };
|
ACCESS_SET = {
|
||||||
|
path: 'access/set',
|
||||||
|
method: 'POST',
|
||||||
|
};
|
||||||
|
|
||||||
getAccessList() {
|
getAccessList() {
|
||||||
const { path, method } = this.ACCESS_LIST;
|
const { path, method } = this.ACCESS_LIST;
|
||||||
|
@ -437,11 +567,20 @@ class Api {
|
||||||
}
|
}
|
||||||
|
|
||||||
// DNS rewrites
|
// DNS rewrites
|
||||||
REWRITES_LIST = { path: 'rewrite/list', method: 'GET' };
|
REWRITES_LIST = {
|
||||||
|
path: 'rewrite/list',
|
||||||
|
method: 'GET',
|
||||||
|
};
|
||||||
|
|
||||||
REWRITE_ADD = { path: 'rewrite/add', method: 'POST' };
|
REWRITE_ADD = {
|
||||||
|
path: 'rewrite/add',
|
||||||
|
method: 'POST',
|
||||||
|
};
|
||||||
|
|
||||||
REWRITE_DELETE = { path: 'rewrite/delete', method: 'POST' };
|
REWRITE_DELETE = {
|
||||||
|
path: 'rewrite/delete',
|
||||||
|
method: 'POST',
|
||||||
|
};
|
||||||
|
|
||||||
getRewritesList() {
|
getRewritesList() {
|
||||||
const { path, method } = this.REWRITES_LIST;
|
const { path, method } = this.REWRITES_LIST;
|
||||||
|
@ -467,9 +606,15 @@ class Api {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Blocked services
|
// Blocked services
|
||||||
BLOCKED_SERVICES_LIST = { path: 'blocked_services/list', method: 'GET' };
|
BLOCKED_SERVICES_LIST = {
|
||||||
|
path: 'blocked_services/list',
|
||||||
|
method: 'GET',
|
||||||
|
};
|
||||||
|
|
||||||
BLOCKED_SERVICES_SET = { path: 'blocked_services/set', method: 'POST' };
|
BLOCKED_SERVICES_SET = {
|
||||||
|
path: 'blocked_services/set',
|
||||||
|
method: 'POST',
|
||||||
|
};
|
||||||
|
|
||||||
getBlockedServices() {
|
getBlockedServices() {
|
||||||
const { path, method } = this.BLOCKED_SERVICES_LIST;
|
const { path, method } = this.BLOCKED_SERVICES_LIST;
|
||||||
|
@ -486,13 +631,25 @@ class Api {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Settings for statistics
|
// Settings for statistics
|
||||||
GET_STATS = { path: 'stats', method: 'GET' };
|
GET_STATS = {
|
||||||
|
path: 'stats',
|
||||||
|
method: 'GET',
|
||||||
|
};
|
||||||
|
|
||||||
STATS_INFO = { path: 'stats_info', method: 'GET' };
|
STATS_INFO = {
|
||||||
|
path: 'stats_info',
|
||||||
|
method: 'GET',
|
||||||
|
};
|
||||||
|
|
||||||
STATS_CONFIG = { path: 'stats_config', method: 'POST' };
|
STATS_CONFIG = {
|
||||||
|
path: 'stats_config',
|
||||||
|
method: 'POST',
|
||||||
|
};
|
||||||
|
|
||||||
STATS_RESET = { path: 'stats_reset', method: 'POST' };
|
STATS_RESET = {
|
||||||
|
path: 'stats_reset',
|
||||||
|
method: 'POST',
|
||||||
|
};
|
||||||
|
|
||||||
getStats() {
|
getStats() {
|
||||||
const { path, method } = this.GET_STATS;
|
const { path, method } = this.GET_STATS;
|
||||||
|
@ -519,13 +676,25 @@ class Api {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Query log
|
// Query log
|
||||||
GET_QUERY_LOG = { path: 'querylog', method: 'GET' };
|
GET_QUERY_LOG = {
|
||||||
|
path: 'querylog',
|
||||||
|
method: 'GET',
|
||||||
|
};
|
||||||
|
|
||||||
QUERY_LOG_CONFIG = { path: 'querylog_config', method: 'POST' };
|
QUERY_LOG_CONFIG = {
|
||||||
|
path: 'querylog_config',
|
||||||
|
method: 'POST',
|
||||||
|
};
|
||||||
|
|
||||||
QUERY_LOG_INFO = { path: 'querylog_info', method: 'GET' };
|
QUERY_LOG_INFO = {
|
||||||
|
path: 'querylog_info',
|
||||||
|
method: 'GET',
|
||||||
|
};
|
||||||
|
|
||||||
QUERY_LOG_CLEAR = { path: 'querylog_clear', method: 'POST' };
|
QUERY_LOG_CLEAR = {
|
||||||
|
path: 'querylog_clear',
|
||||||
|
method: 'POST',
|
||||||
|
};
|
||||||
|
|
||||||
getQueryLog(params) {
|
getQueryLog(params) {
|
||||||
const { path, method } = this.GET_QUERY_LOG;
|
const { path, method } = this.GET_QUERY_LOG;
|
||||||
|
@ -553,7 +722,10 @@ class Api {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Login
|
// Login
|
||||||
LOGIN = { path: 'login', method: 'POST' };
|
LOGIN = {
|
||||||
|
path: 'login',
|
||||||
|
method: 'POST',
|
||||||
|
};
|
||||||
|
|
||||||
login(data) {
|
login(data) {
|
||||||
const { path, method } = this.LOGIN;
|
const { path, method } = this.LOGIN;
|
||||||
|
@ -565,7 +737,10 @@ class Api {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Profile
|
// Profile
|
||||||
GET_PROFILE = { path: 'profile', method: 'GET' };
|
GET_PROFILE = {
|
||||||
|
path: 'profile',
|
||||||
|
method: 'GET',
|
||||||
|
};
|
||||||
|
|
||||||
getProfile() {
|
getProfile() {
|
||||||
const { path, method } = this.GET_PROFILE;
|
const { path, method } = this.GET_PROFILE;
|
||||||
|
@ -573,9 +748,15 @@ class Api {
|
||||||
}
|
}
|
||||||
|
|
||||||
// DNS config
|
// DNS config
|
||||||
GET_DNS_CONFIG = { path: 'dns_info', method: 'GET' };
|
GET_DNS_CONFIG = {
|
||||||
|
path: 'dns_info',
|
||||||
|
method: 'GET',
|
||||||
|
};
|
||||||
|
|
||||||
SET_DNS_CONFIG = { path: 'dns_config', method: 'POST' };
|
SET_DNS_CONFIG = {
|
||||||
|
path: 'dns_config',
|
||||||
|
method: 'POST',
|
||||||
|
};
|
||||||
|
|
||||||
getDnsConfig() {
|
getDnsConfig() {
|
||||||
const { path, method } = this.GET_DNS_CONFIG;
|
const { path, method } = this.GET_DNS_CONFIG;
|
||||||
|
|
|
@ -1,30 +1,16 @@
|
||||||
import React, { Component, Fragment } from 'react';
|
import React, { useEffect } from 'react';
|
||||||
import { HashRouter, Route } from 'react-router-dom';
|
import { HashRouter, Route } from 'react-router-dom';
|
||||||
import PropTypes from 'prop-types';
|
|
||||||
import { withTranslation } from 'react-i18next';
|
|
||||||
import LoadingBar from 'react-redux-loading-bar';
|
import LoadingBar from 'react-redux-loading-bar';
|
||||||
|
import { hot } from 'react-hot-loader/root';
|
||||||
|
|
||||||
import 'react-table/react-table.css';
|
import 'react-table/react-table.css';
|
||||||
import '../ui/Tabler.css';
|
import '../ui/Tabler.css';
|
||||||
import '../ui/ReactTable.css';
|
import '../ui/ReactTable.css';
|
||||||
import './index.css';
|
import './index.css';
|
||||||
|
|
||||||
import Header from '../../containers/Header';
|
import { shallowEqual, useDispatch, useSelector } from 'react-redux';
|
||||||
import Dashboard from '../../containers/Dashboard';
|
|
||||||
import Settings from '../../containers/Settings';
|
|
||||||
|
|
||||||
import CustomRules from '../../containers/CustomRules';
|
import propTypes from 'prop-types';
|
||||||
import DnsBlocklist from '../../containers/DnsBlocklist';
|
|
||||||
import DnsAllowlist from '../../containers/DnsAllowlist';
|
|
||||||
import DnsRewrites from '../../containers/DnsRewrites';
|
|
||||||
|
|
||||||
import Dns from '../../containers/Dns';
|
|
||||||
import Encryption from '../../containers/Encryption';
|
|
||||||
import Dhcp from '../../containers/Dhcp';
|
|
||||||
import Clients from '../../containers/Clients';
|
|
||||||
|
|
||||||
import Logs from '../../containers/Logs';
|
|
||||||
import SetupGuide from '../../containers/SetupGuide';
|
|
||||||
import Toasts from '../Toasts';
|
import Toasts from '../Toasts';
|
||||||
import Footer from '../ui/Footer';
|
import Footer from '../ui/Footer';
|
||||||
import Status from '../ui/Status';
|
import Status from '../ui/Status';
|
||||||
|
@ -35,31 +21,107 @@ import Icons from '../ui/Icons';
|
||||||
import i18n from '../../i18n';
|
import i18n from '../../i18n';
|
||||||
import Loading from '../ui/Loading';
|
import Loading from '../ui/Loading';
|
||||||
import { FILTERS_URLS, MENU_URLS, SETTINGS_URLS } from '../../helpers/constants';
|
import { FILTERS_URLS, MENU_URLS, SETTINGS_URLS } from '../../helpers/constants';
|
||||||
import Services from '../Filters/Services';
|
|
||||||
import { getLogsUrlParams, setHtmlLangAttr } from '../../helpers/helpers';
|
import { getLogsUrlParams, setHtmlLangAttr } from '../../helpers/helpers';
|
||||||
|
import Header from '../Header';
|
||||||
|
import { changeLanguage, getDnsStatus } from '../../actions';
|
||||||
|
|
||||||
class App extends Component {
|
import Dashboard from '../../containers/Dashboard';
|
||||||
componentDidMount() {
|
import Logs from '../../containers/Logs';
|
||||||
this.props.getDnsStatus();
|
import SetupGuide from '../../containers/SetupGuide';
|
||||||
}
|
import Settings from '../../containers/Settings';
|
||||||
|
import Dns from '../../containers/Dns';
|
||||||
|
import Encryption from '../../containers/Encryption';
|
||||||
|
import Dhcp from '../../containers/Dhcp';
|
||||||
|
import Clients from '../../containers/Clients';
|
||||||
|
import DnsBlocklist from '../../containers/DnsBlocklist';
|
||||||
|
import DnsAllowlist from '../../containers/DnsAllowlist';
|
||||||
|
import DnsRewrites from '../../containers/DnsRewrites';
|
||||||
|
import CustomRules from '../../containers/CustomRules';
|
||||||
|
import Services from '../Filters/Services';
|
||||||
|
|
||||||
componentDidUpdate(prevProps) {
|
const ROUTES = [
|
||||||
if (this.props.dashboard.language !== prevProps.dashboard.language) {
|
{
|
||||||
this.setLanguage();
|
path: MENU_URLS.root,
|
||||||
}
|
component: Dashboard,
|
||||||
}
|
exact: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: [`${MENU_URLS.logs}${getLogsUrlParams(':search?', ':response_status?')}`, MENU_URLS.logs],
|
||||||
|
component: Logs,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: MENU_URLS.guide,
|
||||||
|
component: SetupGuide,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: SETTINGS_URLS.settings,
|
||||||
|
component: Settings,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: SETTINGS_URLS.dns,
|
||||||
|
component: Dns,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: SETTINGS_URLS.encryption,
|
||||||
|
component: Encryption,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: SETTINGS_URLS.dhcp,
|
||||||
|
component: Dhcp,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: SETTINGS_URLS.clients,
|
||||||
|
component: Clients,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: FILTERS_URLS.dns_blocklists,
|
||||||
|
component: DnsBlocklist,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: FILTERS_URLS.dns_allowlists,
|
||||||
|
component: DnsAllowlist,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: FILTERS_URLS.dns_rewrites,
|
||||||
|
component: DnsRewrites,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: FILTERS_URLS.custom_rules,
|
||||||
|
component: CustomRules,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: FILTERS_URLS.blocked_services,
|
||||||
|
component: Services,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
reloadPage = () => {
|
const renderRoute = ({ path, component, exact }, idx) => <Route
|
||||||
window.location.reload();
|
key={idx}
|
||||||
};
|
exact={exact}
|
||||||
|
path={path}
|
||||||
|
component={component}
|
||||||
|
/>;
|
||||||
|
|
||||||
handleUpdate = () => {
|
const App = () => {
|
||||||
this.props.getUpdate();
|
const dispatch = useDispatch();
|
||||||
};
|
const {
|
||||||
|
language,
|
||||||
|
isCoreRunning,
|
||||||
|
isUpdateAvailable,
|
||||||
|
processing,
|
||||||
|
} = useSelector((state) => state.dashboard, shallowEqual);
|
||||||
|
|
||||||
setLanguage = () => {
|
const { processing: processingEncryption } = useSelector((
|
||||||
const { processing, language } = this.props.dashboard;
|
state,
|
||||||
|
) => state.encryption, shallowEqual);
|
||||||
|
|
||||||
|
const updateAvailable = isCoreRunning && isUpdateAvailable;
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
dispatch(getDnsStatus());
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const setLanguage = () => {
|
||||||
if (!processing) {
|
if (!processing) {
|
||||||
if (language) {
|
if (language) {
|
||||||
i18n.changeLanguage(language);
|
i18n.changeLanguage(language);
|
||||||
|
@ -68,93 +130,52 @@ class App extends Component {
|
||||||
}
|
}
|
||||||
|
|
||||||
i18n.on('languageChanged', (lang) => {
|
i18n.on('languageChanged', (lang) => {
|
||||||
this.props.changeLanguage(lang);
|
dispatch(changeLanguage(lang));
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
render() {
|
useEffect(() => {
|
||||||
const { dashboard, encryption, getVersion } = this.props;
|
setLanguage();
|
||||||
const updateAvailable = dashboard.isCoreRunning && dashboard.isUpdateAvailable;
|
}, [language]);
|
||||||
|
|
||||||
return (
|
const reloadPage = () => {
|
||||||
<HashRouter hashType="noslash">
|
window.location.reload();
|
||||||
<Fragment>
|
};
|
||||||
{updateAvailable && (
|
|
||||||
<Fragment>
|
return (
|
||||||
<UpdateTopline
|
<HashRouter hashType="noslash">
|
||||||
url={dashboard.announcementUrl}
|
<>
|
||||||
version={dashboard.newVersion}
|
{updateAvailable && <>
|
||||||
canAutoUpdate={dashboard.canAutoUpdate}
|
<UpdateTopline />
|
||||||
getUpdate={this.handleUpdate}
|
<UpdateOverlay />
|
||||||
processingUpdate={dashboard.processingUpdate}
|
</>}
|
||||||
/>
|
{!processingEncryption && <EncryptionTopline />}
|
||||||
<UpdateOverlay processingUpdate={dashboard.processingUpdate} />
|
<LoadingBar className="loading-bar" updateTime={1000} />
|
||||||
</Fragment>
|
<Header />
|
||||||
)}
|
<div className="container container--wrap pb-5">
|
||||||
{!encryption.processing && (
|
{processing && <Loading />}
|
||||||
<EncryptionTopline notAfter={encryption.not_after} />
|
{!isCoreRunning && (
|
||||||
)}
|
<div className="row row-cards">
|
||||||
<LoadingBar className="loading-bar" updateTime={1000} />
|
<div className="col-lg-12">
|
||||||
<Route component={Header} />
|
<Status reloadPage={reloadPage} message="dns_start" />
|
||||||
<div className="container container--wrap pb-5">
|
<Loading />
|
||||||
{dashboard.processing && <Loading />}
|
|
||||||
{!dashboard.isCoreRunning && (
|
|
||||||
<div className="row row-cards">
|
|
||||||
<div className="col-lg-12">
|
|
||||||
<Status reloadPage={this.reloadPage}
|
|
||||||
message="dns_start"
|
|
||||||
/>
|
|
||||||
<Loading />
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
)}
|
</div>
|
||||||
{!dashboard.processing && dashboard.isCoreRunning && (
|
)}
|
||||||
<>
|
{!processing && isCoreRunning && ROUTES.map(renderRoute)}
|
||||||
<Route path={MENU_URLS.root} exact component={Dashboard} />
|
</div>
|
||||||
<Route
|
<Footer />
|
||||||
path={[`${MENU_URLS.logs}${getLogsUrlParams(':search?', ':response_status?')}`, MENU_URLS.logs]}
|
<Toasts />
|
||||||
component={Logs} />
|
<Icons />
|
||||||
<Route path={MENU_URLS.guide} component={SetupGuide} />
|
</>
|
||||||
<Route path={SETTINGS_URLS.settings} component={Settings} />
|
</HashRouter>
|
||||||
<Route path={SETTINGS_URLS.dns} component={Dns} />
|
);
|
||||||
<Route path={SETTINGS_URLS.encryption} component={Encryption} />
|
|
||||||
<Route path={SETTINGS_URLS.dhcp} component={Dhcp} />
|
|
||||||
<Route path={SETTINGS_URLS.clients} component={Clients} />
|
|
||||||
<Route path={FILTERS_URLS.dns_blocklists}
|
|
||||||
component={DnsBlocklist} />
|
|
||||||
<Route path={FILTERS_URLS.dns_allowlists}
|
|
||||||
component={DnsAllowlist} />
|
|
||||||
<Route path={FILTERS_URLS.dns_rewrites} component={DnsRewrites} />
|
|
||||||
<Route path={FILTERS_URLS.custom_rules} component={CustomRules} />
|
|
||||||
<Route path={FILTERS_URLS.blocked_services} component={Services} />
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
<Footer
|
|
||||||
dnsVersion={dashboard.dnsVersion}
|
|
||||||
dnsPort={dashboard.dnsPort}
|
|
||||||
processingVersion={dashboard.processingVersion}
|
|
||||||
getVersion={getVersion}
|
|
||||||
checkUpdateFlag={dashboard.checkUpdateFlag}
|
|
||||||
/>
|
|
||||||
<Toasts />
|
|
||||||
<Icons />
|
|
||||||
</Fragment>
|
|
||||||
</HashRouter>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
App.propTypes = {
|
|
||||||
getDnsStatus: PropTypes.func,
|
|
||||||
getUpdate: PropTypes.func,
|
|
||||||
enableDns: PropTypes.func,
|
|
||||||
dashboard: PropTypes.object,
|
|
||||||
isCoreRunning: PropTypes.bool,
|
|
||||||
error: PropTypes.string,
|
|
||||||
changeLanguage: PropTypes.func,
|
|
||||||
encryption: PropTypes.object,
|
|
||||||
getVersion: PropTypes.func,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export default withTranslation()(App);
|
renderRoute.propTypes = {
|
||||||
|
path: propTypes.oneOfType([propTypes.string, propTypes.arrayOf(propTypes.string)]).isRequired,
|
||||||
|
component: propTypes.element.isRequired,
|
||||||
|
exact: propTypes.bool,
|
||||||
|
};
|
||||||
|
|
||||||
|
export default hot(App);
|
||||||
|
|
|
@ -59,6 +59,4 @@ const Services = () => {
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
Services.propTypes = {};
|
|
||||||
|
|
||||||
export default Services;
|
export default Services;
|
||||||
|
|
|
@ -90,9 +90,8 @@ class Menu extends Component {
|
||||||
};
|
};
|
||||||
|
|
||||||
getActiveClassForDropdown = (URLS) => {
|
getActiveClassForDropdown = (URLS) => {
|
||||||
const { pathname } = this.props.location;
|
|
||||||
const isActivePage = Object.values(URLS)
|
const isActivePage = Object.values(URLS)
|
||||||
.some((item) => item === pathname);
|
.some((item) => item === this.props.pathname);
|
||||||
|
|
||||||
return isActivePage ? 'active' : '';
|
return isActivePage ? 'active' : '';
|
||||||
};
|
};
|
||||||
|
@ -180,9 +179,9 @@ class Menu extends Component {
|
||||||
}
|
}
|
||||||
|
|
||||||
Menu.propTypes = {
|
Menu.propTypes = {
|
||||||
isMenuOpen: PropTypes.bool,
|
isMenuOpen: PropTypes.bool.isRequired,
|
||||||
closeMenu: PropTypes.func,
|
closeMenu: PropTypes.func.isRequired,
|
||||||
location: PropTypes.object,
|
pathname: PropTypes.string.isRequired,
|
||||||
t: PropTypes.func,
|
t: PropTypes.func,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -1,83 +1,77 @@
|
||||||
import React, { Component } from 'react';
|
import React, { useState } from 'react';
|
||||||
import { Link } from 'react-router-dom';
|
import { Link, useLocation } from 'react-router-dom';
|
||||||
import PropTypes from 'prop-types';
|
import { shallowEqual, useSelector } from 'react-redux';
|
||||||
|
import { Trans } from 'react-i18next';
|
||||||
import classnames from 'classnames';
|
import classnames from 'classnames';
|
||||||
import { Trans, withTranslation } from 'react-i18next';
|
|
||||||
|
|
||||||
import Menu from './Menu';
|
import Menu from './Menu';
|
||||||
import logo from '../ui/svg/logo.svg';
|
import logo from '../ui/svg/logo.svg';
|
||||||
import './Header.css';
|
import './Header.css';
|
||||||
|
|
||||||
class Header extends Component {
|
const Header = () => {
|
||||||
state = {
|
const [isMenuOpen, setIsMenuOpen] = useState(false);
|
||||||
isMenuOpen: false,
|
|
||||||
|
const {
|
||||||
|
protectionEnabled,
|
||||||
|
processing,
|
||||||
|
isCoreRunning,
|
||||||
|
processingProfile,
|
||||||
|
name,
|
||||||
|
} = useSelector((state) => state.dashboard, shallowEqual);
|
||||||
|
|
||||||
|
const { pathname } = useLocation();
|
||||||
|
|
||||||
|
const toggleMenuOpen = () => {
|
||||||
|
setIsMenuOpen((isMenuOpen) => !isMenuOpen);
|
||||||
};
|
};
|
||||||
|
|
||||||
toggleMenuOpen = () => {
|
const closeMenu = () => {
|
||||||
this.setState((prevState) => ({ isMenuOpen: !prevState.isMenuOpen }));
|
setIsMenuOpen(false);
|
||||||
};
|
};
|
||||||
|
|
||||||
closeMenu = () => {
|
const badgeClass = classnames('badge dns-status', {
|
||||||
this.setState({ isMenuOpen: false });
|
'badge-success': protectionEnabled,
|
||||||
};
|
'badge-danger': !protectionEnabled,
|
||||||
|
});
|
||||||
|
|
||||||
render() {
|
return (
|
||||||
const { dashboard, location } = this.props;
|
<div className="header">
|
||||||
const { isMenuOpen } = this.state;
|
<div className="header__container">
|
||||||
const badgeClass = classnames({
|
<div className="header__row">
|
||||||
'badge dns-status': true,
|
<div
|
||||||
'badge-success': dashboard.protectionEnabled,
|
className="header-toggler d-lg-none ml-lg-0 collapsed"
|
||||||
'badge-danger': !dashboard.protectionEnabled,
|
onClick={toggleMenuOpen}
|
||||||
});
|
>
|
||||||
|
<span className="header-toggler-icon" />
|
||||||
return (
|
</div>
|
||||||
<div className="header">
|
<div className="header__column">
|
||||||
<div className="header__container">
|
<div className="d-flex align-items-center">
|
||||||
<div className="header__row">
|
<Link to="/" className="nav-link pl-0 pr-1">
|
||||||
<div
|
<img src={logo} alt="" className="header-brand-img" />
|
||||||
className="header-toggler d-lg-none ml-lg-0 collapsed"
|
</Link>
|
||||||
onClick={this.toggleMenuOpen}
|
{!processing && isCoreRunning && (
|
||||||
>
|
<span className={badgeClass}>
|
||||||
<span className="header-toggler-icon" />
|
<Trans>{protectionEnabled ? 'on' : 'off'}</Trans>
|
||||||
</div>
|
|
||||||
<div className="header__column">
|
|
||||||
<div className="d-flex align-items-center">
|
|
||||||
<Link to="/" className="nav-link pl-0 pr-1">
|
|
||||||
<img src={logo} alt="" className="header-brand-img" />
|
|
||||||
</Link>
|
|
||||||
{!dashboard.processing && dashboard.isCoreRunning && (
|
|
||||||
<span className={badgeClass}>
|
|
||||||
<Trans>{dashboard.protectionEnabled ? 'on' : 'off'}</Trans>
|
|
||||||
</span>
|
</span>
|
||||||
)}
|
)}
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
<Menu
|
</div>
|
||||||
location={location}
|
<Menu
|
||||||
isMenuOpen={isMenuOpen}
|
pathname={pathname}
|
||||||
closeMenu={this.closeMenu}
|
isMenuOpen={isMenuOpen}
|
||||||
/>
|
closeMenu={closeMenu}
|
||||||
<div className="header__column">
|
/>
|
||||||
<div className="header__right">
|
<div className="header__column">
|
||||||
{!dashboard.processingProfile && dashboard.name
|
<div className="header__right">
|
||||||
&& <a href="control/logout" className="btn btn-sm btn-outline-secondary">
|
{!processingProfile && name
|
||||||
<Trans>sign_out</Trans>
|
&& <a href="control/logout" className="btn btn-sm btn-outline-secondary">
|
||||||
</a>
|
<Trans>sign_out</Trans>
|
||||||
}
|
</a>}
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
</div>
|
||||||
}
|
);
|
||||||
}
|
|
||||||
|
|
||||||
Header.propTypes = {
|
|
||||||
dashboard: PropTypes.object.isRequired,
|
|
||||||
location: PropTypes.object.isRequired,
|
|
||||||
getVersion: PropTypes.func.isRequired,
|
|
||||||
t: PropTypes.func.isRequired,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export default withTranslation()(Header);
|
export default Header;
|
||||||
|
|
|
@ -437,7 +437,7 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
.custom-select__arrow--left {
|
.custom-select__arrow--left {
|
||||||
background: #fff url('./chevron-down.svg') no-repeat;
|
background: var(--white) url('../ui/svg/chevron-down.svg') no-repeat;
|
||||||
background-position: 5px 9px;
|
background-position: 5px 9px;
|
||||||
background-size: 22px;
|
background-size: 22px;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1 +0,0 @@
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#9aa0ac" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-chevron-down"><polyline points="6 9 12 15 18 9"></polyline></svg>
|
|
Before Width: | Height: | Size: 264 B |
|
@ -1,4 +1,4 @@
|
||||||
import React, { Component, Fragment } from 'react';
|
import React, { Component } from 'react';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import classnames from 'classnames';
|
import classnames from 'classnames';
|
||||||
import { Trans, withTranslation } from 'react-i18next';
|
import { Trans, withTranslation } from 'react-i18next';
|
||||||
|
@ -34,13 +34,14 @@ class Dhcp extends Component {
|
||||||
} = this.props.dhcp;
|
} = this.props.dhcp;
|
||||||
const otherDhcpFound = check?.otherServer
|
const otherDhcpFound = check?.otherServer
|
||||||
&& check.otherServer.found === DHCP_STATUS_RESPONSE.YES;
|
&& check.otherServer.found === DHCP_STATUS_RESPONSE.YES;
|
||||||
const filledConfig = Object.keys(config).every((key) => {
|
const filledConfig = Object.keys(config)
|
||||||
if (key === 'enabled' || key === 'icmp_timeout_msec') {
|
.every((key) => {
|
||||||
return true;
|
if (key === 'enabled' || key === 'icmp_timeout_msec') {
|
||||||
}
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
return config[key];
|
return config[key];
|
||||||
});
|
});
|
||||||
|
|
||||||
if (config.enabled) {
|
if (config.enabled) {
|
||||||
return (
|
return (
|
||||||
|
@ -114,40 +115,35 @@ class Dhcp extends Component {
|
||||||
|
|
||||||
getStaticIpWarning = (t, check, interfaceName) => {
|
getStaticIpWarning = (t, check, interfaceName) => {
|
||||||
if (check.staticIP.static === DHCP_STATUS_RESPONSE.ERROR) {
|
if (check.staticIP.static === DHCP_STATUS_RESPONSE.ERROR) {
|
||||||
return (
|
return <>
|
||||||
<Fragment>
|
<div className="text-danger mb-2">
|
||||||
<div className="text-danger mb-2">
|
<Trans>dhcp_static_ip_error</Trans>
|
||||||
<Trans>dhcp_static_ip_error</Trans>
|
<div className="mt-2 mb-2">
|
||||||
<div className="mt-2 mb-2">
|
<Accordion label={t('error_details')}>
|
||||||
<Accordion label={t('error_details')}>
|
<span>{check.staticIP.error}</span>
|
||||||
<span>{check.staticIP.error}</span>
|
</Accordion>
|
||||||
</Accordion>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
<hr className="mt-4 mb-4" />
|
</div>
|
||||||
</Fragment>
|
<hr className="mt-4 mb-4" />
|
||||||
);
|
</>;
|
||||||
} if (
|
}
|
||||||
check.staticIP.static === DHCP_STATUS_RESPONSE.NO
|
if (check.staticIP.static === DHCP_STATUS_RESPONSE.NO
|
||||||
&& check.staticIP.ip
|
&& check.staticIP.ip
|
||||||
&& interfaceName
|
&& interfaceName) {
|
||||||
) {
|
return <>
|
||||||
return (
|
<div className="text-secondary mb-2">
|
||||||
<Fragment>
|
<Trans
|
||||||
<div className="text-secondary mb-2">
|
components={[<strong key="0">example</strong>]}
|
||||||
<Trans
|
values={{
|
||||||
components={[<strong key="0">example</strong>]}
|
interfaceName,
|
||||||
values={{
|
ipAddress: check.staticIP.ip,
|
||||||
interfaceName,
|
}}
|
||||||
ipAddress: check.staticIP.ip,
|
>
|
||||||
}}
|
dhcp_dynamic_ip_found
|
||||||
>
|
</Trans>
|
||||||
dhcp_dynamic_ip_found
|
</div>
|
||||||
</Trans>
|
<hr className="mt-4 mb-4" />
|
||||||
</div>
|
</>;
|
||||||
<hr className="mt-4 mb-4" />
|
|
||||||
</Fragment>
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return '';
|
return '';
|
||||||
|
@ -163,104 +159,101 @@ class Dhcp extends Component {
|
||||||
removeStaticLease,
|
removeStaticLease,
|
||||||
toggleLeaseModal,
|
toggleLeaseModal,
|
||||||
} = this.props;
|
} = this.props;
|
||||||
|
|
||||||
const statusButtonClass = classnames({
|
const statusButtonClass = classnames({
|
||||||
'btn btn-primary btn-standard': true,
|
'btn btn-primary btn-standard': true,
|
||||||
'btn btn-primary btn-standard btn-loading': dhcp.processingStatus,
|
'btn btn-primary btn-standard btn-loading': dhcp.processingStatus,
|
||||||
});
|
});
|
||||||
const { enabled, interface_name, ...values } = dhcp.config;
|
const { enabled, interface_name, ...values } = dhcp.config;
|
||||||
|
|
||||||
return (
|
return <>
|
||||||
<Fragment>
|
<PageTitle title={t('dhcp_settings')} />
|
||||||
<PageTitle title={t('dhcp_settings')} />
|
{(dhcp.processing || dhcp.processingInterfaces) && <Loading />}
|
||||||
{(dhcp.processing || dhcp.processingInterfaces) && <Loading />}
|
{!dhcp.processing && !dhcp.processingInterfaces && <>
|
||||||
{!dhcp.processing && !dhcp.processingInterfaces && (
|
<Card
|
||||||
<Fragment>
|
title={t('dhcp_title')}
|
||||||
<Card
|
subtitle={t('dhcp_description')}
|
||||||
title={t('dhcp_title')}
|
bodyType="card-body box-body--settings"
|
||||||
subtitle={t('dhcp_description')}
|
>
|
||||||
bodyType="card-body box-body--settings"
|
<div className="dhcp">
|
||||||
>
|
<>
|
||||||
<div className="dhcp">
|
<Form
|
||||||
<Fragment>
|
onSubmit={this.handleFormSubmit}
|
||||||
<Form
|
initialValues={{
|
||||||
onSubmit={this.handleFormSubmit}
|
interface_name,
|
||||||
initialValues={{
|
...values,
|
||||||
interface_name,
|
}}
|
||||||
...values,
|
interfaces={dhcp.interfaces}
|
||||||
}}
|
processingConfig={dhcp.processingConfig}
|
||||||
interfaces={dhcp.interfaces}
|
processingInterfaces={dhcp.processingInterfaces}
|
||||||
processingConfig={dhcp.processingConfig}
|
enabled={enabled}
|
||||||
processingInterfaces={dhcp.processingInterfaces}
|
resetDhcp={resetDhcp}
|
||||||
enabled={enabled}
|
/>
|
||||||
resetDhcp={resetDhcp}
|
<hr />
|
||||||
/>
|
<div className="card-actions mb-3">
|
||||||
<hr />
|
{this.getToggleDhcpButton()}
|
||||||
<div className="card-actions mb-3">
|
<button
|
||||||
{this.getToggleDhcpButton()}
|
type="button"
|
||||||
<button
|
className={statusButtonClass}
|
||||||
type="button"
|
onClick={() => findActiveDhcp(interface_name)}
|
||||||
className={statusButtonClass}
|
disabled={
|
||||||
onClick={() => findActiveDhcp(interface_name)}
|
enabled || !interface_name || dhcp.processingConfig
|
||||||
disabled={
|
}
|
||||||
enabled || !interface_name || dhcp.processingConfig
|
>
|
||||||
}
|
<Trans>check_dhcp_servers</Trans>
|
||||||
>
|
</button>
|
||||||
<Trans>check_dhcp_servers</Trans>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
{!enabled && dhcp.check && (
|
|
||||||
<Fragment>
|
|
||||||
{this.getStaticIpWarning(t, dhcp.check, interface_name)}
|
|
||||||
{this.getActiveDhcpMessage(t, dhcp.check)}
|
|
||||||
{this.getDhcpWarning(dhcp.check)}
|
|
||||||
</Fragment>
|
|
||||||
)}
|
|
||||||
</Fragment>
|
|
||||||
</div>
|
</div>
|
||||||
</Card>
|
{!enabled && dhcp.check && (
|
||||||
{dhcp.config.enabled && (
|
<>
|
||||||
<Card
|
{this.getStaticIpWarning(t, dhcp.check, interface_name)}
|
||||||
title={t('dhcp_leases')}
|
{this.getActiveDhcpMessage(t, dhcp.check)}
|
||||||
bodyType="card-body box-body--settings"
|
{this.getDhcpWarning(dhcp.check)}
|
||||||
>
|
</>
|
||||||
<div className="row">
|
)}
|
||||||
<div className="col">
|
</>
|
||||||
<Leases leases={dhcp.leases} />
|
</div>
|
||||||
</div>
|
</Card>
|
||||||
</div>
|
{dhcp.config.enabled && (
|
||||||
</Card>
|
<Card
|
||||||
)}
|
title={t('dhcp_leases')}
|
||||||
<Card
|
bodyType="card-body box-body--settings"
|
||||||
title={t('dhcp_static_leases')}
|
>
|
||||||
bodyType="card-body box-body--settings"
|
<div className="row">
|
||||||
>
|
<div className="col">
|
||||||
<div className="row">
|
<Leases leases={dhcp.leases} />
|
||||||
<div className="col-12">
|
|
||||||
<StaticLeases
|
|
||||||
staticLeases={dhcp.staticLeases}
|
|
||||||
isModalOpen={dhcp.isModalOpen}
|
|
||||||
addStaticLease={addStaticLease}
|
|
||||||
removeStaticLease={removeStaticLease}
|
|
||||||
toggleLeaseModal={toggleLeaseModal}
|
|
||||||
processingAdding={dhcp.processingAdding}
|
|
||||||
processingDeleting={dhcp.processingDeleting}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div className="col-12">
|
|
||||||
<button
|
|
||||||
type="button"
|
|
||||||
className="btn btn-success btn-standard mt-3"
|
|
||||||
onClick={() => toggleLeaseModal()}
|
|
||||||
>
|
|
||||||
<Trans>dhcp_add_static_lease</Trans>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</Card>
|
</div>
|
||||||
</Fragment>
|
</Card>
|
||||||
)}
|
)}
|
||||||
</Fragment>
|
<Card
|
||||||
);
|
title={t('dhcp_static_leases')}
|
||||||
|
bodyType="card-body box-body--settings"
|
||||||
|
>
|
||||||
|
<div className="row">
|
||||||
|
<div className="col-12">
|
||||||
|
<StaticLeases
|
||||||
|
staticLeases={dhcp.staticLeases}
|
||||||
|
isModalOpen={dhcp.isModalOpen}
|
||||||
|
addStaticLease={addStaticLease}
|
||||||
|
removeStaticLease={removeStaticLease}
|
||||||
|
toggleLeaseModal={toggleLeaseModal}
|
||||||
|
processingAdding={dhcp.processingAdding}
|
||||||
|
processingDeleting={dhcp.processingDeleting}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div className="col-12">
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
className="btn btn-success btn-standard mt-3"
|
||||||
|
onClick={() => toggleLeaseModal()}
|
||||||
|
>
|
||||||
|
<Trans>dhcp_add_static_lease</Trans>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</Card>
|
||||||
|
</>}
|
||||||
|
</>;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,40 +1,36 @@
|
||||||
import React, { Component } from 'react';
|
import React from 'react';
|
||||||
import PropTypes from 'prop-types';
|
import { useTranslation } from 'react-i18next';
|
||||||
import { withTranslation } from 'react-i18next';
|
import { shallowEqual, useDispatch, useSelector } from 'react-redux';
|
||||||
|
|
||||||
import Form from './Form';
|
import Form from './Form';
|
||||||
import Card from '../../../ui/Card';
|
import Card from '../../../ui/Card';
|
||||||
|
import { setAccessList } from '../../../../actions/access';
|
||||||
|
|
||||||
class Access extends Component {
|
const Access = () => {
|
||||||
handleFormSubmit = (values) => {
|
const { t } = useTranslation();
|
||||||
this.props.setAccessList(values);
|
const dispatch = useDispatch();
|
||||||
|
const {
|
||||||
|
processing,
|
||||||
|
processingSet,
|
||||||
|
...values
|
||||||
|
} = useSelector((state) => state.access, shallowEqual);
|
||||||
|
|
||||||
|
const handleFormSubmit = (values) => {
|
||||||
|
dispatch(setAccessList(values));
|
||||||
};
|
};
|
||||||
|
|
||||||
render() {
|
return (
|
||||||
const { t, access } = this.props;
|
<Card
|
||||||
|
title={t('access_title')}
|
||||||
const { processing, processingSet, ...values } = access;
|
subtitle={t('access_desc')}
|
||||||
|
bodyType="card-body box-body--settings"
|
||||||
return (
|
>
|
||||||
<Card
|
<Form
|
||||||
title={t('access_title')}
|
initialValues={values}
|
||||||
subtitle={t('access_desc')}
|
onSubmit={handleFormSubmit}
|
||||||
bodyType="card-body box-body--settings"
|
processingSet={processingSet}
|
||||||
>
|
/>
|
||||||
<Form
|
</Card>
|
||||||
initialValues={values}
|
);
|
||||||
onSubmit={this.handleFormSubmit}
|
|
||||||
processingSet={processingSet}
|
|
||||||
/>
|
|
||||||
</Card>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Access.propTypes = {
|
|
||||||
access: PropTypes.object.isRequired,
|
|
||||||
setAccessList: PropTypes.func.isRequired,
|
|
||||||
t: PropTypes.func.isRequired,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export default withTranslation()(Access);
|
export default Access;
|
||||||
|
|
|
@ -1,9 +1,8 @@
|
||||||
import React, { Fragment } from 'react';
|
import React from 'react';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import { connect } from 'react-redux';
|
import { shallowEqual, useSelector } from 'react-redux';
|
||||||
import { Field, reduxForm, formValueSelector } from 'redux-form';
|
import { Field, reduxForm } from 'redux-form';
|
||||||
import { Trans, withTranslation } from 'react-i18next';
|
import { Trans, useTranslation } from 'react-i18next';
|
||||||
import flow from 'lodash/flow';
|
|
||||||
import {
|
import {
|
||||||
renderInputField,
|
renderInputField,
|
||||||
renderRadioField,
|
renderRadioField,
|
||||||
|
@ -18,32 +17,36 @@ import {
|
||||||
} from '../../../../helpers/validators';
|
} from '../../../../helpers/validators';
|
||||||
import { BLOCKING_MODES, FORM_NAME } from '../../../../helpers/constants';
|
import { BLOCKING_MODES, FORM_NAME } from '../../../../helpers/constants';
|
||||||
|
|
||||||
const checkboxes = [{
|
const checkboxes = [
|
||||||
name: 'edns_cs_enabled',
|
{
|
||||||
placeholder: 'edns_enable',
|
name: 'edns_cs_enabled',
|
||||||
subtitle: 'edns_cs_desc',
|
placeholder: 'edns_enable',
|
||||||
},
|
subtitle: 'edns_cs_desc',
|
||||||
{
|
},
|
||||||
name: 'dnssec_enabled',
|
{
|
||||||
placeholder: 'dnssec_enable',
|
name: 'dnssec_enabled',
|
||||||
subtitle: 'dnssec_enable_desc',
|
placeholder: 'dnssec_enable',
|
||||||
},
|
subtitle: 'dnssec_enable_desc',
|
||||||
{
|
},
|
||||||
name: 'disable_ipv6',
|
{
|
||||||
placeholder: 'disable_ipv6',
|
name: 'disable_ipv6',
|
||||||
subtitle: 'disable_ipv6_desc',
|
placeholder: 'disable_ipv6',
|
||||||
}];
|
subtitle: 'disable_ipv6_desc',
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
const customIps = [{
|
const customIps = [
|
||||||
description: 'blocking_ipv4_desc',
|
{
|
||||||
name: 'blocking_ipv4',
|
description: 'blocking_ipv4_desc',
|
||||||
validateIp: validateIpv4,
|
name: 'blocking_ipv4',
|
||||||
},
|
validateIp: validateIpv4,
|
||||||
{
|
},
|
||||||
description: 'blocking_ipv6_desc',
|
{
|
||||||
name: 'blocking_ipv6',
|
description: 'blocking_ipv6_desc',
|
||||||
validateIp: validateIpv6,
|
name: 'blocking_ipv6',
|
||||||
}];
|
validateIp: validateIpv6,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
const getFields = (processing, t) => Object.values(BLOCKING_MODES)
|
const getFields = (processing, t) => Object.values(BLOCKING_MODES)
|
||||||
.map((mode) => (
|
.map((mode) => (
|
||||||
|
@ -58,114 +61,107 @@ const getFields = (processing, t) => Object.values(BLOCKING_MODES)
|
||||||
/>
|
/>
|
||||||
));
|
));
|
||||||
|
|
||||||
let Form = ({
|
const Form = ({
|
||||||
handleSubmit, submitting, invalid, processing, blockingMode, t,
|
handleSubmit, submitting, invalid, processing,
|
||||||
}) => <form onSubmit={handleSubmit}>
|
}) => {
|
||||||
<div className="row">
|
const { t } = useTranslation();
|
||||||
<div className="col-12 col-sm-6">
|
const {
|
||||||
<div className="form__group form__group--settings">
|
blocking_mode,
|
||||||
<label htmlFor="ratelimit"
|
} = useSelector((state) => state.form[FORM_NAME.BLOCKING_MODE].values ?? {}, shallowEqual);
|
||||||
className="form__label form__label--with-desc">
|
|
||||||
<Trans>rate_limit</Trans>
|
return <form onSubmit={handleSubmit}>
|
||||||
</label>
|
<div className="row">
|
||||||
<div className="form__desc form__desc--top">
|
<div className="col-12 col-sm-6">
|
||||||
<Trans>rate_limit_desc</Trans>
|
<div className="form__group form__group--settings">
|
||||||
</div>
|
<label htmlFor="ratelimit"
|
||||||
<Field
|
className="form__label form__label--with-desc">
|
||||||
name="ratelimit"
|
<Trans>rate_limit</Trans>
|
||||||
type="number"
|
</label>
|
||||||
component={renderInputField}
|
<div className="form__desc form__desc--top">
|
||||||
className="form-control"
|
<Trans>rate_limit_desc</Trans>
|
||||||
placeholder={t('form_enter_rate_limit')}
|
|
||||||
normalize={toNumber}
|
|
||||||
validate={[validateRequiredValue, validateBiggerOrEqualZeroValue]}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
{checkboxes.map(({ name, placeholder, subtitle }) => <div className="col-12" key={name}>
|
|
||||||
<div className="form__group form__group--settings">
|
|
||||||
<Field
|
|
||||||
name={name}
|
|
||||||
type="checkbox"
|
|
||||||
component={renderSelectField}
|
|
||||||
placeholder={t(placeholder)}
|
|
||||||
disabled={processing}
|
|
||||||
subtitle={t(subtitle)}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>)}
|
|
||||||
<div className="col-12">
|
|
||||||
<div className="form__group form__group--settings mb-4">
|
|
||||||
<label className="form__label form__label--with-desc">
|
|
||||||
<Trans>blocking_mode</Trans>
|
|
||||||
</label>
|
|
||||||
<div className="form__desc form__desc--top">
|
|
||||||
{Object.values(BLOCKING_MODES)
|
|
||||||
.map((mode) => (
|
|
||||||
<li key={mode}>
|
|
||||||
<Trans>{`blocking_mode_${mode}`}</Trans>
|
|
||||||
</li>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
<div className="custom-controls-stacked">
|
|
||||||
{getFields(processing, t)}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
{blockingMode === BLOCKING_MODES.custom_ip && (
|
|
||||||
<Fragment>
|
|
||||||
{customIps.map(({
|
|
||||||
description,
|
|
||||||
name,
|
|
||||||
validateIp,
|
|
||||||
}) => <div className="col-12 col-sm-6" key={name}>
|
|
||||||
<div className="form__group form__group--settings">
|
|
||||||
<label className="form__label form__label--with-desc"
|
|
||||||
htmlFor={name}><Trans>{name}</Trans>
|
|
||||||
</label>
|
|
||||||
<div className="form__desc form__desc--top">
|
|
||||||
<Trans>{description}</Trans>
|
|
||||||
</div>
|
|
||||||
<Field
|
|
||||||
name={name}
|
|
||||||
component={renderInputField}
|
|
||||||
className="form-control"
|
|
||||||
placeholder={t('form_enter_ip')}
|
|
||||||
validate={[validateIp, validateRequiredValue]}
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
</div>)}
|
<Field
|
||||||
</Fragment>
|
name="ratelimit"
|
||||||
)}
|
type="number"
|
||||||
</div>
|
component={renderInputField}
|
||||||
<button
|
className="form-control"
|
||||||
type="submit"
|
placeholder={t('form_enter_rate_limit')}
|
||||||
className="btn btn-success btn-standard btn-large"
|
normalize={toNumber}
|
||||||
disabled={submitting || invalid || processing}
|
validate={[validateRequiredValue, validateBiggerOrEqualZeroValue]}
|
||||||
>
|
/>
|
||||||
<Trans>save_btn</Trans>
|
</div>
|
||||||
</button>
|
</div>
|
||||||
</form>;
|
{checkboxes.map(({ name, placeholder, subtitle }) => <div className="col-12" key={name}>
|
||||||
|
<div className="form__group form__group--settings">
|
||||||
|
<Field
|
||||||
|
name={name}
|
||||||
|
type="checkbox"
|
||||||
|
component={renderSelectField}
|
||||||
|
placeholder={t(placeholder)}
|
||||||
|
disabled={processing}
|
||||||
|
subtitle={t(subtitle)}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>)}
|
||||||
|
<div className="col-12">
|
||||||
|
<div className="form__group form__group--settings mb-4">
|
||||||
|
<label className="form__label form__label--with-desc">
|
||||||
|
<Trans>blocking_mode</Trans>
|
||||||
|
</label>
|
||||||
|
<div className="form__desc form__desc--top">
|
||||||
|
{Object.values(BLOCKING_MODES)
|
||||||
|
.map((mode) => (
|
||||||
|
<li key={mode}>
|
||||||
|
<Trans>{`blocking_mode_${mode}`}</Trans>
|
||||||
|
</li>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
<div className="custom-controls-stacked">
|
||||||
|
{getFields(processing, t)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{blocking_mode === BLOCKING_MODES.custom_ip && (
|
||||||
|
<>
|
||||||
|
{customIps.map(({
|
||||||
|
description,
|
||||||
|
name,
|
||||||
|
validateIp,
|
||||||
|
}) => <div className="col-12 col-sm-6" key={name}>
|
||||||
|
<div className="form__group form__group--settings">
|
||||||
|
<label className="form__label form__label--with-desc"
|
||||||
|
htmlFor={name}><Trans>{name}</Trans>
|
||||||
|
</label>
|
||||||
|
<div className="form__desc form__desc--top">
|
||||||
|
<Trans>{description}</Trans>
|
||||||
|
</div>
|
||||||
|
<Field
|
||||||
|
name={name}
|
||||||
|
component={renderInputField}
|
||||||
|
className="form-control"
|
||||||
|
placeholder={t('form_enter_ip')}
|
||||||
|
validate={[validateIp, validateRequiredValue]}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>)}
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
<button
|
||||||
|
type="submit"
|
||||||
|
className="btn btn-success btn-standard btn-large"
|
||||||
|
disabled={submitting || invalid || processing}
|
||||||
|
>
|
||||||
|
<Trans>save_btn</Trans>
|
||||||
|
</button>
|
||||||
|
</form>;
|
||||||
|
};
|
||||||
|
|
||||||
Form.propTypes = {
|
Form.propTypes = {
|
||||||
blockingMode: PropTypes.string.isRequired,
|
|
||||||
handleSubmit: PropTypes.func.isRequired,
|
handleSubmit: PropTypes.func.isRequired,
|
||||||
submitting: PropTypes.bool.isRequired,
|
submitting: PropTypes.bool.isRequired,
|
||||||
invalid: PropTypes.bool.isRequired,
|
invalid: PropTypes.bool.isRequired,
|
||||||
processing: PropTypes.bool.isRequired,
|
processing: PropTypes.bool.isRequired,
|
||||||
t: PropTypes.func.isRequired,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const selector = formValueSelector(FORM_NAME.BLOCKING_MODE);
|
export default reduxForm({ form: FORM_NAME.BLOCKING_MODE })(Form);
|
||||||
|
|
||||||
Form = connect((state) => {
|
|
||||||
const blockingMode = selector(state, 'blocking_mode');
|
|
||||||
return {
|
|
||||||
blockingMode,
|
|
||||||
};
|
|
||||||
})(Form);
|
|
||||||
|
|
||||||
export default flow([
|
|
||||||
withTranslation(),
|
|
||||||
reduxForm({ form: FORM_NAME.BLOCKING_MODE }),
|
|
||||||
])(Form);
|
|
||||||
|
|
|
@ -1,15 +1,13 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import PropTypes from 'prop-types';
|
import { useTranslation } from 'react-i18next';
|
||||||
import { withTranslation } from 'react-i18next';
|
import { shallowEqual, useDispatch, useSelector } from 'react-redux';
|
||||||
|
|
||||||
import Card from '../../../ui/Card';
|
import Card from '../../../ui/Card';
|
||||||
import Form from './Form';
|
import Form from './Form';
|
||||||
|
import { setDnsConfig } from '../../../../actions/dnsConfig';
|
||||||
|
|
||||||
const Config = ({ t, dnsConfig, setDnsConfig }) => {
|
const Config = () => {
|
||||||
const handleFormSubmit = (values) => {
|
const { t } = useTranslation();
|
||||||
setDnsConfig(values);
|
const dispatch = useDispatch();
|
||||||
};
|
|
||||||
|
|
||||||
const {
|
const {
|
||||||
blocking_mode,
|
blocking_mode,
|
||||||
ratelimit,
|
ratelimit,
|
||||||
|
@ -19,7 +17,11 @@ const Config = ({ t, dnsConfig, setDnsConfig }) => {
|
||||||
dnssec_enabled,
|
dnssec_enabled,
|
||||||
disable_ipv6,
|
disable_ipv6,
|
||||||
processingSetConfig,
|
processingSetConfig,
|
||||||
} = dnsConfig;
|
} = useSelector((state) => state.dnsConfig, shallowEqual);
|
||||||
|
|
||||||
|
const handleFormSubmit = (values) => {
|
||||||
|
dispatch(setDnsConfig(values));
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Card
|
<Card
|
||||||
|
@ -46,10 +48,4 @@ const Config = ({ t, dnsConfig, setDnsConfig }) => {
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
Config.propTypes = {
|
export default Config;
|
||||||
dnsConfig: PropTypes.object.isRequired,
|
|
||||||
setDnsConfig: PropTypes.func.isRequired,
|
|
||||||
t: PropTypes.func.isRequired,
|
|
||||||
};
|
|
||||||
|
|
||||||
export default withTranslation()(Config);
|
|
||||||
|
|
|
@ -1,29 +1,26 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import PropTypes from 'prop-types';
|
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import { useDispatch } from 'react-redux';
|
import { shallowEqual, useDispatch } from 'react-redux';
|
||||||
import Form from './Form';
|
import Form from './Form';
|
||||||
import Card from '../../../ui/Card';
|
import Card from '../../../ui/Card';
|
||||||
import { setDnsConfig } from '../../../../actions/dnsConfig';
|
import { setDnsConfig } from '../../../../actions/dnsConfig';
|
||||||
|
|
||||||
const Upstream = (props) => {
|
const Upstream = () => {
|
||||||
const [t] = useTranslation();
|
const { t } = useTranslation();
|
||||||
const dispatch = useDispatch();
|
const dispatch = useDispatch();
|
||||||
|
const {
|
||||||
|
upstream_dns,
|
||||||
|
bootstrap_dns,
|
||||||
|
upstream_mode,
|
||||||
|
processingSetConfig,
|
||||||
|
} = ((state) => state.dnsConfig, shallowEqual);
|
||||||
|
|
||||||
|
const { processingTestUpstream } = ((state) => state.settings, shallowEqual);
|
||||||
|
|
||||||
const handleSubmit = (values) => {
|
const handleSubmit = (values) => {
|
||||||
dispatch(setDnsConfig(values));
|
dispatch(setDnsConfig(values));
|
||||||
};
|
};
|
||||||
|
|
||||||
const {
|
|
||||||
processingTestUpstream,
|
|
||||||
dnsConfig: {
|
|
||||||
upstream_dns,
|
|
||||||
bootstrap_dns,
|
|
||||||
processingSetConfig,
|
|
||||||
upstream_mode,
|
|
||||||
},
|
|
||||||
} = props;
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Card
|
<Card
|
||||||
title={t('upstream_dns')}
|
title={t('upstream_dns')}
|
||||||
|
@ -48,9 +45,4 @@ const Upstream = (props) => {
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
Upstream.propTypes = {
|
|
||||||
processingTestUpstream: PropTypes.bool.isRequired,
|
|
||||||
dnsConfig: PropTypes.object.isRequired,
|
|
||||||
};
|
|
||||||
|
|
||||||
export default Upstream;
|
export default Upstream;
|
||||||
|
|
|
@ -1,67 +1,40 @@
|
||||||
import React, { useEffect } from 'react';
|
import React, { useEffect } from 'react';
|
||||||
import PropTypes from 'prop-types';
|
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
|
|
||||||
|
import { useDispatch, useSelector } from 'react-redux';
|
||||||
import Upstream from './Upstream';
|
import Upstream from './Upstream';
|
||||||
import Access from './Access';
|
import Access from './Access';
|
||||||
import Config from './Config';
|
import Config from './Config';
|
||||||
import PageTitle from '../../ui/PageTitle';
|
import PageTitle from '../../ui/PageTitle';
|
||||||
import Loading from '../../ui/Loading';
|
import Loading from '../../ui/Loading';
|
||||||
import CacheConfig from './Cache';
|
import CacheConfig from './Cache';
|
||||||
|
import { getDnsConfig } from '../../../actions/dnsConfig';
|
||||||
|
import { getAccessList } from '../../../actions/access';
|
||||||
|
|
||||||
const Dns = (props) => {
|
const Dns = () => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
const dispatch = useDispatch();
|
||||||
|
const processing = useSelector((state) => state.access.processing);
|
||||||
|
const processingGetConfig = useSelector((state) => state.dnsConfig.processingGetConfig);
|
||||||
|
|
||||||
|
const isDataLoading = processing || processingGetConfig;
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
props.getAccessList();
|
dispatch(getAccessList());
|
||||||
props.getDnsConfig();
|
dispatch(getDnsConfig());
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const {
|
return <>
|
||||||
settings,
|
<PageTitle title={t('dns_settings')} />
|
||||||
access,
|
{isDataLoading
|
||||||
setAccessList,
|
? <Loading />
|
||||||
dnsConfig,
|
: <>
|
||||||
setDnsConfig,
|
<Upstream />
|
||||||
} = props;
|
<Config />
|
||||||
|
<CacheConfig />
|
||||||
const isDataLoading = access.processing || dnsConfig.processingGetConfig;
|
<Access />
|
||||||
|
</>}
|
||||||
return (
|
</>;
|
||||||
<>
|
|
||||||
<PageTitle title={t('dns_settings')} />
|
|
||||||
{isDataLoading
|
|
||||||
? <Loading />
|
|
||||||
: <>
|
|
||||||
<Upstream
|
|
||||||
processingTestUpstream={settings.processingTestUpstream}
|
|
||||||
dnsConfig={dnsConfig}
|
|
||||||
/>
|
|
||||||
<Config
|
|
||||||
dnsConfig={dnsConfig}
|
|
||||||
setDnsConfig={setDnsConfig}
|
|
||||||
/>
|
|
||||||
<CacheConfig
|
|
||||||
dnsConfig={dnsConfig}
|
|
||||||
setDnsConfig={setDnsConfig}
|
|
||||||
/>
|
|
||||||
<Access
|
|
||||||
access={access}
|
|
||||||
setAccessList={setAccessList}
|
|
||||||
/>
|
|
||||||
</>}
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
Dns.propTypes = {
|
|
||||||
settings: PropTypes.object.isRequired,
|
|
||||||
getAccessList: PropTypes.func.isRequired,
|
|
||||||
setAccessList: PropTypes.func.isRequired,
|
|
||||||
access: PropTypes.object.isRequired,
|
|
||||||
dnsConfig: PropTypes.object.isRequired,
|
|
||||||
setDnsConfig: PropTypes.func.isRequired,
|
|
||||||
getDnsConfig: PropTypes.func.isRequired,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export default Dns;
|
export default Dns;
|
||||||
|
|
|
@ -1,19 +1,20 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import PropTypes from 'prop-types';
|
import { Trans } from 'react-i18next';
|
||||||
import { Trans, withTranslation } from 'react-i18next';
|
|
||||||
import isAfter from 'date-fns/is_after';
|
import isAfter from 'date-fns/is_after';
|
||||||
import addDays from 'date-fns/add_days';
|
import addDays from 'date-fns/add_days';
|
||||||
|
import { useSelector } from 'react-redux';
|
||||||
import Topline from './Topline';
|
import Topline from './Topline';
|
||||||
import { EMPTY_DATE } from '../../helpers/constants';
|
import { EMPTY_DATE } from '../../helpers/constants';
|
||||||
|
|
||||||
const EncryptionTopline = (props) => {
|
const EncryptionTopline = () => {
|
||||||
if (props.notAfter === EMPTY_DATE) {
|
const not_after = useSelector((state) => state.encryption.not_after);
|
||||||
return false;
|
|
||||||
|
if (not_after === EMPTY_DATE) {
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
const isAboutExpire = isAfter(addDays(Date.now(), 30), props.notAfter);
|
const isAboutExpire = isAfter(addDays(Date.now(), 30), not_after);
|
||||||
const isExpired = isAfter(Date.now(), props.notAfter);
|
const isExpired = isAfter(Date.now(), not_after);
|
||||||
|
|
||||||
if (isExpired) {
|
if (isExpired) {
|
||||||
return (
|
return (
|
||||||
|
@ -23,7 +24,9 @@ const EncryptionTopline = (props) => {
|
||||||
</Trans>
|
</Trans>
|
||||||
</Topline>
|
</Topline>
|
||||||
);
|
);
|
||||||
} if (isAboutExpire) {
|
}
|
||||||
|
|
||||||
|
if (isAboutExpire) {
|
||||||
return (
|
return (
|
||||||
<Topline type="warning">
|
<Topline type="warning">
|
||||||
<Trans components={[<a href="#encryption" key="0">link</a>]}>
|
<Trans components={[<a href="#encryption" key="0">link</a>]}>
|
||||||
|
@ -36,8 +39,4 @@ const EncryptionTopline = (props) => {
|
||||||
return false;
|
return false;
|
||||||
};
|
};
|
||||||
|
|
||||||
EncryptionTopline.propTypes = {
|
export default EncryptionTopline;
|
||||||
notAfter: PropTypes.string.isRequired,
|
|
||||||
};
|
|
||||||
|
|
||||||
export default withTranslation()(EncryptionTopline);
|
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import PropTypes from 'prop-types';
|
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
|
|
||||||
|
@ -28,7 +27,7 @@ const linksData = [
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
const Footer = (props) => {
|
const Footer = () => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
|
||||||
const getYear = () => {
|
const getYear = () => {
|
||||||
|
@ -59,11 +58,6 @@ const Footer = (props) => {
|
||||||
{t(name)}
|
{t(name)}
|
||||||
</a>);
|
</a>);
|
||||||
|
|
||||||
|
|
||||||
const {
|
|
||||||
dnsVersion, processingVersion, getVersion, checkUpdateFlag,
|
|
||||||
} = props;
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<footer className="footer">
|
<footer className="footer">
|
||||||
|
@ -94,12 +88,7 @@ const Footer = (props) => {
|
||||||
<div className="footer__row">
|
<div className="footer__row">
|
||||||
{renderCopyright()}
|
{renderCopyright()}
|
||||||
<div className="footer__column footer__column--language">
|
<div className="footer__column footer__column--language">
|
||||||
<Version
|
<Version />
|
||||||
dnsVersion={dnsVersion}
|
|
||||||
processingVersion={processingVersion}
|
|
||||||
getVersion={getVersion}
|
|
||||||
checkUpdateFlag={checkUpdateFlag}
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -108,11 +97,4 @@ const Footer = (props) => {
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
Footer.propTypes = {
|
|
||||||
dnsVersion: PropTypes.string,
|
|
||||||
processingVersion: PropTypes.bool,
|
|
||||||
getVersion: PropTypes.func,
|
|
||||||
checkUpdateFlag: PropTypes.bool,
|
|
||||||
};
|
|
||||||
|
|
||||||
export default Footer;
|
export default Footer;
|
||||||
|
|
|
@ -1,9 +1,14 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
|
import classNames from 'classnames';
|
||||||
import './Loading.css';
|
import './Loading.css';
|
||||||
|
|
||||||
const Loading = () => (
|
const Loading = ({ className }) => (
|
||||||
<div className="loading" />
|
<div className={classNames('loading', className)} />
|
||||||
);
|
);
|
||||||
|
|
||||||
|
Loading.propTypes = {
|
||||||
|
className: PropTypes.string,
|
||||||
|
};
|
||||||
|
|
||||||
export default Loading;
|
export default Loading;
|
||||||
|
|
|
@ -1,14 +1,13 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import PropTypes from 'prop-types';
|
import { Trans } from 'react-i18next';
|
||||||
import { Trans, withTranslation } from 'react-i18next';
|
|
||||||
import classnames from 'classnames';
|
import classnames from 'classnames';
|
||||||
|
import { useSelector } from 'react-redux';
|
||||||
import './Overlay.css';
|
import './Overlay.css';
|
||||||
|
|
||||||
const UpdateOverlay = (props) => {
|
const UpdateOverlay = () => {
|
||||||
const overlayClass = classnames({
|
const processingUpdate = useSelector((state) => state.dashboard.processingUpdate);
|
||||||
overlay: true,
|
const overlayClass = classnames('overlay', {
|
||||||
'overlay--visible': props.processingUpdate,
|
'overlay--visible': processingUpdate,
|
||||||
});
|
});
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
@ -19,8 +18,4 @@ const UpdateOverlay = (props) => {
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
UpdateOverlay.propTypes = {
|
export default UpdateOverlay;
|
||||||
processingUpdate: PropTypes.bool,
|
|
||||||
};
|
|
||||||
|
|
||||||
export default withTranslation()(UpdateOverlay);
|
|
||||||
|
|
|
@ -1,42 +1,46 @@
|
||||||
import React, { Fragment } from 'react';
|
import React from 'react';
|
||||||
import PropTypes from 'prop-types';
|
import { Trans } from 'react-i18next';
|
||||||
import { Trans, withTranslation } from 'react-i18next';
|
import { shallowEqual, useDispatch, useSelector } from 'react-redux';
|
||||||
|
|
||||||
import Topline from './Topline';
|
import Topline from './Topline';
|
||||||
|
import { getUpdate } from '../../actions';
|
||||||
|
|
||||||
const UpdateTopline = (props) => (
|
const UpdateTopline = () => {
|
||||||
<Topline type="info">
|
const {
|
||||||
<Fragment>
|
announcementUrl,
|
||||||
|
newVersion,
|
||||||
|
canAutoUpdate,
|
||||||
|
processingUpdate,
|
||||||
|
} = useSelector((state) => state.dashboard, shallowEqual);
|
||||||
|
const dispatch = useDispatch();
|
||||||
|
|
||||||
|
const handleUpdate = () => {
|
||||||
|
dispatch(getUpdate());
|
||||||
|
};
|
||||||
|
|
||||||
|
return <Topline type="info">
|
||||||
|
<>
|
||||||
<Trans
|
<Trans
|
||||||
values={{ version: props.version }}
|
values={{ version: newVersion }}
|
||||||
components={[
|
components={[
|
||||||
<a href={props.url} target="_blank" rel="noopener noreferrer" key="0">
|
<a href={announcementUrl} target="_blank" rel="noopener noreferrer" key="0">
|
||||||
Click here
|
Click here
|
||||||
</a>,
|
</a>,
|
||||||
]}
|
]}
|
||||||
>
|
>
|
||||||
update_announcement
|
update_announcement
|
||||||
</Trans>
|
</Trans>
|
||||||
{props.canAutoUpdate
|
{canAutoUpdate
|
||||||
&& <button
|
&& <button
|
||||||
type="button"
|
type="button"
|
||||||
className="btn btn-sm btn-primary ml-3"
|
className="btn btn-sm btn-primary ml-3"
|
||||||
onClick={props.getUpdate}
|
onClick={handleUpdate}
|
||||||
disabled={props.processingUpdate}
|
disabled={processingUpdate}
|
||||||
>
|
>
|
||||||
<Trans>update_now</Trans>
|
<Trans>update_now</Trans>
|
||||||
</button>
|
</button>
|
||||||
}
|
}
|
||||||
</Fragment>
|
</>
|
||||||
</Topline>
|
</Topline>;
|
||||||
);
|
|
||||||
|
|
||||||
UpdateTopline.propTypes = {
|
|
||||||
version: PropTypes.string,
|
|
||||||
url: PropTypes.string.isRequired,
|
|
||||||
canAutoUpdate: PropTypes.bool,
|
|
||||||
getUpdate: PropTypes.func,
|
|
||||||
processingUpdate: PropTypes.bool,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export default withTranslation()(UpdateTopline);
|
export default UpdateTopline;
|
||||||
|
|
|
@ -1,13 +1,21 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import PropTypes from 'prop-types';
|
import { Trans, useTranslation } from 'react-i18next';
|
||||||
import { Trans, withTranslation } from 'react-i18next';
|
import { shallowEqual, useDispatch, useSelector } from 'react-redux';
|
||||||
|
import { getVersion } from '../../actions';
|
||||||
import './Version.css';
|
import './Version.css';
|
||||||
|
|
||||||
const Version = (props) => {
|
const Version = () => {
|
||||||
|
const dispatch = useDispatch();
|
||||||
|
const { t } = useTranslation();
|
||||||
const {
|
const {
|
||||||
dnsVersion, processingVersion, t, checkUpdateFlag,
|
dnsVersion,
|
||||||
} = props;
|
processingVersion,
|
||||||
|
checkUpdateFlag,
|
||||||
|
} = useSelector((state) => state?.dashboard ?? {}, shallowEqual);
|
||||||
|
|
||||||
|
const onClick = () => {
|
||||||
|
dispatch(getVersion(true));
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="version">
|
<div className="version">
|
||||||
|
@ -20,7 +28,7 @@ const Version = (props) => {
|
||||||
{checkUpdateFlag && <button
|
{checkUpdateFlag && <button
|
||||||
type="button"
|
type="button"
|
||||||
className="btn btn-icon btn-icon-sm btn-outline-primary btn-sm ml-2"
|
className="btn btn-icon btn-icon-sm btn-outline-primary btn-sm ml-2"
|
||||||
onClick={() => props.getVersion(true)}
|
onClick={onClick}
|
||||||
disabled={processingVersion}
|
disabled={processingVersion}
|
||||||
title={t('check_updates_now')}
|
title={t('check_updates_now')}
|
||||||
>
|
>
|
||||||
|
@ -33,12 +41,4 @@ const Version = (props) => {
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
Version.propTypes = {
|
export default Version;
|
||||||
dnsVersion: PropTypes.string,
|
|
||||||
getVersion: PropTypes.func,
|
|
||||||
processingVersion: PropTypes.bool,
|
|
||||||
checkUpdateFlag: PropTypes.bool,
|
|
||||||
t: PropTypes.func.isRequired,
|
|
||||||
};
|
|
||||||
|
|
||||||
export default withTranslation()(Version);
|
|
||||||
|
|
|
@ -1 +1,4 @@
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#9aa0ac" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-chevron-down"><polyline points="6 9 12 15 18 9"></polyline></svg>
|
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#9aa0ac"
|
||||||
|
stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-chevron-down">
|
||||||
|
<polyline points="6 9 12 15 18 9"></polyline>
|
||||||
|
</svg>
|
||||||
|
|
Before Width: | Height: | Size: 264 B After Width: | Height: | Size: 276 B |
|
@ -1 +1,5 @@
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#9aa0ac" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-globe"><circle cx="12" cy="12" r="10"/><path d="M2 12h20M12 2a15.3 15.3 0 0 1 4 10 15.3 15.3 0 0 1-4 10 15.3 15.3 0 0 1-4-10 15.3 15.3 0 0 1 4-10z"/></svg>
|
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#9aa0ac"
|
||||||
|
stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-globe">
|
||||||
|
<circle cx="12" cy="12" r="10"/>
|
||||||
|
<path d="M2 12h20M12 2a15.3 15.3 0 0 1 4 10 15.3 15.3 0 0 1-4 10 15.3 15.3 0 0 1-4-10 15.3 15.3 0 0 1 4-10z"/>
|
||||||
|
</svg>
|
||||||
|
|
Before Width: | Height: | Size: 354 B After Width: | Height: | Size: 371 B |
|
@ -1 +1,6 @@
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#9aa0ac" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-help-circle"><circle cx="12" cy="12" r="10"></circle><path d="M9.09 9a3 3 0 0 1 5.83 1c0 2-3 3-3 3"></path><line x1="12" y1="17" x2="12" y2="17"></line></svg>
|
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#9aa0ac"
|
||||||
|
stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-help-circle">
|
||||||
|
<circle cx="12" cy="12" r="10"></circle>
|
||||||
|
<path d="M9.09 9a3 3 0 0 1 5.83 1c0 2-3 3-3 3"></path>
|
||||||
|
<line x1="12" y1="17" x2="12" y2="17"></line>
|
||||||
|
</svg>
|
||||||
|
|
Before Width: | Height: | Size: 357 B After Width: | Height: | Size: 379 B |
|
@ -1 +1,8 @@
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" width="164" height="41" viewBox="0 0 164 41"><g fill-rule="evenodd"><path d="M129.984 22l-1.162-2.945h-5.792L121.931 22H118l6.277-15h3.509L134 22h-4.016zm-4.016-10.996l-1.902 5.149h3.762l-1.86-5.149zM117 16.1c0 .88-.153 1.682-.46 2.404a5.223 5.223 0 0 1-1.318 1.857c-.57.516-1.26.918-2.066 1.207-.807.289-1.703.433-2.688.433-1 0-1.9-.144-2.699-.433-.8-.29-1.477-.691-2.034-1.207a5.232 5.232 0 0 1-1.285-1.857c-.3-.722-.45-1.524-.45-2.404V7h3.64v8.81c0 .4.054.777.161 1.135.108.358.272.677.493.96.221.281.514.505.878.67.364.165.803.248 1.317.248.514 0 .953-.083 1.317-.248.365-.165.66-.389.89-.67.228-.283.392-.602.492-.96.1-.358.15-.736.15-1.135V7H117v9.099zm-16 4.673c-.733.362-1.59.658-2.57.886-.98.228-2.047.342-3.203.342-1.199 0-2.302-.181-3.31-.544-1.008-.362-1.875-.872-2.601-1.53a6.977 6.977 0 0 1-1.703-2.366c-.409-.92-.613-1.943-.613-3.07 0-1.141.208-2.175.624-3.1a6.903 6.903 0 0 1 1.723-2.367 7.71 7.71 0 0 1 2.58-1.5C92.914 7.174 93.98 7 95.121 7c1.184 0 2.284.171 3.299.513 1.015.343 1.84.802 2.474 1.38l-2.284 2.476c-.352-.39-.817-.708-1.395-.956-.579-.249-1.234-.373-1.967-.373-.635 0-1.22.111-1.756.332a4.23 4.23 0 0 0-1.395.927 4.178 4.178 0 0 0-.92 1.41 4.734 4.734 0 0 0-.328 1.78c0 .659.099 1.263.296 1.813.197.55.49 1.024.878 1.42.387.395.867.704 1.438.926.57.221 1.223.332 1.956.332.423 0 .825-.03 1.205-.09.381-.061.733-.158 1.058-.293V16h-2.855v-2.779H101v7.55zm63-6.314c0 1.313-.244 2.447-.73 3.4a6.855 6.855 0 0 1-1.928 2.352 8.035 8.035 0 0 1-2.7 1.356 10.94 10.94 0 0 1-3.05.434H150V7h5.422c1.06 0 2.104.124 3.135.37a7.866 7.866 0 0 1 2.753 1.23c.805.572 1.454 1.338 1.949 2.298.494.96.741 2.147.741 3.56zm-3.77 0c0-.848-.138-1.55-.413-2.108a3.549 3.549 0 0 0-1.101-1.335 4.405 4.405 0 0 0-1.568-.71 7.7 7.7 0 0 0-1.81-.212h-1.8v8.771h1.715c.65 0 1.274-.074 1.874-.222a4.43 4.43 0 0 0 1.589-.731 3.62 3.62 0 0 0 1.1-1.356c.276-.565.414-1.264.414-2.097zm-75.23 0c0 1.313-.244 2.447-.73 3.4a6.855 6.855 0 0 1-1.928 2.352 8.035 8.035 0 0 1-2.7 1.356 10.94 10.94 0 0 1-3.05.434H71V7h5.422c1.06 0 2.104.124 3.135.37A7.866 7.866 0 0 1 82.31 8.6c.805.572 1.454 1.338 1.949 2.298.494.96.741 2.147.741 3.56zm-3.77 0c0-.848-.138-1.55-.413-2.108a3.549 3.549 0 0 0-1.101-1.335 4.405 4.405 0 0 0-1.568-.71 7.7 7.7 0 0 0-1.81-.212h-1.8v8.771h1.715c.65 0 1.274-.074 1.874-.222a4.43 4.43 0 0 0 1.589-.731 3.62 3.62 0 0 0 1.1-1.356c.276-.565.414-1.264.414-2.097zM65.984 22l-1.162-2.945H59.03L57.931 22H54l6.277-15h3.509L70 22h-4.016zm-4.016-10.996l-1.902 5.149h3.762l-1.86-5.149zM143.855 22l-3.171-5.953h-1.202V22H136V7h5.596c.705 0 1.392.074 2.062.222.67.149 1.271.4 1.803.753a3.9 3.9 0 0 1 1.275 1.398c.318.579.476 1.3.476 2.16 0 1.018-.269 1.872-.808 2.564-.539.693-1.285 1.187-2.238 1.484L148 22h-4.145zm-.145-10.403c0-.353-.073-.639-.218-.858a1.502 1.502 0 0 0-.56-.508 2.393 2.393 0 0 0-.766-.244 5.535 5.535 0 0 0-.819-.063h-1.886v3.495h1.679c.29 0 .587-.024.891-.074.304-.05.58-.137.83-.264.248-.128.452-.311.61-.551.16-.24.239-.551.239-.933zM55 37.851v-8.702h.951v3.866h4.866V29.15h.952v8.702h-.952v-3.916h-4.866v3.916H55zM68.068 38c-2.565 0-4.288-2.076-4.288-4.5 0-2.4 1.747-4.5 4.312-4.5 2.565 0 4.288 2.076 4.288 4.5 0 2.4-1.747 4.5-4.312 4.5zm.024-.907c1.927 0 3.3-1.592 3.3-3.593 0-1.977-1.397-3.593-3.324-3.593-1.927 0-3.3 1.592-3.3 3.593 0 1.977 1.397 3.593 3.324 3.593zm6.3.758v-8.702h.963l3.07 4.749 3.072-4.749h.964v8.702h-.952v-7.049l-3.071 4.662h-.048l-3.071-4.65v7.037h-.928zm10.453 0v-8.702h6.095v.895h-5.143v2.971h4.6v.895h-4.6v3.046H91v.895h-6.155z"/><path fill-rule="nonzero" d="M2.831 14.045c.775 4.287 2.266 8.333 4.685 12.143 2.958 4.659 7.21 8.797 12.984 12.319 5.774-3.522 10.026-7.66 12.984-12.319 2.42-3.81 3.91-7.856 4.685-12.143.489-2.706.644-4.844.672-8.003C33.368 3.522 26.636 2.14 20.5 2.14c-6.137 0-12.869 1.381-18.341 3.9.028 3.16.183 5.298.672 8.004zM20.5 0C26.908 0 34.637 1.47 41 4.706c0 6.988.087 24.398-20.5 36.294C-.088 29.104 0 11.694 0 4.706 6.363 1.47 14.092 0 20.5 0z"/><path d="M20.234 27L33 11.344c-.935-.682-1.756-.2-2.208.172l-.016.001-10.644 10.076-4.01-4.392c-1.913-2.011-4.514-.477-5.122-.072L20.234 27"/></g></svg>
|
<svg xmlns="http://www.w3.org/2000/svg" width="164" height="41" viewBox="0 0 164 41">
|
||||||
|
<g fill-rule="evenodd">
|
||||||
|
<path d="M129.984 22l-1.162-2.945h-5.792L121.931 22H118l6.277-15h3.509L134 22h-4.016zm-4.016-10.996l-1.902 5.149h3.762l-1.86-5.149zM117 16.1c0 .88-.153 1.682-.46 2.404a5.223 5.223 0 0 1-1.318 1.857c-.57.516-1.26.918-2.066 1.207-.807.289-1.703.433-2.688.433-1 0-1.9-.144-2.699-.433-.8-.29-1.477-.691-2.034-1.207a5.232 5.232 0 0 1-1.285-1.857c-.3-.722-.45-1.524-.45-2.404V7h3.64v8.81c0 .4.054.777.161 1.135.108.358.272.677.493.96.221.281.514.505.878.67.364.165.803.248 1.317.248.514 0 .953-.083 1.317-.248.365-.165.66-.389.89-.67.228-.283.392-.602.492-.96.1-.358.15-.736.15-1.135V7H117v9.099zm-16 4.673c-.733.362-1.59.658-2.57.886-.98.228-2.047.342-3.203.342-1.199 0-2.302-.181-3.31-.544-1.008-.362-1.875-.872-2.601-1.53a6.977 6.977 0 0 1-1.703-2.366c-.409-.92-.613-1.943-.613-3.07 0-1.141.208-2.175.624-3.1a6.903 6.903 0 0 1 1.723-2.367 7.71 7.71 0 0 1 2.58-1.5C92.914 7.174 93.98 7 95.121 7c1.184 0 2.284.171 3.299.513 1.015.343 1.84.802 2.474 1.38l-2.284 2.476c-.352-.39-.817-.708-1.395-.956-.579-.249-1.234-.373-1.967-.373-.635 0-1.22.111-1.756.332a4.23 4.23 0 0 0-1.395.927 4.178 4.178 0 0 0-.92 1.41 4.734 4.734 0 0 0-.328 1.78c0 .659.099 1.263.296 1.813.197.55.49 1.024.878 1.42.387.395.867.704 1.438.926.57.221 1.223.332 1.956.332.423 0 .825-.03 1.205-.09.381-.061.733-.158 1.058-.293V16h-2.855v-2.779H101v7.55zm63-6.314c0 1.313-.244 2.447-.73 3.4a6.855 6.855 0 0 1-1.928 2.352 8.035 8.035 0 0 1-2.7 1.356 10.94 10.94 0 0 1-3.05.434H150V7h5.422c1.06 0 2.104.124 3.135.37a7.866 7.866 0 0 1 2.753 1.23c.805.572 1.454 1.338 1.949 2.298.494.96.741 2.147.741 3.56zm-3.77 0c0-.848-.138-1.55-.413-2.108a3.549 3.549 0 0 0-1.101-1.335 4.405 4.405 0 0 0-1.568-.71 7.7 7.7 0 0 0-1.81-.212h-1.8v8.771h1.715c.65 0 1.274-.074 1.874-.222a4.43 4.43 0 0 0 1.589-.731 3.62 3.62 0 0 0 1.1-1.356c.276-.565.414-1.264.414-2.097zm-75.23 0c0 1.313-.244 2.447-.73 3.4a6.855 6.855 0 0 1-1.928 2.352 8.035 8.035 0 0 1-2.7 1.356 10.94 10.94 0 0 1-3.05.434H71V7h5.422c1.06 0 2.104.124 3.135.37A7.866 7.866 0 0 1 82.31 8.6c.805.572 1.454 1.338 1.949 2.298.494.96.741 2.147.741 3.56zm-3.77 0c0-.848-.138-1.55-.413-2.108a3.549 3.549 0 0 0-1.101-1.335 4.405 4.405 0 0 0-1.568-.71 7.7 7.7 0 0 0-1.81-.212h-1.8v8.771h1.715c.65 0 1.274-.074 1.874-.222a4.43 4.43 0 0 0 1.589-.731 3.62 3.62 0 0 0 1.1-1.356c.276-.565.414-1.264.414-2.097zM65.984 22l-1.162-2.945H59.03L57.931 22H54l6.277-15h3.509L70 22h-4.016zm-4.016-10.996l-1.902 5.149h3.762l-1.86-5.149zM143.855 22l-3.171-5.953h-1.202V22H136V7h5.596c.705 0 1.392.074 2.062.222.67.149 1.271.4 1.803.753a3.9 3.9 0 0 1 1.275 1.398c.318.579.476 1.3.476 2.16 0 1.018-.269 1.872-.808 2.564-.539.693-1.285 1.187-2.238 1.484L148 22h-4.145zm-.145-10.403c0-.353-.073-.639-.218-.858a1.502 1.502 0 0 0-.56-.508 2.393 2.393 0 0 0-.766-.244 5.535 5.535 0 0 0-.819-.063h-1.886v3.495h1.679c.29 0 .587-.024.891-.074.304-.05.58-.137.83-.264.248-.128.452-.311.61-.551.16-.24.239-.551.239-.933zM55 37.851v-8.702h.951v3.866h4.866V29.15h.952v8.702h-.952v-3.916h-4.866v3.916H55zM68.068 38c-2.565 0-4.288-2.076-4.288-4.5 0-2.4 1.747-4.5 4.312-4.5 2.565 0 4.288 2.076 4.288 4.5 0 2.4-1.747 4.5-4.312 4.5zm.024-.907c1.927 0 3.3-1.592 3.3-3.593 0-1.977-1.397-3.593-3.324-3.593-1.927 0-3.3 1.592-3.3 3.593 0 1.977 1.397 3.593 3.324 3.593zm6.3.758v-8.702h.963l3.07 4.749 3.072-4.749h.964v8.702h-.952v-7.049l-3.071 4.662h-.048l-3.071-4.65v7.037h-.928zm10.453 0v-8.702h6.095v.895h-5.143v2.971h4.6v.895h-4.6v3.046H91v.895h-6.155z"/>
|
||||||
|
<path fill-rule="nonzero"
|
||||||
|
d="M2.831 14.045c.775 4.287 2.266 8.333 4.685 12.143 2.958 4.659 7.21 8.797 12.984 12.319 5.774-3.522 10.026-7.66 12.984-12.319 2.42-3.81 3.91-7.856 4.685-12.143.489-2.706.644-4.844.672-8.003C33.368 3.522 26.636 2.14 20.5 2.14c-6.137 0-12.869 1.381-18.341 3.9.028 3.16.183 5.298.672 8.004zM20.5 0C26.908 0 34.637 1.47 41 4.706c0 6.988.087 24.398-20.5 36.294C-.088 29.104 0 11.694 0 4.706 6.363 1.47 14.092 0 20.5 0z"/>
|
||||||
|
<path d="M20.234 27L33 11.344c-.935-.682-1.756-.2-2.208.172l-.016.001-10.644 10.076-4.01-4.392c-1.913-2.011-4.514-.477-5.122-.072L20.234 27"/>
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
|
Before Width: | Height: | Size: 4.0 KiB After Width: | Height: | Size: 4.1 KiB |
|
@ -1 +1,7 @@
|
||||||
<svg stroke="#84868c" fill="none" height="24" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" viewBox="0 0 24 24" width="24" xmlns="http://www.w3.org/2000/svg"><path d="m3 6h2 16"/><path d="m19 6v14a2 2 0 0 1 -2 2h-10a2 2 0 0 1 -2-2v-14m3 0v-2a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2"/><path d="m10 11v6"/><path d="m14 11v6"/></svg>
|
<svg stroke="#84868c" fill="none" height="24" stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
|
||||||
|
viewBox="0 0 24 24" width="24" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path d="m3 6h2 16"/>
|
||||||
|
<path d="m19 6v14a2 2 0 0 1 -2 2h-10a2 2 0 0 1 -2-2v-14m3 0v-2a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2"/>
|
||||||
|
<path d="m10 11v6"/>
|
||||||
|
<path d="m14 11v6"/>
|
||||||
|
</svg>
|
||||||
|
|
Before Width: | Height: | Size: 340 B After Width: | Height: | Size: 367 B |
|
@ -1 +1,5 @@
|
||||||
<svg stroke="#84868c" fill="none" height="24" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" viewBox="0 0 24 24" width="24" xmlns="http://www.w3.org/2000/svg"><path d="m18 6-12 12"/><path d="m6 6 12 12"/></svg>
|
<svg stroke="#84868c" fill="none" height="24" stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
|
||||||
|
viewBox="0 0 24 24" width="24" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path d="m18 6-12 12"/>
|
||||||
|
<path d="m6 6 12 12"/>
|
||||||
|
</svg>
|
||||||
|
|
Before Width: | Height: | Size: 227 B After Width: | Height: | Size: 244 B |
|
@ -1,14 +0,0 @@
|
||||||
import { connect } from 'react-redux';
|
|
||||||
import * as actionCreators from '../actions';
|
|
||||||
import App from '../components/App';
|
|
||||||
|
|
||||||
const mapStateToProps = (state) => {
|
|
||||||
const { dashboard, encryption } = state;
|
|
||||||
const props = { dashboard, encryption };
|
|
||||||
return props;
|
|
||||||
};
|
|
||||||
|
|
||||||
export default connect(
|
|
||||||
mapStateToProps,
|
|
||||||
actionCreators,
|
|
||||||
)(App);
|
|
|
@ -1,18 +0,0 @@
|
||||||
import { connect } from 'react-redux';
|
|
||||||
import { getVersion } from '../actions';
|
|
||||||
import Header from '../components/Header';
|
|
||||||
|
|
||||||
const mapStateToProps = (state) => {
|
|
||||||
const { dashboard } = state;
|
|
||||||
const props = { dashboard };
|
|
||||||
return props;
|
|
||||||
};
|
|
||||||
|
|
||||||
const mapDispatchToProps = {
|
|
||||||
getVersion,
|
|
||||||
};
|
|
||||||
|
|
||||||
export default connect(
|
|
||||||
mapStateToProps,
|
|
||||||
mapDispatchToProps,
|
|
||||||
)(Header);
|
|
|
@ -363,10 +363,11 @@ export const RESPONSE_FILTER = {
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
export const RESPONSE_FILTER_QUERIES = Object.values(RESPONSE_FILTER).reduce((acc, { query }) => {
|
export const RESPONSE_FILTER_QUERIES = Object.values(RESPONSE_FILTER)
|
||||||
acc[query] = query;
|
.reduce((acc, { query }) => {
|
||||||
return acc;
|
acc[query] = query;
|
||||||
}, {});
|
return acc;
|
||||||
|
}, {});
|
||||||
|
|
||||||
export const FILTERED_STATUS_TO_META_MAP = {
|
export const FILTERED_STATUS_TO_META_MAP = {
|
||||||
[FILTERED_STATUS.NOT_FILTERED_WHITE_LIST]: {
|
[FILTERED_STATUS.NOT_FILTERED_WHITE_LIST]: {
|
||||||
|
|
|
@ -1,13 +1,14 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import ReactDOM from 'react-dom';
|
import ReactDOM from 'react-dom';
|
||||||
import { Provider } from 'react-redux';
|
import { Provider } from 'react-redux';
|
||||||
import './components/App/index.css';
|
|
||||||
import App from './containers/App';
|
|
||||||
import configureStore from './configureStore';
|
import configureStore from './configureStore';
|
||||||
import reducers from './reducers';
|
import reducers from './reducers';
|
||||||
|
import App from './components/App';
|
||||||
|
import './components/App/index.css';
|
||||||
import './i18n';
|
import './i18n';
|
||||||
|
|
||||||
const store = configureStore(reducers, {}); // set initial state
|
const store = configureStore(reducers, {}); // set initial state
|
||||||
|
|
||||||
ReactDOM.render(
|
ReactDOM.render(
|
||||||
<Provider store={store}>
|
<Provider store={store}>
|
||||||
<App />
|
<App />
|
||||||
|
|
|
@ -5,6 +5,7 @@ const flexBugsFixes = require('postcss-flexbugs-fixes');
|
||||||
const { CleanWebpackPlugin } = require('clean-webpack-plugin');
|
const { CleanWebpackPlugin } = require('clean-webpack-plugin');
|
||||||
const CopyPlugin = require('copy-webpack-plugin');
|
const CopyPlugin = require('copy-webpack-plugin');
|
||||||
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
|
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
|
||||||
|
const { BUILD_ENVS } = require('./constants');
|
||||||
|
|
||||||
const RESOURCES_PATH = path.resolve(__dirname);
|
const RESOURCES_PATH = path.resolve(__dirname);
|
||||||
const ENTRY_REACT = path.resolve(RESOURCES_PATH, 'src/index.js');
|
const ENTRY_REACT = path.resolve(RESOURCES_PATH, 'src/index.js');
|
||||||
|
@ -18,13 +19,10 @@ const ASSETS_PATH = path.resolve(RESOURCES_PATH, 'public/assets');
|
||||||
const PUBLIC_PATH = path.resolve(__dirname, '../build/static');
|
const PUBLIC_PATH = path.resolve(__dirname, '../build/static');
|
||||||
const PUBLIC_ASSETS_PATH = path.resolve(PUBLIC_PATH, 'assets');
|
const PUBLIC_ASSETS_PATH = path.resolve(PUBLIC_PATH, 'assets');
|
||||||
|
|
||||||
const BUILD_ENVS = {
|
|
||||||
dev: 'development',
|
|
||||||
prod: 'production',
|
|
||||||
};
|
|
||||||
|
|
||||||
const BUILD_ENV = BUILD_ENVS[process.env.BUILD_ENV];
|
const BUILD_ENV = BUILD_ENVS[process.env.BUILD_ENV];
|
||||||
|
|
||||||
|
const isDev = BUILD_ENV === BUILD_ENVS.dev;
|
||||||
|
|
||||||
const config = {
|
const config = {
|
||||||
mode: BUILD_ENV,
|
mode: BUILD_ENV,
|
||||||
target: 'web',
|
target: 'web',
|
||||||
|
@ -36,22 +34,35 @@ const config = {
|
||||||
},
|
},
|
||||||
output: {
|
output: {
|
||||||
path: PUBLIC_PATH,
|
path: PUBLIC_PATH,
|
||||||
filename: '[name].[chunkhash].js',
|
filename: '[name].[hash].js',
|
||||||
},
|
},
|
||||||
resolve: {
|
resolve: {
|
||||||
modules: ['node_modules'],
|
modules: ['node_modules'],
|
||||||
alias: {
|
alias: {
|
||||||
MainRoot: path.resolve(__dirname, '../'),
|
MainRoot: path.resolve(__dirname, '../'),
|
||||||
ClientRoot: path.resolve(__dirname, './src'),
|
ClientRoot: path.resolve(__dirname, './src'),
|
||||||
|
// TODO: change to '@hot-loader/react-dom' when v16.13.1 is released
|
||||||
|
// https://stackoverflow.com/a/62671689/12942752
|
||||||
|
'react-dom': 'react-dom',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
module: {
|
module: {
|
||||||
rules: [
|
rules: [
|
||||||
|
{
|
||||||
|
test: /\.ya?ml$/,
|
||||||
|
type: 'json',
|
||||||
|
use: 'yaml-loader',
|
||||||
|
},
|
||||||
{
|
{
|
||||||
test: /\.css$/i,
|
test: /\.css$/i,
|
||||||
use: [
|
use: [
|
||||||
'style-loader',
|
'style-loader',
|
||||||
MiniCssExtractPlugin.loader,
|
{
|
||||||
|
loader: MiniCssExtractPlugin.loader,
|
||||||
|
options: {
|
||||||
|
hmr: isDev,
|
||||||
|
},
|
||||||
|
},
|
||||||
{
|
{
|
||||||
loader: 'css-loader',
|
loader: 'css-loader',
|
||||||
options: {
|
options: {
|
||||||
|
@ -122,7 +133,8 @@ const config = {
|
||||||
template: HTML_LOGIN_PATH,
|
template: HTML_LOGIN_PATH,
|
||||||
}),
|
}),
|
||||||
new MiniCssExtractPlugin({
|
new MiniCssExtractPlugin({
|
||||||
filename: '[name].[contenthash].css',
|
filename: isDev ? '[name].css' : '[name].[hash].css',
|
||||||
|
chunkFilename: isDev ? '[id].css' : '[id].[hash].css',
|
||||||
}),
|
}),
|
||||||
new CopyPlugin({
|
new CopyPlugin({
|
||||||
patterns: [
|
patterns: [
|
||||||
|
|
|
@ -1,5 +1,50 @@
|
||||||
const merge = require('webpack-merge');
|
const merge = require('webpack-merge');
|
||||||
|
const yaml = require('js-yaml');
|
||||||
|
const fs = require('fs');
|
||||||
const common = require('./webpack.common.js');
|
const common = require('./webpack.common.js');
|
||||||
|
const { BASE_URL } = require('./constants');
|
||||||
|
|
||||||
|
const ZERO_HOST = '0.0.0.0';
|
||||||
|
const LOCALHOST = '127.0.0.1';
|
||||||
|
const DEFAULT_PORT = 80;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get document, or throw exception on error
|
||||||
|
* @returns {{bind_host: string, bind_port: number}}
|
||||||
|
*/
|
||||||
|
const importConfig = () => {
|
||||||
|
try {
|
||||||
|
const doc = yaml.safeLoad(fs.readFileSync('../AdguardHome.yaml', 'utf8'));
|
||||||
|
const { bind_host, bind_port } = doc;
|
||||||
|
return {
|
||||||
|
bind_host,
|
||||||
|
bind_port,
|
||||||
|
};
|
||||||
|
} catch (e) {
|
||||||
|
console.error(e);
|
||||||
|
return {
|
||||||
|
bind_host: ZERO_HOST,
|
||||||
|
bind_port: DEFAULT_PORT,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const getDevServerConfig = (proxyUrl = BASE_URL) => {
|
||||||
|
const { bind_host: host, bind_port: port } = importConfig();
|
||||||
|
const { DEV_SERVER_PORT } = process.env;
|
||||||
|
|
||||||
|
const devServerHost = host === ZERO_HOST ? LOCALHOST : host;
|
||||||
|
const devServerPort = DEV_SERVER_PORT || port + 8000;
|
||||||
|
|
||||||
|
return {
|
||||||
|
hot: true,
|
||||||
|
host: devServerHost,
|
||||||
|
port: devServerPort,
|
||||||
|
proxy: {
|
||||||
|
[proxyUrl]: `http://${devServerHost}:${port}`,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
module.exports = merge(common, {
|
module.exports = merge(common, {
|
||||||
devtool: 'eval-source-map',
|
devtool: 'eval-source-map',
|
||||||
|
@ -16,4 +61,5 @@ module.exports = merge(common, {
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
devServer: process.env.WEBPACK_DEV_SERVER ? getDevServerConfig(BASE_URL) : undefined,
|
||||||
});
|
});
|
||||||
|
|
|
@ -2,18 +2,19 @@ const StyleLintPlugin = require('stylelint-webpack-plugin');
|
||||||
const merge = require('webpack-merge');
|
const merge = require('webpack-merge');
|
||||||
const common = require('./webpack.common.js');
|
const common = require('./webpack.common.js');
|
||||||
|
|
||||||
|
|
||||||
module.exports = merge(common, {
|
module.exports = merge(common, {
|
||||||
module: {
|
module: {
|
||||||
rules: [{
|
rules: [
|
||||||
test: /\.js$/,
|
{
|
||||||
exclude: /node_modules/,
|
test: /\.js$/,
|
||||||
loader: 'eslint-loader',
|
exclude: /node_modules/,
|
||||||
options: {
|
loader: 'eslint-loader',
|
||||||
failOnError: true,
|
options: {
|
||||||
configFile: 'prod.eslintrc',
|
failOnError: true,
|
||||||
|
configFile: 'prod.eslintrc',
|
||||||
|
},
|
||||||
},
|
},
|
||||||
}],
|
],
|
||||||
},
|
},
|
||||||
plugins: [
|
plugins: [
|
||||||
new StyleLintPlugin({
|
new StyleLintPlugin({
|
||||||
|
|