Updates #3419.
Squashed commit of the following:
commit 370094c00d9c15b1336fbedb1e233bd4436c9898
Author: Dmitriy Seregin <d.seregin@adguard.com>
Date: Fri Sep 10 17:31:16 2021 +0300
added link to github issue
commit 407ba9b2db46b887a30ddb081bd37c56e56b0496
Merge: 426c8146 80548233
Author: Dmitriy Seregin <d.seregin@adguard.com>
Date: Fri Sep 10 17:29:52 2021 +0300
Merge branch 'master' into 3419-client-allowlist-collision
commit 426c8146cff5c112ebb25192af276c6601200528
Author: Dmitriy Seregin <d.seregin@adguard.com>
Date: Fri Sep 10 16:28:11 2021 +0300
fix en
commit d28c6022321828c6bdc55c3f9a4f655b26d146d2
Author: Dmitriy Seregin <d.seregin@adguard.com>
Date: Fri Sep 10 15:49:12 2021 +0300
added missing space
commit b374a09327968ca5343c1595d1ab8cf317c15ffe
Author: Dmitriy Seregin <d.seregin@adguard.com>
Date: Fri Sep 10 15:43:55 2021 +0300
fixes after review
commit 2be629d66e4703e2f5a85615bf1eaaa92e03c6fd
Author: Dmitriy Seregin <d.seregin@adguard.com>
Date: Thu Sep 9 14:17:19 2021 +0300
fixes
commit 5c2aa6201cc0ecf404d4057e354fbb0bdadcdd6d
Author: Dmitriy Seregin <d.seregin@adguard.com>
Date: Wed Sep 8 15:04:30 2021 +0300
return empty line to locale file
commit 3631c3772babbd595b1c3de4a7e91be6bac3e80f
Author: Dmitriy Seregin <d.seregin@adguard.com>
Date: Wed Sep 8 13:57:51 2021 +0300
all: fix collisions in access lists && expand block/unblock client
160 lines
5.3 KiB
JavaScript
160 lines
5.3 KiB
JavaScript
import React from 'react';
|
||
import ReactTable from 'react-table';
|
||
import PropTypes from 'prop-types';
|
||
import { Trans, useTranslation } from 'react-i18next';
|
||
|
||
import { shallowEqual, useDispatch, useSelector } from 'react-redux';
|
||
import classNames from 'classnames';
|
||
import Card from '../ui/Card';
|
||
import Cell from '../ui/Cell';
|
||
|
||
import { getPercent, sortIp } from '../../helpers/helpers';
|
||
import { BLOCK_ACTIONS, STATUS_COLORS } from '../../helpers/constants';
|
||
import { toggleClientBlock } from '../../actions/access';
|
||
import { renderFormattedClientCell } from '../../helpers/renderFormattedClientCell';
|
||
import { getStats } from '../../actions/stats';
|
||
|
||
const getClientsPercentColor = (percent) => {
|
||
if (percent > 50) {
|
||
return STATUS_COLORS.green;
|
||
}
|
||
if (percent > 10) {
|
||
return STATUS_COLORS.yellow;
|
||
}
|
||
return STATUS_COLORS.red;
|
||
};
|
||
|
||
const CountCell = (row) => {
|
||
const { value, original: { ip } } = row;
|
||
const numDnsQueries = useSelector((state) => state.stats.numDnsQueries, shallowEqual);
|
||
|
||
const percent = getPercent(numDnsQueries, value);
|
||
const percentColor = getClientsPercentColor(percent);
|
||
|
||
return <Cell value={value} percent={percent} color={percentColor} search={ip} />;
|
||
};
|
||
|
||
const renderBlockingButton = (ip, disallowed, disallowed_rule) => {
|
||
const dispatch = useDispatch();
|
||
const { t } = useTranslation();
|
||
const processingSet = useSelector((state) => state.access.processingSet);
|
||
const allowedСlients = useSelector((state) => state.access.allowed_clients, shallowEqual);
|
||
|
||
const buttonClass = classNames('button-action button-action--main', {
|
||
'button-action--unblock': disallowed,
|
||
});
|
||
|
||
const toggleClientStatus = async (ip, disallowed, disallowed_rule) => {
|
||
let confirmMessage;
|
||
|
||
if (disallowed) {
|
||
confirmMessage = t('client_confirm_unblock', { ip: disallowed_rule || ip });
|
||
} else {
|
||
confirmMessage = `${t('adg_will_drop_dns_queries')} ${t('client_confirm_block', { ip })}`;
|
||
if (allowedСlients.length > 0) {
|
||
confirmMessage = confirmMessage.concat(`\n\n${t('filter_allowlist', { disallowed_rule })}`);
|
||
}
|
||
}
|
||
|
||
if (window.confirm(confirmMessage)) {
|
||
await dispatch(toggleClientBlock(ip, disallowed, disallowed_rule));
|
||
await dispatch(getStats());
|
||
}
|
||
};
|
||
|
||
const onClick = () => toggleClientStatus(ip, disallowed, disallowed_rule);
|
||
|
||
const text = disallowed ? BLOCK_ACTIONS.UNBLOCK : BLOCK_ACTIONS.BLOCK;
|
||
|
||
const lastRuleInAllowlist = !disallowed && allowedСlients === disallowed_rule;
|
||
const disabled = processingSet || lastRuleInAllowlist;
|
||
return (
|
||
<div className="table__action pl-4">
|
||
<button
|
||
type="button"
|
||
className={buttonClass}
|
||
onClick={onClick}
|
||
disabled={disabled}
|
||
title={lastRuleInAllowlist ? t('last_rule_in_allowlist', { disallowed_rule }) : ''}
|
||
>
|
||
<Trans>{text}</Trans>
|
||
</button>
|
||
</div>
|
||
);
|
||
};
|
||
|
||
const ClientCell = (row) => {
|
||
const { value, original: { info, info: { disallowed, disallowed_rule } } } = row;
|
||
|
||
return <>
|
||
<div className="logs__row logs__row--overflow logs__row--column d-flex align-items-center">
|
||
{renderFormattedClientCell(value, info, true)}
|
||
{renderBlockingButton(value, disallowed, disallowed_rule)}
|
||
</div>
|
||
</>;
|
||
};
|
||
|
||
const Clients = ({
|
||
refreshButton,
|
||
subtitle,
|
||
}) => {
|
||
const { t } = useTranslation();
|
||
const topClients = useSelector((state) => state.stats.topClients, shallowEqual);
|
||
|
||
return (
|
||
<Card
|
||
title={t('top_clients')}
|
||
subtitle={subtitle}
|
||
bodyType="card-table"
|
||
refresh={refreshButton}
|
||
>
|
||
<ReactTable
|
||
data={topClients.map(({
|
||
name: ip, count, info, blocked,
|
||
}) => ({
|
||
ip,
|
||
count,
|
||
info,
|
||
blocked,
|
||
}))}
|
||
columns={[
|
||
{
|
||
Header: <Trans>client_table_header</Trans>,
|
||
accessor: 'ip',
|
||
sortMethod: sortIp,
|
||
Cell: ClientCell,
|
||
},
|
||
{
|
||
Header: <Trans>requests_count</Trans>,
|
||
accessor: 'count',
|
||
minWidth: 180,
|
||
maxWidth: 200,
|
||
Cell: CountCell,
|
||
},
|
||
]}
|
||
showPagination={false}
|
||
noDataText={t('no_clients_found')}
|
||
minRows={6}
|
||
defaultPageSize={100}
|
||
className="-highlight card-table-overflow--limited clients__table"
|
||
getTrProps={(_state, rowInfo) => {
|
||
if (!rowInfo) {
|
||
return {};
|
||
}
|
||
|
||
const { info: { disallowed } } = rowInfo.original;
|
||
|
||
return disallowed ? { className: 'logs__row--red' } : {};
|
||
}}
|
||
/>
|
||
</Card>
|
||
);
|
||
};
|
||
|
||
Clients.propTypes = {
|
||
refreshButton: PropTypes.node.isRequired,
|
||
subtitle: PropTypes.string.isRequired,
|
||
};
|
||
|
||
export default Clients;
|