Merge: - client: block/unblock client without requesting stats

Closes #896

Squashed commit of the following:

commit 66b781438aa668a16b19455c3e0dc5100417d869
Author: Ildar Kamalov <i.kamalov@adguard.com>
Date:   Mon Feb 3 12:59:25 2020 +0300

    - client: block/unblock client without requesting stats

commit e70f62738d549e32339bae3d5e996a912b99c21d
Author: Ildar Kamalov <i.kamalov@adguard.com>
Date:   Mon Feb 3 12:22:13 2020 +0300

    - client: get current access settings before set

commit 65c59d1d55f3255f33f917b61b6ab2c6c2e54271
Author: Ildar Kamalov <i.kamalov@adguard.com>
Date:   Mon Feb 3 12:21:12 2020 +0300

    * client: mode svg-url-loader to devDependencies
This commit is contained in:
Ildar Kamalov 2020-02-04 15:48:07 +03:00
parent d4069f824a
commit a5c2ad1b2f
8 changed files with 63 additions and 45 deletions

30
client/package-lock.json generated vendored
View File

@ -821,6 +821,7 @@
"version": "6.5.3", "version": "6.5.3",
"resolved": "https://registry.npmjs.org/ajv/-/ajv-6.5.3.tgz", "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.5.3.tgz",
"integrity": "sha512-LqZ9wY+fx3UMiiPd741yB2pj3hhil+hQc8taf4o2QGRFpWgZ2V5C8HA165DY9sS3fJwsk7uT7ZlFEyC3Ig3lLg==", "integrity": "sha512-LqZ9wY+fx3UMiiPd741yB2pj3hhil+hQc8taf4o2QGRFpWgZ2V5C8HA165DY9sS3fJwsk7uT7ZlFEyC3Ig3lLg==",
"dev": true,
"requires": { "requires": {
"fast-deep-equal": "^2.0.1", "fast-deep-equal": "^2.0.1",
"fast-json-stable-stringify": "^2.0.0", "fast-json-stable-stringify": "^2.0.0",
@ -837,7 +838,8 @@
"ajv-keywords": { "ajv-keywords": {
"version": "3.2.0", "version": "3.2.0",
"resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.2.0.tgz", "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.2.0.tgz",
"integrity": "sha1-6GuBnGAs+IIa1jdBNpjx3sAhhHo=" "integrity": "sha1-6GuBnGAs+IIa1jdBNpjx3sAhhHo=",
"dev": true
}, },
"align-text": { "align-text": {
"version": "0.1.4", "version": "0.1.4",
@ -2278,7 +2280,8 @@
"big.js": { "big.js": {
"version": "3.2.0", "version": "3.2.0",
"resolved": "https://registry.npmjs.org/big.js/-/big.js-3.2.0.tgz", "resolved": "https://registry.npmjs.org/big.js/-/big.js-3.2.0.tgz",
"integrity": "sha512-+hN/Zh2D08Mx65pZ/4g5bsmNiZUuChDiQfTUQ7qJr4/kuopCr88xZsAXv6mBoZEsUI4OuGHlX59qE94K2mMW8Q==" "integrity": "sha512-+hN/Zh2D08Mx65pZ/4g5bsmNiZUuChDiQfTUQ7qJr4/kuopCr88xZsAXv6mBoZEsUI4OuGHlX59qE94K2mMW8Q==",
"dev": true
}, },
"binary-extensions": { "binary-extensions": {
"version": "1.11.0", "version": "1.11.0",
@ -4218,7 +4221,8 @@
"emojis-list": { "emojis-list": {
"version": "2.1.0", "version": "2.1.0",
"resolved": "https://registry.npmjs.org/emojis-list/-/emojis-list-2.1.0.tgz", "resolved": "https://registry.npmjs.org/emojis-list/-/emojis-list-2.1.0.tgz",
"integrity": "sha1-TapNnbAPmBmIDHn6RXrlsJof04k=" "integrity": "sha1-TapNnbAPmBmIDHn6RXrlsJof04k=",
"dev": true
}, },
"encodeurl": { "encodeurl": {
"version": "1.0.2", "version": "1.0.2",
@ -5231,7 +5235,8 @@
"fast-deep-equal": { "fast-deep-equal": {
"version": "2.0.1", "version": "2.0.1",
"resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-2.0.1.tgz", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-2.0.1.tgz",
"integrity": "sha1-ewUhjd+WZ79/Nwv3/bLLFf3Qqkk=" "integrity": "sha1-ewUhjd+WZ79/Nwv3/bLLFf3Qqkk=",
"dev": true
}, },
"fast-glob": { "fast-glob": {
"version": "2.2.6", "version": "2.2.6",
@ -5250,7 +5255,8 @@
"fast-json-stable-stringify": { "fast-json-stable-stringify": {
"version": "2.0.0", "version": "2.0.0",
"resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.0.0.tgz", "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.0.0.tgz",
"integrity": "sha1-1RQsDK7msRifh9OnYREGT4bIu/I=" "integrity": "sha1-1RQsDK7msRifh9OnYREGT4bIu/I=",
"dev": true
}, },
"fast-levenshtein": { "fast-levenshtein": {
"version": "2.0.6", "version": "2.0.6",
@ -7551,7 +7557,8 @@
"json-schema-traverse": { "json-schema-traverse": {
"version": "0.4.1", "version": "0.4.1",
"resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz",
"integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==" "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==",
"dev": true
}, },
"json-stable-stringify-without-jsonify": { "json-stable-stringify-without-jsonify": {
"version": "1.0.1", "version": "1.0.1",
@ -7568,7 +7575,8 @@
"json5": { "json5": {
"version": "0.5.1", "version": "0.5.1",
"resolved": "https://registry.npmjs.org/json5/-/json5-0.5.1.tgz", "resolved": "https://registry.npmjs.org/json5/-/json5-0.5.1.tgz",
"integrity": "sha1-Hq3nrMASA0rYTiOWdn6tn6VJWCE=" "integrity": "sha1-Hq3nrMASA0rYTiOWdn6tn6VJWCE=",
"dev": true
}, },
"jsx-ast-utils": { "jsx-ast-utils": {
"version": "1.4.1", "version": "1.4.1",
@ -7706,6 +7714,7 @@
"version": "1.1.0", "version": "1.1.0",
"resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.1.0.tgz", "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.1.0.tgz",
"integrity": "sha1-yYrvSIvM7aL/teLeZG1qdUQp9c0=", "integrity": "sha1-yYrvSIvM7aL/teLeZG1qdUQp9c0=",
"dev": true,
"requires": { "requires": {
"big.js": "^3.1.3", "big.js": "^3.1.3",
"emojis-list": "^2.0.0", "emojis-list": "^2.0.0",
@ -10287,7 +10296,8 @@
"punycode": { "punycode": {
"version": "2.1.1", "version": "2.1.1",
"resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz",
"integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==" "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==",
"dev": true
}, },
"q": { "q": {
"version": "1.5.1", "version": "1.5.1",
@ -11137,6 +11147,7 @@
"version": "0.4.7", "version": "0.4.7",
"resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-0.4.7.tgz", "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-0.4.7.tgz",
"integrity": "sha512-v/iwU6wvwGK8HbU9yi3/nhGzP0yGSuhQMzL6ySiec1FSrZZDkhm4noOSWzrNFo/jEc+SJY6jRTwuwbSXJPDUnQ==", "integrity": "sha512-v/iwU6wvwGK8HbU9yi3/nhGzP0yGSuhQMzL6ySiec1FSrZZDkhm4noOSWzrNFo/jEc+SJY6jRTwuwbSXJPDUnQ==",
"dev": true,
"requires": { "requires": {
"ajv": "^6.1.0", "ajv": "^6.1.0",
"ajv-keywords": "^3.1.0" "ajv-keywords": "^3.1.0"
@ -12564,6 +12575,7 @@
"version": "2.3.2", "version": "2.3.2",
"resolved": "https://registry.npmjs.org/svg-url-loader/-/svg-url-loader-2.3.2.tgz", "resolved": "https://registry.npmjs.org/svg-url-loader/-/svg-url-loader-2.3.2.tgz",
"integrity": "sha1-3YaybBn+O5FPBOoQ7zlZTq3gRGQ=", "integrity": "sha1-3YaybBn+O5FPBOoQ7zlZTq3gRGQ=",
"dev": true,
"requires": { "requires": {
"file-loader": "1.1.11", "file-loader": "1.1.11",
"loader-utils": "1.1.0" "loader-utils": "1.1.0"
@ -12573,6 +12585,7 @@
"version": "1.1.11", "version": "1.1.11",
"resolved": "https://registry.npmjs.org/file-loader/-/file-loader-1.1.11.tgz", "resolved": "https://registry.npmjs.org/file-loader/-/file-loader-1.1.11.tgz",
"integrity": "sha512-TGR4HU7HUsGg6GCOPJnFk06RhWgEWFLAGWiT6rcD+GRC2keU3s9RGJ+b3Z6/U73jwwNb2gKLJ7YCrp+jvU4ALg==", "integrity": "sha512-TGR4HU7HUsGg6GCOPJnFk06RhWgEWFLAGWiT6rcD+GRC2keU3s9RGJ+b3Z6/U73jwwNb2gKLJ7YCrp+jvU4ALg==",
"dev": true,
"requires": { "requires": {
"loader-utils": "^1.0.2", "loader-utils": "^1.0.2",
"schema-utils": "^0.4.5" "schema-utils": "^0.4.5"
@ -13044,6 +13057,7 @@
"version": "4.2.2", "version": "4.2.2",
"resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.2.2.tgz", "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.2.2.tgz",
"integrity": "sha512-KY9Frmirql91X2Qgjry0Wd4Y+YTdrdZheS8TFwvkbLWf/G5KNJDCh6pKL5OZctEW4+0Baa5idK2ZQuELRwPznQ==", "integrity": "sha512-KY9Frmirql91X2Qgjry0Wd4Y+YTdrdZheS8TFwvkbLWf/G5KNJDCh6pKL5OZctEW4+0Baa5idK2ZQuELRwPznQ==",
"dev": true,
"requires": { "requires": {
"punycode": "^2.1.0" "punycode": "^2.1.0"
} }

4
client/package.json vendored
View File

@ -34,7 +34,6 @@
"redux-actions": "^2.4.0", "redux-actions": "^2.4.0",
"redux-form": "^7.4.2", "redux-form": "^7.4.2",
"redux-thunk": "^2.3.0", "redux-thunk": "^2.3.0",
"svg-url-loader": "^2.3.2",
"url-polyfill": "^1.1.7" "url-polyfill": "^1.1.7"
}, },
"devDependencies": { "devDependencies": {
@ -75,6 +74,7 @@
"url-loader": "^1.0.1", "url-loader": "^1.0.1",
"webpack": "3.8.1", "webpack": "3.8.1",
"webpack-dev-server": "^3.1.14", "webpack-dev-server": "^3.1.14",
"webpack-merge": "^4.1.3" "webpack-merge": "^4.1.3",
"svg-url-loader": "^2.3.2"
} }
} }

View File

@ -3,7 +3,6 @@ import { t } from 'i18next';
import apiClient from '../api/Api'; import apiClient from '../api/Api';
import { addErrorToast, addSuccessToast } from './index'; import { addErrorToast, addSuccessToast } from './index';
import { getStats, getStatsConfig } from './stats';
import { normalizeTextarea } from '../helpers/helpers'; import { normalizeTextarea } from '../helpers/helpers';
import { ACTION } from '../helpers/constants'; import { ACTION } from '../helpers/constants';
@ -50,11 +49,13 @@ export const toggleClientBlockRequest = createAction('TOGGLE_CLIENT_BLOCK_REQUES
export const toggleClientBlockFailure = createAction('TOGGLE_CLIENT_BLOCK_FAILURE'); export const toggleClientBlockFailure = createAction('TOGGLE_CLIENT_BLOCK_FAILURE');
export const toggleClientBlockSuccess = createAction('TOGGLE_CLIENT_BLOCK_SUCCESS'); export const toggleClientBlockSuccess = createAction('TOGGLE_CLIENT_BLOCK_SUCCESS');
export const toggleClientBlock = (type, ip) => async (dispatch, getState) => { export const toggleClientBlock = (type, ip) => async (dispatch) => {
dispatch(toggleClientBlockRequest()); dispatch(toggleClientBlockRequest());
try { try {
const { allowed_clients, disallowed_clients, blocked_hosts } = getState().access; const {
let updatedDisallowedClients = normalizeTextarea(disallowed_clients); allowed_clients, disallowed_clients, blocked_hosts,
} = await apiClient.getAccessList();
let updatedDisallowedClients = disallowed_clients;
if (type === ACTION.unblock && updatedDisallowedClients.includes(ip)) { if (type === ACTION.unblock && updatedDisallowedClients.includes(ip)) {
updatedDisallowedClients = updatedDisallowedClients.filter(client => client !== ip); updatedDisallowedClients = updatedDisallowedClients.filter(client => client !== ip);
@ -63,23 +64,19 @@ export const toggleClientBlock = (type, ip) => async (dispatch, getState) => {
} }
const values = { const values = {
allowed_clients: normalizeTextarea(allowed_clients), allowed_clients,
blocked_hosts: normalizeTextarea(blocked_hosts), blocked_hosts,
disallowed_clients: updatedDisallowedClients, disallowed_clients: updatedDisallowedClients,
}; };
await apiClient.setAccessList(values); await apiClient.setAccessList(values);
dispatch(toggleClientBlockSuccess()); dispatch(toggleClientBlockSuccess(values));
if (type === ACTION.unblock) { if (type === ACTION.unblock) {
dispatch(addSuccessToast(t('client_unblocked', { ip }))); dispatch(addSuccessToast(t('client_unblocked', { ip })));
} else if (type === ACTION.block) { } else if (type === ACTION.block) {
dispatch(addSuccessToast(t('client_blocked', { ip }))); dispatch(addSuccessToast(t('client_blocked', { ip })));
} }
dispatch(getStats());
dispatch(getStatsConfig());
dispatch(getAccessList());
} catch (error) { } catch (error) {
dispatch(addErrorToast({ error })); dispatch(addErrorToast({ error }));
dispatch(toggleClientBlockFailure()); dispatch(toggleClientBlockFailure());

View File

@ -2,7 +2,7 @@ import { createAction } from 'redux-actions';
import apiClient from '../api/Api'; import apiClient from '../api/Api';
import { addErrorToast, addSuccessToast } from './index'; import { addErrorToast, addSuccessToast } from './index';
import { normalizeTopStats, secondsToMilliseconds, getParamsForClientsSearch, addClientInfo, addClientStatus } from '../helpers/helpers'; import { normalizeTopStats, secondsToMilliseconds, getParamsForClientsSearch, addClientInfo } from '../helpers/helpers';
export const getStatsConfigRequest = createAction('GET_STATS_CONFIG_REQUEST'); export const getStatsConfigRequest = createAction('GET_STATS_CONFIG_REQUEST');
export const getStatsConfigFailure = createAction('GET_STATS_CONFIG_FAILURE'); export const getStatsConfigFailure = createAction('GET_STATS_CONFIG_FAILURE');
@ -46,15 +46,12 @@ export const getStats = () => async (dispatch) => {
const normalizedTopClients = normalizeTopStats(stats.top_clients); const normalizedTopClients = normalizeTopStats(stats.top_clients);
const clientsParams = getParamsForClientsSearch(normalizedTopClients, 'name'); const clientsParams = getParamsForClientsSearch(normalizedTopClients, 'name');
const clients = await apiClient.findClients(clientsParams); const clients = await apiClient.findClients(clientsParams);
const accessData = await apiClient.getAccessList();
const { disallowed_clients } = accessData;
const topClientsWithInfo = addClientInfo(normalizedTopClients, clients, 'name'); const topClientsWithInfo = addClientInfo(normalizedTopClients, clients, 'name');
const topClientsWithStatus = addClientStatus(topClientsWithInfo, disallowed_clients, 'name');
const normalizedStats = { const normalizedStats = {
...stats, ...stats,
top_blocked_domains: normalizeTopStats(stats.top_blocked_domains), top_blocked_domains: normalizeTopStats(stats.top_blocked_domains),
top_clients: topClientsWithStatus, top_clients: topClientsWithInfo,
top_queried_domains: normalizeTopStats(stats.top_queried_domains), top_queried_domains: normalizeTopStats(stats.top_queried_domains),
avg_processing_time: secondsToMilliseconds(stats.avg_processing_time), avg_processing_time: secondsToMilliseconds(stats.avg_processing_time),
}; };

View File

@ -57,10 +57,12 @@ const renderBlockingButton = (blocked, ip, handleClick, processing) => {
); );
}; };
const clientCell = (t, toggleClientStatus, processing) => const isBlockedClient = (clients, ip) => !!(clients && clients.includes(ip));
const clientCell = (t, toggleClientStatus, processing, disallowedClients) =>
function cell(row) { function cell(row) {
const { original, value } = row; const { value } = row;
const { blocked } = original; const blocked = isBlockedClient(disallowedClients, value);
return ( return (
<Fragment> <Fragment>
@ -80,6 +82,7 @@ const Clients = ({
dnsQueries, dnsQueries,
toggleClientStatus, toggleClientStatus,
processingAccessSet, processingAccessSet,
disallowedClients,
}) => ( }) => (
<Card <Card
title={t('top_clients')} title={t('top_clients')}
@ -102,7 +105,7 @@ const Clients = ({
accessor: 'ip', accessor: 'ip',
sortMethod: (a, b) => sortMethod: (a, b) =>
parseInt(a.replace(/\./g, ''), 10) - parseInt(b.replace(/\./g, ''), 10), parseInt(a.replace(/\./g, ''), 10) - parseInt(b.replace(/\./g, ''), 10),
Cell: clientCell(t, toggleClientStatus, processingAccessSet), Cell: clientCell(t, toggleClientStatus, processingAccessSet, disallowedClients),
}, },
{ {
Header: <Trans>requests_count</Trans>, Header: <Trans>requests_count</Trans>,
@ -122,9 +125,9 @@ const Clients = ({
return {}; return {};
} }
const { blocked } = rowInfo.original; const { ip } = rowInfo.original;
if (blocked) { if (isBlockedClient(disallowedClients, ip)) {
return { return {
className: 'red', className: 'red',
}; };
@ -148,6 +151,7 @@ Clients.propTypes = {
t: PropTypes.func.isRequired, t: PropTypes.func.isRequired,
toggleClientStatus: PropTypes.func.isRequired, toggleClientStatus: PropTypes.func.isRequired,
processingAccessSet: PropTypes.bool.isRequired, processingAccessSet: PropTypes.bool.isRequired,
disallowedClients: PropTypes.string.isRequired,
}; };
export default withNamespaces()(Clients); export default withNamespaces()(Clients);

View File

@ -19,6 +19,7 @@ class Dashboard extends Component {
} }
getAllStats = () => { getAllStats = () => {
this.props.getAccessList();
this.props.getStats(); this.props.getStats();
this.props.getStatsConfig(); this.props.getStatsConfig();
}; };
@ -53,7 +54,8 @@ class Dashboard extends Component {
dashboard, stats, access, t, dashboard, stats, access, t,
} = this.props; } = this.props;
const statsProcessing = stats.processingStats const statsProcessing = stats.processingStats
|| stats.processingGetConfig; || stats.processingGetConfig
|| access.processing;
const subtitle = const subtitle =
stats.interval === 1 stats.interval === 1
@ -130,6 +132,7 @@ class Dashboard extends Component {
refreshButton={refreshButton} refreshButton={refreshButton}
toggleClientStatus={this.toggleClientStatus} toggleClientStatus={this.toggleClientStatus}
processingAccessSet={access.processingSet} processingAccessSet={access.processingSet}
disallowedClients={access.disallowed_clients}
/> />
</div> </div>
<div className="col-lg-6"> <div className="col-lg-6">

View File

@ -122,17 +122,6 @@ export const addClientInfo = (data, clients, param) => (
}) })
); );
export const addClientStatus = (data, disallowedClients, param) => (
data.map((row) => {
const clientIp = row[param];
const blocked = !!(disallowedClients && disallowedClients.includes(clientIp));
return {
...row,
blocked,
};
})
);
export const normalizeFilteringStatus = (filteringStatus) => { export const normalizeFilteringStatus = (filteringStatus) => {
const { const {
enabled, filters, user_rules: userRules, interval, enabled, filters, user_rules: userRules, interval,

View File

@ -34,7 +34,21 @@ const access = handleActions(
[actions.toggleClientBlockRequest]: state => ({ ...state, processingSet: true }), [actions.toggleClientBlockRequest]: state => ({ ...state, processingSet: true }),
[actions.toggleClientBlockFailure]: state => ({ ...state, processingSet: false }), [actions.toggleClientBlockFailure]: state => ({ ...state, processingSet: false }),
[actions.toggleClientBlockSuccess]: state => ({ ...state, processingSet: false }), [actions.toggleClientBlockSuccess]: (state, { payload }) => {
const {
allowed_clients,
disallowed_clients,
blocked_hosts,
} = payload;
const newState = {
...state,
allowed_clients: (allowed_clients && allowed_clients.join('\n')) || '',
disallowed_clients: (disallowed_clients && disallowed_clients.join('\n')) || '',
blocked_hosts: (blocked_hosts && blocked_hosts.join('\n')) || '',
processingSet: false,
};
return newState;
},
}, },
{ {
processing: true, processing: true,