diff --git a/client/src/actions/index.js b/client/src/actions/index.js index ad63ed16..aa8626a9 100644 --- a/client/src/actions/index.js +++ b/client/src/actions/index.js @@ -665,3 +665,18 @@ export const toggleDhcp = config => async (dispatch) => { } } }; + +export const getClientsRequest = createAction('GET_CLIENTS_REQUEST'); +export const getClientsFailure = createAction('GET_CLIENTS_FAILURE'); +export const getClientsSuccess = createAction('GET_CLIENTS_SUCCESS'); + +export const getClients = () => async (dispatch) => { + dispatch(getClientsRequest()); + try { + const clients = await apiClient.getGlobalClients(); + dispatch(getClientsSuccess(clients)); + } catch (error) { + dispatch(addErrorToast({ error })); + dispatch(getClientsFailure()); + } +}; diff --git a/client/src/api/Api.js b/client/src/api/Api.js index 17df1221..e16a419e 100644 --- a/client/src/api/Api.js +++ b/client/src/api/Api.js @@ -39,6 +39,7 @@ export default class Api { GLOBAL_VERSION = { path: 'version.json', method: 'GET' }; GLOBAL_ENABLE_PROTECTION = { path: 'enable_protection', method: 'POST' }; GLOBAL_DISABLE_PROTECTION = { path: 'disable_protection', method: 'POST' }; + GLOBAL_CLIENTS = { path: 'clients', method: 'GET' } restartGlobalFiltering() { const { path, method } = this.GLOBAL_RESTART; @@ -139,6 +140,11 @@ export default class Api { return this.makeRequest(path, method); } + getGlobalClients() { + const { path, method } = this.GLOBAL_CLIENTS; + return this.makeRequest(path, method); + } + // Filtering FILTERING_STATUS = { path: 'filtering/status', method: 'GET' }; FILTERING_ENABLE = { path: 'filtering/enable', method: 'POST' }; diff --git a/client/src/components/App/index.js b/client/src/components/App/index.js index a2676dd0..fdc4cc2a 100644 --- a/client/src/components/App/index.js +++ b/client/src/components/App/index.js @@ -26,6 +26,7 @@ class App extends Component { componentDidMount() { this.props.getDnsStatus(); this.props.getVersion(); + this.props.getClients(); } componentDidUpdate(prevProps) { @@ -108,6 +109,7 @@ App.propTypes = { getVersion: PropTypes.func, changeLanguage: PropTypes.func, encryption: PropTypes.object, + getClients: PropTypes.func, }; export default withNamespaces()(App); diff --git a/client/src/components/Dashboard/Clients.js b/client/src/components/Dashboard/Clients.js index bd9bb4c4..d7081e38 100644 --- a/client/src/components/Dashboard/Clients.js +++ b/client/src/components/Dashboard/Clients.js @@ -7,7 +7,7 @@ import { Trans, withNamespaces } from 'react-i18next'; import Card from '../ui/Card'; import Cell from '../ui/Cell'; -import { getPercent } from '../../helpers/helpers'; +import { getPercent, getClientName } from '../../helpers/helpers'; import { STATUS_COLORS } from '../../helpers/constants'; class Clients extends Component { @@ -23,7 +23,24 @@ class Clients extends Component { columns = [{ Header: 'IP', accessor: 'ip', - Cell: ({ value }) => (
{value}
), + Cell: ({ value }) => { + const clientName = getClientName(this.props.clients, value); + let client; + + if (clientName) { + client = {clientName} ({value}); + } else { + client = value; + } + + return ( +
+ + {client} + +
+ ); + }, sortMethod: (a, b) => parseInt(a.replace(/\./g, ''), 10) - parseInt(b.replace(/\./g, ''), 10), }, { Header: requests_count, @@ -61,6 +78,7 @@ Clients.propTypes = { topClients: PropTypes.object.isRequired, dnsQueries: PropTypes.number.isRequired, refreshButton: PropTypes.node.isRequired, + clients: PropTypes.array.isRequired, t: PropTypes.func, }; diff --git a/client/src/components/Dashboard/index.js b/client/src/components/Dashboard/index.js index dbd9901c..84215d56 100644 --- a/client/src/components/Dashboard/index.js +++ b/client/src/components/Dashboard/index.js @@ -46,6 +46,7 @@ class Dashboard extends Component { dashboard.processing || dashboard.processingStats || dashboard.processingStatsHistory || + dashboard.processingClients || dashboard.processingTopStats; const refreshFullButton = ; @@ -94,6 +95,7 @@ class Dashboard extends Component { dnsQueries={dashboard.stats.dns_queries} refreshButton={refreshButton} topClients={dashboard.topStats.top_clients} + clients={dashboard.clients} />
diff --git a/client/src/components/Logs/index.js b/client/src/components/Logs/index.js index 2499c1f1..7791025a 100644 --- a/client/src/components/Logs/index.js +++ b/client/src/components/Logs/index.js @@ -6,7 +6,7 @@ import escapeRegExp from 'lodash/escapeRegExp'; import endsWith from 'lodash/endsWith'; import { Trans, withNamespaces } from 'react-i18next'; -import { formatTime } from '../../helpers/helpers'; +import { formatTime, getClientName } from '../../helpers/helpers'; import { getTrackerData } from '../../helpers/trackers/trackers'; import PageTitle from '../ui/PageTitle'; import Card from '../ui/Card'; @@ -86,7 +86,7 @@ class Logs extends Component { } renderLogs(logs) { - const { t } = this.props; + const { t, dashboard } = this.props; const columns = [{ Header: t('time_table_header'), accessor: 'time', @@ -196,11 +196,19 @@ class Logs extends Component { Cell: (row) => { const { reason } = row.original; const isFiltered = row ? reason.indexOf('Filtered') === 0 : false; + const clientName = getClientName(dashboard.clients, row.value); + let client; + + if (clientName) { + client = {clientName} ({row.value}); + } else { + client = row.value; + } return (
- {row.value} + {client}
{this.renderBlockingButton(isFiltered, row.original.domain)}
@@ -315,9 +323,18 @@ class Logs extends Component {
- {queryLogEnabled && queryLogs.getLogsProcessing && } - {queryLogEnabled && !queryLogs.getLogsProcessing && - this.renderLogs(queryLogs.logs)} + { + queryLogEnabled + && queryLogs.getLogsProcessing + && dashboard.processingClients + && + } + { + queryLogEnabled + && !queryLogs.getLogsProcessing + && !dashboard.processingClients + && this.renderLogs(queryLogs.logs) + } ); diff --git a/client/src/helpers/helpers.js b/client/src/helpers/helpers.js index fbfa3b23..d3128f5d 100644 --- a/client/src/helpers/helpers.js +++ b/client/src/helpers/helpers.js @@ -203,3 +203,8 @@ export const redirectToCurrentProtocol = (values, httpPort = 80) => { }; export const normalizeTextarea = text => text && text.replace(/[;, ]/g, '\n').split('\n').filter(n => n); + +export const getClientName = (clients, ip) => { + const client = clients.find(item => ip === item.ip); + return (client && client.name) || ''; +}; diff --git a/client/src/reducers/index.js b/client/src/reducers/index.js index a6daef82..404679eb 100644 --- a/client/src/reducers/index.js +++ b/client/src/reducers/index.js @@ -167,6 +167,17 @@ const dashboard = handleActions({ const newState = { ...state, language: payload }; return newState; }, + + [actions.getClientsRequest]: state => ({ ...state, processingClients: true }), + [actions.getClientsFailure]: state => ({ ...state, processingClients: false }), + [actions.getClientsSuccess]: (state, { payload }) => { + const newState = { + ...state, + clients: payload, + processingClients: false, + }; + return newState; + }, }, { processing: true, isCoreRunning: false, @@ -175,6 +186,7 @@ const dashboard = handleActions({ logStatusProcessing: false, processingVersion: true, processingFiltering: true, + processingClients: true, upstreamDns: '', bootstrapDns: '', allServers: false, @@ -184,6 +196,7 @@ const dashboard = handleActions({ dnsPort: 53, dnsAddresses: [], dnsVersion: '', + clients: [], }); const queryLogs = handleActions({