diff --git a/client/src/__locales/en.json b/client/src/__locales/en.json
index b594c20a..9e11009a 100644
--- a/client/src/__locales/en.json
+++ b/client/src/__locales/en.json
@@ -446,5 +446,17 @@
"autofix_warning_result": "As a result all DNS requests from your system will be processed by AdGuardHome by default.",
"tags_title": "Tags",
"tags_desc": "You can select the tags that correspond to the client. Tags can be included in the filtering rules and allow you to apply them more accurately. <0>Learn more0>",
- "form_select_tags": "Select client tags"
+ "form_select_tags": "Select client tags",
+ "check_title": "Check the filtering",
+ "check_desc": "Check if the host name is filtered",
+ "check": "Check",
+ "form_enter_host": "Enter a host name",
+ "filtered_custom_rules": "Filtered by Custom filtering rules",
+ "host_whitelisted": "The host is whitelisted",
+ "check_ip": "IP addresses: {{ip}}",
+ "check_cname": "CNAME: {{cname}}",
+ "check_reason": "Reason: {{reason}}",
+ "check_rule": "Rule: {{rule}}",
+ "check_service": "Service name: {{service}}",
+ "check_not_found": "Doesn't exist in any filter"
}
diff --git a/client/src/actions/filtering.js b/client/src/actions/filtering.js
index 117c22ee..ee8d5c03 100644
--- a/client/src/actions/filtering.js
+++ b/client/src/actions/filtering.js
@@ -161,3 +161,23 @@ export const setFiltersConfig = config => async (dispatch, getState) => {
dispatch(setFiltersConfigFailure());
}
};
+
+export const checkHostRequest = createAction('CHECK_HOST_REQUEST');
+export const checkHostFailure = createAction('CHECK_HOST_FAILURE');
+export const checkHostSuccess = createAction('CHECK_HOST_SUCCESS');
+
+export const checkHost = host => async (dispatch) => {
+ dispatch(checkHostRequest());
+ try {
+ const data = await apiClient.checkHost(host);
+ const [hostname] = Object.values(host);
+
+ dispatch(checkHostSuccess({
+ hostname,
+ ...data,
+ }));
+ } catch (error) {
+ dispatch(addErrorToast({ error }));
+ dispatch(checkHostFailure());
+ }
+};
diff --git a/client/src/api/Api.js b/client/src/api/Api.js
index 3d215144..97d2cc6f 100644
--- a/client/src/api/Api.js
+++ b/client/src/api/Api.js
@@ -82,6 +82,7 @@ class Api {
FILTERING_REFRESH = { path: 'filtering/refresh', method: 'POST' };
FILTERING_SET_URL = { path: 'filtering/set_url', method: 'POST' };
FILTERING_CONFIG = { path: 'filtering/config', method: 'POST' };
+ FILTERING_CHECK_HOST = { path: 'filtering/check_host', method: 'GET' };
getFilteringStatus() {
const { path, method } = this.FILTERING_STATUS;
@@ -141,6 +142,12 @@ class Api {
return this.makeRequest(path, method, parameters);
}
+ checkHost(params) {
+ const { path, method } = this.FILTERING_CHECK_HOST;
+ const url = getPathWithQueryString(path, params);
+ return this.makeRequest(url, method);
+ }
+
// Parental
PARENTAL_STATUS = { path: 'parental/status', method: 'GET' };
PARENTAL_ENABLE = { path: 'parental/enable', method: 'POST' };
diff --git a/client/src/components/Filters/Check/Info.js b/client/src/components/Filters/Check/Info.js
new file mode 100644
index 00000000..d3cd0136
--- /dev/null
+++ b/client/src/components/Filters/Check/Info.js
@@ -0,0 +1,127 @@
+import React, { Fragment } from 'react';
+import PropTypes from 'prop-types';
+import { withNamespaces } from 'react-i18next';
+
+import { checkFiltered, checkRewrite, checkBlackList, checkNotFilteredNotFound, checkWhiteList } from '../../../helpers/helpers';
+
+const getFilterName = (id, filters, t) => {
+ if (id === 0) {
+ return t('filtered_custom_rules');
+ }
+
+ const filter = filters.find(filter => filter.id === id);
+
+ if (filter && filter.name) {
+ return t('query_log_filtered', { filter: filter.name });
+ }
+
+ return '';
+};
+
+const getTitle = (reason, filterName, t) => {
+ if (checkNotFilteredNotFound(reason)) {
+ return t('check_not_found');
+ }
+
+ if (checkRewrite(reason)) {
+ return t('rewrite_applied');
+ }
+
+ if (checkBlackList(reason)) {
+ return filterName;
+ }
+
+ if (checkWhiteList(reason)) {
+ return (
+
+
+ {t('host_whitelisted')}
+
+
+ {filterName}
+
+
+ );
+ }
+
+ return (
+
+
+ {t('check_reason', { reason })}
+
+
+ {filterName}
+
+
+ );
+};
+
+const getColor = (reason) => {
+ if (checkFiltered(reason)) {
+ return 'red';
+ } else if (checkRewrite(reason)) {
+ return 'blue';
+ } else if (checkWhiteList(reason)) {
+ return 'green';
+ }
+
+ return '';
+};
+
+const Info = ({
+ filters,
+ hostname,
+ reason,
+ filter_id,
+ rule,
+ service_name,
+ cname,
+ ip_addrs,
+ t,
+}) => {
+ const filterName = getFilterName(filter_id, filters, t);
+ const title = getTitle(reason, filterName, t);
+ const color = getColor(reason);
+
+ return (
+
+
+ {hostname}
+
+
+
{title}
+
+ {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(', ') })}
+
+ )}
+
+ );
+};
+
+Info.propTypes = {
+ filters: PropTypes.array.isRequired,
+ hostname: PropTypes.string.isRequired,
+ reason: PropTypes.string.isRequired,
+ filter_id: PropTypes.number,
+ rule: PropTypes.string,
+ service_name: PropTypes.string,
+ cname: PropTypes.string,
+ ip_addrs: PropTypes.array,
+ t: PropTypes.func.isRequired,
+};
+
+export default withNamespaces()(Info);
diff --git a/client/src/components/Filters/Check/index.js b/client/src/components/Filters/Check/index.js
new file mode 100644
index 00000000..13011157
--- /dev/null
+++ b/client/src/components/Filters/Check/index.js
@@ -0,0 +1,95 @@
+import React, { Fragment } from 'react';
+import PropTypes from 'prop-types';
+import { Trans, withNamespaces } from 'react-i18next';
+import { Field, reduxForm } from 'redux-form';
+import flow from 'lodash/flow';
+import Card from '../../ui/Card';
+
+import { renderInputField } from '../../../helpers/form';
+import Info from './Info';
+
+const Check = (props) => {
+ const {
+ t,
+ handleSubmit,
+ pristine,
+ invalid,
+ processing,
+ check,
+ filters,
+ } = props;
+
+ const {
+ hostname,
+ reason,
+ filter_id,
+ rule,
+ service_name,
+ cname,
+ ip_addrs,
+ } = check;
+
+ return (
+
+
+
+ );
+};
+
+Check.propTypes = {
+ t: PropTypes.func.isRequired,
+ handleSubmit: PropTypes.func.isRequired,
+ pristine: PropTypes.bool.isRequired,
+ invalid: PropTypes.bool.isRequired,
+ processing: PropTypes.bool.isRequired,
+ check: PropTypes.object.isRequired,
+ filters: PropTypes.array.isRequired,
+};
+
+export default flow([
+ withNamespaces(),
+ reduxForm({ form: 'domainCheckForm' }),
+])(Check);
diff --git a/client/src/components/Filters/UserRules.js b/client/src/components/Filters/UserRules.js
index a2f10507..312d175c 100644
--- a/client/src/components/Filters/UserRules.js
+++ b/client/src/components/Filters/UserRules.js
@@ -26,7 +26,7 @@ class UserRules extends Component {
/>
(
normalizeOnBlur
? input.onBlur(normalizeOnBlur(event.target.value))
: input.onBlur());
+
+export const checkFiltered = reason => reason.indexOf(FILTERED) === 0;
+export const checkRewrite = reason => reason === FILTERED_STATUS.REWRITE;
+export const checkBlackList = reason => reason === FILTERED_STATUS.FILTERED_BLACK_LIST;
+export const checkWhiteList = reason => reason === FILTERED_STATUS.NOT_FILTERED_WHITE_LIST;
+export const checkNotFilteredNotFound = reason => reason === FILTERED_STATUS.NOT_FILTERED_NOT_FOUND;
diff --git a/client/src/reducers/filtering.js b/client/src/reducers/filtering.js
index f3f3d894..da298426 100644
--- a/client/src/reducers/filtering.js
+++ b/client/src/reducers/filtering.js
@@ -79,6 +79,14 @@ const filtering = handleActions(
...payload,
processingSetConfig: false,
}),
+
+ [actions.checkHostRequest]: state => ({ ...state, processingCheck: true }),
+ [actions.checkHostFailure]: state => ({ ...state, processingCheck: false }),
+ [actions.checkHostSuccess]: (state, { payload }) => ({
+ ...state,
+ check: payload,
+ processingCheck: false,
+ }),
},
{
isModalOpen: false,
@@ -89,6 +97,7 @@ const filtering = handleActions(
processingConfigFilter: false,
processingRemoveFilter: false,
processingSetConfig: false,
+ processingCheck: false,
isFilterAdded: false,
filters: [],
userRules: '',
@@ -96,6 +105,7 @@ const filtering = handleActions(
enabled: true,
modalType: '',
modalFilterUrl: '',
+ check: {},
},
);