diff --git a/client/package-lock.json b/client/package-lock.json
index 0c596c27..b2070cdb 100644
--- a/client/package-lock.json
+++ b/client/package-lock.json
@@ -5027,11 +5027,6 @@
}
}
},
- "file-saver": {
- "version": "1.3.8",
- "resolved": "https://registry.npmjs.org/file-saver/-/file-saver-1.3.8.tgz",
- "integrity": "sha512-spKHSBQIxxS81N/O21WmuXA2F6wppUCsutpzenOeZzOCCJ5gEfcbqJP983IrpLXzYmXnMUa6J03SubcNPdKrlg=="
- },
"fill-range": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz",
diff --git a/client/package.json b/client/package.json
index 6c5f8e4e..0e8b6d4d 100644
--- a/client/package.json
+++ b/client/package.json
@@ -13,7 +13,6 @@
"axios": "^0.19.0",
"classnames": "^2.2.6",
"date-fns": "^1.29.0",
- "file-saver": "^1.3.8",
"i18next": "^12.0.0",
"i18next-browser-languagedetector": "^2.2.3",
"lodash": "^4.17.15",
diff --git a/client/src/__locales/en.json b/client/src/__locales/en.json
index 119c1828..570d28e1 100644
--- a/client/src/__locales/en.json
+++ b/client/src/__locales/en.json
@@ -98,7 +98,6 @@
"enforce_safe_search": "Enforce safe search",
"enforce_save_search_hint": "AdGuard Home can enforce safe search in the following search engines: Google, Youtube, Bing, DuckDuckGo and Yandex.",
"no_servers_specified": "No servers specified",
- "no_settings": "No settings",
"general_settings": "General settings",
"dns_settings": "DNS settings",
"encryption_settings": "Encryption settings",
@@ -163,10 +162,7 @@
"show_all_filter_type": "Show all",
"show_filtered_type": "Show filtered",
"no_logs_found": "No logs found",
- "disabled_log_btn": "Disable log",
- "download_log_file_btn": "Download log file",
"refresh_btn": "Refresh",
- "enabled_log_btn": "Enable log",
"last_dns_queries": "Last 5000 DNS queries",
"previous_btn": "Previous",
"next_btn": "Next",
@@ -177,10 +173,15 @@
"updated_custom_filtering_toast": "Updated the custom filtering rules",
"rule_removed_from_custom_filtering_toast": "Rule removed from the custom filtering rules",
"rule_added_to_custom_filtering_toast": "Rule added to the custom filtering rules",
- "query_log_disabled_toast": "Query log disabled",
- "query_log_enabled_toast": "Query log enabled",
"query_log_response_status": "Status: {{value}}",
"query_log_filtered": "Filtered by {{filter}}",
+ "query_log_confirm_clear": "Are you sure you want to clear the entire query log? This will also clear statistics on the dashboard.",
+ "query_log_cleared": "The query log has been successfully cleared",
+ "query_log_clear": "Clear query logs",
+ "query_log_retention": "Query logs retention",
+ "query_log_enable": "Enable log",
+ "query_log_configuration": "Logs configuration",
+ "query_log_disabled": "The query log is disabled and can be configured in the <0>settings0>",
"source_label": "Source",
"found_in_known_domain_db": "Found in the known domains database.",
"category_label": "Category",
@@ -372,7 +373,7 @@
"domain": "Domain",
"answer": "Answer",
"filter_added_successfully": "The filter has been successfully added",
- "statistics_logs": "Statistics and logs",
+ "statistics_configuration": "Statistics configuration",
"statistics_retention": "Statistics retention",
"statistics_retention_desc": "If you decrease the interval value, some data will be lost",
"statistics_clear": " Clear statistics",
diff --git a/client/src/actions/index.js b/client/src/actions/index.js
index 98d77ca4..70913e29 100644
--- a/client/src/actions/index.js
+++ b/client/src/actions/index.js
@@ -4,7 +4,7 @@ import { showLoading, hideLoading } from 'react-redux-loading-bar';
import axios from 'axios';
import versionCompare from '../helpers/versionCompare';
-import { normalizeFilteringStatus, normalizeLogs, normalizeTextarea, sortClients } from '../helpers/helpers';
+import { normalizeFilteringStatus, normalizeTextarea, sortClients } from '../helpers/helpers';
import { SETTINGS_NAMES, CHECK_TIMEOUT } from '../helpers/constants';
import { getTlsStatus } from './encryption';
import Api from '../api/Api';
@@ -292,52 +292,6 @@ export const disableDns = () => async (dispatch) => {
}
};
-export const getLogsRequest = createAction('GET_LOGS_REQUEST');
-export const getLogsFailure = createAction('GET_LOGS_FAILURE');
-export const getLogsSuccess = createAction('GET_LOGS_SUCCESS');
-
-export const getLogs = () => async (dispatch, getState) => {
- dispatch(getLogsRequest());
- const timer = setInterval(async () => {
- const state = getState();
- if (state.dashboard.isCoreRunning) {
- clearInterval(timer);
- try {
- const logs = normalizeLogs(await apiClient.getQueryLog());
- dispatch(getLogsSuccess(logs));
- } catch (error) {
- dispatch(addErrorToast({ error }));
- dispatch(getLogsFailure(error));
- }
- }
- }, 100);
-};
-
-export const toggleLogStatusRequest = createAction('TOGGLE_LOGS_REQUEST');
-export const toggleLogStatusFailure = createAction('TOGGLE_LOGS_FAILURE');
-export const toggleLogStatusSuccess = createAction('TOGGLE_LOGS_SUCCESS');
-
-export const toggleLogStatus = queryLogEnabled => async (dispatch) => {
- dispatch(toggleLogStatusRequest());
- let toggleMethod;
- let successMessage;
- if (queryLogEnabled) {
- toggleMethod = apiClient.disableQueryLog.bind(apiClient);
- successMessage = 'query_log_disabled_toast';
- } else {
- toggleMethod = apiClient.enableQueryLog.bind(apiClient);
- successMessage = 'query_log_enabled_toast';
- }
- try {
- await toggleMethod();
- dispatch(addSuccessToast(successMessage));
- dispatch(toggleLogStatusSuccess());
- } catch (error) {
- dispatch(addErrorToast({ error }));
- dispatch(toggleLogStatusFailure());
- }
-};
-
export const setRulesRequest = createAction('SET_RULES_REQUEST');
export const setRulesFailure = createAction('SET_RULES_FAILURE');
export const setRulesSuccess = createAction('SET_RULES_SUCCESS');
@@ -465,23 +419,6 @@ export const removeFilter = url => async (dispatch) => {
export const toggleFilteringModal = createAction('FILTERING_MODAL_TOGGLE');
-export const downloadQueryLogRequest = createAction('DOWNLOAD_QUERY_LOG_REQUEST');
-export const downloadQueryLogFailure = createAction('DOWNLOAD_QUERY_LOG_FAILURE');
-export const downloadQueryLogSuccess = createAction('DOWNLOAD_QUERY_LOG_SUCCESS');
-
-export const downloadQueryLog = () => async (dispatch) => {
- let data;
- dispatch(downloadQueryLogRequest());
- try {
- data = await apiClient.downloadQueryLog();
- dispatch(downloadQueryLogSuccess());
- } catch (error) {
- dispatch(addErrorToast({ error }));
- dispatch(downloadQueryLogFailure());
- }
- return data;
-};
-
export const handleUpstreamChange = createAction('HANDLE_UPSTREAM_CHANGE');
export const setUpstreamRequest = createAction('SET_UPSTREAM_REQUEST');
export const setUpstreamFailure = createAction('SET_UPSTREAM_FAILURE');
diff --git a/client/src/actions/queryLogs.js b/client/src/actions/queryLogs.js
new file mode 100644
index 00000000..1295ff6a
--- /dev/null
+++ b/client/src/actions/queryLogs.js
@@ -0,0 +1,69 @@
+import { createAction } from 'redux-actions';
+
+import Api from '../api/Api';
+import { addErrorToast, addSuccessToast } from './index';
+import { normalizeLogs } from '../helpers/helpers';
+
+const apiClient = new Api();
+
+export const getLogsRequest = createAction('GET_LOGS_REQUEST');
+export const getLogsFailure = createAction('GET_LOGS_FAILURE');
+export const getLogsSuccess = createAction('GET_LOGS_SUCCESS');
+
+export const getLogs = () => async (dispatch) => {
+ dispatch(getLogsRequest());
+ try {
+ const logs = normalizeLogs(await apiClient.getQueryLog());
+ dispatch(getLogsSuccess(logs));
+ } catch (error) {
+ dispatch(addErrorToast({ error }));
+ dispatch(getLogsFailure(error));
+ }
+};
+
+export const clearLogsRequest = createAction('CLEAR_LOGS_REQUEST');
+export const clearLogsFailure = createAction('CLEAR_LOGS_FAILURE');
+export const clearLogsSuccess = createAction('CLEAR_LOGS_SUCCESS');
+
+export const clearLogs = () => async (dispatch) => {
+ dispatch(clearLogsRequest());
+ try {
+ await apiClient.clearQueryLog();
+ dispatch(clearLogsSuccess());
+ dispatch(addSuccessToast('query_log_cleared'));
+ } catch (error) {
+ dispatch(addErrorToast({ error }));
+ dispatch(clearLogsFailure(error));
+ }
+};
+
+export const getLogsConfigRequest = createAction('GET_LOGS_CONFIG_REQUEST');
+export const getLogsConfigFailure = createAction('GET_LOGS_CONFIG_FAILURE');
+export const getLogsConfigSuccess = createAction('GET_LOGS_CONFIG_SUCCESS');
+
+export const getLogsConfig = () => async (dispatch) => {
+ dispatch(getLogsConfigRequest());
+ try {
+ const data = await apiClient.getQueryLogInfo();
+ dispatch(getLogsConfigSuccess(data));
+ } catch (error) {
+ dispatch(addErrorToast({ error }));
+ dispatch(getLogsConfigFailure());
+ }
+};
+
+export const setLogsConfigRequest = createAction('SET_LOGS_CONFIG_REQUEST');
+export const setLogsConfigFailure = createAction('SET_LOGS_CONFIG_FAILURE');
+export const setLogsConfigSuccess = createAction('SET_LOGS_CONFIG_SUCCESS');
+
+export const setLogsConfig = config => async (dispatch) => {
+ dispatch(setLogsConfigRequest());
+ try {
+ await apiClient.setQueryLogConfig(config);
+ dispatch(addSuccessToast('config_successfully_saved'));
+ dispatch(setLogsConfigSuccess(config));
+ } catch (error) {
+ dispatch(addErrorToast({ error }));
+ dispatch(setLogsConfigFailure());
+ }
+};
diff --git a/client/src/actions/stats.js b/client/src/actions/stats.js
index a24b3ec7..e8483443 100644
--- a/client/src/actions/stats.js
+++ b/client/src/actions/stats.js
@@ -6,9 +6,9 @@ import { normalizeTopStats, secondsToMilliseconds } from '../helpers/helpers';
const apiClient = new Api();
-export const getStatsConfigRequest = createAction('GET_LOGS_CONFIG_REQUEST');
-export const getStatsConfigFailure = createAction('GET_LOGS_CONFIG_FAILURE');
-export const getStatsConfigSuccess = createAction('GET_LOGS_CONFIG_SUCCESS');
+export const getStatsConfigRequest = createAction('GET_STATS_CONFIG_REQUEST');
+export const getStatsConfigFailure = createAction('GET_STATS_CONFIG_FAILURE');
+export const getStatsConfigSuccess = createAction('GET_STATS_CONFIG_SUCCESS');
export const getStatsConfig = () => async (dispatch) => {
dispatch(getStatsConfigRequest());
diff --git a/client/src/api/Api.js b/client/src/api/Api.js
index 0f63f2ea..495be5b8 100644
--- a/client/src/api/Api.js
+++ b/client/src/api/Api.js
@@ -25,9 +25,6 @@ export default class Api {
GLOBAL_START = { path: 'start', method: 'POST' };
GLOBAL_STATUS = { path: 'status', method: 'GET' };
GLOBAL_STOP = { path: 'stop', method: 'POST' };
- GLOBAL_QUERY_LOG = { path: 'querylog', method: 'GET' };
- GLOBAL_QUERY_LOG_ENABLE = { path: 'querylog_enable', method: 'POST' };
- GLOBAL_QUERY_LOG_DISABLE = { path: 'querylog_disable', method: 'POST' };
GLOBAL_SET_UPSTREAM_DNS = { path: 'set_upstreams_config', method: 'POST' };
GLOBAL_TEST_UPSTREAM_DNS = { path: 'test_upstream_dns', method: 'POST' };
GLOBAL_VERSION = { path: 'version.json', method: 'POST' };
@@ -50,27 +47,6 @@ export default class Api {
return this.makeRequest(path, method);
}
- getQueryLog() {
- const { path, method } = this.GLOBAL_QUERY_LOG;
- return this.makeRequest(path, method);
- }
-
- downloadQueryLog() {
- const { path, method } = this.GLOBAL_QUERY_LOG;
- const queryString = '?download=1';
- return this.makeRequest(path + queryString, method);
- }
-
- enableQueryLog() {
- const { path, method } = this.GLOBAL_QUERY_LOG_ENABLE;
- return this.makeRequest(path, method);
- }
-
- disableQueryLog() {
- const { path, method } = this.GLOBAL_QUERY_LOG_DISABLE;
- return this.makeRequest(path, method);
- }
-
setUpstream(url) {
const { path, method } = this.GLOBAL_SET_UPSTREAM_DNS;
const config = {
@@ -521,4 +497,34 @@ export default class Api {
const { path, method } = this.STATS_RESET;
return this.makeRequest(path, method);
}
+
+ // Query log
+ GET_QUERY_LOG = { path: 'querylog', method: 'GET' };
+ QUERY_LOG_CONFIG = { path: 'querylog_config', method: 'POST' };
+ QUERY_LOG_INFO = { path: 'querylog_info', method: 'GET' };
+ QUERY_LOG_CLEAR = { path: 'querylog_clear', method: 'POST' };
+
+ getQueryLog() {
+ const { path, method } = this.GET_QUERY_LOG;
+ return this.makeRequest(path, method);
+ }
+
+ getQueryLogInfo() {
+ const { path, method } = this.QUERY_LOG_INFO;
+ return this.makeRequest(path, method);
+ }
+
+ setQueryLogConfig(data) {
+ const { path, method } = this.QUERY_LOG_CONFIG;
+ const config = {
+ data,
+ headers: { 'Content-Type': 'application/json' },
+ };
+ return this.makeRequest(path, method, config);
+ }
+
+ clearQueryLog() {
+ const { path, method } = this.QUERY_LOG_CLEAR;
+ return this.makeRequest(path, method);
+ }
}
diff --git a/client/src/components/Logs/index.js b/client/src/components/Logs/index.js
index 5d5471e9..5c1db03c 100644
--- a/client/src/components/Logs/index.js
+++ b/client/src/components/Logs/index.js
@@ -1,7 +1,6 @@
import React, { Component, Fragment } from 'react';
import PropTypes from 'prop-types';
import ReactTable from 'react-table';
-import { saveAs } from 'file-saver/FileSaver';
import escapeRegExp from 'lodash/escapeRegExp';
import endsWith from 'lodash/endsWith';
import { Trans, withNamespaces } from 'react-i18next';
@@ -17,7 +16,6 @@ import PopoverFiltered from '../ui/PopoverFilter';
import Popover from '../ui/Popover';
import './Logs.css';
-const DOWNLOAD_LOG_FILENAME = 'dns-logs.txt';
const FILTERED_REASON = 'Filtered';
const RESPONSE_FILTER = {
ALL: 'all',
@@ -29,18 +27,19 @@ class Logs extends Component {
this.getLogs();
this.props.getFilteringStatus();
this.props.getClients();
+ this.props.getLogsConfig();
}
componentDidUpdate(prevProps) {
// get logs when queryLog becomes enabled
- if (this.props.dashboard.queryLogEnabled && !prevProps.dashboard.queryLogEnabled) {
+ if (this.props.queryLogs.enabled && !prevProps.queryLogs.enabled) {
this.props.getLogs();
}
}
getLogs = () => {
// get logs on initialization if queryLogIsEnabled
- if (this.props.dashboard.queryLogEnabled) {
+ if (this.props.queryLogs.enabled) {
this.props.getLogs();
}
};
@@ -155,10 +154,7 @@ class Logs extends Component {
} else {
const filterItem = Object.keys(filters).filter(key => filters[key].id === filterId)[0];
- if (
- typeof filterItem !== 'undefined' &&
- typeof filters[filterItem] !== 'undefined'
- ) {
+ if (typeof filterItem !== 'undefined' && typeof filters[filterItem] !== 'undefined') {
filterName = filters[filterItem].name;
}
@@ -255,10 +251,7 @@ class Logs extends Component {
if (filter.value === RESPONSE_FILTER.FILTERED) {
// eslint-disable-next-line no-underscore-dangle
const { reason } = row._original;
- return (
- this.checkFiltered(reason) ||
- this.checkWhiteList(reason)
- );
+ return this.checkFiltered(reason) || this.checkWhiteList(reason);
}
return true;
},
@@ -347,74 +340,44 @@ class Logs extends Component {
return null;
}
- handleDownloadButton = async (e) => {
- e.preventDefault();
- const data = await this.props.downloadQueryLog();
- const jsonStr = JSON.stringify(data);
- const dataBlob = new Blob([jsonStr], { type: 'text/plain;charset=utf-8' });
- saveAs(dataBlob, DOWNLOAD_LOG_FILENAME);
- };
-
- renderButtons(queryLogEnabled, logStatusProcessing) {
- if (queryLogEnabled) {
- return (
-