Pull request 2599: AGDNS-3682 Fixed Top clients block/unblock requests

Squashed commit of the following:

commit 5355055771d602619b26ee4f127973ec722813a7
Merge: 75461a4aa 5558089e7
Author: Ildar Kamalov <ik@adguard.com>
Date:   Fri Mar 13 10:38:26 2026 +0300

    Merge branch 'master' into AGDNS-3682

commit 75461a4aac910f493be5c94cd9cc7653eed89e85
Merge: eb1549d96 354ce04a5
Author: Ildar Kamalov <ik@adguard.com>
Date:   Wed Mar 11 15:06:12 2026 +0300

    Merge branch 'master' into AGDNS-3682

commit eb1549d96c57007e50bdb3a329d9cf9cf347a9c3
Author: Ildar Kamalov <ik@adguard.com>
Date:   Wed Mar 11 11:34:06 2026 +0300

    add method

commit 4c10975e53aa2a0830c194f1fd044e53e6bb942a
Author: Ildar Kamalov <ik@adguard.com>
Date:   Tue Mar 10 18:16:50 2026 +0300

    AGDNS-3682 fix top clients block request
This commit is contained in:
Ildar Kamalov 2026-03-13 07:45:44 +00:00
parent 5558089e7b
commit 31dc811ffc
4 changed files with 101 additions and 43 deletions

View file

@ -17,6 +17,11 @@ See also the [v0.107.74 GitHub milestone][ms-v0.107.74].
NOTE: Add new changes BELOW THIS COMMENT.
-->
### Fixed
- Fixed clients block/unblock when moving clients between allowed and disallowed lists.
<!--
NOTE: Add new changes ABOVE THIS COMMENT.
-->

View file

@ -49,41 +49,91 @@ export const toggleClientBlockRequest = createAction('TOGGLE_CLIENT_BLOCK_REQUES
export const toggleClientBlockFailure = createAction('TOGGLE_CLIENT_BLOCK_FAILURE');
export const toggleClientBlockSuccess = createAction('TOGGLE_CLIENT_BLOCK_SUCCESS');
export const toggleClientBlock = (ip: any, disallowed: any, disallowed_rule: any) => async (dispatch: any) => {
dispatch(toggleClientBlockRequest());
try {
const accessList = await apiClient.getAccessList();
const blocked_hosts = accessList.blocked_hosts ?? [];
let allowed_clients = accessList.allowed_clients ?? [];
let disallowed_clients = accessList.disallowed_clients ?? [];
if (disallowed) {
if (!disallowed_rule) {
allowed_clients = allowed_clients.concat(ip);
} else {
disallowed_clients = disallowed_clients.filter((client: any) => client !== disallowed_rule);
}
} else if (allowed_clients.length > 1) {
allowed_clients = allowed_clients.filter((client: any) => client !== disallowed_rule);
} else {
disallowed_clients = disallowed_clients.concat(ip);
}
const values = {
allowed_clients,
blocked_hosts,
disallowed_clients,
};
await apiClient.setAccessList(values);
dispatch(toggleClientBlockSuccess(values));
if (disallowed) {
dispatch(addSuccessToast(i18next.t('client_unblocked', { ip: disallowed_rule || ip })));
} else {
dispatch(addSuccessToast(i18next.t('client_blocked', { ip })));
}
} catch (error) {
dispatch(addErrorToast({ error }));
dispatch(toggleClientBlockFailure());
}
type AccessList = {
allowed_clients?: string[];
disallowed_clients?: string[];
blocked_hosts?: string[];
};
type AccessListValues = {
allowed_clients: string[];
disallowed_clients: string[];
blocked_hosts: string[];
};
type GetNextClientAccessListArgs = {
accessList: AccessList;
ip: string;
disallowed: boolean;
disallowedRule: string;
};
const addUnique = (items: string[], value: string) => (items.includes(value) ? items : items.concat(value));
const removeValue = (items: string[], value: string) => items.filter((item) => item !== value);
const getNextClientAccessList = ({
accessList,
ip,
disallowed,
disallowedRule,
}: GetNextClientAccessListArgs): AccessListValues => {
const values = {
blocked_hosts: accessList.blocked_hosts ?? [],
allowed_clients: accessList.allowed_clients ?? [],
disallowed_clients: accessList.disallowed_clients ?? [],
};
const isAllowlistMode = values.allowed_clients.length > 0;
if (disallowed && isAllowlistMode) {
return {
...values,
allowed_clients: addUnique(values.allowed_clients, ip),
};
}
if (disallowed) {
return {
...values,
disallowed_clients: removeValue(values.disallowed_clients, disallowedRule || ip),
};
}
if (isAllowlistMode) {
return {
...values,
allowed_clients: removeValue(values.allowed_clients, ip),
};
}
return {
...values,
disallowed_clients: addUnique(values.disallowed_clients, ip),
};
};
export const toggleClientBlock =
(ip: string, disallowed: boolean, disallowed_rule: string) => async (dispatch: any) => {
dispatch(toggleClientBlockRequest());
try {
const accessList: AccessList = await apiClient.getAccessList();
const values = getNextClientAccessList({
accessList,
ip,
disallowed,
disallowedRule: disallowed_rule,
});
await apiClient.setAccessList(values);
dispatch(toggleClientBlockSuccess(values));
if (disallowed) {
dispatch(addSuccessToast(i18next.t('client_unblocked', { ip: disallowed_rule || ip })));
} else {
dispatch(addSuccessToast(i18next.t('client_blocked', { ip })));
}
} catch (error) {
dispatch(addErrorToast({ error }));
dispatch(toggleClientBlockFailure());
}
};

View file

@ -10,7 +10,7 @@ import classNames from 'classnames';
import Card from '../ui/Card';
import Cell from '../ui/Cell';
import { getPercent, sortIp } from '../../helpers/helpers';
import { getPercent, sortIp, splitByNewLine } from '../../helpers/helpers';
import {
BLOCK_ACTIONS,
DASHBOARD_TABLES_DEFAULT_PAGE_SIZE,
@ -72,7 +72,7 @@ const renderBlockingButton = (ip: any, disallowed: any, disallowed_rule: any) =>
} else {
confirmMessage = `${t('adg_will_drop_dns_queries')} ${t('client_confirm_block', { ip })}`;
if (allowedClients.length > 0) {
confirmMessage = confirmMessage.concat(`\n\n${t('filter_allowlist', { disallowed_rule })}`);
confirmMessage = confirmMessage.concat(`\n\n${t('filter_allowlist', { disallowed_rule: ip })}`);
}
}
@ -89,7 +89,8 @@ const renderBlockingButton = (ip: any, disallowed: any, disallowed_rule: any) =>
const text = disallowed ? BLOCK_ACTIONS.UNBLOCK : BLOCK_ACTIONS.BLOCK;
const lastRuleInAllowlist = !disallowed && allowedClients === disallowed_rule;
const allowedClientsList = splitByNewLine(allowedClients || '');
const lastRuleInAllowlist = !disallowed && allowedClientsList.length === 1 && allowedClientsList[0] === ip;
const disabled = processingSet || lastRuleInAllowlist;
return (
<div className="table__action">
@ -112,7 +113,7 @@ const renderBlockingButton = (ip: any, disallowed: any, disallowed_rule: any) =>
)}
onClick={onClick}
disabled={disabled}
title={lastRuleInAllowlist ? t('last_rule_in_allowlist', { disallowed_rule }) : ''}>
title={lastRuleInAllowlist ? t('last_rule_in_allowlist', { disallowed_rule: ip }) : ''}>
<Trans>{text}</Trans>
</button>
}

View file

@ -1,4 +1,5 @@
import i18next from 'i18next';
import { splitByNewLine } from '../../../../helpers/helpers';
export const BUTTON_PREFIX = 'btn_';
@ -10,12 +11,13 @@ export const getBlockClientInfo = (ip: any, disallowed: any, disallowed_rule: an
} else {
confirmMessage = `${i18next.t('adg_will_drop_dns_queries')} ${i18next.t('client_confirm_block', { ip })}`;
if (allowedClients.length > 0) {
confirmMessage = confirmMessage.concat(`\n\n${i18next.t('filter_allowlist', { disallowed_rule })}`);
confirmMessage = confirmMessage.concat(`\n\n${i18next.t('filter_allowlist', { disallowed_rule: ip })}`);
}
}
const buttonKey = i18next.t(disallowed ? 'allow_this_client' : 'disallow_this_client');
const lastRuleInAllowlist = !disallowed && allowedClients === disallowed_rule;
const allowedClientsList = splitByNewLine(allowedClients || '');
const lastRuleInAllowlist = !disallowed && allowedClientsList.length === 1 && allowedClientsList[0] === ip;
return {
confirmMessage,