diff --git a/client/src/__locales/en.json b/client/src/__locales/en.json index 4e834147..9ff11424 100644 --- a/client/src/__locales/en.json +++ b/client/src/__locales/en.json @@ -299,10 +299,11 @@ "client_edit": "Edit Client", "client_identifier": "Identifier", "ip_address": "IP address", - "client_identifier_desc": "Clients can be identified by the IP address, MAC address, CIDR. Please note, that using MAC as identifier is possible only if AdGuard Home is also a <0>DHCP server", + "client_identifier_desc": "Clients can be identified by the IP address, CIDR, MAC address. Please note, that using MAC as identifier is possible only if AdGuard Home is also a <0>DHCP server", "form_enter_ip": "Enter IP", "form_enter_mac": "Enter MAC", "form_enter_id": "Enter identifier", + "form_add_id": "Add identifier", "form_client_name": "Enter client name", "client_global_settings": "Use global settings", "client_deleted": "Client \"{{key}}\" successfully deleted", diff --git a/client/src/actions/queryLogs.js b/client/src/actions/queryLogs.js index 35b4f7af..155f0a76 100644 --- a/client/src/actions/queryLogs.js +++ b/client/src/actions/queryLogs.js @@ -2,7 +2,7 @@ import { createAction } from 'redux-actions'; import apiClient from '../api/Api'; import { addErrorToast, addSuccessToast } from './index'; -import { normalizeLogs } from '../helpers/helpers'; +import { normalizeLogs, getParamsForClientsSearch, addClientInfo } from '../helpers/helpers'; import { TABLE_DEFAULT_PAGE_SIZE } from '../helpers/constants'; const getLogsWithParams = async (config) => { @@ -10,9 +10,12 @@ const getLogsWithParams = async (config) => { const rawLogs = await apiClient.getQueryLog({ ...filter, older_than }); const { data, oldest } = rawLogs; const logs = normalizeLogs(data); + const clientsParams = getParamsForClientsSearch(logs, 'client'); + const clients = await apiClient.findClients(clientsParams); + const logsWithClientInfo = addClientInfo(logs, clients, 'client'); return { - logs, oldest, older_than, filter, ...values, + logs: logsWithClientInfo, oldest, older_than, filter, ...values, }; }; diff --git a/client/src/actions/stats.js b/client/src/actions/stats.js index d8ab5bf5..25897aab 100644 --- a/client/src/actions/stats.js +++ b/client/src/actions/stats.js @@ -2,7 +2,7 @@ import { createAction } from 'redux-actions'; import apiClient from '../api/Api'; import { addErrorToast, addSuccessToast } from './index'; -import { normalizeTopStats, secondsToMilliseconds } from '../helpers/helpers'; +import { normalizeTopStats, secondsToMilliseconds, getParamsForClientsSearch, addClientInfo } from '../helpers/helpers'; export const getStatsConfigRequest = createAction('GET_STATS_CONFIG_REQUEST'); export const getStatsConfigFailure = createAction('GET_STATS_CONFIG_FAILURE'); @@ -43,11 +43,15 @@ export const getStats = () => async (dispatch) => { dispatch(getStatsRequest()); try { const stats = await apiClient.getStats(); + const normalizedTopClients = normalizeTopStats(stats.top_clients); + const clientsParams = getParamsForClientsSearch(normalizedTopClients, 'name'); + const clients = await apiClient.findClients(clientsParams); + const topClientsWithInfo = addClientInfo(normalizedTopClients, clients, 'name'); const normalizedStats = { ...stats, top_blocked_domains: normalizeTopStats(stats.top_blocked_domains), - top_clients: normalizeTopStats(stats.top_clients), + top_clients: topClientsWithInfo, top_queried_domains: normalizeTopStats(stats.top_queried_domains), avg_processing_time: secondsToMilliseconds(stats.avg_processing_time), }; diff --git a/client/src/components/Dashboard/Clients.js b/client/src/components/Dashboard/Clients.js index ace7ed21..e83addcb 100644 --- a/client/src/components/Dashboard/Clients.js +++ b/client/src/components/Dashboard/Clients.js @@ -28,19 +28,17 @@ const countCell = dnsQueries => return ; }; -const clientCell = (clients, autoClients, t) => +const clientCell = t => function cell(row) { - const { value } = row; - return (
- {formatClientCell(value, clients, autoClients, t)} + {formatClientCell(row, t)}
); }; const Clients = ({ - t, refreshButton, topClients, subtitle, clients, autoClients, dnsQueries, + t, refreshButton, topClients, subtitle, dnsQueries, }) => ( ({ + data={topClients.map(({ name: ip, count, info }) => ({ ip, count, + info, }))} columns={[ { @@ -59,7 +58,7 @@ const Clients = ({ accessor: 'ip', sortMethod: (a, b) => parseInt(a.replace(/\./g, ''), 10) - parseInt(b.replace(/\./g, ''), 10), - Cell: clientCell(clients, autoClients, t), + Cell: clientCell(t), }, { Header: requests_count, diff --git a/client/src/components/Dashboard/index.js b/client/src/components/Dashboard/index.js index 1960a793..b4889db6 100644 --- a/client/src/components/Dashboard/index.js +++ b/client/src/components/Dashboard/index.js @@ -20,7 +20,6 @@ class Dashboard extends Component { getAllStats = () => { this.props.getStats(); this.props.getStatsConfig(); - this.props.getClients(); }; getToggleFilteringButton = () => { @@ -44,7 +43,6 @@ class Dashboard extends Component { const { dashboard, stats, t } = this.props; const dashboardProcessing = dashboard.processing || - dashboard.processingClients || stats.processingStats || stats.processingGetConfig; diff --git a/client/src/components/Logs/index.js b/client/src/components/Logs/index.js index 68b9cc61..3b52f4e4 100644 --- a/client/src/components/Logs/index.js +++ b/client/src/components/Logs/index.js @@ -31,7 +31,6 @@ class Logs extends Component { this.props.setLogsPage(TABLE_FIRST_PAGE); this.getLogs(...INITIAL_REQUEST_DATA); this.props.getFilteringStatus(); - this.props.getClients(); this.props.getLogsConfig(); } @@ -191,9 +190,9 @@ class Logs extends Component { ); }; - getClientCell = ({ original, value }) => { - const { dashboard, t } = this.props; - const { clients, autoClients } = dashboard; + getClientCell = (row) => { + const { original } = row; + const { t } = this.props; const { reason, domain } = original; const isFiltered = this.checkFiltered(reason); const isRewrite = this.checkRewrite(reason); @@ -201,7 +200,7 @@ class Logs extends Component { return (
- {formatClientCell(value, clients, autoClients, t)} + {formatClientCell(row, t)}
{isRewrite ? (
@@ -232,12 +231,11 @@ class Logs extends Component { }; renderLogs() { - const { queryLogs, dashboard, t } = this.props; - const { processingClients } = dashboard; + const { queryLogs, t } = this.props; const { processingGetLogs, processingGetConfig, logs, pages, page, } = queryLogs; - const isLoading = processingGetLogs || processingClients || processingGetConfig; + const isLoading = processingGetLogs || processingGetConfig; const columns = [ { diff --git a/client/src/helpers/formatClientCell.js b/client/src/helpers/formatClientCell.js index 30e9e99b..c5626061 100644 --- a/client/src/helpers/formatClientCell.js +++ b/client/src/helpers/formatClientCell.js @@ -1,5 +1,5 @@ import React, { Fragment } from 'react'; -import { getClientInfo, getAutoClientInfo, normalizeWhois } from './helpers'; +import { normalizeWhois } from './helpers'; import { WHOIS_ICONS } from './constants'; const getFormattedWhois = (whois, t) => { @@ -22,26 +22,29 @@ const getFormattedWhois = (whois, t) => { ); }; -export const formatClientCell = (value, clients, autoClients, t) => { - const clientInfo = getClientInfo(clients, value) || getAutoClientInfo(autoClients, value); - const { name, whois } = clientInfo; +export const formatClientCell = (row, t) => { + const { value, original: { info } } = row; let whoisContainer = ''; let nameContainer = value; - if (name) { - nameContainer = ( - - {name} ({value}) - - ); - } + if (info) { + const { name, whois } = info; - if (whois) { - whoisContainer = ( -
- {getFormattedWhois(whois, t)} -
- ); + if (name) { + nameContainer = ( + + {name} ({value}) + + ); + } + + if (whois) { + whoisContainer = ( +
+ {getFormattedWhois(whois, t)} +
+ ); + } } return ( diff --git a/client/src/helpers/helpers.js b/client/src/helpers/helpers.js index 82389111..089f0604 100644 --- a/client/src/helpers/helpers.js +++ b/client/src/helpers/helpers.js @@ -8,6 +8,7 @@ import subDays from 'date-fns/sub_days'; import round from 'lodash/round'; import axios from 'axios'; import i18n from 'i18next'; +import uniqBy from 'lodash/uniqBy'; import versionCompare from './versionCompare'; import { @@ -92,6 +93,17 @@ export const normalizeTopStats = stats => ( })) ); +export const addClientInfo = (data, clients, param) => ( + data.map((row) => { + const clientIp = row[param]; + const info = clients.find(item => item[clientIp]) || ''; + return { + ...row, + info: (info && info[clientIp]) || '', + }; + }) +); + export const normalizeFilteringStatus = (filteringStatus) => { const { enabled, filters, user_rules: userRules, interval, @@ -342,3 +354,13 @@ export const getPathWithQueryString = (path, params) => { return `${path}?${searchParams.toString()}`; }; + +export const getParamsForClientsSearch = (data, param) => { + const uniqueClients = uniqBy(data, param); + return uniqueClients + .reduce((acc, item, idx) => { + const key = `ip${idx}`; + acc[key] = item[param]; + return acc; + }, {}); +};