diff --git a/client/src/__locales/en.json b/client/src/__locales/en.json
index 6ba40384..bbfe2336 100644
--- a/client/src/__locales/en.json
+++ b/client/src/__locales/en.json
@@ -286,7 +286,9 @@
"client_deleted": "Client \"{{key}}\" successfully deleted",
"client_added": "Client \"{{key}}\" successfully added",
"client_updated": "Client \"{{key}}\" successfully updated",
- "table_statistics": "Statistics (last 24 hours)",
+ "table_statistics": "Requests count (last 24 hours)",
"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}}\"?",
+ "auto_clients_title": "Clients (runtime)",
+ "auto_clients_desc": "Data on the clients that use AdGuard Home, but not stored in the configuration"
}
\ No newline at end of file
diff --git a/client/src/actions/index.js b/client/src/actions/index.js
index 493a4140..39224388 100644
--- a/client/src/actions/index.js
+++ b/client/src/actions/index.js
@@ -213,9 +213,14 @@ export const getClientsSuccess = createAction('GET_CLIENTS_SUCCESS');
export const getClients = () => async (dispatch) => {
dispatch(getClientsRequest());
try {
- const clients = await apiClient.getClients();
- const sortedClients = sortClients(clients);
- dispatch(getClientsSuccess(sortedClients));
+ const data = await apiClient.getClients();
+ const sortedClients = data.clients && sortClients(data.clients);
+ const sortedAutoClients = data.auto_clients && sortClients(data.auto_clients);
+
+ dispatch(getClientsSuccess({
+ clients: sortedClients || [],
+ autoClients: sortedAutoClients || [],
+ }));
} catch (error) {
dispatch(addErrorToast({ error }));
dispatch(getClientsFailure());
diff --git a/client/src/components/Dashboard/Clients.js b/client/src/components/Dashboard/Clients.js
index d7081e38..fbef279e 100644
--- a/client/src/components/Dashboard/Clients.js
+++ b/client/src/components/Dashboard/Clients.js
@@ -24,7 +24,8 @@ class Clients extends Component {
Header: 'IP',
accessor: 'ip',
Cell: ({ value }) => {
- const clientName = getClientName(this.props.clients, value);
+ const clientName = getClientName(this.props.clients, value)
+ || getClientName(this.props.autoClients, value);
let client;
if (clientName) {
@@ -79,6 +80,7 @@ Clients.propTypes = {
dnsQueries: PropTypes.number.isRequired,
refreshButton: PropTypes.node.isRequired,
clients: PropTypes.array.isRequired,
+ autoClients: PropTypes.array.isRequired,
t: PropTypes.func,
};
diff --git a/client/src/components/Dashboard/index.js b/client/src/components/Dashboard/index.js
index 84215d56..51ede02c 100644
--- a/client/src/components/Dashboard/index.js
+++ b/client/src/components/Dashboard/index.js
@@ -96,6 +96,7 @@ class Dashboard extends Component {
refreshButton={refreshButton}
topClients={dashboard.topStats.top_clients}
clients={dashboard.clients}
+ autoClients={dashboard.autoClients}
/>
diff --git a/client/src/components/Logs/index.js b/client/src/components/Logs/index.js
index 7791025a..beae9e53 100644
--- a/client/src/components/Logs/index.js
+++ b/client/src/components/Logs/index.js
@@ -196,7 +196,8 @@ 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);
+ const clientName = getClientName(dashboard.clients, row.value)
+ || getClientName(dashboard.autoClients, row.value);
let client;
if (clientName) {
diff --git a/client/src/components/Settings/Clients/AutoClients.js b/client/src/components/Settings/Clients/AutoClients.js
new file mode 100644
index 00000000..1f750909
--- /dev/null
+++ b/client/src/components/Settings/Clients/AutoClients.js
@@ -0,0 +1,131 @@
+import React, { Component } from 'react';
+import PropTypes from 'prop-types';
+import { withNamespaces } from 'react-i18next';
+import ReactTable from 'react-table';
+
+import { CLIENT_ID } from '../../../helpers/constants';
+import Card from '../../ui/Card';
+
+class AutoClients extends Component {
+ getClient = (name, clients) => {
+ const client = clients.find(item => name === item.name);
+
+ if (client) {
+ const identifier = client.mac ? CLIENT_ID.MAC : CLIENT_ID.IP;
+
+ return {
+ identifier,
+ use_global_settings: true,
+ ...client,
+ };
+ }
+
+ return {
+ identifier: 'ip',
+ use_global_settings: true,
+ };
+ };
+
+ getStats = (ip, stats) => {
+ if (stats && stats.top_clients) {
+ return stats.top_clients[ip];
+ }
+
+ return '';
+ };
+
+ columns = [
+ {
+ Header: this.props.t('table_client'),
+ accessor: 'ip',
+ Cell: (row) => {
+ if (row.value) {
+ return (
+
+
+ {row.value} (IP)
+
+
+ );
+ } else if (row.original && row.original.mac) {
+ return (
+
+
+ {row.original.mac} (MAC)
+
+
+ );
+ }
+
+ return '';
+ },
+ },
+ {
+ Header: this.props.t('table_name'),
+ accessor: 'name',
+ Cell: ({ value }) => (
+
+
+ {value}
+
+
+ ),
+ },
+ {
+ Header: this.props.t('table_statistics'),
+ accessor: 'statistics',
+ Cell: (row) => {
+ const clientIP = row.original.ip;
+ const clientStats = clientIP && this.getStats(clientIP, this.props.topStats);
+
+ if (clientStats) {
+ return (
+
+ );
+ }
+
+ return '–';
+ },
+ },
+ ];
+
+ render() {
+ const { t, autoClients } = this.props;
+
+ return (
+
+
+
+ );
+ }
+}
+
+AutoClients.propTypes = {
+ t: PropTypes.func.isRequired,
+ autoClients: PropTypes.array.isRequired,
+ topStats: PropTypes.object.isRequired,
+};
+
+export default withNamespaces()(AutoClients);
diff --git a/client/src/components/Settings/index.js b/client/src/components/Settings/index.js
index 9292efb3..3435e610 100644
--- a/client/src/components/Settings/index.js
+++ b/client/src/components/Settings/index.js
@@ -1,14 +1,17 @@
import React, { Component, Fragment } from 'react';
import PropTypes from 'prop-types';
import { withNamespaces, Trans } from 'react-i18next';
+
import Upstream from './Upstream';
import Dhcp from './Dhcp';
import Encryption from './Encryption';
import Clients from './Clients';
+import AutoClients from './Clients/AutoClients';
import Checkbox from '../ui/Checkbox';
import Loading from '../ui/Loading';
import PageTitle from '../ui/PageTitle';
import Card from '../ui/Card';
+
import './Settings.css';
class Settings extends Component {
@@ -93,20 +96,26 @@ class Settings extends Component {
processingSetUpstream={settings.processingSetUpstream}
/>
{!dashboard.processingTopStats && !dashboard.processingClients && (
-
+
+
+
+
)}
{
const newState = {
...state,
- clients: payload,
+ clients: payload.clients,
+ autoClients: payload.autoClients,
processingClients: false,
};
return newState;
@@ -210,6 +211,7 @@ const dashboard = handleActions({
dnsAddresses: [],
dnsVersion: '',
clients: [],
+ autoClients: [],
topStats: [],
});