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
2
.github/workflows/lint.yml
vendored
@ -44,4 +44,4 @@ jobs:
|
||||
fields: repo,message,commit,author
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_URL }}
|
||||
SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_URL }}
|
||||
|
1
client/babel.config.js
vendored
@ -11,6 +11,7 @@ module.exports = (api) => {
|
||||
'@babel/plugin-proposal-object-rest-spread',
|
||||
'@babel/plugin-proposal-nullish-coalescing-operator',
|
||||
'@babel/plugin-proposal-optional-chaining',
|
||||
'react-hot-loader/babel',
|
||||
],
|
||||
};
|
||||
};
|
||||
|
11
client/constants.js
vendored
Normal file
@ -0,0 +1,11 @@
|
||||
const BUILD_ENVS = {
|
||||
dev: 'development',
|
||||
prod: 'production',
|
||||
};
|
||||
|
||||
const BASE_URL = '/control';
|
||||
|
||||
module.exports = {
|
||||
BUILD_ENVS,
|
||||
BASE_URL,
|
||||
};
|
143
client/package-lock.json
generated
vendored
@ -1356,6 +1356,17 @@
|
||||
"resolved": "https://registry.npmjs.org/@emotion/weak-memoize/-/weak-memoize-0.2.5.tgz",
|
||||
"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": {
|
||||
"version": "1.1.0",
|
||||
"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/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": {
|
||||
"version": "3.0.3",
|
||||
"resolved": "https://registry.npmjs.org/@types/minimatch/-/minimatch-3.0.3.tgz",
|
||||
@ -2728,7 +2745,6 @@
|
||||
"version": "1.0.10",
|
||||
"resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz",
|
||||
"integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"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": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/domain-browser/-/domain-browser-1.2.0.tgz",
|
||||
@ -5201,9 +5223,9 @@
|
||||
"dev": true
|
||||
},
|
||||
"elliptic": {
|
||||
"version": "6.5.2",
|
||||
"resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.5.2.tgz",
|
||||
"integrity": "sha512-f4x70okzZbIQl/NSRLkI/+tteV/9WqL98zx+SQ69KbXxmVrmjwsNUPn/gYJJ0sHvEak24cZgHIPegRePAtA/xw==",
|
||||
"version": "6.5.3",
|
||||
"resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.5.3.tgz",
|
||||
"integrity": "sha512-IMqzv5wNQf+E6aHeIqATs0tOLeOTwj1QKbRcS3jBbYkl5oLAserA8yJTT7/VyHUYG91PRmPyeQDObKLPpeS4dw==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"bn.js": "^4.4.0",
|
||||
@ -5216,9 +5238,9 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"bn.js": {
|
||||
"version": "4.11.8",
|
||||
"resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.8.tgz",
|
||||
"integrity": "sha512-ItfYfPLkWHUjckQCk8xC+LwxgK8NYcXywGigJgSwOP8Y2iyWT4f2vsZnoOXTTbo+o5yXmIUJ4gn5538SO5S3gA==",
|
||||
"version": "4.11.9",
|
||||
"resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.9.tgz",
|
||||
"integrity": "sha512-E6QoYqCKZfgatHTdHzs1RRKP7ip4vvm+EyRUeE2RF0NblwVvb0p6jSVeNTOFxPn26QXN2o6SMfNxKp6kU8zQaw==",
|
||||
"dev": true
|
||||
}
|
||||
}
|
||||
@ -5887,8 +5909,7 @@
|
||||
"esprima": {
|
||||
"version": "4.0.1",
|
||||
"resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz",
|
||||
"integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==",
|
||||
"dev": true
|
||||
"integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A=="
|
||||
},
|
||||
"esquery": {
|
||||
"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": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/global-modules/-/global-modules-2.0.0.tgz",
|
||||
@ -7357,18 +7388,6 @@
|
||||
"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": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz",
|
||||
@ -9898,10 +9917,9 @@
|
||||
"integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ=="
|
||||
},
|
||||
"js-yaml": {
|
||||
"version": "3.13.1",
|
||||
"resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.13.1.tgz",
|
||||
"integrity": "sha512-YfbcO7jXDdyj0DGxYVSlSeQNHbD7XPWvrVWeVUujrQEoZzWJIRrCPoyk6kL6IAjAG2IolMK4T0hNUe0HOUs5Jw==",
|
||||
"dev": true,
|
||||
"version": "3.14.0",
|
||||
"resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.0.tgz",
|
||||
"integrity": "sha512-/4IbIeHcD9VMHFqDR/gQ7EdZdLimOvW2DdcxFjdyyZ9NsbS+ccrXqVWDtab/lRl5AlUqmpBx8EhPaWR+OtY17A==",
|
||||
"requires": {
|
||||
"argparse": "^1.0.7",
|
||||
"esprima": "^4.0.0"
|
||||
@ -10628,6 +10646,15 @@
|
||||
"integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==",
|
||||
"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": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/min-indent/-/min-indent-1.0.0.tgz",
|
||||
@ -12287,6 +12314,39 @@
|
||||
"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": {
|
||||
"version": "11.4.0",
|
||||
"resolved": "https://registry.npmjs.org/react-i18next/-/react-i18next-11.4.0.tgz",
|
||||
@ -13328,6 +13388,12 @@
|
||||
"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": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz",
|
||||
@ -13491,7 +13557,8 @@
|
||||
},
|
||||
"kind-of": {
|
||||
"version": "6.0.2",
|
||||
"resolved": "",
|
||||
"resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz",
|
||||
"integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==",
|
||||
"dev": true
|
||||
}
|
||||
}
|
||||
@ -13751,8 +13818,7 @@
|
||||
"sprintf-js": {
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz",
|
||||
"integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=",
|
||||
"dev": true
|
||||
"integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw="
|
||||
},
|
||||
"sshpk": {
|
||||
"version": "1.16.1",
|
||||
@ -14079,12 +14145,13 @@
|
||||
}
|
||||
},
|
||||
"schema-utils": {
|
||||
"version": "2.6.6",
|
||||
"resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-2.6.6.tgz",
|
||||
"integrity": "sha512-wHutF/WPSbIi9x6ctjGGk2Hvl0VOz5l3EKEuKbjPlB30mKZUzb9A5k9yEXRX3pwyqVLPvpfZZEllaFq/M718hA==",
|
||||
"version": "2.7.0",
|
||||
"resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-2.7.0.tgz",
|
||||
"integrity": "sha512-0ilKFI6QQF5nxDZLFn2dMjvc4hjg/Wkg7rHd3jK6/A4a1Hl9VFdQWvgB1UMGoU94pad1P/8N7fMcEnLnSiju8A==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"ajv": "^6.12.0",
|
||||
"@types/json-schema": "^7.0.4",
|
||||
"ajv": "^6.12.2",
|
||||
"ajv-keywords": "^3.4.1"
|
||||
}
|
||||
}
|
||||
@ -15930,6 +15997,18 @@
|
||||
"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": {
|
||||
"version": "2.1.2",
|
||||
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
|
||||
|
6
client/package.json
vendored
@ -4,14 +4,16 @@
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"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",
|
||||
"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:fix": "eslint src --fix",
|
||||
"test": "jest",
|
||||
"test:watch": "jest --watch"
|
||||
},
|
||||
"dependencies": {
|
||||
"@hot-loader/react-dom": "^16.13.0",
|
||||
"@nivo/line": "^0.49.1",
|
||||
"axios": "^0.19.2",
|
||||
"classnames": "^2.2.6",
|
||||
@ -19,6 +21,7 @@
|
||||
"i18next": "^19.4.4",
|
||||
"i18next-browser-languagedetector": "^4.2.0",
|
||||
"ipaddr.js": "^1.9.1",
|
||||
"js-yaml": "^3.14.0",
|
||||
"lodash": "^4.17.19",
|
||||
"nanoid": "^3.1.9",
|
||||
"prop-types": "^15.7.2",
|
||||
@ -73,6 +76,7 @@
|
||||
"path": "^0.12.7",
|
||||
"postcss-flexbugs-fixes": "4.2.1",
|
||||
"postcss-loader": "^3.0.0",
|
||||
"react-hot-loader": "^4.12.21",
|
||||
"style-loader": "^1.2.1",
|
||||
"stylelint": "^13.5.0",
|
||||
"stylelint-webpack-plugin": "2.0.0",
|
||||
|
@ -148,7 +148,7 @@ const checkStatus = async (handleRequestSuccess, handleRequestError, attempts =
|
||||
const rmTimeout = (t) => t && clearTimeout(t);
|
||||
|
||||
try {
|
||||
const response = await axios.get('control/status');
|
||||
const response = await axios.get(`${apiClient.baseUrl}/status`);
|
||||
rmTimeout(timeout);
|
||||
if (response?.status === 200) {
|
||||
handleRequestSuccess(response);
|
||||
|
@ -2,9 +2,10 @@ import axios from 'axios';
|
||||
|
||||
import { getPathWithQueryString } from '../helpers/helpers';
|
||||
import { R_PATH_LAST_PART } from '../helpers/constants';
|
||||
import { BASE_URL } from '../../constants';
|
||||
|
||||
class Api {
|
||||
baseUrl = 'control';
|
||||
baseUrl = BASE_URL;
|
||||
|
||||
async makeRequest(path, method = 'POST', config) {
|
||||
try {
|
||||
@ -26,18 +27,30 @@ class Api {
|
||||
|
||||
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_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() {
|
||||
const { path, method } = this.GLOBAL_STATUS;
|
||||
@ -68,21 +81,45 @@ class Api {
|
||||
}
|
||||
|
||||
// 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() {
|
||||
const { path, method } = this.FILTERING_STATUS;
|
||||
@ -153,11 +190,20 @@ class Api {
|
||||
}
|
||||
|
||||
// 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() {
|
||||
const { path, method } = this.PARENTAL_STATUS;
|
||||
@ -180,11 +226,20 @@ class Api {
|
||||
}
|
||||
|
||||
// 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() {
|
||||
const { path, method } = this.SAFEBROWSING_STATUS;
|
||||
@ -202,11 +257,20 @@ class Api {
|
||||
}
|
||||
|
||||
// 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() {
|
||||
const { path, method } = this.SAFESEARCH_STATUS;
|
||||
@ -224,9 +288,15 @@ class Api {
|
||||
}
|
||||
|
||||
// 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() {
|
||||
const { path, method } = this.CURRENT_LANGUAGE;
|
||||
@ -243,19 +313,40 @@ class Api {
|
||||
}
|
||||
|
||||
// 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() {
|
||||
const { path, method } = this.DHCP_STATUS;
|
||||
@ -309,11 +400,20 @@ class Api {
|
||||
}
|
||||
|
||||
// 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() {
|
||||
const { path, method } = this.INSTALL_GET_ADDRESSES;
|
||||
@ -339,11 +439,20 @@ class Api {
|
||||
}
|
||||
|
||||
// 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() {
|
||||
const { path, method } = this.TLS_STATUS;
|
||||
@ -369,15 +478,30 @@ class Api {
|
||||
}
|
||||
|
||||
// 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() {
|
||||
const { path, method } = this.GET_CLIENTS;
|
||||
@ -418,9 +542,15 @@ class Api {
|
||||
}
|
||||
|
||||
// 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() {
|
||||
const { path, method } = this.ACCESS_LIST;
|
||||
@ -437,11 +567,20 @@ class Api {
|
||||
}
|
||||
|
||||
// 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() {
|
||||
const { path, method } = this.REWRITES_LIST;
|
||||
@ -467,9 +606,15 @@ class Api {
|
||||
}
|
||||
|
||||
// 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() {
|
||||
const { path, method } = this.BLOCKED_SERVICES_LIST;
|
||||
@ -486,13 +631,25 @@ class Api {
|
||||
}
|
||||
|
||||
// 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() {
|
||||
const { path, method } = this.GET_STATS;
|
||||
@ -519,13 +676,25 @@ class Api {
|
||||
}
|
||||
|
||||
// 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) {
|
||||
const { path, method } = this.GET_QUERY_LOG;
|
||||
@ -553,7 +722,10 @@ class Api {
|
||||
}
|
||||
|
||||
// Login
|
||||
LOGIN = { path: 'login', method: 'POST' };
|
||||
LOGIN = {
|
||||
path: 'login',
|
||||
method: 'POST',
|
||||
};
|
||||
|
||||
login(data) {
|
||||
const { path, method } = this.LOGIN;
|
||||
@ -565,7 +737,10 @@ class Api {
|
||||
}
|
||||
|
||||
// Profile
|
||||
GET_PROFILE = { path: 'profile', method: 'GET' };
|
||||
GET_PROFILE = {
|
||||
path: 'profile',
|
||||
method: 'GET',
|
||||
};
|
||||
|
||||
getProfile() {
|
||||
const { path, method } = this.GET_PROFILE;
|
||||
@ -573,9 +748,15 @@ class Api {
|
||||
}
|
||||
|
||||
// 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() {
|
||||
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 PropTypes from 'prop-types';
|
||||
import { withTranslation } from 'react-i18next';
|
||||
import LoadingBar from 'react-redux-loading-bar';
|
||||
import { hot } from 'react-hot-loader/root';
|
||||
|
||||
import 'react-table/react-table.css';
|
||||
import '../ui/Tabler.css';
|
||||
import '../ui/ReactTable.css';
|
||||
import './index.css';
|
||||
|
||||
import Header from '../../containers/Header';
|
||||
import Dashboard from '../../containers/Dashboard';
|
||||
import Settings from '../../containers/Settings';
|
||||
import { shallowEqual, useDispatch, useSelector } from 'react-redux';
|
||||
|
||||
import CustomRules from '../../containers/CustomRules';
|
||||
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 propTypes from 'prop-types';
|
||||
import Toasts from '../Toasts';
|
||||
import Footer from '../ui/Footer';
|
||||
import Status from '../ui/Status';
|
||||
@ -35,31 +21,107 @@ import Icons from '../ui/Icons';
|
||||
import i18n from '../../i18n';
|
||||
import Loading from '../ui/Loading';
|
||||
import { FILTERS_URLS, MENU_URLS, SETTINGS_URLS } from '../../helpers/constants';
|
||||
import Services from '../Filters/Services';
|
||||
import { getLogsUrlParams, setHtmlLangAttr } from '../../helpers/helpers';
|
||||
import Header from '../Header';
|
||||
import { changeLanguage, getDnsStatus } from '../../actions';
|
||||
|
||||
class App extends Component {
|
||||
componentDidMount() {
|
||||
this.props.getDnsStatus();
|
||||
}
|
||||
import Dashboard from '../../containers/Dashboard';
|
||||
import Logs from '../../containers/Logs';
|
||||
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) {
|
||||
if (this.props.dashboard.language !== prevProps.dashboard.language) {
|
||||
this.setLanguage();
|
||||
}
|
||||
}
|
||||
const ROUTES = [
|
||||
{
|
||||
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 = () => {
|
||||
window.location.reload();
|
||||
};
|
||||
const renderRoute = ({ path, component, exact }, idx) => <Route
|
||||
key={idx}
|
||||
exact={exact}
|
||||
path={path}
|
||||
component={component}
|
||||
/>;
|
||||
|
||||
handleUpdate = () => {
|
||||
this.props.getUpdate();
|
||||
};
|
||||
const App = () => {
|
||||
const dispatch = useDispatch();
|
||||
const {
|
||||
language,
|
||||
isCoreRunning,
|
||||
isUpdateAvailable,
|
||||
processing,
|
||||
} = useSelector((state) => state.dashboard, shallowEqual);
|
||||
|
||||
setLanguage = () => {
|
||||
const { processing, language } = this.props.dashboard;
|
||||
const { processing: processingEncryption } = useSelector((
|
||||
state,
|
||||
) => state.encryption, shallowEqual);
|
||||
|
||||
const updateAvailable = isCoreRunning && isUpdateAvailable;
|
||||
|
||||
useEffect(() => {
|
||||
dispatch(getDnsStatus());
|
||||
}, []);
|
||||
|
||||
const setLanguage = () => {
|
||||
if (!processing) {
|
||||
if (language) {
|
||||
i18n.changeLanguage(language);
|
||||
@ -68,93 +130,52 @@ class App extends Component {
|
||||
}
|
||||
|
||||
i18n.on('languageChanged', (lang) => {
|
||||
this.props.changeLanguage(lang);
|
||||
dispatch(changeLanguage(lang));
|
||||
});
|
||||
};
|
||||
|
||||
render() {
|
||||
const { dashboard, encryption, getVersion } = this.props;
|
||||
const updateAvailable = dashboard.isCoreRunning && dashboard.isUpdateAvailable;
|
||||
useEffect(() => {
|
||||
setLanguage();
|
||||
}, [language]);
|
||||
|
||||
return (
|
||||
<HashRouter hashType="noslash">
|
||||
<Fragment>
|
||||
{updateAvailable && (
|
||||
<Fragment>
|
||||
<UpdateTopline
|
||||
url={dashboard.announcementUrl}
|
||||
version={dashboard.newVersion}
|
||||
canAutoUpdate={dashboard.canAutoUpdate}
|
||||
getUpdate={this.handleUpdate}
|
||||
processingUpdate={dashboard.processingUpdate}
|
||||
/>
|
||||
<UpdateOverlay processingUpdate={dashboard.processingUpdate} />
|
||||
</Fragment>
|
||||
)}
|
||||
{!encryption.processing && (
|
||||
<EncryptionTopline notAfter={encryption.not_after} />
|
||||
)}
|
||||
<LoadingBar className="loading-bar" updateTime={1000} />
|
||||
<Route component={Header} />
|
||||
<div className="container container--wrap pb-5">
|
||||
{dashboard.processing && <Loading />}
|
||||
{!dashboard.isCoreRunning && (
|
||||
<div className="row row-cards">
|
||||
<div className="col-lg-12">
|
||||
<Status reloadPage={this.reloadPage}
|
||||
message="dns_start"
|
||||
/>
|
||||
<Loading />
|
||||
</div>
|
||||
const reloadPage = () => {
|
||||
window.location.reload();
|
||||
};
|
||||
|
||||
return (
|
||||
<HashRouter hashType="noslash">
|
||||
<>
|
||||
{updateAvailable && <>
|
||||
<UpdateTopline />
|
||||
<UpdateOverlay />
|
||||
</>}
|
||||
{!processingEncryption && <EncryptionTopline />}
|
||||
<LoadingBar className="loading-bar" updateTime={1000} />
|
||||
<Header />
|
||||
<div className="container container--wrap pb-5">
|
||||
{processing && <Loading />}
|
||||
{!isCoreRunning && (
|
||||
<div className="row row-cards">
|
||||
<div className="col-lg-12">
|
||||
<Status reloadPage={reloadPage} message="dns_start" />
|
||||
<Loading />
|
||||
</div>
|
||||
)}
|
||||
{!dashboard.processing && dashboard.isCoreRunning && (
|
||||
<>
|
||||
<Route path={MENU_URLS.root} exact component={Dashboard} />
|
||||
<Route
|
||||
path={[`${MENU_URLS.logs}${getLogsUrlParams(':search?', ':response_status?')}`, MENU_URLS.logs]}
|
||||
component={Logs} />
|
||||
<Route path={MENU_URLS.guide} component={SetupGuide} />
|
||||
<Route path={SETTINGS_URLS.settings} component={Settings} />
|
||||
<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,
|
||||
</div>
|
||||
)}
|
||||
{!processing && isCoreRunning && ROUTES.map(renderRoute)}
|
||||
</div>
|
||||
<Footer />
|
||||
<Toasts />
|
||||
<Icons />
|
||||
</>
|
||||
</HashRouter>
|
||||
);
|
||||
};
|
||||
|
||||
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;
|
||||
|
@ -90,9 +90,8 @@ class Menu extends Component {
|
||||
};
|
||||
|
||||
getActiveClassForDropdown = (URLS) => {
|
||||
const { pathname } = this.props.location;
|
||||
const isActivePage = Object.values(URLS)
|
||||
.some((item) => item === pathname);
|
||||
.some((item) => item === this.props.pathname);
|
||||
|
||||
return isActivePage ? 'active' : '';
|
||||
};
|
||||
@ -180,9 +179,9 @@ class Menu extends Component {
|
||||
}
|
||||
|
||||
Menu.propTypes = {
|
||||
isMenuOpen: PropTypes.bool,
|
||||
closeMenu: PropTypes.func,
|
||||
location: PropTypes.object,
|
||||
isMenuOpen: PropTypes.bool.isRequired,
|
||||
closeMenu: PropTypes.func.isRequired,
|
||||
pathname: PropTypes.string.isRequired,
|
||||
t: PropTypes.func,
|
||||
};
|
||||
|
||||
|
@ -1,83 +1,77 @@
|
||||
import React, { Component } from 'react';
|
||||
import { Link } from 'react-router-dom';
|
||||
import PropTypes from 'prop-types';
|
||||
import React, { useState } from 'react';
|
||||
import { Link, useLocation } from 'react-router-dom';
|
||||
import { shallowEqual, useSelector } from 'react-redux';
|
||||
import { Trans } from 'react-i18next';
|
||||
import classnames from 'classnames';
|
||||
import { Trans, withTranslation } from 'react-i18next';
|
||||
|
||||
import Menu from './Menu';
|
||||
import logo from '../ui/svg/logo.svg';
|
||||
import './Header.css';
|
||||
|
||||
class Header extends Component {
|
||||
state = {
|
||||
isMenuOpen: false,
|
||||
const Header = () => {
|
||||
const [isMenuOpen, setIsMenuOpen] = useState(false);
|
||||
|
||||
const {
|
||||
protectionEnabled,
|
||||
processing,
|
||||
isCoreRunning,
|
||||
processingProfile,
|
||||
name,
|
||||
} = useSelector((state) => state.dashboard, shallowEqual);
|
||||
|
||||
const { pathname } = useLocation();
|
||||
|
||||
const toggleMenuOpen = () => {
|
||||
setIsMenuOpen((isMenuOpen) => !isMenuOpen);
|
||||
};
|
||||
|
||||
toggleMenuOpen = () => {
|
||||
this.setState((prevState) => ({ isMenuOpen: !prevState.isMenuOpen }));
|
||||
const closeMenu = () => {
|
||||
setIsMenuOpen(false);
|
||||
};
|
||||
|
||||
closeMenu = () => {
|
||||
this.setState({ isMenuOpen: false });
|
||||
};
|
||||
const badgeClass = classnames('badge dns-status', {
|
||||
'badge-success': protectionEnabled,
|
||||
'badge-danger': !protectionEnabled,
|
||||
});
|
||||
|
||||
render() {
|
||||
const { dashboard, location } = this.props;
|
||||
const { isMenuOpen } = this.state;
|
||||
const badgeClass = classnames({
|
||||
'badge dns-status': true,
|
||||
'badge-success': dashboard.protectionEnabled,
|
||||
'badge-danger': !dashboard.protectionEnabled,
|
||||
});
|
||||
|
||||
return (
|
||||
<div className="header">
|
||||
<div className="header__container">
|
||||
<div className="header__row">
|
||||
<div
|
||||
className="header-toggler d-lg-none ml-lg-0 collapsed"
|
||||
onClick={this.toggleMenuOpen}
|
||||
>
|
||||
<span className="header-toggler-icon" />
|
||||
</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>
|
||||
return (
|
||||
<div className="header">
|
||||
<div className="header__container">
|
||||
<div className="header__row">
|
||||
<div
|
||||
className="header-toggler d-lg-none ml-lg-0 collapsed"
|
||||
onClick={toggleMenuOpen}
|
||||
>
|
||||
<span className="header-toggler-icon" />
|
||||
</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>
|
||||
{!processing && isCoreRunning && (
|
||||
<span className={badgeClass}>
|
||||
<Trans>{protectionEnabled ? 'on' : 'off'}</Trans>
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
<Menu
|
||||
location={location}
|
||||
isMenuOpen={isMenuOpen}
|
||||
closeMenu={this.closeMenu}
|
||||
/>
|
||||
<div className="header__column">
|
||||
<div className="header__right">
|
||||
{!dashboard.processingProfile && dashboard.name
|
||||
&& <a href="control/logout" className="btn btn-sm btn-outline-secondary">
|
||||
<Trans>sign_out</Trans>
|
||||
</a>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
<Menu
|
||||
pathname={pathname}
|
||||
isMenuOpen={isMenuOpen}
|
||||
closeMenu={closeMenu}
|
||||
/>
|
||||
<div className="header__column">
|
||||
<div className="header__right">
|
||||
{!processingProfile && name
|
||||
&& <a href="control/logout" className="btn btn-sm btn-outline-secondary">
|
||||
<Trans>sign_out</Trans>
|
||||
</a>}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Header.propTypes = {
|
||||
dashboard: PropTypes.object.isRequired,
|
||||
location: PropTypes.object.isRequired,
|
||||
getVersion: PropTypes.func.isRequired,
|
||||
t: PropTypes.func.isRequired,
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default withTranslation()(Header);
|
||||
export default Header;
|
||||
|
@ -437,7 +437,7 @@
|
||||
}
|
||||
|
||||
.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-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 classnames from 'classnames';
|
||||
import { Trans, withTranslation } from 'react-i18next';
|
||||
@ -34,13 +34,14 @@ class Dhcp extends Component {
|
||||
} = this.props.dhcp;
|
||||
const otherDhcpFound = check?.otherServer
|
||||
&& check.otherServer.found === DHCP_STATUS_RESPONSE.YES;
|
||||
const filledConfig = Object.keys(config).every((key) => {
|
||||
if (key === 'enabled' || key === 'icmp_timeout_msec') {
|
||||
return true;
|
||||
}
|
||||
const filledConfig = Object.keys(config)
|
||||
.every((key) => {
|
||||
if (key === 'enabled' || key === 'icmp_timeout_msec') {
|
||||
return true;
|
||||
}
|
||||
|
||||
return config[key];
|
||||
});
|
||||
return config[key];
|
||||
});
|
||||
|
||||
if (config.enabled) {
|
||||
return (
|
||||
@ -114,40 +115,35 @@ class Dhcp extends Component {
|
||||
|
||||
getStaticIpWarning = (t, check, interfaceName) => {
|
||||
if (check.staticIP.static === DHCP_STATUS_RESPONSE.ERROR) {
|
||||
return (
|
||||
<Fragment>
|
||||
<div className="text-danger mb-2">
|
||||
<Trans>dhcp_static_ip_error</Trans>
|
||||
<div className="mt-2 mb-2">
|
||||
<Accordion label={t('error_details')}>
|
||||
<span>{check.staticIP.error}</span>
|
||||
</Accordion>
|
||||
</div>
|
||||
return <>
|
||||
<div className="text-danger mb-2">
|
||||
<Trans>dhcp_static_ip_error</Trans>
|
||||
<div className="mt-2 mb-2">
|
||||
<Accordion label={t('error_details')}>
|
||||
<span>{check.staticIP.error}</span>
|
||||
</Accordion>
|
||||
</div>
|
||||
<hr className="mt-4 mb-4" />
|
||||
</Fragment>
|
||||
);
|
||||
} if (
|
||||
check.staticIP.static === DHCP_STATUS_RESPONSE.NO
|
||||
</div>
|
||||
<hr className="mt-4 mb-4" />
|
||||
</>;
|
||||
}
|
||||
if (check.staticIP.static === DHCP_STATUS_RESPONSE.NO
|
||||
&& check.staticIP.ip
|
||||
&& interfaceName
|
||||
) {
|
||||
return (
|
||||
<Fragment>
|
||||
<div className="text-secondary mb-2">
|
||||
<Trans
|
||||
components={[<strong key="0">example</strong>]}
|
||||
values={{
|
||||
interfaceName,
|
||||
ipAddress: check.staticIP.ip,
|
||||
}}
|
||||
>
|
||||
dhcp_dynamic_ip_found
|
||||
</Trans>
|
||||
</div>
|
||||
<hr className="mt-4 mb-4" />
|
||||
</Fragment>
|
||||
);
|
||||
&& interfaceName) {
|
||||
return <>
|
||||
<div className="text-secondary mb-2">
|
||||
<Trans
|
||||
components={[<strong key="0">example</strong>]}
|
||||
values={{
|
||||
interfaceName,
|
||||
ipAddress: check.staticIP.ip,
|
||||
}}
|
||||
>
|
||||
dhcp_dynamic_ip_found
|
||||
</Trans>
|
||||
</div>
|
||||
<hr className="mt-4 mb-4" />
|
||||
</>;
|
||||
}
|
||||
|
||||
return '';
|
||||
@ -163,104 +159,101 @@ class Dhcp extends Component {
|
||||
removeStaticLease,
|
||||
toggleLeaseModal,
|
||||
} = this.props;
|
||||
|
||||
const statusButtonClass = classnames({
|
||||
'btn btn-primary btn-standard': true,
|
||||
'btn btn-primary btn-standard btn-loading': dhcp.processingStatus,
|
||||
});
|
||||
const { enabled, interface_name, ...values } = dhcp.config;
|
||||
|
||||
return (
|
||||
<Fragment>
|
||||
<PageTitle title={t('dhcp_settings')} />
|
||||
{(dhcp.processing || dhcp.processingInterfaces) && <Loading />}
|
||||
{!dhcp.processing && !dhcp.processingInterfaces && (
|
||||
<Fragment>
|
||||
<Card
|
||||
title={t('dhcp_title')}
|
||||
subtitle={t('dhcp_description')}
|
||||
bodyType="card-body box-body--settings"
|
||||
>
|
||||
<div className="dhcp">
|
||||
<Fragment>
|
||||
<Form
|
||||
onSubmit={this.handleFormSubmit}
|
||||
initialValues={{
|
||||
interface_name,
|
||||
...values,
|
||||
}}
|
||||
interfaces={dhcp.interfaces}
|
||||
processingConfig={dhcp.processingConfig}
|
||||
processingInterfaces={dhcp.processingInterfaces}
|
||||
enabled={enabled}
|
||||
resetDhcp={resetDhcp}
|
||||
/>
|
||||
<hr />
|
||||
<div className="card-actions mb-3">
|
||||
{this.getToggleDhcpButton()}
|
||||
<button
|
||||
type="button"
|
||||
className={statusButtonClass}
|
||||
onClick={() => findActiveDhcp(interface_name)}
|
||||
disabled={
|
||||
enabled || !interface_name || dhcp.processingConfig
|
||||
}
|
||||
>
|
||||
<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>
|
||||
return <>
|
||||
<PageTitle title={t('dhcp_settings')} />
|
||||
{(dhcp.processing || dhcp.processingInterfaces) && <Loading />}
|
||||
{!dhcp.processing && !dhcp.processingInterfaces && <>
|
||||
<Card
|
||||
title={t('dhcp_title')}
|
||||
subtitle={t('dhcp_description')}
|
||||
bodyType="card-body box-body--settings"
|
||||
>
|
||||
<div className="dhcp">
|
||||
<>
|
||||
<Form
|
||||
onSubmit={this.handleFormSubmit}
|
||||
initialValues={{
|
||||
interface_name,
|
||||
...values,
|
||||
}}
|
||||
interfaces={dhcp.interfaces}
|
||||
processingConfig={dhcp.processingConfig}
|
||||
processingInterfaces={dhcp.processingInterfaces}
|
||||
enabled={enabled}
|
||||
resetDhcp={resetDhcp}
|
||||
/>
|
||||
<hr />
|
||||
<div className="card-actions mb-3">
|
||||
{this.getToggleDhcpButton()}
|
||||
<button
|
||||
type="button"
|
||||
className={statusButtonClass}
|
||||
onClick={() => findActiveDhcp(interface_name)}
|
||||
disabled={
|
||||
enabled || !interface_name || dhcp.processingConfig
|
||||
}
|
||||
>
|
||||
<Trans>check_dhcp_servers</Trans>
|
||||
</button>
|
||||
</div>
|
||||
</Card>
|
||||
{dhcp.config.enabled && (
|
||||
<Card
|
||||
title={t('dhcp_leases')}
|
||||
bodyType="card-body box-body--settings"
|
||||
>
|
||||
<div className="row">
|
||||
<div className="col">
|
||||
<Leases leases={dhcp.leases} />
|
||||
</div>
|
||||
</div>
|
||||
</Card>
|
||||
)}
|
||||
<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>
|
||||
{!enabled && dhcp.check && (
|
||||
<>
|
||||
{this.getStaticIpWarning(t, dhcp.check, interface_name)}
|
||||
{this.getActiveDhcpMessage(t, dhcp.check)}
|
||||
{this.getDhcpWarning(dhcp.check)}
|
||||
</>
|
||||
)}
|
||||
</>
|
||||
</div>
|
||||
</Card>
|
||||
{dhcp.config.enabled && (
|
||||
<Card
|
||||
title={t('dhcp_leases')}
|
||||
bodyType="card-body box-body--settings"
|
||||
>
|
||||
<div className="row">
|
||||
<div className="col">
|
||||
<Leases leases={dhcp.leases} />
|
||||
</div>
|
||||
</Card>
|
||||
</Fragment>
|
||||
</div>
|
||||
</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 PropTypes from 'prop-types';
|
||||
import { withTranslation } from 'react-i18next';
|
||||
|
||||
import React from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { shallowEqual, useDispatch, useSelector } from 'react-redux';
|
||||
import Form from './Form';
|
||||
import Card from '../../../ui/Card';
|
||||
import { setAccessList } from '../../../../actions/access';
|
||||
|
||||
class Access extends Component {
|
||||
handleFormSubmit = (values) => {
|
||||
this.props.setAccessList(values);
|
||||
const Access = () => {
|
||||
const { t } = useTranslation();
|
||||
const dispatch = useDispatch();
|
||||
const {
|
||||
processing,
|
||||
processingSet,
|
||||
...values
|
||||
} = useSelector((state) => state.access, shallowEqual);
|
||||
|
||||
const handleFormSubmit = (values) => {
|
||||
dispatch(setAccessList(values));
|
||||
};
|
||||
|
||||
render() {
|
||||
const { t, access } = this.props;
|
||||
|
||||
const { processing, processingSet, ...values } = access;
|
||||
|
||||
return (
|
||||
<Card
|
||||
title={t('access_title')}
|
||||
subtitle={t('access_desc')}
|
||||
bodyType="card-body box-body--settings"
|
||||
>
|
||||
<Form
|
||||
initialValues={values}
|
||||
onSubmit={this.handleFormSubmit}
|
||||
processingSet={processingSet}
|
||||
/>
|
||||
</Card>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Access.propTypes = {
|
||||
access: PropTypes.object.isRequired,
|
||||
setAccessList: PropTypes.func.isRequired,
|
||||
t: PropTypes.func.isRequired,
|
||||
return (
|
||||
<Card
|
||||
title={t('access_title')}
|
||||
subtitle={t('access_desc')}
|
||||
bodyType="card-body box-body--settings"
|
||||
>
|
||||
<Form
|
||||
initialValues={values}
|
||||
onSubmit={handleFormSubmit}
|
||||
processingSet={processingSet}
|
||||
/>
|
||||
</Card>
|
||||
);
|
||||
};
|
||||
|
||||
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 { connect } from 'react-redux';
|
||||
import { Field, reduxForm, formValueSelector } from 'redux-form';
|
||||
import { Trans, withTranslation } from 'react-i18next';
|
||||
import flow from 'lodash/flow';
|
||||
import { shallowEqual, useSelector } from 'react-redux';
|
||||
import { Field, reduxForm } from 'redux-form';
|
||||
import { Trans, useTranslation } from 'react-i18next';
|
||||
import {
|
||||
renderInputField,
|
||||
renderRadioField,
|
||||
@ -18,32 +17,36 @@ import {
|
||||
} from '../../../../helpers/validators';
|
||||
import { BLOCKING_MODES, FORM_NAME } from '../../../../helpers/constants';
|
||||
|
||||
const checkboxes = [{
|
||||
name: 'edns_cs_enabled',
|
||||
placeholder: 'edns_enable',
|
||||
subtitle: 'edns_cs_desc',
|
||||
},
|
||||
{
|
||||
name: 'dnssec_enabled',
|
||||
placeholder: 'dnssec_enable',
|
||||
subtitle: 'dnssec_enable_desc',
|
||||
},
|
||||
{
|
||||
name: 'disable_ipv6',
|
||||
placeholder: 'disable_ipv6',
|
||||
subtitle: 'disable_ipv6_desc',
|
||||
}];
|
||||
const checkboxes = [
|
||||
{
|
||||
name: 'edns_cs_enabled',
|
||||
placeholder: 'edns_enable',
|
||||
subtitle: 'edns_cs_desc',
|
||||
},
|
||||
{
|
||||
name: 'dnssec_enabled',
|
||||
placeholder: 'dnssec_enable',
|
||||
subtitle: 'dnssec_enable_desc',
|
||||
},
|
||||
{
|
||||
name: 'disable_ipv6',
|
||||
placeholder: 'disable_ipv6',
|
||||
subtitle: 'disable_ipv6_desc',
|
||||
},
|
||||
];
|
||||
|
||||
const customIps = [{
|
||||
description: 'blocking_ipv4_desc',
|
||||
name: 'blocking_ipv4',
|
||||
validateIp: validateIpv4,
|
||||
},
|
||||
{
|
||||
description: 'blocking_ipv6_desc',
|
||||
name: 'blocking_ipv6',
|
||||
validateIp: validateIpv6,
|
||||
}];
|
||||
const customIps = [
|
||||
{
|
||||
description: 'blocking_ipv4_desc',
|
||||
name: 'blocking_ipv4',
|
||||
validateIp: validateIpv4,
|
||||
},
|
||||
{
|
||||
description: 'blocking_ipv6_desc',
|
||||
name: 'blocking_ipv6',
|
||||
validateIp: validateIpv6,
|
||||
},
|
||||
];
|
||||
|
||||
const getFields = (processing, t) => Object.values(BLOCKING_MODES)
|
||||
.map((mode) => (
|
||||
@ -58,114 +61,107 @@ const getFields = (processing, t) => Object.values(BLOCKING_MODES)
|
||||
/>
|
||||
));
|
||||
|
||||
let Form = ({
|
||||
handleSubmit, submitting, invalid, processing, blockingMode, t,
|
||||
}) => <form onSubmit={handleSubmit}>
|
||||
<div className="row">
|
||||
<div className="col-12 col-sm-6">
|
||||
<div className="form__group form__group--settings">
|
||||
<label htmlFor="ratelimit"
|
||||
className="form__label form__label--with-desc">
|
||||
<Trans>rate_limit</Trans>
|
||||
</label>
|
||||
<div className="form__desc form__desc--top">
|
||||
<Trans>rate_limit_desc</Trans>
|
||||
</div>
|
||||
<Field
|
||||
name="ratelimit"
|
||||
type="number"
|
||||
component={renderInputField}
|
||||
className="form-control"
|
||||
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]}
|
||||
/>
|
||||
const Form = ({
|
||||
handleSubmit, submitting, invalid, processing,
|
||||
}) => {
|
||||
const { t } = useTranslation();
|
||||
const {
|
||||
blocking_mode,
|
||||
} = useSelector((state) => state.form[FORM_NAME.BLOCKING_MODE].values ?? {}, shallowEqual);
|
||||
|
||||
return <form onSubmit={handleSubmit}>
|
||||
<div className="row">
|
||||
<div className="col-12 col-sm-6">
|
||||
<div className="form__group form__group--settings">
|
||||
<label htmlFor="ratelimit"
|
||||
className="form__label form__label--with-desc">
|
||||
<Trans>rate_limit</Trans>
|
||||
</label>
|
||||
<div className="form__desc form__desc--top">
|
||||
<Trans>rate_limit_desc</Trans>
|
||||
</div>
|
||||
</div>)}
|
||||
</Fragment>
|
||||
)}
|
||||
</div>
|
||||
<button
|
||||
type="submit"
|
||||
className="btn btn-success btn-standard btn-large"
|
||||
disabled={submitting || invalid || processing}
|
||||
>
|
||||
<Trans>save_btn</Trans>
|
||||
</button>
|
||||
</form>;
|
||||
<Field
|
||||
name="ratelimit"
|
||||
type="number"
|
||||
component={renderInputField}
|
||||
className="form-control"
|
||||
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>
|
||||
{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 = {
|
||||
blockingMode: PropTypes.string.isRequired,
|
||||
handleSubmit: PropTypes.func.isRequired,
|
||||
submitting: PropTypes.bool.isRequired,
|
||||
invalid: PropTypes.bool.isRequired,
|
||||
processing: PropTypes.bool.isRequired,
|
||||
t: PropTypes.func.isRequired,
|
||||
};
|
||||
|
||||
const selector = formValueSelector(FORM_NAME.BLOCKING_MODE);
|
||||
|
||||
Form = connect((state) => {
|
||||
const blockingMode = selector(state, 'blocking_mode');
|
||||
return {
|
||||
blockingMode,
|
||||
};
|
||||
})(Form);
|
||||
|
||||
export default flow([
|
||||
withTranslation(),
|
||||
reduxForm({ form: FORM_NAME.BLOCKING_MODE }),
|
||||
])(Form);
|
||||
export default reduxForm({ form: FORM_NAME.BLOCKING_MODE })(Form);
|
||||
|
@ -1,15 +1,13 @@
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { withTranslation } from 'react-i18next';
|
||||
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { shallowEqual, useDispatch, useSelector } from 'react-redux';
|
||||
import Card from '../../../ui/Card';
|
||||
import Form from './Form';
|
||||
import { setDnsConfig } from '../../../../actions/dnsConfig';
|
||||
|
||||
const Config = ({ t, dnsConfig, setDnsConfig }) => {
|
||||
const handleFormSubmit = (values) => {
|
||||
setDnsConfig(values);
|
||||
};
|
||||
|
||||
const Config = () => {
|
||||
const { t } = useTranslation();
|
||||
const dispatch = useDispatch();
|
||||
const {
|
||||
blocking_mode,
|
||||
ratelimit,
|
||||
@ -19,7 +17,11 @@ const Config = ({ t, dnsConfig, setDnsConfig }) => {
|
||||
dnssec_enabled,
|
||||
disable_ipv6,
|
||||
processingSetConfig,
|
||||
} = dnsConfig;
|
||||
} = useSelector((state) => state.dnsConfig, shallowEqual);
|
||||
|
||||
const handleFormSubmit = (values) => {
|
||||
dispatch(setDnsConfig(values));
|
||||
};
|
||||
|
||||
return (
|
||||
<Card
|
||||
@ -46,10 +48,4 @@ const Config = ({ t, dnsConfig, setDnsConfig }) => {
|
||||
);
|
||||
};
|
||||
|
||||
Config.propTypes = {
|
||||
dnsConfig: PropTypes.object.isRequired,
|
||||
setDnsConfig: PropTypes.func.isRequired,
|
||||
t: PropTypes.func.isRequired,
|
||||
};
|
||||
|
||||
export default withTranslation()(Config);
|
||||
export default Config;
|
||||
|
@ -1,29 +1,26 @@
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { useDispatch } from 'react-redux';
|
||||
import { shallowEqual, useDispatch } from 'react-redux';
|
||||
import Form from './Form';
|
||||
import Card from '../../../ui/Card';
|
||||
import { setDnsConfig } from '../../../../actions/dnsConfig';
|
||||
|
||||
const Upstream = (props) => {
|
||||
const [t] = useTranslation();
|
||||
const Upstream = () => {
|
||||
const { t } = useTranslation();
|
||||
const dispatch = useDispatch();
|
||||
const {
|
||||
upstream_dns,
|
||||
bootstrap_dns,
|
||||
upstream_mode,
|
||||
processingSetConfig,
|
||||
} = ((state) => state.dnsConfig, shallowEqual);
|
||||
|
||||
const { processingTestUpstream } = ((state) => state.settings, shallowEqual);
|
||||
|
||||
const handleSubmit = (values) => {
|
||||
dispatch(setDnsConfig(values));
|
||||
};
|
||||
|
||||
const {
|
||||
processingTestUpstream,
|
||||
dnsConfig: {
|
||||
upstream_dns,
|
||||
bootstrap_dns,
|
||||
processingSetConfig,
|
||||
upstream_mode,
|
||||
},
|
||||
} = props;
|
||||
|
||||
return (
|
||||
<Card
|
||||
title={t('upstream_dns')}
|
||||
@ -48,9 +45,4 @@ const Upstream = (props) => {
|
||||
);
|
||||
};
|
||||
|
||||
Upstream.propTypes = {
|
||||
processingTestUpstream: PropTypes.bool.isRequired,
|
||||
dnsConfig: PropTypes.object.isRequired,
|
||||
};
|
||||
|
||||
export default Upstream;
|
||||
|
@ -1,67 +1,40 @@
|
||||
import React, { useEffect } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
import { useDispatch, useSelector } from 'react-redux';
|
||||
import Upstream from './Upstream';
|
||||
import Access from './Access';
|
||||
import Config from './Config';
|
||||
import PageTitle from '../../ui/PageTitle';
|
||||
import Loading from '../../ui/Loading';
|
||||
import CacheConfig from './Cache';
|
||||
import { getDnsConfig } from '../../../actions/dnsConfig';
|
||||
import { getAccessList } from '../../../actions/access';
|
||||
|
||||
const Dns = (props) => {
|
||||
const Dns = () => {
|
||||
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(() => {
|
||||
props.getAccessList();
|
||||
props.getDnsConfig();
|
||||
dispatch(getAccessList());
|
||||
dispatch(getDnsConfig());
|
||||
}, []);
|
||||
|
||||
const {
|
||||
settings,
|
||||
access,
|
||||
setAccessList,
|
||||
dnsConfig,
|
||||
setDnsConfig,
|
||||
} = props;
|
||||
|
||||
const isDataLoading = access.processing || dnsConfig.processingGetConfig;
|
||||
|
||||
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,
|
||||
return <>
|
||||
<PageTitle title={t('dns_settings')} />
|
||||
{isDataLoading
|
||||
? <Loading />
|
||||
: <>
|
||||
<Upstream />
|
||||
<Config />
|
||||
<CacheConfig />
|
||||
<Access />
|
||||
</>}
|
||||
</>;
|
||||
};
|
||||
|
||||
export default Dns;
|
||||
|
@ -1,19 +1,20 @@
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { Trans, withTranslation } from 'react-i18next';
|
||||
import { Trans } from 'react-i18next';
|
||||
import isAfter from 'date-fns/is_after';
|
||||
import addDays from 'date-fns/add_days';
|
||||
|
||||
import { useSelector } from 'react-redux';
|
||||
import Topline from './Topline';
|
||||
import { EMPTY_DATE } from '../../helpers/constants';
|
||||
|
||||
const EncryptionTopline = (props) => {
|
||||
if (props.notAfter === EMPTY_DATE) {
|
||||
return false;
|
||||
const EncryptionTopline = () => {
|
||||
const not_after = useSelector((state) => state.encryption.not_after);
|
||||
|
||||
if (not_after === EMPTY_DATE) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const isAboutExpire = isAfter(addDays(Date.now(), 30), props.notAfter);
|
||||
const isExpired = isAfter(Date.now(), props.notAfter);
|
||||
const isAboutExpire = isAfter(addDays(Date.now(), 30), not_after);
|
||||
const isExpired = isAfter(Date.now(), not_after);
|
||||
|
||||
if (isExpired) {
|
||||
return (
|
||||
@ -23,7 +24,9 @@ const EncryptionTopline = (props) => {
|
||||
</Trans>
|
||||
</Topline>
|
||||
);
|
||||
} if (isAboutExpire) {
|
||||
}
|
||||
|
||||
if (isAboutExpire) {
|
||||
return (
|
||||
<Topline type="warning">
|
||||
<Trans components={[<a href="#encryption" key="0">link</a>]}>
|
||||
@ -36,8 +39,4 @@ const EncryptionTopline = (props) => {
|
||||
return false;
|
||||
};
|
||||
|
||||
EncryptionTopline.propTypes = {
|
||||
notAfter: PropTypes.string.isRequired,
|
||||
};
|
||||
|
||||
export default withTranslation()(EncryptionTopline);
|
||||
export default EncryptionTopline;
|
||||
|
@ -1,5 +1,4 @@
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import classNames from 'classnames';
|
||||
|
||||
@ -28,7 +27,7 @@ const linksData = [
|
||||
},
|
||||
];
|
||||
|
||||
const Footer = (props) => {
|
||||
const Footer = () => {
|
||||
const { t } = useTranslation();
|
||||
|
||||
const getYear = () => {
|
||||
@ -59,11 +58,6 @@ const Footer = (props) => {
|
||||
{t(name)}
|
||||
</a>);
|
||||
|
||||
|
||||
const {
|
||||
dnsVersion, processingVersion, getVersion, checkUpdateFlag,
|
||||
} = props;
|
||||
|
||||
return (
|
||||
<>
|
||||
<footer className="footer">
|
||||
@ -94,12 +88,7 @@ const Footer = (props) => {
|
||||
<div className="footer__row">
|
||||
{renderCopyright()}
|
||||
<div className="footer__column footer__column--language">
|
||||
<Version
|
||||
dnsVersion={dnsVersion}
|
||||
processingVersion={processingVersion}
|
||||
getVersion={getVersion}
|
||||
checkUpdateFlag={checkUpdateFlag}
|
||||
/>
|
||||
<Version />
|
||||
</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;
|
||||
|
@ -1,9 +1,14 @@
|
||||
import React from 'react';
|
||||
|
||||
import PropTypes from 'prop-types';
|
||||
import classNames from 'classnames';
|
||||
import './Loading.css';
|
||||
|
||||
const Loading = () => (
|
||||
<div className="loading" />
|
||||
const Loading = ({ className }) => (
|
||||
<div className={classNames('loading', className)} />
|
||||
);
|
||||
|
||||
Loading.propTypes = {
|
||||
className: PropTypes.string,
|
||||
};
|
||||
|
||||
export default Loading;
|
||||
|
@ -1,14 +1,13 @@
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { Trans, withTranslation } from 'react-i18next';
|
||||
import { Trans } from 'react-i18next';
|
||||
import classnames from 'classnames';
|
||||
|
||||
import { useSelector } from 'react-redux';
|
||||
import './Overlay.css';
|
||||
|
||||
const UpdateOverlay = (props) => {
|
||||
const overlayClass = classnames({
|
||||
overlay: true,
|
||||
'overlay--visible': props.processingUpdate,
|
||||
const UpdateOverlay = () => {
|
||||
const processingUpdate = useSelector((state) => state.dashboard.processingUpdate);
|
||||
const overlayClass = classnames('overlay', {
|
||||
'overlay--visible': processingUpdate,
|
||||
});
|
||||
|
||||
return (
|
||||
@ -19,8 +18,4 @@ const UpdateOverlay = (props) => {
|
||||
);
|
||||
};
|
||||
|
||||
UpdateOverlay.propTypes = {
|
||||
processingUpdate: PropTypes.bool,
|
||||
};
|
||||
|
||||
export default withTranslation()(UpdateOverlay);
|
||||
export default UpdateOverlay;
|
||||
|
@ -1,42 +1,46 @@
|
||||
import React, { Fragment } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { Trans, withTranslation } from 'react-i18next';
|
||||
|
||||
import React from 'react';
|
||||
import { Trans } from 'react-i18next';
|
||||
import { shallowEqual, useDispatch, useSelector } from 'react-redux';
|
||||
import Topline from './Topline';
|
||||
import { getUpdate } from '../../actions';
|
||||
|
||||
const UpdateTopline = (props) => (
|
||||
<Topline type="info">
|
||||
<Fragment>
|
||||
const UpdateTopline = () => {
|
||||
const {
|
||||
announcementUrl,
|
||||
newVersion,
|
||||
canAutoUpdate,
|
||||
processingUpdate,
|
||||
} = useSelector((state) => state.dashboard, shallowEqual);
|
||||
const dispatch = useDispatch();
|
||||
|
||||
const handleUpdate = () => {
|
||||
dispatch(getUpdate());
|
||||
};
|
||||
|
||||
return <Topline type="info">
|
||||
<>
|
||||
<Trans
|
||||
values={{ version: props.version }}
|
||||
values={{ version: newVersion }}
|
||||
components={[
|
||||
<a href={props.url} target="_blank" rel="noopener noreferrer" key="0">
|
||||
<a href={announcementUrl} target="_blank" rel="noopener noreferrer" key="0">
|
||||
Click here
|
||||
</a>,
|
||||
]}
|
||||
>
|
||||
update_announcement
|
||||
</Trans>
|
||||
{props.canAutoUpdate
|
||||
&& <button
|
||||
type="button"
|
||||
className="btn btn-sm btn-primary ml-3"
|
||||
onClick={props.getUpdate}
|
||||
disabled={props.processingUpdate}
|
||||
>
|
||||
<Trans>update_now</Trans>
|
||||
</button>
|
||||
{canAutoUpdate
|
||||
&& <button
|
||||
type="button"
|
||||
className="btn btn-sm btn-primary ml-3"
|
||||
onClick={handleUpdate}
|
||||
disabled={processingUpdate}
|
||||
>
|
||||
<Trans>update_now</Trans>
|
||||
</button>
|
||||
}
|
||||
</Fragment>
|
||||
</Topline>
|
||||
);
|
||||
|
||||
UpdateTopline.propTypes = {
|
||||
version: PropTypes.string,
|
||||
url: PropTypes.string.isRequired,
|
||||
canAutoUpdate: PropTypes.bool,
|
||||
getUpdate: PropTypes.func,
|
||||
processingUpdate: PropTypes.bool,
|
||||
</>
|
||||
</Topline>;
|
||||
};
|
||||
|
||||
export default withTranslation()(UpdateTopline);
|
||||
export default UpdateTopline;
|
||||
|
@ -1,13 +1,21 @@
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { Trans, withTranslation } from 'react-i18next';
|
||||
|
||||
import { Trans, useTranslation } from 'react-i18next';
|
||||
import { shallowEqual, useDispatch, useSelector } from 'react-redux';
|
||||
import { getVersion } from '../../actions';
|
||||
import './Version.css';
|
||||
|
||||
const Version = (props) => {
|
||||
const Version = () => {
|
||||
const dispatch = useDispatch();
|
||||
const { t } = useTranslation();
|
||||
const {
|
||||
dnsVersion, processingVersion, t, checkUpdateFlag,
|
||||
} = props;
|
||||
dnsVersion,
|
||||
processingVersion,
|
||||
checkUpdateFlag,
|
||||
} = useSelector((state) => state?.dashboard ?? {}, shallowEqual);
|
||||
|
||||
const onClick = () => {
|
||||
dispatch(getVersion(true));
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="version">
|
||||
@ -20,7 +28,7 @@ const Version = (props) => {
|
||||
{checkUpdateFlag && <button
|
||||
type="button"
|
||||
className="btn btn-icon btn-icon-sm btn-outline-primary btn-sm ml-2"
|
||||
onClick={() => props.getVersion(true)}
|
||||
onClick={onClick}
|
||||
disabled={processingVersion}
|
||||
title={t('check_updates_now')}
|
||||
>
|
||||
@ -33,12 +41,4 @@ const Version = (props) => {
|
||||
);
|
||||
};
|
||||
|
||||
Version.propTypes = {
|
||||
dnsVersion: PropTypes.string,
|
||||
getVersion: PropTypes.func,
|
||||
processingVersion: PropTypes.bool,
|
||||
checkUpdateFlag: PropTypes.bool,
|
||||
t: PropTypes.func.isRequired,
|
||||
};
|
||||
|
||||
export default withTranslation()(Version);
|
||||
export default 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 }) => {
|
||||
acc[query] = query;
|
||||
return acc;
|
||||
}, {});
|
||||
export const RESPONSE_FILTER_QUERIES = Object.values(RESPONSE_FILTER)
|
||||
.reduce((acc, { query }) => {
|
||||
acc[query] = query;
|
||||
return acc;
|
||||
}, {});
|
||||
|
||||
export const FILTERED_STATUS_TO_META_MAP = {
|
||||
[FILTERED_STATUS.NOT_FILTERED_WHITE_LIST]: {
|
||||
|
@ -1,13 +1,14 @@
|
||||
import React from 'react';
|
||||
import ReactDOM from 'react-dom';
|
||||
import { Provider } from 'react-redux';
|
||||
import './components/App/index.css';
|
||||
import App from './containers/App';
|
||||
import configureStore from './configureStore';
|
||||
import reducers from './reducers';
|
||||
import App from './components/App';
|
||||
import './components/App/index.css';
|
||||
import './i18n';
|
||||
|
||||
const store = configureStore(reducers, {}); // set initial state
|
||||
|
||||
ReactDOM.render(
|
||||
<Provider store={store}>
|
||||
<App />
|
||||
|
28
client/webpack.common.js
vendored
@ -5,6 +5,7 @@ const flexBugsFixes = require('postcss-flexbugs-fixes');
|
||||
const { CleanWebpackPlugin } = require('clean-webpack-plugin');
|
||||
const CopyPlugin = require('copy-webpack-plugin');
|
||||
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
|
||||
const { BUILD_ENVS } = require('./constants');
|
||||
|
||||
const RESOURCES_PATH = path.resolve(__dirname);
|
||||
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_ASSETS_PATH = path.resolve(PUBLIC_PATH, 'assets');
|
||||
|
||||
const BUILD_ENVS = {
|
||||
dev: 'development',
|
||||
prod: 'production',
|
||||
};
|
||||
|
||||
const BUILD_ENV = BUILD_ENVS[process.env.BUILD_ENV];
|
||||
|
||||
const isDev = BUILD_ENV === BUILD_ENVS.dev;
|
||||
|
||||
const config = {
|
||||
mode: BUILD_ENV,
|
||||
target: 'web',
|
||||
@ -36,22 +34,35 @@ const config = {
|
||||
},
|
||||
output: {
|
||||
path: PUBLIC_PATH,
|
||||
filename: '[name].[chunkhash].js',
|
||||
filename: '[name].[hash].js',
|
||||
},
|
||||
resolve: {
|
||||
modules: ['node_modules'],
|
||||
alias: {
|
||||
MainRoot: path.resolve(__dirname, '../'),
|
||||
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: {
|
||||
rules: [
|
||||
{
|
||||
test: /\.ya?ml$/,
|
||||
type: 'json',
|
||||
use: 'yaml-loader',
|
||||
},
|
||||
{
|
||||
test: /\.css$/i,
|
||||
use: [
|
||||
'style-loader',
|
||||
MiniCssExtractPlugin.loader,
|
||||
{
|
||||
loader: MiniCssExtractPlugin.loader,
|
||||
options: {
|
||||
hmr: isDev,
|
||||
},
|
||||
},
|
||||
{
|
||||
loader: 'css-loader',
|
||||
options: {
|
||||
@ -122,7 +133,8 @@ const config = {
|
||||
template: HTML_LOGIN_PATH,
|
||||
}),
|
||||
new MiniCssExtractPlugin({
|
||||
filename: '[name].[contenthash].css',
|
||||
filename: isDev ? '[name].css' : '[name].[hash].css',
|
||||
chunkFilename: isDev ? '[id].css' : '[id].[hash].css',
|
||||
}),
|
||||
new CopyPlugin({
|
||||
patterns: [
|
||||
|
46
client/webpack.dev.js
vendored
@ -1,5 +1,50 @@
|
||||
const merge = require('webpack-merge');
|
||||
const yaml = require('js-yaml');
|
||||
const fs = require('fs');
|
||||
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, {
|
||||
devtool: 'eval-source-map',
|
||||
@ -16,4 +61,5 @@ module.exports = merge(common, {
|
||||
},
|
||||
],
|
||||
},
|
||||
devServer: process.env.WEBPACK_DEV_SERVER ? getDevServerConfig(BASE_URL) : undefined,
|
||||
});
|
||||
|
19
client/webpack.prod.js
vendored
@ -2,18 +2,19 @@ const StyleLintPlugin = require('stylelint-webpack-plugin');
|
||||
const merge = require('webpack-merge');
|
||||
const common = require('./webpack.common.js');
|
||||
|
||||
|
||||
module.exports = merge(common, {
|
||||
module: {
|
||||
rules: [{
|
||||
test: /\.js$/,
|
||||
exclude: /node_modules/,
|
||||
loader: 'eslint-loader',
|
||||
options: {
|
||||
failOnError: true,
|
||||
configFile: 'prod.eslintrc',
|
||||
rules: [
|
||||
{
|
||||
test: /\.js$/,
|
||||
exclude: /node_modules/,
|
||||
loader: 'eslint-loader',
|
||||
options: {
|
||||
failOnError: true,
|
||||
configFile: 'prod.eslintrc',
|
||||
},
|
||||
},
|
||||
}],
|
||||
],
|
||||
},
|
||||
plugins: [
|
||||
new StyleLintPlugin({
|
||||
|