diff --git a/CHANGELOG.md b/CHANGELOG.md
index 2b060ada..a8f44577 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -25,18 +25,44 @@ NOTE: Add new changes BELOW THIS COMMENT.
### Added
+- Two new HTTP APIs, `PUT /control/stats/config/update` and `GET
+ control/stats/config`, which can be used to set and receive the query log
+ configuration. See openapi/openapi.yaml for the full description.
+- Two new HTTP APIs, `PUT /control/querylog/config/update` and `GET
+ control/querylog/config`, which can be used to set and receive the statistics
+ configuration. See openapi/openapi.yaml for the full description.
+- The ability to set custom IP for EDNS Client Subnet by using the DNS-server
+ configuration section on the DNS settings page in the UI ([#1472]).
- The ability to manage safesearch for each service by using the new
`safe_search` field ([#1163]).
-### Changed
+### Changed
- ARPA domain names containing a subnet within private networks now also
considered private, behaving closer to [RFC 6761][rfc6761] ([#5567]).
#### Configuration Changes
-In this release, the schema version has changed from 17 to 19.
+In this release, the schema version has changed from 17 to 20.
+- Property `statistics.interval`, which in schema versions 19 and earlier used
+ to be an integer number of days, is now a string with a human-readable
+ duration:
+
+ ```yaml
+ # BEFORE:
+ 'statistics':
+ # …
+ 'interval': 1
+
+ # AFTER:
+ 'statistics':
+ # …
+ 'interval': '24h'
+ ```
+
+ To rollback this change, convert the property back into days and change the
+ `schema_version` back to `19`.
- The `dns.safesearch_enabled` field has been replaced with `safe_search`
object containing per-service settings.
- The `clients.persistent.safesearch_enabled` field has been replaced with
@@ -62,12 +88,41 @@ In this release, the schema version has changed from 17 to 19.
client's specific `clients.persistent.safesearch` and then change the
`schema_version` back to `17`.
+### Deprecated
+
+- The `POST /control/safesearch/enable` HTTP API is deprecated. Use the new
+ `PUT /control/safesearch/settings` API.
+- The `POST /control/safesearch/disable` HTTP API is deprecated. Use the new
+ `PUT /control/safesearch/settings` API
+- The `safesearch_enabled` field is deprecated in the following HTTP APIs:
+ - `GET /control/clients`
+ - `POST /control/clients/add`
+ - `POST /control/clients/update`
+ - `GET /control/clients/find?ip0=...&ip1=...&ip2=...`
+
+ Check `openapi/openapi.yaml` for more details.
+- The `GET /control/stats_info` HTTP API; use the new `GET
+ /control/stats/config` API instead.
+
+ **NOTE:** If interval is custom then it will be equal to `90` days for
+ compatibility reasons. See openapi/openapi.yaml and `openapi/CHANGELOG.md`.
+- The `POST /control/stats_config` HTTP API; use the new `PUT
+ /control/stats/config/update` API instead.
+- The `GET /control/querylog_info` HTTP API; use the new `GET
+ /control/querylog/config` API instead.
+
+ **NOTE:** If interval is custom then it will be equal to `90` days for
+ compatibility reasons. See openapi/openapi.yaml and `openapi/CHANGELOG.md`.
+- The `POST /control/querylog_config` HTTP API; use the new `PUT
+ /control/querylog/config/update` API instead.
+
### Fixed
- Panic caused by empty top-level domain name label in `/etc/hosts` files
([#5584]).
[#1163]: https://github.com/AdguardTeam/AdGuardHome/issues/1163
+[#1472]: https://github.com/AdguardTeam/AdGuardHome/issues/1472
[#5567]: https://github.com/AdguardTeam/AdGuardHome/issues/5567
[#5584]: https://github.com/AdguardTeam/AdGuardHome/issues/5584
@@ -100,8 +155,6 @@ See also the [v0.107.26 GitHub milestone][ms-v0.107.26].
#### Configuration Changes
-In this release, the schema version has changed from 16 to 17.
-
- Property `edns_client_subnet`, which in schema versions 16 and earlier used
to be a part of the `dns` object, is now part of the `dns.edns_client_subnet`
object:
diff --git a/client/src/__locales/en.json b/client/src/__locales/en.json
index 2afa38bd..08f3c08f 100644
--- a/client/src/__locales/en.json
+++ b/client/src/__locales/en.json
@@ -167,6 +167,7 @@
"enabled_parental_toast": "Enabled Parental Control",
"disabled_safe_search_toast": "Disabled Safe Search",
"enabled_save_search_toast": "Enabled Safe Search",
+ "updated_save_search_toast": "Safe Search settings updated",
"enabled_table_header": "Enabled",
"name_table_header": "Name",
"list_url_table_header": "List URL",
@@ -290,6 +291,8 @@
"rate_limit": "Rate limit",
"edns_enable": "Enable EDNS client subnet",
"edns_cs_desc": "Add the EDNS Client Subnet option (ECS) to upstream requests and log the values sent by the clients in the query log.",
+ "edns_use_custom_ip": "Use custom IP for EDNS",
+ "edns_use_custom_ip_desc": "Allow to use custom IP for EDNS",
"rate_limit_desc": "The number of requests per second allowed per client. Setting it to 0 means no limit.",
"blocking_ipv4_desc": "IP address to be returned for a blocked A request",
"blocking_ipv6_desc": "IP address to be returned for a blocked AAAA request",
@@ -523,6 +526,10 @@
"statistics_retention_confirm": "Are you sure you want to change statistics retention? If you decrease the interval value, some data will be lost",
"statistics_cleared": "Statistics successfully cleared",
"statistics_enable": "Enable statistics",
+ "ignore_domains": "Ignored domains (separated by newline)",
+ "ignore_domains_title": "Ignored domains",
+ "ignore_domains_desc_stats": "Queries for these domains are not written to the statistics",
+ "ignore_domains_desc_query": "Queries for these domains are not written to the query log",
"interval_hours": "{{count}} hour",
"interval_hours_plural": "{{count}} hours",
"filters_configuration": "Filters configuration",
@@ -642,5 +649,6 @@
"anonymizer_notification": "<0>Note:0> IP anonymization is enabled. You can disable it in <1>General settings1>.",
"confirm_dns_cache_clear": "Are you sure you want to clear DNS cache?",
"cache_cleared": "DNS cache successfully cleared",
- "clear_cache": "Clear cache"
+ "clear_cache": "Clear cache",
+ "make_static": "Make static"
}
diff --git a/client/src/actions/index.js b/client/src/actions/index.js
index 2780c94d..a164f51a 100644
--- a/client/src/actions/index.js
+++ b/client/src/actions/index.js
@@ -24,6 +24,12 @@ import { getFilteringStatus, setRules } from './filtering';
export const toggleSettingStatus = createAction('SETTING_STATUS_TOGGLE');
export const showSettingsFailure = createAction('SETTINGS_FAILURE_SHOW');
+/**
+ *
+ * @param {*} settingKey = SETTINGS_NAMES
+ * @param {*} status: boolean | SafeSearchConfig
+ * @returns
+ */
export const toggleSetting = (settingKey, status) => async (dispatch) => {
let successMessage = '';
try {
@@ -49,14 +55,9 @@ export const toggleSetting = (settingKey, status) => async (dispatch) => {
dispatch(toggleSettingStatus({ settingKey }));
break;
case SETTINGS_NAMES.safesearch:
- if (status) {
- successMessage = 'disabled_safe_search_toast';
- await apiClient.disableSafesearch();
- } else {
- successMessage = 'enabled_save_search_toast';
- await apiClient.enableSafesearch();
- }
- dispatch(toggleSettingStatus({ settingKey }));
+ successMessage = 'updated_save_search_toast';
+ await apiClient.updateSafesearch(status);
+ dispatch(toggleSettingStatus({ settingKey, value: status }));
break;
default:
break;
@@ -71,7 +72,9 @@ export const initSettingsRequest = createAction('SETTINGS_INIT_REQUEST');
export const initSettingsFailure = createAction('SETTINGS_INIT_FAILURE');
export const initSettingsSuccess = createAction('SETTINGS_INIT_SUCCESS');
-export const initSettings = (settingsList) => async (dispatch) => {
+export const initSettings = (settingsList = {
+ safebrowsing: {}, parental: {},
+}) => async (dispatch) => {
dispatch(initSettingsRequest());
try {
const safebrowsingStatus = await apiClient.getSafebrowsingStatus();
@@ -80,7 +83,6 @@ export const initSettings = (settingsList) => async (dispatch) => {
const {
safebrowsing,
parental,
- safesearch,
} = settingsList;
const newSettingsList = {
safebrowsing: {
@@ -92,8 +94,7 @@ export const initSettings = (settingsList) => async (dispatch) => {
enabled: parentalStatus.enabled,
},
safesearch: {
- ...safesearch,
- enabled: safesearchStatus.enabled,
+ ...safesearchStatus,
},
};
dispatch(initSettingsSuccess({ settingsList: newSettingsList }));
diff --git a/client/src/actions/queryLogs.js b/client/src/actions/queryLogs.js
index 99da2cb0..e07c6fae 100644
--- a/client/src/actions/queryLogs.js
+++ b/client/src/actions/queryLogs.js
@@ -177,7 +177,7 @@ export const getLogsConfigSuccess = createAction('GET_LOGS_CONFIG_SUCCESS');
export const getLogsConfig = () => async (dispatch) => {
dispatch(getLogsConfigRequest());
try {
- const data = await apiClient.getQueryLogInfo();
+ const data = await apiClient.getQueryLogConfig();
dispatch(getLogsConfigSuccess(data));
} catch (error) {
dispatch(addErrorToast({ error }));
diff --git a/client/src/actions/stats.js b/client/src/actions/stats.js
index d3948efa..0e5b416e 100644
--- a/client/src/actions/stats.js
+++ b/client/src/actions/stats.js
@@ -13,7 +13,7 @@ export const getStatsConfigSuccess = createAction('GET_STATS_CONFIG_SUCCESS');
export const getStatsConfig = () => async (dispatch) => {
dispatch(getStatsConfigRequest());
try {
- const data = await apiClient.getStatsInfo();
+ const data = await apiClient.getStatsConfig();
dispatch(getStatsConfigSuccess(data));
} catch (error) {
dispatch(addErrorToast({ error }));
diff --git a/client/src/api/Api.js b/client/src/api/Api.js
index d984bbb8..caf836b8 100644
--- a/client/src/api/Api.js
+++ b/client/src/api/Api.js
@@ -208,24 +208,40 @@ class Api {
// Safesearch
SAFESEARCH_STATUS = { path: 'safesearch/status', method: 'GET' };
- SAFESEARCH_ENABLE = { path: 'safesearch/enable', method: 'POST' };
-
- SAFESEARCH_DISABLE = { path: 'safesearch/disable', method: 'POST' };
+ SAFESEARCH_UPDATE = { path: 'safesearch/settings', method: 'PUT' };
getSafesearchStatus() {
const { path, method } = this.SAFESEARCH_STATUS;
return this.makeRequest(path, method);
}
- enableSafesearch() {
- const { path, method } = this.SAFESEARCH_ENABLE;
- return this.makeRequest(path, method);
+ /**
+ * interface SafeSearchConfig {
+ "enabled": boolean,
+ "bing": boolean,
+ "duckduckgo": boolean,
+ "google": boolean,
+ "pixabay": boolean,
+ "yandex": boolean,
+ "youtube": boolean
+ * }
+ * @param {*} data - SafeSearchConfig
+ * @returns 200 ok
+ */
+ updateSafesearch(data) {
+ const { path, method } = this.SAFESEARCH_UPDATE;
+ return this.makeRequest(path, method, { data });
}
- disableSafesearch() {
- const { path, method } = this.SAFESEARCH_DISABLE;
- return this.makeRequest(path, method);
- }
+ // enableSafesearch() {
+ // const { path, method } = this.SAFESEARCH_ENABLE;
+ // return this.makeRequest(path, method);
+ // }
+
+ // disableSafesearch() {
+ // const { path, method } = this.SAFESEARCH_DISABLE;
+ // return this.makeRequest(path, method);
+ // }
// Language
@@ -497,9 +513,9 @@ class Api {
// Settings for statistics
GET_STATS = { path: 'stats', method: 'GET' };
- STATS_INFO = { path: 'stats_info', method: 'GET' };
+ GET_STATS_CONFIG = { path: 'stats/config', method: 'GET' };
- STATS_CONFIG = { path: 'stats_config', method: 'POST' };
+ UPDATE_STATS_CONFIG = { path: 'stats/config/update', method: 'PUT' };
STATS_RESET = { path: 'stats_reset', method: 'POST' };
@@ -508,13 +524,13 @@ class Api {
return this.makeRequest(path, method);
}
- getStatsInfo() {
- const { path, method } = this.STATS_INFO;
+ getStatsConfig() {
+ const { path, method } = this.GET_STATS_CONFIG;
return this.makeRequest(path, method);
}
setStatsConfig(data) {
- const { path, method } = this.STATS_CONFIG;
+ const { path, method } = this.UPDATE_STATS_CONFIG;
const config = {
data,
};
@@ -529,9 +545,9 @@ class Api {
// Query log
GET_QUERY_LOG = { path: 'querylog', method: 'GET' };
- QUERY_LOG_CONFIG = { path: 'querylog_config', method: 'POST' };
+ UPDATE_QUERY_LOG_CONFIG = { path: 'querylog/config/update', method: 'PUT' };
- QUERY_LOG_INFO = { path: 'querylog_info', method: 'GET' };
+ GET_QUERY_LOG_CONFIG = { path: 'querylog/config', method: 'GET' };
QUERY_LOG_CLEAR = { path: 'querylog_clear', method: 'POST' };
@@ -543,13 +559,13 @@ class Api {
return this.makeRequest(url, method);
}
- getQueryLogInfo() {
- const { path, method } = this.QUERY_LOG_INFO;
+ getQueryLogConfig() {
+ const { path, method } = this.GET_QUERY_LOG_CONFIG;
return this.makeRequest(path, method);
}
setQueryLogConfig(data) {
- const { path, method } = this.QUERY_LOG_CONFIG;
+ const { path, method } = this.UPDATE_QUERY_LOG_CONFIG;
const config = {
data,
};
diff --git a/client/src/components/Dashboard/BlockedDomains.js b/client/src/components/Dashboard/BlockedDomains.js
index 144f5bee..73829a1b 100644
--- a/client/src/components/Dashboard/BlockedDomains.js
+++ b/client/src/components/Dashboard/BlockedDomains.js
@@ -29,8 +29,11 @@ const BlockedDomains = ({
blockedFiltering,
replacedSafebrowsing,
replacedParental,
+ replacedSafesearch,
}) => {
- const totalBlocked = blockedFiltering + replacedSafebrowsing + replacedParental;
+ const totalBlocked = (
+ blockedFiltering + replacedSafebrowsing + replacedParental + replacedSafesearch
+ );
return (