+ client: handle the new statistics format
This commit is contained in:
parent
011bc3e36b
commit
6b2496d050
|
@ -6,7 +6,7 @@
|
||||||
"build-dev": "NODE_ENV=development ./node_modules/.bin/webpack --config webpack.dev.js",
|
"build-dev": "NODE_ENV=development ./node_modules/.bin/webpack --config webpack.dev.js",
|
||||||
"watch": "NODE_ENV=development ./node_modules/.bin/webpack --config webpack.dev.js --watch",
|
"watch": "NODE_ENV=development ./node_modules/.bin/webpack --config webpack.dev.js --watch",
|
||||||
"build-prod": "NODE_ENV=production ./node_modules/.bin/webpack --config webpack.prod.js",
|
"build-prod": "NODE_ENV=production ./node_modules/.bin/webpack --config webpack.prod.js",
|
||||||
"lint": "eslint frontend/"
|
"lint": "eslint client/"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@nivo/line": "^0.49.1",
|
"@nivo/line": "^0.49.1",
|
||||||
|
|
|
@ -72,12 +72,14 @@
|
||||||
"stats_adult": "Blocked adult websites",
|
"stats_adult": "Blocked adult websites",
|
||||||
"stats_query_domain": "Top queried domains",
|
"stats_query_domain": "Top queried domains",
|
||||||
"for_last_24_hours": "for the last 24 hours",
|
"for_last_24_hours": "for the last 24 hours",
|
||||||
|
"for_last_days": "for the last {{value}} days",
|
||||||
"no_domains_found": "No domains found",
|
"no_domains_found": "No domains found",
|
||||||
"requests_count": "Requests count",
|
"requests_count": "Requests count",
|
||||||
"top_blocked_domains": "Top blocked domains",
|
"top_blocked_domains": "Top blocked domains",
|
||||||
"top_clients": "Top clients",
|
"top_clients": "Top clients",
|
||||||
"no_clients_found": "No clients found",
|
"no_clients_found": "No clients found",
|
||||||
"general_statistics": "General statistics",
|
"general_statistics": "General statistics",
|
||||||
|
"number_of_dns_query_days": "A number of DNS quieries processed for the last {{value}} days",
|
||||||
"number_of_dns_query_24_hours": "A number of DNS quieries processed for the last 24 hours",
|
"number_of_dns_query_24_hours": "A number of DNS quieries processed for the last 24 hours",
|
||||||
"number_of_dns_query_blocked_24_hours": "A number of DNS requests blocked by adblock filters and hosts blocklists",
|
"number_of_dns_query_blocked_24_hours": "A number of DNS requests blocked by adblock filters and hosts blocklists",
|
||||||
"number_of_dns_query_blocked_24_hours_by_sec": "A number of DNS requests blocked by the AdGuard browsing security module",
|
"number_of_dns_query_blocked_24_hours_by_sec": "A number of DNS requests blocked by the AdGuard browsing security module",
|
||||||
|
@ -300,7 +302,6 @@
|
||||||
"client_deleted": "Client \"{{key}}\" successfully deleted",
|
"client_deleted": "Client \"{{key}}\" successfully deleted",
|
||||||
"client_added": "Client \"{{key}}\" successfully added",
|
"client_added": "Client \"{{key}}\" successfully added",
|
||||||
"client_updated": "Client \"{{key}}\" successfully updated",
|
"client_updated": "Client \"{{key}}\" successfully updated",
|
||||||
"table_statistics": "Requests count (last 24 hours)",
|
|
||||||
"clients_not_found": "No clients found",
|
"clients_not_found": "No clients found",
|
||||||
"client_confirm_delete": "Are you sure you want to delete client \"{{key}}\"?",
|
"client_confirm_delete": "Are you sure you want to delete client \"{{key}}\"?",
|
||||||
"filter_confirm_delete": "Are you sure you want to delete filter?",
|
"filter_confirm_delete": "Are you sure you want to delete filter?",
|
||||||
|
@ -366,5 +367,6 @@
|
||||||
"config_successfully_saved": "Configuration successfully saved",
|
"config_successfully_saved": "Configuration successfully saved",
|
||||||
"interval_24_hour": "24 hours",
|
"interval_24_hour": "24 hours",
|
||||||
"interval_days": "{{value}} days",
|
"interval_days": "{{value}} days",
|
||||||
"time_period": "Time period"
|
"time_period": "Time period",
|
||||||
|
"domain": "Domain"
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,11 +1,10 @@
|
||||||
import { createAction } from 'redux-actions';
|
import { createAction } from 'redux-actions';
|
||||||
import round from 'lodash/round';
|
|
||||||
import { t } from 'i18next';
|
import { t } from 'i18next';
|
||||||
import { showLoading, hideLoading } from 'react-redux-loading-bar';
|
import { showLoading, hideLoading } from 'react-redux-loading-bar';
|
||||||
import axios from 'axios';
|
import axios from 'axios';
|
||||||
|
|
||||||
import versionCompare from '../helpers/versionCompare';
|
import versionCompare from '../helpers/versionCompare';
|
||||||
import { normalizeHistory, normalizeFilteringStatus, normalizeLogs, normalizeTextarea, sortClients } from '../helpers/helpers';
|
import { normalizeFilteringStatus, normalizeLogs, normalizeTextarea, sortClients } from '../helpers/helpers';
|
||||||
import { SETTINGS_NAMES, CHECK_TIMEOUT } from '../helpers/constants';
|
import { SETTINGS_NAMES, CHECK_TIMEOUT } from '../helpers/constants';
|
||||||
import { getTlsStatus } from './encryption';
|
import { getTlsStatus } from './encryption';
|
||||||
import Api from '../api/Api';
|
import Api from '../api/Api';
|
||||||
|
@ -246,27 +245,6 @@ export const getClients = () => async (dispatch) => {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
export const getTopStatsRequest = createAction('GET_TOP_STATS_REQUEST');
|
|
||||||
export const getTopStatsFailure = createAction('GET_TOP_STATS_FAILURE');
|
|
||||||
export const getTopStatsSuccess = createAction('GET_TOP_STATS_SUCCESS');
|
|
||||||
|
|
||||||
export const getTopStats = () => async (dispatch, getState) => {
|
|
||||||
dispatch(getTopStatsRequest());
|
|
||||||
const timer = setInterval(async () => {
|
|
||||||
const state = getState();
|
|
||||||
if (state.dashboard.isCoreRunning) {
|
|
||||||
clearInterval(timer);
|
|
||||||
try {
|
|
||||||
const stats = await apiClient.getGlobalStatsTop();
|
|
||||||
dispatch(getTopStatsSuccess(stats));
|
|
||||||
} catch (error) {
|
|
||||||
dispatch(addErrorToast({ error }));
|
|
||||||
dispatch(getTopStatsFailure(error));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}, 100);
|
|
||||||
};
|
|
||||||
|
|
||||||
export const dnsStatusRequest = createAction('DNS_STATUS_REQUEST');
|
export const dnsStatusRequest = createAction('DNS_STATUS_REQUEST');
|
||||||
export const dnsStatusFailure = createAction('DNS_STATUS_FAILURE');
|
export const dnsStatusFailure = createAction('DNS_STATUS_FAILURE');
|
||||||
export const dnsStatusSuccess = createAction('DNS_STATUS_SUCCESS');
|
export const dnsStatusSuccess = createAction('DNS_STATUS_SUCCESS');
|
||||||
|
@ -314,27 +292,6 @@ export const disableDns = () => async (dispatch) => {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
export const getStatsRequest = createAction('GET_STATS_REQUEST');
|
|
||||||
export const getStatsFailure = createAction('GET_STATS_FAILURE');
|
|
||||||
export const getStatsSuccess = createAction('GET_STATS_SUCCESS');
|
|
||||||
|
|
||||||
export const getStats = () => async (dispatch) => {
|
|
||||||
dispatch(getStatsRequest());
|
|
||||||
try {
|
|
||||||
const stats = await apiClient.getGlobalStats();
|
|
||||||
|
|
||||||
const processedStats = {
|
|
||||||
...stats,
|
|
||||||
avg_processing_time: round(stats.avg_processing_time, 2),
|
|
||||||
};
|
|
||||||
|
|
||||||
dispatch(getStatsSuccess(processedStats));
|
|
||||||
} catch (error) {
|
|
||||||
dispatch(addErrorToast({ error }));
|
|
||||||
dispatch(getStatsFailure());
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
export const getLogsRequest = createAction('GET_LOGS_REQUEST');
|
export const getLogsRequest = createAction('GET_LOGS_REQUEST');
|
||||||
export const getLogsFailure = createAction('GET_LOGS_FAILURE');
|
export const getLogsFailure = createAction('GET_LOGS_FAILURE');
|
||||||
export const getLogsSuccess = createAction('GET_LOGS_SUCCESS');
|
export const getLogsSuccess = createAction('GET_LOGS_SUCCESS');
|
||||||
|
@ -473,22 +430,6 @@ export const refreshFilters = () => async (dispatch) => {
|
||||||
|
|
||||||
export const handleRulesChange = createAction('HANDLE_RULES_CHANGE');
|
export const handleRulesChange = createAction('HANDLE_RULES_CHANGE');
|
||||||
|
|
||||||
export const getStatsHistoryRequest = createAction('GET_STATS_HISTORY_REQUEST');
|
|
||||||
export const getStatsHistoryFailure = createAction('GET_STATS_HISTORY_FAILURE');
|
|
||||||
export const getStatsHistorySuccess = createAction('GET_STATS_HISTORY_SUCCESS');
|
|
||||||
|
|
||||||
export const getStatsHistory = () => async (dispatch) => {
|
|
||||||
dispatch(getStatsHistoryRequest());
|
|
||||||
try {
|
|
||||||
const statsHistory = await apiClient.getGlobalStatsHistory();
|
|
||||||
const normalizedHistory = normalizeHistory(statsHistory);
|
|
||||||
dispatch(getStatsHistorySuccess(normalizedHistory));
|
|
||||||
} catch (error) {
|
|
||||||
dispatch(addErrorToast({ error }));
|
|
||||||
dispatch(getStatsHistoryFailure());
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
export const addFilterRequest = createAction('ADD_FILTER_REQUEST');
|
export const addFilterRequest = createAction('ADD_FILTER_REQUEST');
|
||||||
export const addFilterFailure = createAction('ADD_FILTER_FAILURE');
|
export const addFilterFailure = createAction('ADD_FILTER_FAILURE');
|
||||||
export const addFilterSuccess = createAction('ADD_FILTER_SUCCESS');
|
export const addFilterSuccess = createAction('ADD_FILTER_SUCCESS');
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
import { createAction } from 'redux-actions';
|
import { createAction } from 'redux-actions';
|
||||||
|
|
||||||
import Api from '../api/Api';
|
import Api from '../api/Api';
|
||||||
import { addErrorToast, addSuccessToast } from './index';
|
import { addErrorToast, addSuccessToast } from './index';
|
||||||
|
import { normalizeTopStats } from '../helpers/helpers';
|
||||||
|
|
||||||
const apiClient = new Api();
|
const apiClient = new Api();
|
||||||
|
|
||||||
|
@ -34,3 +36,26 @@ export const setStatsConfig = config => async (dispatch) => {
|
||||||
dispatch(setStatsConfigFailure());
|
dispatch(setStatsConfigFailure());
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const getStatsRequest = createAction('GET_STATS_REQUEST');
|
||||||
|
export const getStatsFailure = createAction('GET_STATS_FAILURE');
|
||||||
|
export const getStatsSuccess = createAction('GET_STATS_SUCCESS');
|
||||||
|
|
||||||
|
export const getStats = () => async (dispatch) => {
|
||||||
|
dispatch(getStatsRequest());
|
||||||
|
try {
|
||||||
|
const stats = await apiClient.getStats();
|
||||||
|
|
||||||
|
const normalizedStats = {
|
||||||
|
...stats,
|
||||||
|
top_blocked_domains: normalizeTopStats(stats.top_blocked_domains),
|
||||||
|
top_clients: normalizeTopStats(stats.top_clients),
|
||||||
|
top_queried_domains: normalizeTopStats(stats.top_queried_domains),
|
||||||
|
};
|
||||||
|
|
||||||
|
dispatch(getStatsSuccess(normalizedStats));
|
||||||
|
} catch (error) {
|
||||||
|
dispatch(addErrorToast({ error }));
|
||||||
|
dispatch(getStatsFailure());
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
|
@ -1,6 +1,4 @@
|
||||||
import axios from 'axios';
|
import axios from 'axios';
|
||||||
import subHours from 'date-fns/sub_hours';
|
|
||||||
import dateFormat from 'date-fns/format';
|
|
||||||
|
|
||||||
export default class Api {
|
export default class Api {
|
||||||
baseUrl = 'control';
|
baseUrl = 'control';
|
||||||
|
@ -26,11 +24,8 @@ export default class Api {
|
||||||
// Global methods
|
// Global methods
|
||||||
GLOBAL_RESTART = { path: 'restart', method: 'POST' };
|
GLOBAL_RESTART = { path: 'restart', method: 'POST' };
|
||||||
GLOBAL_START = { path: 'start', method: 'POST' };
|
GLOBAL_START = { path: 'start', method: 'POST' };
|
||||||
GLOBAL_STATS = { path: 'stats', method: 'GET' };
|
|
||||||
GLOBAL_STATS_HISTORY = { path: 'stats_history', method: 'GET' };
|
|
||||||
GLOBAL_STATUS = { path: 'status', method: 'GET' };
|
GLOBAL_STATUS = { path: 'status', method: 'GET' };
|
||||||
GLOBAL_STOP = { path: 'stop', method: 'POST' };
|
GLOBAL_STOP = { path: 'stop', method: 'POST' };
|
||||||
GLOBAL_STATS_TOP = { path: 'stats_top', method: 'GET' };
|
|
||||||
GLOBAL_QUERY_LOG = { path: 'querylog', method: 'GET' };
|
GLOBAL_QUERY_LOG = { path: 'querylog', method: 'GET' };
|
||||||
GLOBAL_QUERY_LOG_ENABLE = { path: 'querylog_enable', method: 'POST' };
|
GLOBAL_QUERY_LOG_ENABLE = { path: 'querylog_enable', method: 'POST' };
|
||||||
GLOBAL_QUERY_LOG_DISABLE = { path: 'querylog_disable', method: 'POST' };
|
GLOBAL_QUERY_LOG_DISABLE = { path: 'querylog_disable', method: 'POST' };
|
||||||
|
@ -56,36 +51,11 @@ export default class Api {
|
||||||
return this.makeRequest(path, method);
|
return this.makeRequest(path, method);
|
||||||
}
|
}
|
||||||
|
|
||||||
getGlobalStats() {
|
|
||||||
const { path, method } = this.GLOBAL_STATS;
|
|
||||||
return this.makeRequest(path, method);
|
|
||||||
}
|
|
||||||
|
|
||||||
getGlobalStatsHistory() {
|
|
||||||
const { path, method } = this.GLOBAL_STATS_HISTORY;
|
|
||||||
const format = 'YYYY-MM-DDTHH:mm:ssZ';
|
|
||||||
const dateNow = Date.now();
|
|
||||||
|
|
||||||
const config = {
|
|
||||||
params: {
|
|
||||||
start_time: dateFormat(subHours(dateNow, 24), format),
|
|
||||||
end_time: dateFormat(dateNow, format),
|
|
||||||
time_unit: 'hours',
|
|
||||||
},
|
|
||||||
};
|
|
||||||
return this.makeRequest(path, method, config);
|
|
||||||
}
|
|
||||||
|
|
||||||
getGlobalStatus() {
|
getGlobalStatus() {
|
||||||
const { path, method } = this.GLOBAL_STATUS;
|
const { path, method } = this.GLOBAL_STATUS;
|
||||||
return this.makeRequest(path, method);
|
return this.makeRequest(path, method);
|
||||||
}
|
}
|
||||||
|
|
||||||
getGlobalStatsTop() {
|
|
||||||
const { path, method } = this.GLOBAL_STATS_TOP;
|
|
||||||
return this.makeRequest(path, method);
|
|
||||||
}
|
|
||||||
|
|
||||||
getQueryLog() {
|
getQueryLog() {
|
||||||
const { path, method } = this.GLOBAL_QUERY_LOG;
|
const { path, method } = this.GLOBAL_QUERY_LOG;
|
||||||
return this.makeRequest(path, method);
|
return this.makeRequest(path, method);
|
||||||
|
@ -529,9 +499,15 @@ export default class Api {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Settings for statistics
|
// Settings for statistics
|
||||||
|
GET_STATS = { path: 'stats', method: 'GET' };
|
||||||
STATS_INFO = { path: 'stats_info', method: 'GET' };
|
STATS_INFO = { path: 'stats_info', method: 'GET' };
|
||||||
STATS_CONFIG = { path: 'stats_config', method: 'POST' };
|
STATS_CONFIG = { path: 'stats_config', method: 'POST' };
|
||||||
|
|
||||||
|
getStats() {
|
||||||
|
const { path, method } = this.GET_STATS;
|
||||||
|
return this.makeRequest(path, method);
|
||||||
|
}
|
||||||
|
|
||||||
getStatsInfo() {
|
getStatsInfo() {
|
||||||
const { path, method } = this.STATS_INFO;
|
const { path, method } = this.STATS_INFO;
|
||||||
return this.makeRequest(path, method);
|
return this.makeRequest(path, method);
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
import React, { Component } from 'react';
|
import React, { Component } from 'react';
|
||||||
import ReactTable from 'react-table';
|
import ReactTable from 'react-table';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import map from 'lodash/map';
|
|
||||||
import { withNamespaces, Trans } from 'react-i18next';
|
import { withNamespaces, Trans } from 'react-i18next';
|
||||||
|
|
||||||
import Card from '../ui/Card';
|
import Card from '../ui/Card';
|
||||||
|
@ -13,52 +12,58 @@ import { getPercent } from '../../helpers/helpers';
|
||||||
import { STATUS_COLORS } from '../../helpers/constants';
|
import { STATUS_COLORS } from '../../helpers/constants';
|
||||||
|
|
||||||
class BlockedDomains extends Component {
|
class BlockedDomains extends Component {
|
||||||
columns = [{
|
columns = [
|
||||||
Header: 'IP',
|
{
|
||||||
accessor: 'ip',
|
Header: <Trans>domain</Trans>,
|
||||||
Cell: (row) => {
|
accessor: 'domain',
|
||||||
const { value } = row;
|
Cell: (row) => {
|
||||||
const trackerData = getTrackerData(value);
|
const { value } = row;
|
||||||
|
const trackerData = getTrackerData(value);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="logs__row">
|
<div className="logs__row">
|
||||||
<div className="logs__text" title={value}>
|
<div className="logs__text" title={value}>
|
||||||
{value}
|
{value}
|
||||||
|
</div>
|
||||||
|
{trackerData && <Popover data={trackerData} />}
|
||||||
</div>
|
</div>
|
||||||
{trackerData && <Popover data={trackerData} />}
|
);
|
||||||
</div>
|
},
|
||||||
);
|
|
||||||
},
|
},
|
||||||
}, {
|
{
|
||||||
Header: <Trans>requests_count</Trans>,
|
Header: <Trans>requests_count</Trans>,
|
||||||
accessor: 'domain',
|
accessor: 'count',
|
||||||
maxWidth: 190,
|
maxWidth: 190,
|
||||||
Cell: ({ value }) => {
|
Cell: ({ value }) => {
|
||||||
const {
|
const { blockedFiltering, replacedSafebrowsing, replacedParental } = this.props;
|
||||||
blockedFiltering,
|
const blocked = blockedFiltering + replacedSafebrowsing + replacedParental;
|
||||||
replacedSafebrowsing,
|
const percent = getPercent(blocked, value);
|
||||||
replacedParental,
|
|
||||||
} = this.props;
|
|
||||||
const blocked = blockedFiltering + replacedSafebrowsing + replacedParental;
|
|
||||||
const percent = getPercent(blocked, value);
|
|
||||||
|
|
||||||
return (
|
return <Cell value={value} percent={percent} color={STATUS_COLORS.red} />;
|
||||||
<Cell value={value} percent={percent} color={STATUS_COLORS.red} />
|
},
|
||||||
);
|
|
||||||
},
|
},
|
||||||
}];
|
];
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const { t } = this.props;
|
const {
|
||||||
|
t, refreshButton, topBlockedDomains, subtitle,
|
||||||
|
} = this.props;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Card title={ t('top_blocked_domains') } subtitle={ t('for_last_24_hours') } bodyType="card-table" refresh={this.props.refreshButton}>
|
<Card
|
||||||
|
title={t('top_blocked_domains')}
|
||||||
|
subtitle={subtitle}
|
||||||
|
bodyType="card-table"
|
||||||
|
refresh={refreshButton}
|
||||||
|
>
|
||||||
<ReactTable
|
<ReactTable
|
||||||
data={map(this.props.topBlockedDomains, (value, prop) => (
|
data={topBlockedDomains.map(item => ({
|
||||||
{ ip: prop, domain: value }
|
domain: item.name,
|
||||||
))}
|
count: item.count,
|
||||||
|
}))}
|
||||||
columns={this.columns}
|
columns={this.columns}
|
||||||
showPagination={false}
|
showPagination={false}
|
||||||
noDataText={ t('no_domains_found') }
|
noDataText={t('no_domains_found')}
|
||||||
minRows={6}
|
minRows={6}
|
||||||
className="-striped -highlight card-table-overflow stats__table"
|
className="-striped -highlight card-table-overflow stats__table"
|
||||||
/>
|
/>
|
||||||
|
@ -68,12 +73,13 @@ class BlockedDomains extends Component {
|
||||||
}
|
}
|
||||||
|
|
||||||
BlockedDomains.propTypes = {
|
BlockedDomains.propTypes = {
|
||||||
topBlockedDomains: PropTypes.object.isRequired,
|
topBlockedDomains: PropTypes.array.isRequired,
|
||||||
blockedFiltering: PropTypes.number.isRequired,
|
blockedFiltering: PropTypes.number.isRequired,
|
||||||
replacedSafebrowsing: PropTypes.number.isRequired,
|
replacedSafebrowsing: PropTypes.number.isRequired,
|
||||||
replacedParental: PropTypes.number.isRequired,
|
replacedParental: PropTypes.number.isRequired,
|
||||||
refreshButton: PropTypes.node.isRequired,
|
refreshButton: PropTypes.node.isRequired,
|
||||||
t: PropTypes.func,
|
subtitle: PropTypes.string.isRequired,
|
||||||
|
t: PropTypes.func.isRequired,
|
||||||
};
|
};
|
||||||
|
|
||||||
export default withNamespaces()(BlockedDomains);
|
export default withNamespaces()(BlockedDomains);
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
import React, { Component } from 'react';
|
import React, { Component } from 'react';
|
||||||
import ReactTable from 'react-table';
|
import ReactTable from 'react-table';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import map from 'lodash/map';
|
|
||||||
import { Trans, withNamespaces } from 'react-i18next';
|
import { Trans, withNamespaces } from 'react-i18next';
|
||||||
|
|
||||||
import Card from '../ui/Card';
|
import Card from '../ui/Card';
|
||||||
|
@ -18,55 +17,71 @@ class Clients extends Component {
|
||||||
return STATUS_COLORS.yellow;
|
return STATUS_COLORS.yellow;
|
||||||
}
|
}
|
||||||
return STATUS_COLORS.red;
|
return STATUS_COLORS.red;
|
||||||
}
|
};
|
||||||
|
|
||||||
columns = [{
|
columns = [
|
||||||
Header: 'IP',
|
{
|
||||||
accessor: 'ip',
|
Header: 'IP',
|
||||||
Cell: ({ value }) => {
|
accessor: 'ip',
|
||||||
const clientName = getClientName(this.props.clients, value)
|
Cell: ({ value }) => {
|
||||||
|| getClientName(this.props.autoClients, value);
|
const clientName =
|
||||||
let client;
|
getClientName(this.props.clients, value) ||
|
||||||
|
getClientName(this.props.autoClients, value);
|
||||||
|
let client;
|
||||||
|
|
||||||
if (clientName) {
|
if (clientName) {
|
||||||
client = <span>{clientName} <small>({value})</small></span>;
|
client = (
|
||||||
} else {
|
<span>
|
||||||
client = value;
|
{clientName} <small>({value})</small>
|
||||||
}
|
</span>
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
client = value;
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="logs__row logs__row--overflow">
|
<div className="logs__row logs__row--overflow">
|
||||||
<span className="logs__text" title={value}>
|
<span className="logs__text" title={value}>
|
||||||
{client}
|
{client}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
},
|
||||||
|
sortMethod: (a, b) =>
|
||||||
|
parseInt(a.replace(/\./g, ''), 10) - parseInt(b.replace(/\./g, ''), 10),
|
||||||
},
|
},
|
||||||
sortMethod: (a, b) => parseInt(a.replace(/\./g, ''), 10) - parseInt(b.replace(/\./g, ''), 10),
|
{
|
||||||
}, {
|
Header: <Trans>requests_count</Trans>,
|
||||||
Header: <Trans>requests_count</Trans>,
|
accessor: 'count',
|
||||||
accessor: 'count',
|
Cell: ({ value }) => {
|
||||||
Cell: ({ value }) => {
|
const percent = getPercent(this.props.dnsQueries, value);
|
||||||
const percent = getPercent(this.props.dnsQueries, value);
|
const percentColor = this.getPercentColor(percent);
|
||||||
const percentColor = this.getPercentColor(percent);
|
|
||||||
|
|
||||||
return (
|
return <Cell value={value} percent={percent} color={percentColor} />;
|
||||||
<Cell value={value} percent={percent} color={percentColor} />
|
},
|
||||||
);
|
|
||||||
},
|
},
|
||||||
}];
|
];
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const { t } = this.props;
|
const {
|
||||||
|
t, refreshButton, topClients, subtitle,
|
||||||
|
} = this.props;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Card title={ t('top_clients') } subtitle={ t('for_last_24_hours') } bodyType="card-table" refresh={this.props.refreshButton}>
|
<Card
|
||||||
|
title={t('top_clients')}
|
||||||
|
subtitle={subtitle}
|
||||||
|
bodyType="card-table"
|
||||||
|
refresh={refreshButton}
|
||||||
|
>
|
||||||
<ReactTable
|
<ReactTable
|
||||||
data={map(this.props.topClients, (value, prop) => (
|
data={topClients.map(item => ({
|
||||||
{ ip: prop, count: value }
|
ip: item.name,
|
||||||
))}
|
count: item.count,
|
||||||
|
}))}
|
||||||
columns={this.columns}
|
columns={this.columns}
|
||||||
showPagination={false}
|
showPagination={false}
|
||||||
noDataText={ t('no_clients_found') }
|
noDataText={t('no_clients_found')}
|
||||||
minRows={6}
|
minRows={6}
|
||||||
className="-striped -highlight card-table-overflow"
|
className="-striped -highlight card-table-overflow"
|
||||||
/>
|
/>
|
||||||
|
@ -76,12 +91,13 @@ class Clients extends Component {
|
||||||
}
|
}
|
||||||
|
|
||||||
Clients.propTypes = {
|
Clients.propTypes = {
|
||||||
topClients: PropTypes.object.isRequired,
|
topClients: PropTypes.array.isRequired,
|
||||||
dnsQueries: PropTypes.number.isRequired,
|
dnsQueries: PropTypes.number.isRequired,
|
||||||
refreshButton: PropTypes.node.isRequired,
|
refreshButton: PropTypes.node.isRequired,
|
||||||
clients: PropTypes.array.isRequired,
|
clients: PropTypes.array.isRequired,
|
||||||
autoClients: PropTypes.array.isRequired,
|
autoClients: PropTypes.array.isRequired,
|
||||||
t: PropTypes.func,
|
subtitle: PropTypes.string.isRequired,
|
||||||
|
t: PropTypes.func.isRequired,
|
||||||
};
|
};
|
||||||
|
|
||||||
export default withNamespaces()(Clients);
|
export default withNamespaces()(Clients);
|
||||||
|
|
|
@ -1,88 +1,116 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import { Trans, withNamespaces } from 'react-i18next';
|
import { Trans, withNamespaces } from 'react-i18next';
|
||||||
|
import round from 'lodash/round';
|
||||||
|
|
||||||
import Card from '../ui/Card';
|
import Card from '../ui/Card';
|
||||||
import Tooltip from '../ui/Tooltip';
|
import Tooltip from '../ui/Tooltip';
|
||||||
|
|
||||||
const tooltipType = 'tooltip-custom--narrow';
|
const tooltipType = 'tooltip-custom--narrow';
|
||||||
|
|
||||||
const Counters = props => (
|
const Counters = (props) => {
|
||||||
<Card title={ props.t('general_statistics') } subtitle={ props.t('for_last_24_hours') } bodyType="card-table" refresh={props.refreshButton}>
|
const {
|
||||||
<table className="table card-table">
|
t,
|
||||||
<tbody>
|
interval,
|
||||||
<tr>
|
refreshButton,
|
||||||
<td>
|
subtitle,
|
||||||
<Trans>dns_query</Trans>
|
dnsQueries,
|
||||||
<Tooltip text={ props.t('number_of_dns_query_24_hours') } type={tooltipType} />
|
blockedFiltering,
|
||||||
</td>
|
replacedSafebrowsing,
|
||||||
<td className="text-right">
|
replacedParental,
|
||||||
<span className="text-muted">
|
replacedSafesearch,
|
||||||
{props.dnsQueries}
|
avgProcessingTime,
|
||||||
</span>
|
} = props;
|
||||||
</td>
|
|
||||||
</tr>
|
const tooltipTitle =
|
||||||
<tr>
|
interval === 1
|
||||||
<td>
|
? t('number_of_dns_query_24_hours')
|
||||||
<a href="#filters">
|
: t('number_of_dns_query_days', { value: interval });
|
||||||
<Trans>blocked_by</Trans>
|
|
||||||
</a>
|
return (
|
||||||
<Tooltip text={ props.t('number_of_dns_query_blocked_24_hours') } type={tooltipType} />
|
<Card
|
||||||
</td>
|
title={t('general_statistics')}
|
||||||
<td className="text-right">
|
subtitle={subtitle}
|
||||||
<span className="text-muted">
|
bodyType="card-table"
|
||||||
{props.blockedFiltering}
|
refresh={refreshButton}
|
||||||
</span>
|
>
|
||||||
</td>
|
<table className="table card-table">
|
||||||
</tr>
|
<tbody>
|
||||||
<tr>
|
<tr>
|
||||||
<td>
|
<td>
|
||||||
<Trans>stats_malware_phishing</Trans>
|
<Trans>dns_query</Trans>
|
||||||
<Tooltip text={ props.t('number_of_dns_query_blocked_24_hours_by_sec') } type={tooltipType} />
|
<Tooltip text={tooltipTitle} type={tooltipType} />
|
||||||
</td>
|
</td>
|
||||||
<td className="text-right">
|
<td className="text-right">
|
||||||
<span className="text-muted">
|
<span className="text-muted">{dnsQueries}</span>
|
||||||
{props.replacedSafebrowsing}
|
</td>
|
||||||
</span>
|
</tr>
|
||||||
</td>
|
<tr>
|
||||||
</tr>
|
<td>
|
||||||
<tr>
|
<a href="#filters">
|
||||||
<td>
|
<Trans>blocked_by</Trans>
|
||||||
<Trans>stats_adult</Trans>
|
</a>
|
||||||
<Tooltip text={ props.t('number_of_dns_query_blocked_24_hours_adult') } type={tooltipType} />
|
<Tooltip
|
||||||
</td>
|
text={t('number_of_dns_query_blocked_24_hours')}
|
||||||
<td className="text-right">
|
type={tooltipType}
|
||||||
<span className="text-muted">
|
/>
|
||||||
{props.replacedParental}
|
</td>
|
||||||
</span>
|
<td className="text-right">
|
||||||
</td>
|
<span className="text-muted">{blockedFiltering}</span>
|
||||||
</tr>
|
</td>
|
||||||
<tr>
|
</tr>
|
||||||
<td>
|
<tr>
|
||||||
<Trans>enforced_save_search</Trans>
|
<td>
|
||||||
<Tooltip text={ props.t('number_of_dns_query_to_safe_search') } type={tooltipType} />
|
<Trans>stats_malware_phishing</Trans>
|
||||||
</td>
|
<Tooltip
|
||||||
<td className="text-right">
|
text={t('number_of_dns_query_blocked_24_hours_by_sec')}
|
||||||
<span className="text-muted">
|
type={tooltipType}
|
||||||
{props.replacedSafesearch}
|
/>
|
||||||
</span>
|
</td>
|
||||||
</td>
|
<td className="text-right">
|
||||||
</tr>
|
<span className="text-muted">{replacedSafebrowsing}</span>
|
||||||
<tr>
|
</td>
|
||||||
<td>
|
</tr>
|
||||||
<Trans>average_processing_time</Trans>
|
<tr>
|
||||||
<Tooltip text={ props.t('average_processing_time_hint') } type={tooltipType} />
|
<td>
|
||||||
</td>
|
<Trans>stats_adult</Trans>
|
||||||
<td className="text-right">
|
<Tooltip
|
||||||
<span className="text-muted">
|
text={t('number_of_dns_query_blocked_24_hours_adult')}
|
||||||
{props.avgProcessingTime}
|
type={tooltipType}
|
||||||
</span>
|
/>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
<td className="text-right">
|
||||||
</tbody>
|
<span className="text-muted">{replacedParental}</span>
|
||||||
</table>
|
</td>
|
||||||
</Card>
|
</tr>
|
||||||
);
|
<tr>
|
||||||
|
<td>
|
||||||
|
<Trans>enforced_save_search</Trans>
|
||||||
|
<Tooltip
|
||||||
|
text={t('number_of_dns_query_to_safe_search')}
|
||||||
|
type={tooltipType}
|
||||||
|
/>
|
||||||
|
</td>
|
||||||
|
<td className="text-right">
|
||||||
|
<span className="text-muted">{replacedSafesearch}</span>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
<Trans>average_processing_time</Trans>
|
||||||
|
<Tooltip text={t('average_processing_time_hint')} type={tooltipType} />
|
||||||
|
</td>
|
||||||
|
<td className="text-right">
|
||||||
|
<span className="text-muted">
|
||||||
|
{avgProcessingTime ? `${round(avgProcessingTime, 2)} ms` : 0}
|
||||||
|
</span>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</Card>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
Counters.propTypes = {
|
Counters.propTypes = {
|
||||||
dnsQueries: PropTypes.number.isRequired,
|
dnsQueries: PropTypes.number.isRequired,
|
||||||
|
@ -92,6 +120,8 @@ Counters.propTypes = {
|
||||||
replacedSafesearch: PropTypes.number.isRequired,
|
replacedSafesearch: PropTypes.number.isRequired,
|
||||||
avgProcessingTime: PropTypes.number.isRequired,
|
avgProcessingTime: PropTypes.number.isRequired,
|
||||||
refreshButton: PropTypes.node.isRequired,
|
refreshButton: PropTypes.node.isRequired,
|
||||||
|
subtitle: PropTypes.string.isRequired,
|
||||||
|
interval: PropTypes.number.isRequired,
|
||||||
t: PropTypes.func.isRequired,
|
t: PropTypes.func.isRequired,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -20,3 +20,8 @@
|
||||||
border-top: 6px solid transparent;
|
border-top: 6px solid transparent;
|
||||||
border-bottom: 6px solid #585965;
|
border-bottom: 6px solid #585965;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.card-chart-bg {
|
||||||
|
left: -20px;
|
||||||
|
width: calc(100% + 20px);
|
||||||
|
}
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
import React, { Component } from 'react';
|
import React, { Component } from 'react';
|
||||||
import ReactTable from 'react-table';
|
import ReactTable from 'react-table';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import map from 'lodash/map';
|
|
||||||
import { withNamespaces, Trans } from 'react-i18next';
|
import { withNamespaces, Trans } from 'react-i18next';
|
||||||
|
|
||||||
import Card from '../ui/Card';
|
import Card from '../ui/Card';
|
||||||
|
@ -20,49 +19,58 @@ class QueriedDomains extends Component {
|
||||||
return STATUS_COLORS.yellow;
|
return STATUS_COLORS.yellow;
|
||||||
}
|
}
|
||||||
return STATUS_COLORS.green;
|
return STATUS_COLORS.green;
|
||||||
}
|
};
|
||||||
|
|
||||||
columns = [{
|
columns = [
|
||||||
Header: 'IP',
|
{
|
||||||
accessor: 'ip',
|
Header: <Trans>domain</Trans>,
|
||||||
Cell: (row) => {
|
accessor: 'domain',
|
||||||
const { value } = row;
|
Cell: (row) => {
|
||||||
const trackerData = getTrackerData(value);
|
const { value } = row;
|
||||||
|
const trackerData = getTrackerData(value);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="logs__row">
|
<div className="logs__row">
|
||||||
<div className="logs__text" title={value}>
|
<div className="logs__text" title={value}>
|
||||||
{value}
|
{value}
|
||||||
|
</div>
|
||||||
|
{trackerData && <Popover data={trackerData} />}
|
||||||
</div>
|
</div>
|
||||||
{trackerData && <Popover data={trackerData} />}
|
);
|
||||||
</div>
|
},
|
||||||
);
|
|
||||||
},
|
},
|
||||||
}, {
|
{
|
||||||
Header: <Trans>requests_count</Trans>,
|
Header: <Trans>requests_count</Trans>,
|
||||||
accessor: 'count',
|
accessor: 'count',
|
||||||
maxWidth: 190,
|
maxWidth: 190,
|
||||||
Cell: ({ value }) => {
|
Cell: ({ value }) => {
|
||||||
const percent = getPercent(this.props.dnsQueries, value);
|
const percent = getPercent(this.props.dnsQueries, value);
|
||||||
const percentColor = this.getPercentColor(percent);
|
const percentColor = this.getPercentColor(percent);
|
||||||
|
|
||||||
return (
|
return <Cell value={value} percent={percent} color={percentColor} />;
|
||||||
<Cell value={value} percent={percent} color={percentColor} />
|
},
|
||||||
);
|
|
||||||
},
|
},
|
||||||
}];
|
];
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const { t } = this.props;
|
const {
|
||||||
|
t, refreshButton, topQueriedDomains, subtitle,
|
||||||
|
} = this.props;
|
||||||
return (
|
return (
|
||||||
<Card title={ t('stats_query_domain') } subtitle={ t('for_last_24_hours') } bodyType="card-table" refresh={this.props.refreshButton}>
|
<Card
|
||||||
|
title={t('stats_query_domain')}
|
||||||
|
subtitle={subtitle}
|
||||||
|
bodyType="card-table"
|
||||||
|
refresh={refreshButton}
|
||||||
|
>
|
||||||
<ReactTable
|
<ReactTable
|
||||||
data={map(this.props.topQueriedDomains, (value, prop) => (
|
data={topQueriedDomains.map(item => ({
|
||||||
{ ip: prop, count: value }
|
domain: item.name,
|
||||||
))}
|
count: item.count,
|
||||||
|
}))}
|
||||||
columns={this.columns}
|
columns={this.columns}
|
||||||
showPagination={false}
|
showPagination={false}
|
||||||
noDataText={ t('no_domains_found') }
|
noDataText={t('no_domains_found')}
|
||||||
minRows={6}
|
minRows={6}
|
||||||
className="-striped -highlight card-table-overflow stats__table"
|
className="-striped -highlight card-table-overflow stats__table"
|
||||||
/>
|
/>
|
||||||
|
@ -72,10 +80,11 @@ class QueriedDomains extends Component {
|
||||||
}
|
}
|
||||||
|
|
||||||
QueriedDomains.propTypes = {
|
QueriedDomains.propTypes = {
|
||||||
topQueriedDomains: PropTypes.object.isRequired,
|
topQueriedDomains: PropTypes.array.isRequired,
|
||||||
dnsQueries: PropTypes.number.isRequired,
|
dnsQueries: PropTypes.number.isRequired,
|
||||||
refreshButton: PropTypes.node.isRequired,
|
refreshButton: PropTypes.node.isRequired,
|
||||||
t: PropTypes.func,
|
subtitle: PropTypes.string.isRequired,
|
||||||
|
t: PropTypes.func.isRequired,
|
||||||
};
|
};
|
||||||
|
|
||||||
export default withNamespaces()(QueriedDomains);
|
export default withNamespaces()(QueriedDomains);
|
||||||
|
|
|
@ -5,37 +5,42 @@ import { Trans, withNamespaces } from 'react-i18next';
|
||||||
import Card from '../ui/Card';
|
import Card from '../ui/Card';
|
||||||
import Line from '../ui/Line';
|
import Line from '../ui/Line';
|
||||||
|
|
||||||
import { getPercent } from '../../helpers/helpers';
|
import { getPercent, normalizeHistory } from '../../helpers/helpers';
|
||||||
import { STATUS_COLORS } from '../../helpers/constants';
|
import { STATUS_COLORS } from '../../helpers/constants';
|
||||||
|
|
||||||
class Statistics extends Component {
|
class Statistics extends Component {
|
||||||
|
getNormalizedHistory = (data, interval, id) => [{ data: normalizeHistory(data, interval), id }];
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const {
|
const {
|
||||||
|
interval,
|
||||||
dnsQueries,
|
dnsQueries,
|
||||||
blockedFiltering,
|
blockedFiltering,
|
||||||
replacedSafebrowsing,
|
replacedSafebrowsing,
|
||||||
replacedParental,
|
replacedParental,
|
||||||
|
numDnsQueries,
|
||||||
|
numBlockedFiltering,
|
||||||
|
numReplacedSafebrowsing,
|
||||||
|
numReplacedParental,
|
||||||
} = this.props;
|
} = this.props;
|
||||||
|
|
||||||
const filteringData = [this.props.history[1]];
|
|
||||||
const queriesData = [this.props.history[2]];
|
|
||||||
const parentalData = [this.props.history[3]];
|
|
||||||
const safebrowsingData = [this.props.history[4]];
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="row">
|
<div className="row">
|
||||||
<div className="col-sm-6 col-lg-3">
|
<div className="col-sm-6 col-lg-3">
|
||||||
<Card type="card--full" bodyType="card-wrap">
|
<Card type="card--full" bodyType="card-wrap">
|
||||||
<div className="card-body-stats">
|
<div className="card-body-stats">
|
||||||
<div className="card-value card-value-stats text-blue">
|
<div className="card-value card-value-stats text-blue">
|
||||||
{dnsQueries}
|
{numDnsQueries}
|
||||||
</div>
|
</div>
|
||||||
<div className="card-title-stats">
|
<div className="card-title-stats">
|
||||||
<Trans>dns_query</Trans>
|
<Trans>dns_query</Trans>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="card-chart-bg">
|
<div className="card-chart-bg">
|
||||||
<Line data={queriesData} color={STATUS_COLORS.blue}/>
|
<Line
|
||||||
|
data={this.getNormalizedHistory(dnsQueries, interval, 'dnsQueries')}
|
||||||
|
color={STATUS_COLORS.blue}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</Card>
|
</Card>
|
||||||
</div>
|
</div>
|
||||||
|
@ -43,10 +48,10 @@ class Statistics extends Component {
|
||||||
<Card type="card--full" bodyType="card-wrap">
|
<Card type="card--full" bodyType="card-wrap">
|
||||||
<div className="card-body-stats">
|
<div className="card-body-stats">
|
||||||
<div className="card-value card-value-stats text-red">
|
<div className="card-value card-value-stats text-red">
|
||||||
{blockedFiltering}
|
{numBlockedFiltering}
|
||||||
</div>
|
</div>
|
||||||
<div className="card-value card-value-percent text-red">
|
<div className="card-value card-value-percent text-red">
|
||||||
{getPercent(dnsQueries, blockedFiltering)}
|
{getPercent(numDnsQueries, numBlockedFiltering)}
|
||||||
</div>
|
</div>
|
||||||
<div className="card-title-stats">
|
<div className="card-title-stats">
|
||||||
<a href="#filters">
|
<a href="#filters">
|
||||||
|
@ -55,7 +60,14 @@ class Statistics extends Component {
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="card-chart-bg">
|
<div className="card-chart-bg">
|
||||||
<Line data={filteringData} color={STATUS_COLORS.red}/>
|
<Line
|
||||||
|
data={this.getNormalizedHistory(
|
||||||
|
blockedFiltering,
|
||||||
|
interval,
|
||||||
|
'blockedFiltering',
|
||||||
|
)}
|
||||||
|
color={STATUS_COLORS.red}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</Card>
|
</Card>
|
||||||
</div>
|
</div>
|
||||||
|
@ -63,17 +75,24 @@ class Statistics extends Component {
|
||||||
<Card type="card--full" bodyType="card-wrap">
|
<Card type="card--full" bodyType="card-wrap">
|
||||||
<div className="card-body-stats">
|
<div className="card-body-stats">
|
||||||
<div className="card-value card-value-stats text-green">
|
<div className="card-value card-value-stats text-green">
|
||||||
{replacedSafebrowsing}
|
{numReplacedSafebrowsing}
|
||||||
</div>
|
</div>
|
||||||
<div className="card-value card-value-percent text-green">
|
<div className="card-value card-value-percent text-green">
|
||||||
{getPercent(dnsQueries, replacedSafebrowsing)}
|
{getPercent(numDnsQueries, numReplacedSafebrowsing)}
|
||||||
</div>
|
</div>
|
||||||
<div className="card-title-stats">
|
<div className="card-title-stats">
|
||||||
<Trans>stats_malware_phishing</Trans>
|
<Trans>stats_malware_phishing</Trans>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="card-chart-bg">
|
<div className="card-chart-bg">
|
||||||
<Line data={safebrowsingData} color={STATUS_COLORS.green}/>
|
<Line
|
||||||
|
data={this.getNormalizedHistory(
|
||||||
|
replacedSafebrowsing,
|
||||||
|
interval,
|
||||||
|
'replacedSafebrowsing',
|
||||||
|
)}
|
||||||
|
color={STATUS_COLORS.green}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</Card>
|
</Card>
|
||||||
</div>
|
</div>
|
||||||
|
@ -81,17 +100,24 @@ class Statistics extends Component {
|
||||||
<Card type="card--full" bodyType="card-wrap">
|
<Card type="card--full" bodyType="card-wrap">
|
||||||
<div className="card-body-stats">
|
<div className="card-body-stats">
|
||||||
<div className="card-value card-value-stats text-yellow">
|
<div className="card-value card-value-stats text-yellow">
|
||||||
{replacedParental}
|
{numReplacedParental}
|
||||||
</div>
|
</div>
|
||||||
<div className="card-value card-value-percent text-yellow">
|
<div className="card-value card-value-percent text-yellow">
|
||||||
{getPercent(dnsQueries, replacedParental)}
|
{getPercent(numDnsQueries, numReplacedParental)}
|
||||||
</div>
|
</div>
|
||||||
<div className="card-title-stats">
|
<div className="card-title-stats">
|
||||||
<Trans>stats_adult</Trans>
|
<Trans>stats_adult</Trans>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="card-chart-bg">
|
<div className="card-chart-bg">
|
||||||
<Line data={parentalData} color={STATUS_COLORS.yellow}/>
|
<Line
|
||||||
|
data={this.getNormalizedHistory(
|
||||||
|
replacedParental,
|
||||||
|
interval,
|
||||||
|
'replacedParental',
|
||||||
|
)}
|
||||||
|
color={STATUS_COLORS.yellow}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</Card>
|
</Card>
|
||||||
</div>
|
</div>
|
||||||
|
@ -101,11 +127,15 @@ class Statistics extends Component {
|
||||||
}
|
}
|
||||||
|
|
||||||
Statistics.propTypes = {
|
Statistics.propTypes = {
|
||||||
history: PropTypes.array.isRequired,
|
interval: PropTypes.number.isRequired,
|
||||||
dnsQueries: PropTypes.number.isRequired,
|
dnsQueries: PropTypes.array.isRequired,
|
||||||
blockedFiltering: PropTypes.number.isRequired,
|
blockedFiltering: PropTypes.array.isRequired,
|
||||||
replacedSafebrowsing: PropTypes.number.isRequired,
|
replacedSafebrowsing: PropTypes.array.isRequired,
|
||||||
replacedParental: PropTypes.number.isRequired,
|
replacedParental: PropTypes.array.isRequired,
|
||||||
|
numDnsQueries: PropTypes.number.isRequired,
|
||||||
|
numBlockedFiltering: PropTypes.number.isRequired,
|
||||||
|
numReplacedSafebrowsing: PropTypes.number.isRequired,
|
||||||
|
numReplacedParental: PropTypes.number.isRequired,
|
||||||
refreshButton: PropTypes.node.isRequired,
|
refreshButton: PropTypes.node.isRequired,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -19,10 +19,9 @@ class Dashboard extends Component {
|
||||||
|
|
||||||
getAllStats = () => {
|
getAllStats = () => {
|
||||||
this.props.getStats();
|
this.props.getStats();
|
||||||
this.props.getStatsHistory();
|
this.props.getStatsConfig();
|
||||||
this.props.getTopStats();
|
|
||||||
this.props.getClients();
|
this.props.getClients();
|
||||||
}
|
};
|
||||||
|
|
||||||
getToggleFilteringButton = () => {
|
getToggleFilteringButton = () => {
|
||||||
const { protectionEnabled, processingProtection } = this.props.dashboard;
|
const { protectionEnabled, processingProtection } = this.props.dashboard;
|
||||||
|
@ -39,16 +38,20 @@ class Dashboard extends Component {
|
||||||
<Trans>{buttonText}</Trans>
|
<Trans>{buttonText}</Trans>
|
||||||
</button>
|
</button>
|
||||||
);
|
);
|
||||||
}
|
};
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const { dashboard, t } = this.props;
|
const { dashboard, stats, t } = this.props;
|
||||||
const dashboardProcessing =
|
const dashboardProcessing =
|
||||||
dashboard.processing ||
|
dashboard.processing ||
|
||||||
dashboard.processingStats ||
|
|
||||||
dashboard.processingStatsHistory ||
|
|
||||||
dashboard.processingClients ||
|
dashboard.processingClients ||
|
||||||
dashboard.processingTopStats;
|
stats.processingStats ||
|
||||||
|
stats.processingGetConfig;
|
||||||
|
|
||||||
|
const subtitle =
|
||||||
|
stats.interval === 1
|
||||||
|
? t('for_last_24_hours')
|
||||||
|
: t('for_last_days', { value: stats.interval });
|
||||||
|
|
||||||
const refreshFullButton = (
|
const refreshFullButton = (
|
||||||
<button
|
<button
|
||||||
|
@ -59,6 +62,7 @@ class Dashboard extends Component {
|
||||||
<Trans>refresh_statics</Trans>
|
<Trans>refresh_statics</Trans>
|
||||||
</button>
|
</button>
|
||||||
);
|
);
|
||||||
|
|
||||||
const refreshButton = (
|
const refreshButton = (
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
|
@ -73,87 +77,85 @@ class Dashboard extends Component {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Fragment>
|
<Fragment>
|
||||||
<PageTitle title={ t('dashboard') }>
|
<PageTitle title={t('dashboard')}>
|
||||||
<div className="page-title__actions">
|
<div className="page-title__actions">
|
||||||
{this.getToggleFilteringButton()}
|
{this.getToggleFilteringButton()}
|
||||||
{refreshFullButton}
|
{refreshFullButton}
|
||||||
</div>
|
</div>
|
||||||
</PageTitle>
|
</PageTitle>
|
||||||
{dashboardProcessing && <Loading />}
|
{dashboardProcessing && <Loading />}
|
||||||
{!dashboardProcessing &&
|
{!dashboardProcessing && (
|
||||||
<div className="row row-cards">
|
<div className="row row-cards">
|
||||||
{dashboard.statsHistory &&
|
<div className="col-lg-12">
|
||||||
<div className="col-lg-12">
|
<Statistics
|
||||||
<Statistics
|
interval={stats.interval}
|
||||||
history={dashboard.statsHistory}
|
dnsQueries={stats.dnsQueries}
|
||||||
refreshButton={refreshButton}
|
blockedFiltering={stats.blockedFiltering}
|
||||||
dnsQueries={dashboard.stats.dns_queries}
|
replacedSafebrowsing={stats.replacedSafebrowsing}
|
||||||
blockedFiltering={dashboard.stats.blocked_filtering}
|
replacedParental={stats.replacedParental}
|
||||||
replacedSafebrowsing={dashboard.stats.replaced_safebrowsing}
|
numDnsQueries={stats.numDnsQueries}
|
||||||
replacedParental={dashboard.stats.replaced_parental}
|
numBlockedFiltering={stats.numBlockedFiltering}
|
||||||
/>
|
numReplacedSafebrowsing={stats.numReplacedSafebrowsing}
|
||||||
</div>
|
numReplacedParental={stats.numReplacedParental}
|
||||||
}
|
refreshButton={refreshButton}
|
||||||
<div className="col-lg-6">
|
/>
|
||||||
{dashboard.stats &&
|
</div>
|
||||||
<Counters
|
<div className="col-lg-6">
|
||||||
refreshButton={refreshButton}
|
<Counters
|
||||||
dnsQueries={dashboard.stats.dns_queries}
|
subtitle={subtitle}
|
||||||
blockedFiltering={dashboard.stats.blocked_filtering}
|
interval={stats.interval}
|
||||||
replacedSafebrowsing={dashboard.stats.replaced_safebrowsing}
|
dnsQueries={stats.numDnsQueries}
|
||||||
replacedParental={dashboard.stats.replaced_parental}
|
blockedFiltering={stats.numBlockedFiltering}
|
||||||
replacedSafesearch={dashboard.stats.replaced_safesearch}
|
replacedSafebrowsing={stats.numReplacedSafebrowsing}
|
||||||
avgProcessingTime={dashboard.stats.avg_processing_time}
|
replacedParental={stats.numReplacedParental}
|
||||||
/>
|
replacedSafesearch={stats.numReplacedSafesearch}
|
||||||
}
|
avgProcessingTime={stats.avgProcessingTime}
|
||||||
|
refreshButton={refreshButton}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div className="col-lg-6">
|
||||||
|
<Clients
|
||||||
|
subtitle={subtitle}
|
||||||
|
dnsQueries={stats.numDnsQueries}
|
||||||
|
topClients={stats.topClients}
|
||||||
|
clients={dashboard.clients}
|
||||||
|
autoClients={dashboard.autoClients}
|
||||||
|
refreshButton={refreshButton}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div className="col-lg-6">
|
||||||
|
<QueriedDomains
|
||||||
|
subtitle={subtitle}
|
||||||
|
dnsQueries={stats.numDnsQueries}
|
||||||
|
topQueriedDomains={stats.topQueriedDomains}
|
||||||
|
refreshButton={refreshButton}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div className="col-lg-6">
|
||||||
|
<BlockedDomains
|
||||||
|
subtitle={subtitle}
|
||||||
|
topBlockedDomains={stats.topBlockedDomains}
|
||||||
|
blockedFiltering={stats.numBlockedFiltering}
|
||||||
|
replacedSafebrowsing={stats.numReplacedSafebrowsing}
|
||||||
|
replacedParental={stats.numReplacedParental}
|
||||||
|
refreshButton={refreshButton}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
{dashboard.topStats &&
|
|
||||||
<Fragment>
|
|
||||||
<div className="col-lg-6">
|
|
||||||
<Clients
|
|
||||||
dnsQueries={dashboard.stats.dns_queries}
|
|
||||||
refreshButton={refreshButton}
|
|
||||||
topClients={dashboard.topStats.top_clients}
|
|
||||||
clients={dashboard.clients}
|
|
||||||
autoClients={dashboard.autoClients}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div className="col-lg-6">
|
|
||||||
<QueriedDomains
|
|
||||||
dnsQueries={dashboard.stats.dns_queries}
|
|
||||||
refreshButton={refreshButton}
|
|
||||||
topQueriedDomains={dashboard.topStats.top_queried_domains}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div className="col-lg-6">
|
|
||||||
<BlockedDomains
|
|
||||||
refreshButton={refreshButton}
|
|
||||||
topBlockedDomains={dashboard.topStats.top_blocked_domains}
|
|
||||||
blockedFiltering={dashboard.stats.blocked_filtering}
|
|
||||||
replacedSafebrowsing={dashboard.stats.replaced_safebrowsing}
|
|
||||||
replacedParental={dashboard.stats.replaced_parental}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</Fragment>
|
|
||||||
}
|
|
||||||
</div>
|
</div>
|
||||||
}
|
)}
|
||||||
</Fragment>
|
</Fragment>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Dashboard.propTypes = {
|
Dashboard.propTypes = {
|
||||||
getStats: PropTypes.func,
|
dashboard: PropTypes.object.isRequired,
|
||||||
getStatsHistory: PropTypes.func,
|
stats: PropTypes.object.isRequired,
|
||||||
getTopStats: PropTypes.func,
|
getStats: PropTypes.func.isRequired,
|
||||||
dashboard: PropTypes.object,
|
getStatsConfig: PropTypes.func.isRequired,
|
||||||
isCoreRunning: PropTypes.bool,
|
toggleProtection: PropTypes.func.isRequired,
|
||||||
getFiltering: PropTypes.func,
|
getClients: PropTypes.func.isRequired,
|
||||||
toggleProtection: PropTypes.func,
|
t: PropTypes.func.isRequired,
|
||||||
getClients: PropTypes.func,
|
|
||||||
processingProtection: PropTypes.bool,
|
|
||||||
t: PropTypes.func,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export default withNamespaces()(Dashboard);
|
export default withNamespaces()(Dashboard);
|
||||||
|
|
|
@ -27,8 +27,9 @@ class AutoClients extends Component {
|
||||||
};
|
};
|
||||||
|
|
||||||
getStats = (ip, stats) => {
|
getStats = (ip, stats) => {
|
||||||
if (stats && stats.top_clients) {
|
if (stats) {
|
||||||
return stats.top_clients[ip];
|
const statsForCurrentIP = stats.find(item => item.name === ip);
|
||||||
|
return statsForCurrentIP && statsForCurrentIP.count;
|
||||||
}
|
}
|
||||||
|
|
||||||
return '';
|
return '';
|
||||||
|
@ -59,11 +60,11 @@ class AutoClients extends Component {
|
||||||
Cell: this.cellWrap,
|
Cell: this.cellWrap,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Header: this.props.t('table_statistics'),
|
Header: this.props.t('requests_count'),
|
||||||
accessor: 'statistics',
|
accessor: 'statistics',
|
||||||
Cell: (row) => {
|
Cell: (row) => {
|
||||||
const clientIP = row.original.ip;
|
const clientIP = row.original.ip;
|
||||||
const clientStats = clientIP && this.getStats(clientIP, this.props.topStats);
|
const clientStats = clientIP && this.getStats(clientIP, this.props.topClients);
|
||||||
|
|
||||||
if (clientStats) {
|
if (clientStats) {
|
||||||
return (
|
return (
|
||||||
|
@ -112,7 +113,7 @@ class AutoClients extends Component {
|
||||||
AutoClients.propTypes = {
|
AutoClients.propTypes = {
|
||||||
t: PropTypes.func.isRequired,
|
t: PropTypes.func.isRequired,
|
||||||
autoClients: PropTypes.array.isRequired,
|
autoClients: PropTypes.array.isRequired,
|
||||||
topStats: PropTypes.object.isRequired,
|
topClients: PropTypes.array.isRequired,
|
||||||
};
|
};
|
||||||
|
|
||||||
export default withNamespaces()(AutoClients);
|
export default withNamespaces()(AutoClients);
|
||||||
|
|
|
@ -63,8 +63,9 @@ class ClientsTable extends Component {
|
||||||
};
|
};
|
||||||
|
|
||||||
getStats = (ip, stats) => {
|
getStats = (ip, stats) => {
|
||||||
if (stats && stats.top_clients) {
|
if (stats) {
|
||||||
return stats.top_clients[ip];
|
const statsForCurrentIP = stats.find(item => item.name === ip);
|
||||||
|
return statsForCurrentIP && statsForCurrentIP.count;
|
||||||
}
|
}
|
||||||
|
|
||||||
return '';
|
return '';
|
||||||
|
@ -149,11 +150,11 @@ class ClientsTable extends Component {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Header: this.props.t('table_statistics'),
|
Header: this.props.t('requests_count'),
|
||||||
accessor: 'statistics',
|
accessor: 'statistics',
|
||||||
Cell: (row) => {
|
Cell: (row) => {
|
||||||
const clientIP = row.original.ip;
|
const clientIP = row.original.ip;
|
||||||
const clientStats = clientIP && this.getStats(clientIP, this.props.topStats);
|
const clientStats = clientIP && this.getStats(clientIP, this.props.topClients);
|
||||||
|
|
||||||
if (clientStats) {
|
if (clientStats) {
|
||||||
return (
|
return (
|
||||||
|
@ -276,7 +277,7 @@ class ClientsTable extends Component {
|
||||||
ClientsTable.propTypes = {
|
ClientsTable.propTypes = {
|
||||||
t: PropTypes.func.isRequired,
|
t: PropTypes.func.isRequired,
|
||||||
clients: PropTypes.array.isRequired,
|
clients: PropTypes.array.isRequired,
|
||||||
topStats: PropTypes.object.isRequired,
|
topClients: PropTypes.array.isRequired,
|
||||||
toggleClientModal: PropTypes.func.isRequired,
|
toggleClientModal: PropTypes.func.isRequired,
|
||||||
deleteClient: PropTypes.func.isRequired,
|
deleteClient: PropTypes.func.isRequired,
|
||||||
addClient: PropTypes.func.isRequired,
|
addClient: PropTypes.func.isRequired,
|
||||||
|
|
|
@ -10,13 +10,14 @@ import Loading from '../../ui/Loading';
|
||||||
class Clients extends Component {
|
class Clients extends Component {
|
||||||
componentDidMount() {
|
componentDidMount() {
|
||||||
this.props.getClients();
|
this.props.getClients();
|
||||||
this.props.getTopStats();
|
this.props.getStats();
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const {
|
const {
|
||||||
t,
|
t,
|
||||||
dashboard,
|
dashboard,
|
||||||
|
stats,
|
||||||
clients,
|
clients,
|
||||||
addClient,
|
addClient,
|
||||||
updateClient,
|
updateClient,
|
||||||
|
@ -27,12 +28,12 @@ class Clients extends Component {
|
||||||
return (
|
return (
|
||||||
<Fragment>
|
<Fragment>
|
||||||
<PageTitle title={t('client_settings')} />
|
<PageTitle title={t('client_settings')} />
|
||||||
{(dashboard.processingTopStats || dashboard.processingClients) && <Loading />}
|
{(stats.processingStats || dashboard.processingClients) && <Loading />}
|
||||||
{!dashboard.processingTopStats && !dashboard.processingClients && (
|
{!stats.processingStats && !dashboard.processingClients && (
|
||||||
<Fragment>
|
<Fragment>
|
||||||
<ClientsTable
|
<ClientsTable
|
||||||
clients={dashboard.clients}
|
clients={dashboard.clients}
|
||||||
topStats={dashboard.topStats}
|
topClients={stats.topClients}
|
||||||
isModalOpen={clients.isModalOpen}
|
isModalOpen={clients.isModalOpen}
|
||||||
modalClientName={clients.modalClientName}
|
modalClientName={clients.modalClientName}
|
||||||
modalType={clients.modalType}
|
modalType={clients.modalType}
|
||||||
|
@ -46,7 +47,7 @@ class Clients extends Component {
|
||||||
/>
|
/>
|
||||||
<AutoClients
|
<AutoClients
|
||||||
autoClients={dashboard.autoClients}
|
autoClients={dashboard.autoClients}
|
||||||
topStats={dashboard.topStats}
|
topClients={stats.topClients}
|
||||||
/>
|
/>
|
||||||
</Fragment>
|
</Fragment>
|
||||||
)}
|
)}
|
||||||
|
@ -58,14 +59,14 @@ class Clients extends Component {
|
||||||
Clients.propTypes = {
|
Clients.propTypes = {
|
||||||
t: PropTypes.func.isRequired,
|
t: PropTypes.func.isRequired,
|
||||||
dashboard: PropTypes.object.isRequired,
|
dashboard: PropTypes.object.isRequired,
|
||||||
|
stats: PropTypes.object.isRequired,
|
||||||
clients: PropTypes.object.isRequired,
|
clients: PropTypes.object.isRequired,
|
||||||
toggleClientModal: PropTypes.func.isRequired,
|
toggleClientModal: PropTypes.func.isRequired,
|
||||||
deleteClient: PropTypes.func.isRequired,
|
deleteClient: PropTypes.func.isRequired,
|
||||||
addClient: PropTypes.func.isRequired,
|
addClient: PropTypes.func.isRequired,
|
||||||
updateClient: PropTypes.func.isRequired,
|
updateClient: PropTypes.func.isRequired,
|
||||||
getClients: PropTypes.func.isRequired,
|
getClients: PropTypes.func.isRequired,
|
||||||
getTopStats: PropTypes.func.isRequired,
|
getStats: PropTypes.func.isRequired,
|
||||||
topStats: PropTypes.object,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export default withNamespaces()(Clients);
|
export default withNamespaces()(Clients);
|
||||||
|
|
|
@ -88,7 +88,7 @@ class Settings extends Component {
|
||||||
<div className="col-md-12">
|
<div className="col-md-12">
|
||||||
<StatsConfig
|
<StatsConfig
|
||||||
interval={stats.interval}
|
interval={stats.interval}
|
||||||
processing={stats.setConfigProcessing}
|
processing={stats.processingSetConfig}
|
||||||
setStatsConfig={setStatsConfig}
|
setStatsConfig={setStatsConfig}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -4,33 +4,27 @@ import { ResponsiveLine } from '@nivo/line';
|
||||||
|
|
||||||
import './Line.css';
|
import './Line.css';
|
||||||
|
|
||||||
const Line = props => (
|
const Line = ({ data, color }) => (
|
||||||
props.data &&
|
data &&
|
||||||
<ResponsiveLine
|
<ResponsiveLine
|
||||||
data={props.data}
|
data={data}
|
||||||
margin={{
|
margin={{
|
||||||
top: 15,
|
top: 15,
|
||||||
right: 0,
|
right: 0,
|
||||||
bottom: 1,
|
bottom: 1,
|
||||||
left: 0,
|
left: 20,
|
||||||
}}
|
}}
|
||||||
minY="auto"
|
minY="auto"
|
||||||
stacked={false}
|
stacked={false}
|
||||||
curve='linear'
|
curve='linear'
|
||||||
axisBottom={{
|
axisBottom={null}
|
||||||
tickSize: 0,
|
axisLeft={null}
|
||||||
tickPadding: 10,
|
|
||||||
}}
|
|
||||||
axisLeft={{
|
|
||||||
tickSize: 0,
|
|
||||||
tickPadding: 10,
|
|
||||||
}}
|
|
||||||
enableGridX={false}
|
enableGridX={false}
|
||||||
enableGridY={false}
|
enableGridY={false}
|
||||||
enableDots={false}
|
enableDots={false}
|
||||||
enableArea={true}
|
enableArea={true}
|
||||||
animate={false}
|
animate={false}
|
||||||
colorBy={() => (props.color)}
|
colorBy={() => (color)}
|
||||||
tooltip={slice => (
|
tooltip={slice => (
|
||||||
<div>
|
<div>
|
||||||
{slice.data.map(d => (
|
{slice.data.map(d => (
|
||||||
|
|
|
@ -1,20 +1,22 @@
|
||||||
import { connect } from 'react-redux';
|
import { connect } from 'react-redux';
|
||||||
import { getClients, getTopStats } from '../actions';
|
import { getClients } from '../actions';
|
||||||
|
import { getStats } from '../actions/stats';
|
||||||
import { addClient, updateClient, deleteClient, toggleClientModal } from '../actions/clients';
|
import { addClient, updateClient, deleteClient, toggleClientModal } from '../actions/clients';
|
||||||
import Clients from '../components/Settings/Clients';
|
import Clients from '../components/Settings/Clients';
|
||||||
|
|
||||||
const mapStateToProps = (state) => {
|
const mapStateToProps = (state) => {
|
||||||
const { dashboard, clients } = state;
|
const { dashboard, clients, stats } = state;
|
||||||
const props = {
|
const props = {
|
||||||
dashboard,
|
dashboard,
|
||||||
clients,
|
clients,
|
||||||
|
stats,
|
||||||
};
|
};
|
||||||
return props;
|
return props;
|
||||||
};
|
};
|
||||||
|
|
||||||
const mapDispatchToProps = {
|
const mapDispatchToProps = {
|
||||||
getClients,
|
getClients,
|
||||||
getTopStats,
|
getStats,
|
||||||
addClient,
|
addClient,
|
||||||
updateClient,
|
updateClient,
|
||||||
deleteClient,
|
deleteClient,
|
||||||
|
|
|
@ -1,14 +1,23 @@
|
||||||
import { connect } from 'react-redux';
|
import { connect } from 'react-redux';
|
||||||
import * as actionCreators from '../actions';
|
import { toggleProtection, getClients } from '../actions';
|
||||||
|
import { getStats, getStatsConfig, setStatsConfig } from '../actions/stats';
|
||||||
import Dashboard from '../components/Dashboard';
|
import Dashboard from '../components/Dashboard';
|
||||||
|
|
||||||
const mapStateToProps = (state) => {
|
const mapStateToProps = (state) => {
|
||||||
const { dashboard } = state;
|
const { dashboard, stats } = state;
|
||||||
const props = { dashboard };
|
const props = { dashboard, stats };
|
||||||
return props;
|
return props;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const mapDispatchToProps = {
|
||||||
|
toggleProtection,
|
||||||
|
getClients,
|
||||||
|
getStats,
|
||||||
|
getStatsConfig,
|
||||||
|
setStatsConfig,
|
||||||
|
};
|
||||||
|
|
||||||
export default connect(
|
export default connect(
|
||||||
mapStateToProps,
|
mapStateToProps,
|
||||||
actionCreators,
|
mapDispatchToProps,
|
||||||
)(Dashboard);
|
)(Dashboard);
|
||||||
|
|
|
@ -2,11 +2,12 @@ import dateParse from 'date-fns/parse';
|
||||||
import dateFormat from 'date-fns/format';
|
import dateFormat from 'date-fns/format';
|
||||||
import subHours from 'date-fns/sub_hours';
|
import subHours from 'date-fns/sub_hours';
|
||||||
import addHours from 'date-fns/add_hours';
|
import addHours from 'date-fns/add_hours';
|
||||||
|
import addDays from 'date-fns/add_days';
|
||||||
|
import subDays from 'date-fns/sub_days';
|
||||||
import round from 'lodash/round';
|
import round from 'lodash/round';
|
||||||
import axios from 'axios';
|
import axios from 'axios';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
STATS_NAMES,
|
|
||||||
STANDARD_DNS_PORT,
|
STANDARD_DNS_PORT,
|
||||||
STANDARD_WEB_PORT,
|
STANDARD_WEB_PORT,
|
||||||
STANDARD_HTTPS_PORT,
|
STANDARD_HTTPS_PORT,
|
||||||
|
@ -49,29 +50,28 @@ export const normalizeLogs = logs => logs.map((log) => {
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
export const normalizeHistory = history => Object.keys(history).map((key) => {
|
export const normalizeHistory = (history, interval) => {
|
||||||
let id = STATS_NAMES[key];
|
if (interval === 1 || interval === 7) {
|
||||||
if (!id) {
|
const hoursAgo = subHours(Date.now(), 24 * interval);
|
||||||
id = key.replace(/_/g, ' ').replace(/^\w/, c => c.toUpperCase());
|
return history.map((item, index) => ({
|
||||||
|
x: dateFormat(addHours(hoursAgo, index), 'D MMM HH:00'),
|
||||||
|
y: round(item, 2),
|
||||||
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
const dayAgo = subHours(Date.now(), 24);
|
const daysAgo = subDays(Date.now(), interval - 1);
|
||||||
|
return history.map((item, index) => ({
|
||||||
|
x: dateFormat(addDays(daysAgo, index), 'D MMM YYYY'),
|
||||||
|
y: round(item, 2),
|
||||||
|
}));
|
||||||
|
};
|
||||||
|
|
||||||
const data = history[key].map((item, index) => {
|
export const normalizeTopStats = stats => (
|
||||||
const formatHour = dateFormat(addHours(dayAgo, index), 'ddd HH:00');
|
stats.map(item => ({
|
||||||
const roundValue = round(item, 2);
|
name: Object.keys(item)[0],
|
||||||
|
count: Object.values(item)[0],
|
||||||
return {
|
}))
|
||||||
x: formatHour,
|
);
|
||||||
y: roundValue,
|
|
||||||
};
|
|
||||||
});
|
|
||||||
|
|
||||||
return {
|
|
||||||
id,
|
|
||||||
data,
|
|
||||||
};
|
|
||||||
});
|
|
||||||
|
|
||||||
export const normalizeFilteringStatus = (filteringStatus) => {
|
export const normalizeFilteringStatus = (filteringStatus) => {
|
||||||
const { enabled, filters, user_rules: userRules } = filteringStatus;
|
const { enabled, filters, user_rules: userRules } = filteringStatus;
|
||||||
|
|
|
@ -94,27 +94,6 @@ const dashboard = handleActions({
|
||||||
return newState;
|
return newState;
|
||||||
},
|
},
|
||||||
|
|
||||||
[actions.getStatsRequest]: state => ({ ...state, processingStats: true }),
|
|
||||||
[actions.getStatsFailure]: state => ({ ...state, processingStats: false }),
|
|
||||||
[actions.getStatsSuccess]: (state, { payload }) => {
|
|
||||||
const newState = { ...state, stats: payload, processingStats: false };
|
|
||||||
return newState;
|
|
||||||
},
|
|
||||||
|
|
||||||
[actions.getTopStatsRequest]: state => ({ ...state, processingTopStats: true }),
|
|
||||||
[actions.getTopStatsFailure]: state => ({ ...state, processingTopStats: false }),
|
|
||||||
[actions.getTopStatsSuccess]: (state, { payload }) => {
|
|
||||||
const newState = { ...state, topStats: payload, processingTopStats: false };
|
|
||||||
return newState;
|
|
||||||
},
|
|
||||||
|
|
||||||
[actions.getStatsHistoryRequest]: state => ({ ...state, processingStatsHistory: true }),
|
|
||||||
[actions.getStatsHistoryFailure]: state => ({ ...state, processingStatsHistory: false }),
|
|
||||||
[actions.getStatsHistorySuccess]: (state, { payload }) => {
|
|
||||||
const newState = { ...state, statsHistory: payload, processingStatsHistory: false };
|
|
||||||
return newState;
|
|
||||||
},
|
|
||||||
|
|
||||||
[actions.toggleLogStatusRequest]: state => ({ ...state, logStatusProcessing: true }),
|
[actions.toggleLogStatusRequest]: state => ({ ...state, logStatusProcessing: true }),
|
||||||
[actions.toggleLogStatusFailure]: state => ({ ...state, logStatusProcessing: false }),
|
[actions.toggleLogStatusFailure]: state => ({ ...state, logStatusProcessing: false }),
|
||||||
[actions.toggleLogStatusSuccess]: (state) => {
|
[actions.toggleLogStatusSuccess]: (state) => {
|
||||||
|
@ -200,8 +179,6 @@ const dashboard = handleActions({
|
||||||
}, {
|
}, {
|
||||||
processing: true,
|
processing: true,
|
||||||
isCoreRunning: false,
|
isCoreRunning: false,
|
||||||
processingTopStats: true,
|
|
||||||
processingStats: true,
|
|
||||||
logStatusProcessing: false,
|
logStatusProcessing: false,
|
||||||
processingVersion: true,
|
processingVersion: true,
|
||||||
processingFiltering: true,
|
processingFiltering: true,
|
||||||
|
@ -218,15 +195,6 @@ const dashboard = handleActions({
|
||||||
dnsVersion: '',
|
dnsVersion: '',
|
||||||
clients: [],
|
clients: [],
|
||||||
autoClients: [],
|
autoClients: [],
|
||||||
topStats: [],
|
|
||||||
stats: {
|
|
||||||
dns_queries: '',
|
|
||||||
blocked_filtering: '',
|
|
||||||
replaced_safebrowsing: '',
|
|
||||||
replaced_parental: '',
|
|
||||||
replaced_safesearch: '',
|
|
||||||
avg_processing_time: '',
|
|
||||||
},
|
|
||||||
});
|
});
|
||||||
|
|
||||||
const queryLogs = handleActions({
|
const queryLogs = handleActions({
|
||||||
|
|
|
@ -2,26 +2,83 @@ import { handleActions } from 'redux-actions';
|
||||||
|
|
||||||
import * as actions from '../actions/stats';
|
import * as actions from '../actions/stats';
|
||||||
|
|
||||||
const stats = handleActions({
|
const stats = handleActions(
|
||||||
[actions.getStatsConfigRequest]: state => ({ ...state, getConfigProcessing: true }),
|
{
|
||||||
[actions.getStatsConfigFailure]: state => ({ ...state, getConfigProcessing: false }),
|
[actions.getStatsConfigRequest]: state => ({ ...state, processingGetConfig: true }),
|
||||||
[actions.getStatsConfigSuccess]: (state, { payload }) => ({
|
[actions.getStatsConfigFailure]: state => ({ ...state, processingGetConfig: false }),
|
||||||
...state,
|
[actions.getStatsConfigSuccess]: (state, { payload }) => ({
|
||||||
interval: payload.interval,
|
...state,
|
||||||
getConfigProcessing: false,
|
interval: payload.interval,
|
||||||
}),
|
processingGetConfig: false,
|
||||||
|
}),
|
||||||
|
|
||||||
[actions.setStatsConfigRequest]: state => ({ ...state, setConfigProcessing: true }),
|
[actions.setStatsConfigRequest]: state => ({ ...state, processingSetConfig: true }),
|
||||||
[actions.setStatsConfigFailure]: state => ({ ...state, setConfigProcessing: false }),
|
[actions.setStatsConfigFailure]: state => ({ ...state, processingSetConfig: false }),
|
||||||
[actions.setStatsConfigSuccess]: (state, { payload }) => ({
|
[actions.setStatsConfigSuccess]: (state, { payload }) => ({
|
||||||
...state,
|
...state,
|
||||||
interval: payload.interval,
|
interval: payload.interval,
|
||||||
setConfigProcessing: false,
|
processingSetConfig: false,
|
||||||
}),
|
}),
|
||||||
}, {
|
|
||||||
getConfigProcessing: false,
|
[actions.getStatsRequest]: state => ({ ...state, processingStats: true }),
|
||||||
setConfigProcessing: false,
|
[actions.getStatsFailure]: state => ({ ...state, processingStats: false }),
|
||||||
interval: 1,
|
[actions.getStatsSuccess]: (state, { payload }) => {
|
||||||
});
|
const {
|
||||||
|
dns_queries: dnsQueries,
|
||||||
|
blocked_filtering: blockedFiltering,
|
||||||
|
replaced_parental: replacedParental,
|
||||||
|
replaced_safebrowsing: replacedSafebrowsing,
|
||||||
|
top_blocked_domains: topBlockedDomains,
|
||||||
|
top_clients: topClients,
|
||||||
|
top_queried_domains: topQueriedDomains,
|
||||||
|
num_blocked_filtering: numBlockedFiltering,
|
||||||
|
num_dns_queries: numDnsQueries,
|
||||||
|
num_replaced_parental: numReplacedParental,
|
||||||
|
num_replaced_safebrowsing: numReplacedSafebrowsing,
|
||||||
|
num_replaced_safesearch: numReplacedSafesearch,
|
||||||
|
avg_processing_time: avgProcessingTime,
|
||||||
|
} = payload;
|
||||||
|
|
||||||
|
const newState = {
|
||||||
|
...state,
|
||||||
|
processingStats: false,
|
||||||
|
dnsQueries,
|
||||||
|
blockedFiltering,
|
||||||
|
replacedParental,
|
||||||
|
replacedSafebrowsing,
|
||||||
|
topBlockedDomains,
|
||||||
|
topClients,
|
||||||
|
topQueriedDomains,
|
||||||
|
numBlockedFiltering,
|
||||||
|
numDnsQueries,
|
||||||
|
numReplacedParental,
|
||||||
|
numReplacedSafebrowsing,
|
||||||
|
numReplacedSafesearch,
|
||||||
|
avgProcessingTime,
|
||||||
|
};
|
||||||
|
|
||||||
|
return newState;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
processingGetConfig: false,
|
||||||
|
processingSetConfig: false,
|
||||||
|
processingStats: true,
|
||||||
|
interval: 1,
|
||||||
|
dnsQueries: [],
|
||||||
|
blockedFiltering: [],
|
||||||
|
replacedParental: [],
|
||||||
|
replacedSafebrowsing: [],
|
||||||
|
topBlockedDomains: [],
|
||||||
|
topClients: [],
|
||||||
|
topQueriedDomains: [],
|
||||||
|
numBlockedFiltering: 0,
|
||||||
|
numDnsQueries: 0,
|
||||||
|
numReplacedParental: 0,
|
||||||
|
numReplacedSafebrowsing: 0,
|
||||||
|
numReplacedSafesearch: 0,
|
||||||
|
avgProcessingTime: 0,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
export default stats;
|
export default stats;
|
||||||
|
|
Loading…
Reference in New Issue