diff --git a/AGHTechDoc.md b/AGHTechDoc.md
index b91b8586..ed7f48ba 100644
--- a/AGHTechDoc.md
+++ b/AGHTechDoc.md
@@ -1846,7 +1846,7 @@ Response:
}
There are also deprecated properties `filter_id` and `rule` on the top level of
-the response object. Their usaga should be replaced with
+the response object. Their usage should be replaced with
`rules[*].filter_list_id` and `rules[*].text` correspondingly. See the
_OpenAPI_ documentation and the `./openapi/CHANGELOG.md` file.
diff --git a/client/src/__locales/en.json b/client/src/__locales/en.json
index 388691b0..afc2f105 100644
--- a/client/src/__locales/en.json
+++ b/client/src/__locales/en.json
@@ -270,7 +270,7 @@
"source_label": "Source",
"found_in_known_domain_db": "Found in the known domains database.",
"category_label": "Category",
- "rule_label": "Rule",
+ "rule_label": "Rule(s)",
"list_label": "List",
"unknown_filter": "Unknown filter {{filterId}}",
"known_tracker": "Known tracker",
@@ -530,7 +530,6 @@
"check_ip": "IP addresses: {{ip}}",
"check_cname": "CNAME: {{cname}}",
"check_reason": "Reason: {{reason}}",
- "check_rule": "Rule: {{rule}}",
"check_service": "Service name: {{service}}",
"service_name": "Service name",
"check_not_found": "Not found in your filter lists",
diff --git a/client/src/components/Filters/Check/Info.js b/client/src/components/Filters/Check/Info.js
index 1d4c22c5..f53f254d 100644
--- a/client/src/components/Filters/Check/Info.js
+++ b/client/src/components/Filters/Check/Info.js
@@ -12,7 +12,7 @@ import {
checkSafeSearch,
checkSafeBrowsing,
checkParental,
- getFilterName,
+ getRulesToFilterList,
} from '../../../helpers/helpers';
import { BLOCK_ACTIONS, FILTERED, FILTERED_STATUS } from '../../../helpers/constants';
import { toggleBlocking } from '../../../actions';
@@ -41,32 +41,27 @@ const renderBlockingButton = (isFiltered, domain) => {
;
};
-const getTitle = (reason) => {
+const getTitle = () => {
const { t } = useTranslation();
const filters = useSelector((state) => state.filtering.filters, shallowEqual);
const whitelistFilters = useSelector((state) => state.filtering.whitelistFilters, shallowEqual);
- const filter_id = useSelector((state) => state.filtering.check.filter_id);
-
- const filterName = getFilterName(
- filters,
- whitelistFilters,
- filter_id,
- 'filtered_custom_rules',
- (filter) => (filter?.name ? t('query_log_filtered', { filter: filter.name }) : ''),
- );
+ const rules = useSelector((state) => state.filtering.check.rules, shallowEqual);
+ const reason = useSelector((state) => state.filtering.check.reason);
const getReasonFiltered = (reason) => {
const filterKey = reason.replace(FILTERED, '');
return i18next.t('query_log_filtered', { filter: filterKey });
};
+ const ruleAndFilterNames = getRulesToFilterList(rules, filters, whitelistFilters);
+
const REASON_TO_TITLE_MAP = {
[FILTERED_STATUS.NOT_FILTERED_NOT_FOUND]: t('check_not_found'),
[FILTERED_STATUS.REWRITE]: t('rewrite_applied'),
[FILTERED_STATUS.REWRITE_HOSTS]: t('rewrite_hosts_applied'),
- [FILTERED_STATUS.FILTERED_BLACK_LIST]: filterName,
- [FILTERED_STATUS.NOT_FILTERED_WHITE_LIST]: filterName,
+ [FILTERED_STATUS.FILTERED_BLACK_LIST]: ruleAndFilterNames,
+ [FILTERED_STATUS.NOT_FILTERED_WHITE_LIST]: ruleAndFilterNames,
[FILTERED_STATUS.FILTERED_SAFE_SEARCH]: getReasonFiltered(reason),
[FILTERED_STATUS.FILTERED_SAFE_BROWSING]: getReasonFiltered(reason),
[FILTERED_STATUS.FILTERED_PARENTAL]: getReasonFiltered(reason),
@@ -78,7 +73,11 @@ const getTitle = (reason) => {
return <>
{t('check_reason', { reason })}
- {filterName}
+
+ {t('rule_label')}:
+
+ {ruleAndFilterNames}
+
>;
};
@@ -86,14 +85,13 @@ const Info = () => {
const {
hostname,
reason,
- rule,
service_name,
cname,
ip_addrs,
} = useSelector((state) => state.filtering.check, shallowEqual);
const { t } = useTranslation();
- const title = getTitle(reason);
+ const title = getTitle();
const className = classNames('card mb-0 p-3', {
'logs__row--red': checkFiltered(reason),
@@ -112,7 +110,6 @@ const Info = () => {
{title}
{!onlyFiltered
&& <>
- {rule && {t('check_rule', { rule })}
}
{service_name && {t('check_service', { service: service_name })}
}
{cname && {t('check_cname', { cname })}
}
{ip_addrs && {t('check_ip', { ip: ip_addrs.join(', ') })}
}
diff --git a/client/src/components/Logs/Cells/ResponseCell.js b/client/src/components/Logs/Cells/ResponseCell.js
index 816f35a3..026dbce1 100644
--- a/client/src/components/Logs/Cells/ResponseCell.js
+++ b/client/src/components/Logs/Cells/ResponseCell.js
@@ -4,8 +4,9 @@ import classNames from 'classnames';
import React from 'react';
import propTypes from 'prop-types';
import {
+ getRulesToFilterList,
formatElapsedMs,
- getFilterName,
+ getFilterNames,
getServiceName,
} from '../../../helpers/helpers';
import { FILTERED_STATUS, FILTERED_STATUS_TO_META_MAP } from '../../../helpers/constants';
@@ -18,8 +19,7 @@ const ResponseCell = ({
response,
status,
upstream,
- rule,
- filterId,
+ rules,
service_name,
}) => {
const { t } = useTranslation();
@@ -36,7 +36,6 @@ const ResponseCell = ({
const statusLabel = t(isBlockedByResponse ? 'blocked_by_cname_or_ip' : FILTERED_STATUS_TO_META_MAP[reason]?.LABEL || reason);
const boldStatusLabel = {statusLabel};
- const filter = getFilterName(filters, whitelistFilters, filterId);
const renderResponses = (responseArr) => {
if (!responseArr || responseArr.length === 0) {
@@ -52,18 +51,23 @@ const ResponseCell = ({
})};
};
+ const rulesList = getRulesToFilterList(rules, filters, whitelistFilters);
+
const COMMON_CONTENT = {
encryption_status: boldStatusLabel,
install_settings_dns: upstream,
elapsed: formattedElapsedMs,
response_code: status,
- ...(service_name ? { service_name: getServiceName(service_name) } : { filter }),
- rule_label: rule,
+ ...(service_name
+ ? { service_name: getServiceName(service_name) }
+ : { }
+ ),
+ rule_label: rulesList,
response_table_header: renderResponses(response),
original_response: renderResponses(originalResponse),
};
- const content = rule
+ const content = rules.length > 0
? Object.entries(COMMON_CONTENT)
: Object.entries({
...COMMON_CONTENT,
@@ -78,7 +82,8 @@ const ResponseCell = ({
}
return getServiceName(service_name);
case FILTERED_STATUS.FILTERED_BLACK_LIST:
- return filter;
+ case FILTERED_STATUS.NOT_FILTERED_WHITE_LIST:
+ return getFilterNames(rules, filters, whitelistFilters).join(', ');
default:
return formattedElapsedMs;
}
@@ -113,8 +118,10 @@ ResponseCell.propTypes = {
response: propTypes.array.isRequired,
status: propTypes.string.isRequired,
upstream: propTypes.string.isRequired,
- rule: propTypes.string,
- filterId: propTypes.number,
+ rules: propTypes.arrayOf(propTypes.shape({
+ text: propTypes.string.isRequired,
+ filter_list_id: propTypes.number.isRequired,
+ })),
service_name: propTypes.string,
};
diff --git a/client/src/components/Logs/Cells/index.js b/client/src/components/Logs/Cells/index.js
index 2e2635d9..8435a617 100644
--- a/client/src/components/Logs/Cells/index.js
+++ b/client/src/components/Logs/Cells/index.js
@@ -6,11 +6,11 @@ import propTypes from 'prop-types';
import {
captitalizeWords,
checkFiltered,
+ getRulesToFilterList,
formatDateTime,
formatElapsedMs,
formatTime,
getBlockingClientName,
- getFilterName,
getServiceName,
processContent,
} from '../../../helpers/helpers';
@@ -70,8 +70,7 @@ const Row = memo(({
upstream,
type,
client_proto,
- filterId,
- rule,
+ rules,
originalResponse,
status,
service_name,
@@ -107,8 +106,6 @@ const Row = memo(({
const sourceData = getSourceData(tracker);
- const filter = getFilterName(filters, whitelistFilters, filterId);
-
const {
confirmMessage,
buttonKey: blockingClientKey,
@@ -172,8 +169,8 @@ const Row = memo(({
response_details: 'title',
install_settings_dns: upstream,
elapsed: formattedElapsedMs,
- filter: rule ? filter : null,
- rule_label: rule,
+ rule_label: rules.length > 0
+ && getRulesToFilterList(rules, filters, whitelistFilters),
response_table_header: response?.join('\n'),
response_code: status,
client_details: 'title',
@@ -235,8 +232,10 @@ Row.propTypes = {
upstream: propTypes.string.isRequired,
type: propTypes.string.isRequired,
client_proto: propTypes.string.isRequired,
- filterId: propTypes.number,
- rule: propTypes.string,
+ rules: propTypes.arrayOf(propTypes.shape({
+ text: propTypes.string.isRequired,
+ filter_list_id: propTypes.number.isRequired,
+ })),
originalResponse: propTypes.array,
status: propTypes.string.isRequired,
service_name: propTypes.string,
diff --git a/client/src/components/Logs/Logs.css b/client/src/components/Logs/Logs.css
index 64300bb9..a48a8d15 100644
--- a/client/src/components/Logs/Logs.css
+++ b/client/src/components/Logs/Logs.css
@@ -428,3 +428,13 @@
margin-right: 1px;
opacity: 0.5;
}
+
+.filteringRules__rule {
+ margin-bottom: 0;
+}
+
+.filteringRules__filter {
+ font-style: italic;
+ font-weight: normal;
+ margin-bottom: 1rem;
+}
diff --git a/client/src/helpers/helpers.js b/client/src/helpers/helpers.js
index 7d44b400..82f30245 100644
--- a/client/src/helpers/helpers.js
+++ b/client/src/helpers/helpers.js
@@ -7,6 +7,7 @@ import i18n from 'i18next';
import uniqBy from 'lodash/uniqBy';
import ipaddr from 'ipaddr.js';
import queryString from 'query-string';
+import React from 'react';
import { getTrackerData } from './trackers/trackers';
import {
@@ -68,6 +69,7 @@ export const normalizeLogs = (logs) => logs.map((log) => {
time,
filterId,
rule,
+ rules,
service_name,
original_answer,
upstream,
@@ -80,6 +82,15 @@ export const normalizeLogs = (logs) => logs.map((log) => {
return `${type}: ${value} (ttl=${ttl})`;
}) : []);
+ let newRules = rules;
+ /* TODO 'filterId' and 'rule' are deprecated, will be removed in 0.106 */
+ if (rule !== undefined && filterId !== undefined && rules !== undefined && rules.length === 0) {
+ newRules = {
+ filter_list_id: filterId,
+ text: rule,
+ };
+ }
+
return {
time,
domain,
@@ -88,8 +99,10 @@ export const normalizeLogs = (logs) => logs.map((log) => {
reason,
client,
client_proto,
+ /* TODO 'filterId' and 'rule' are deprecated, will be removed in 0.106 */
filterId,
rule,
+ rules: newRules,
status,
service_name,
originalAnswer: original_answer,
@@ -726,6 +739,75 @@ export const getFilterName = (
return resolveFilterName(filter);
};
+/**
+ * @param {array} rules
+ * @param {array} filters
+ * @param {array} whitelistFilters
+ * @returns {string[]}
+ */
+export const getFilterNames = (rules, filters, whitelistFilters) => rules.map(
+ ({ filter_list_id }) => getFilterName(filters, whitelistFilters, filter_list_id),
+);
+
+/**
+ * @param {array} rules
+ * @returns {string[]}
+ */
+export const getRuleNames = (rules) => rules.map(({ text }) => text);
+
+/**
+ * @param {array} rules
+ * @param {array} filters
+ * @param {array} whitelistFilters
+ * @returns {object}
+ */
+export const getFilterNameToRulesMap = (rules, filters, whitelistFilters) => rules.reduce(
+ (acc, { text, filter_list_id }) => {
+ const filterName = getFilterName(filters, whitelistFilters, filter_list_id);
+
+ acc[filterName] = (acc[filterName] || []).concat(text);
+ return acc;
+ }, {},
+);
+
+/**
+ * @param {array} rules
+ * @param {array} filters
+ * @param {array} whitelistFilters
+ * @param {object} classes
+ * @returns {JSXElement}
+ */
+export const getRulesToFilterList = (rules, filters, whitelistFilters, classes = {
+ list: 'filteringRules',
+ rule: 'filteringRules__rule font-monospace',
+ filter: 'filteringRules__filter',
+}) => {
+ const filterNameToRulesMap = getFilterNameToRulesMap(rules, filters, whitelistFilters);
+
+ return
+ {Object.entries(filterNameToRulesMap).reduce(
+ (acc, [filterName, rulesArr]) => acc
+ .concat(rulesArr.map((rule, i) => - {rule}
))
+ .concat(- {filterName}
),
+ [],
+ )}
+
;
+};
+
+/**
+* @param {array} rules
+* @param {array} filters
+* @param {array} whitelistFilters
+* @returns {string}
+*/
+export const getRulesAndFilterNames = (rules, filters, whitelistFilters) => {
+ const filterNameToRulesMap = getFilterNameToRulesMap(rules, filters, whitelistFilters);
+
+ return Object.entries(filterNameToRulesMap).map(
+ ([filterName, filterRules]) => filterRules.concat(filterName).join('\n'),
+ ).join('\n\n');
+};
+
/**
* @param ip {string}
* @param gateway_ip {string}
diff --git a/client/src/helpers/renderFormattedClientCell.js b/client/src/helpers/renderFormattedClientCell.js
index d677c4ca..f7e59a84 100644
--- a/client/src/helpers/renderFormattedClientCell.js
+++ b/client/src/helpers/renderFormattedClientCell.js
@@ -31,7 +31,7 @@ const getFormattedWhois = (whois) => {
* @param {object} info.whois_info
* @param {boolean} [isDetailed]
* @param {boolean} [isLogs]
- * @returns {JSX.Element}
+ * @returns {JSXElement}
*/
export const renderFormattedClientCell = (value, info, isDetailed = false, isLogs = false) => {
let whoisContainer = null;