+ client: add icons to the whois info

This commit is contained in:
Ildar Kamalov 2019-09-24 15:28:59 +03:00 committed by Simon Zolin
parent fcf37da312
commit ba62d42949
13 changed files with 192 additions and 102 deletions

View File

@ -393,8 +393,9 @@
"sign_out": "Sign out", "sign_out": "Sign out",
"forgot_password": "Forgot password?", "forgot_password": "Forgot password?",
"forgot_password_desc": "Please follow <0>these steps</0> to create a new password for your user account.", "forgot_password_desc": "Please follow <0>these steps</0> to create a new password for your user account.",
"city": "<0>City:</0> {{value}}", "location": "Location",
"country": "<0>Country:</0> {{value}}", "orgname": "Organisation name",
"orgname": "<0>OrgName:</0> {{value}}", "netname": "Network name",
"descr": "Description",
"whois": "Whois" "whois": "Whois"
} }

View File

@ -28,13 +28,13 @@ const countCell = dnsQueries =>
return <Cell value={value} percent={percent} color={percentColor} />; return <Cell value={value} percent={percent} color={percentColor} />;
}; };
const clientCell = (clients, autoClients) => const clientCell = (clients, autoClients, t) =>
function cell(row) { function cell(row) {
const { value } = row; const { value } = row;
return ( return (
<div className="logs__row logs__row--overflow logs__row--column"> <div className="logs__row logs__row--overflow logs__row--column">
{formatClientCell(value, clients, autoClients)} {formatClientCell(value, clients, autoClients, t)}
</div> </div>
); );
}; };
@ -59,11 +59,13 @@ const Clients = ({
accessor: 'ip', accessor: 'ip',
sortMethod: (a, b) => sortMethod: (a, b) =>
parseInt(a.replace(/\./g, ''), 10) - parseInt(b.replace(/\./g, ''), 10), parseInt(a.replace(/\./g, ''), 10) - parseInt(b.replace(/\./g, ''), 10),
Cell: clientCell(clients, autoClients), Cell: clientCell(clients, autoClients, t),
}, },
{ {
Header: <Trans>requests_count</Trans>, Header: <Trans>requests_count</Trans>,
accessor: 'count', accessor: 'count',
minWidth: 180,
maxWidth: 200,
Cell: countCell(dnsQueries), Cell: countCell(dnsQueries),
}, },
]} ]}

View File

@ -129,3 +129,25 @@
white-space: nowrap; white-space: nowrap;
overflow: hidden; overflow: hidden;
} }
.logs__whois {
display: inline;
}
.logs__whois::after {
content: "|";
padding: 0 5px;
}
.logs__whois:last-child::after {
content: "";
}
.logs__whois-icon.icons {
position: relative;
top: -2px;
width: 16px;
height: 16px;
margin-right: 2px;
opacity: 0.6;
}

View File

@ -191,7 +191,7 @@ class Logs extends Component {
}; };
getClientCell = ({ original, value }) => { getClientCell = ({ original, value }) => {
const { dashboard } = this.props; const { dashboard, t } = this.props;
const { clients, autoClients } = dashboard; const { clients, autoClients } = dashboard;
const { reason, domain } = original; const { reason, domain } = original;
const isFiltered = this.checkFiltered(reason); const isFiltered = this.checkFiltered(reason);
@ -200,7 +200,7 @@ class Logs extends Component {
return ( return (
<Fragment> <Fragment>
<div className="logs__row logs__row--overflow logs__row--column"> <div className="logs__row logs__row--overflow logs__row--column">
{formatClientCell(value, clients, autoClients)} {formatClientCell(value, clients, autoClients, t)}
</div> </div>
{isRewrite ? ( {isRewrite ? (
<div className="logs__action"> <div className="logs__action">

View File

@ -4,8 +4,8 @@ import { withNamespaces } from 'react-i18next';
import ReactTable from 'react-table'; import ReactTable from 'react-table';
import Card from '../../ui/Card'; import Card from '../../ui/Card';
import WhoisCell from './WhoisCell'; import whoisCell from './whoisCell';
import WrapCell from './WrapCell'; import wrapCell from './wrapCell';
class AutoClients extends Component { class AutoClients extends Component {
getStats = (ip, stats) => { getStats = (ip, stats) => {
@ -21,26 +21,31 @@ class AutoClients extends Component {
{ {
Header: this.props.t('table_client'), Header: this.props.t('table_client'),
accessor: 'ip', accessor: 'ip',
Cell: WrapCell, minWidth: 200,
Cell: wrapCell,
}, },
{ {
Header: this.props.t('table_name'), Header: this.props.t('table_name'),
accessor: 'name', accessor: 'name',
Cell: WrapCell, minWidth: 200,
Cell: wrapCell,
}, },
{ {
Header: this.props.t('source_label'), Header: this.props.t('source_label'),
accessor: 'source', accessor: 'source',
Cell: WrapCell, minWidth: 200,
Cell: wrapCell,
}, },
{ {
Header: this.props.t('whois'), Header: this.props.t('whois'),
accessor: 'whois_info', accessor: 'whois_info',
Cell: WhoisCell, minWidth: 200,
Cell: whoisCell(this.props.t),
}, },
{ {
Header: this.props.t('requests_count'), Header: this.props.t('requests_count'),
accessor: 'statistics', accessor: 'statistics',
minWidth: 200,
Cell: (row) => { Cell: (row) => {
const clientIP = row.original.ip; const clientIP = row.original.ip;
const clientStats = clientIP && this.getStats(clientIP, this.props.topClients); const clientStats = clientIP && this.getStats(clientIP, this.props.topClients);

View File

@ -6,8 +6,8 @@ import ReactTable from 'react-table';
import { MODAL_TYPE, CLIENT_ID } from '../../../helpers/constants'; import { MODAL_TYPE, CLIENT_ID } from '../../../helpers/constants';
import Card from '../../ui/Card'; import Card from '../../ui/Card';
import Modal from './Modal'; import Modal from './Modal';
import WrapCell from './WrapCell'; import wrapCell from './wrapCell';
import WhoisCell from './WhoisCell'; import whoisCell from './whoisCell';
class ClientsTable extends Component { class ClientsTable extends Component {
handleFormAdd = (values) => { handleFormAdd = (values) => {
@ -103,7 +103,7 @@ class ClientsTable extends Component {
Header: this.props.t('table_name'), Header: this.props.t('table_name'),
accessor: 'name', accessor: 'name',
minWidth: 120, minWidth: 120,
Cell: WrapCell, Cell: wrapCell,
}, },
{ {
Header: this.props.t('settings'), Header: this.props.t('settings'),
@ -138,11 +138,17 @@ class ClientsTable extends Component {
return ( return (
<div className="logs__row logs__row--icons"> <div className="logs__row logs__row--icons">
{value && value.length > 0 ? value.map(service => ( {value && value.length > 0
<svg className="service__icon service__icon--table" title={service} key={service}> ? value.map(service => (
<use xlinkHref={`#service_${service}`} /> <svg
</svg> className="service__icon service__icon--table"
)) : ''} title={service}
key={service}
>
<use xlinkHref={`#service_${service}`} />
</svg>
))
: ''}
</div> </div>
); );
}, },
@ -151,7 +157,7 @@ class ClientsTable extends Component {
Header: this.props.t('whois'), Header: this.props.t('whois'),
accessor: 'whois_info', accessor: 'whois_info',
minWidth: 200, minWidth: 200,
Cell: WhoisCell, Cell: whoisCell(this.props.t),
}, },
{ {
Header: this.props.t('requests_count'), Header: this.props.t('requests_count'),

View File

@ -1,38 +0,0 @@
import React from 'react';
import PropTypes from 'prop-types';
import { Trans } from 'react-i18next';
const getFormattedWhois = (value) => {
const keys = Object.keys(value);
if (keys.length > 0) {
return (
keys.map(key => (
<div key={key} title={value[key]}>
<Trans
values={{ value: value[key] }}
components={[<small key="0">text</small>]}
>
{key}
</Trans>
</div>
))
);
}
return '';
};
const WhoisCell = ({ value }) => (
<div className="logs__row logs__row--overflow">
<span className="logs__text logs__text--wrap">
{getFormattedWhois(value)}
</span>
</div>
);
WhoisCell.propTypes = {
value: PropTypes.object.isRequired,
};
export default WhoisCell;

View File

@ -0,0 +1,43 @@
import React, { Fragment } from 'react';
import { normalizeWhois } from '../../../helpers/helpers';
import { WHOIS_ICONS } from '../../../helpers/constants';
const getFormattedWhois = (value, t) => {
const whoisInfo = normalizeWhois(value);
const whoisKeys = Object.keys(whoisInfo);
if (whoisKeys.length > 0) {
return whoisKeys.map((key) => {
const icon = WHOIS_ICONS[key];
return (
<div key={key} title={t(key)}>
{icon && (
<Fragment>
<svg className="logs__whois-icon text-muted-dark icons">
<use xlinkHref={`#${icon}`} />
</svg>
&nbsp;
</Fragment>
)}
{whoisInfo[key]}
</div>
);
});
}
return '';
};
const whoisCell = t =>
function cell(row) {
const { value } = row;
return (
<div className="logs__row logs__row--overflow">
<span className="logs__text logs__text--wrap">{getFormattedWhois(value, t)}</span>
</div>
);
};
export default whoisCell;

View File

@ -1,7 +1,7 @@
import React from 'react'; import React from 'react';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
const WrapCell = ({ value }) => ( const wrapCell = ({ value }) => (
<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}>
{value || ''} {value || ''}
@ -9,11 +9,8 @@ const WrapCell = ({ value }) => (
</div> </div>
); );
WrapCell.propTypes = { wrapCell.propTypes = {
value: PropTypes.oneOfType([ value: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
PropTypes.string,
PropTypes.number,
]),
}; };
export default WrapCell; export default wrapCell;

View File

@ -131,6 +131,18 @@ const Icons = () => (
<symbol id="question" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeLinecap="round" strokeLinejoin="round" strokeWidth="2"> <symbol id="question" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeLinecap="round" strokeLinejoin="round" strokeWidth="2">
<circle cx="12" cy="12" r="10" /><path d="M9.09 9a3 3 0 0 1 5.83 1c0 2-3 3-3 3" /><line x1="12" y1="17" x2="12" y2="17" /> <circle cx="12" cy="12" r="10" /><path d="M9.09 9a3 3 0 0 1 5.83 1c0 2-3 3-3 3" /><line x1="12" y1="17" x2="12" y2="17" />
</symbol> </symbol>
<symbol id="question" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeLinecap="round" strokeLinejoin="round" strokeWidth="2">
<circle cx="12" cy="12" r="10" /><path d="M9.09 9a3 3 0 0 1 5.83 1c0 2-3 3-3 3" /><line x1="12" y1="17" x2="12" y2="17" />
</symbol>
<symbol id="network" viewBox="0 0 50 50" fill="currentColor" strokeLinecap="round" strokeLinejoin="round">
<path d="M 25 7 C 15.941406 7 7.339844 10.472656 0.78125 16.773438 L 0.0625 17.464844 L 5.59375 23.230469 L 6.320313 22.539063 C 11.378906 17.679688 18.015625 15 25 15 C 31.984375 15 38.621094 17.679688 43.683594 22.539063 L 44.40625 23.230469 L 49.941406 17.464844 L 49.21875 16.769531 C 42.660156 10.46875 34.058594 7 25 7 Z M 25 19 C 19.046875 19 13.394531 21.28125 9.085938 25.421875 L 8.363281 26.113281 L 13.921875 31.90625 L 14.644531 31.210938 C 17.464844 28.496094 21.144531 27 25 27 C 28.855469 27 32.535156 28.496094 35.355469 31.210938 L 36.078125 31.90625 L 41.636719 26.113281 L 40.917969 25.421875 C 36.605469 21.28125 30.953125 19 25 19 Z M 25 31 C 22.15625 31 19.453125 32.089844 17.390625 34.074219 L 16.671875 34.765625 L 25 43.441406 L 33.328125 34.765625 L 32.609375 34.074219 C 30.546875 32.089844 27.84375 31 25 31 Z"/>
</symbol>
<symbol id="location" viewBox="0 0 24 24" fill="currentColor" strokeLinecap="round" strokeLinejoin="round">
<path d="M12,2C8.134,2,5,5.134,5,9c0,5,7,13,7,13s7-8,7-13C19,5.134,15.866,2,12,2z M12,11.5c-1.381,0-2.5-1.119-2.5-2.5 c0-1.381,1.119-2.5,2.5-2.5s2.5,1.119,2.5,2.5C14.5,10.381,13.381,11.5,12,11.5z"/>
</symbol>
</svg> </svg>
); );

View File

@ -267,3 +267,10 @@ export const STATS_INTERVALS_DAYS = [1, 7, 30, 90];
export const QUERY_LOG_INTERVALS_DAYS = [1, 7, 30, 90]; export const QUERY_LOG_INTERVALS_DAYS = [1, 7, 30, 90];
export const FILTERS_INTERVALS_HOURS = [0, 1, 12, 24, 72, 168]; export const FILTERS_INTERVALS_HOURS = [0, 1, 12, 24, 72, 168];
export const WHOIS_ICONS = {
location: 'location',
orgname: 'network',
netname: 'network',
descr: '',
};

View File

@ -1,32 +1,55 @@
import React, { Fragment } from 'react'; import React, { Fragment } from 'react';
import { getClientInfo } from './helpers'; import { getClientInfo, normalizeWhois } from './helpers';
import { WHOIS_ICONS } from './constants';
export const formatClientCell = (value, clients, autoClients) => { const getFormattedWhois = (whois, t) => {
const whoisInfo = normalizeWhois(whois);
return (
Object.keys(whoisInfo).map((key) => {
const icon = WHOIS_ICONS[key];
return (
<span className="logs__whois text-muted" key={key} title={t(key)}>
{icon && (
<Fragment>
<svg className="logs__whois-icon icons">
<use xlinkHref={`#${icon}`} />
</svg>&nbsp;
</Fragment>
)}{whoisInfo[key]}
</span>
);
})
);
};
export const formatClientCell = (value, clients, autoClients, t) => {
const clientInfo = getClientInfo(clients, value) || getClientInfo(autoClients, value); const clientInfo = getClientInfo(clients, value) || getClientInfo(autoClients, value);
const { name, whois } = clientInfo; const { name, whois } = clientInfo;
let whoisContainer = '';
let nameContainer = value;
if (whois && name) { if (name) {
return ( nameContainer = (
<Fragment>
<div className="logs__text logs__text--wrap" title={`${name} (${value})`}>
{name} <small className="text-muted-dark">({value})</small>
</div>
<div className="logs__text logs__text--wrap" title={whois}>
<small className="text-muted">{whois}</small>
</div>
</Fragment>
);
} else if (name) {
return (
<span className="logs__text logs__text--wrap" title={`${name} (${value})`}> <span className="logs__text logs__text--wrap" title={`${name} (${value})`}>
{name} <small>({value})</small> {name} <small>({value})</small>
</span> </span>
); );
} }
if (whois) {
whoisContainer = (
<div className="logs__text logs__text--wrap mt-1">
{getFormattedWhois(whois, t)}
</div>
);
}
return ( return (
<span className="logs__text" title={value}> <span className="logs__text">
{value} <Fragment>
{nameContainer}
{whoisContainer}
</Fragment>
</span> </span>
); );
}; };

View File

@ -245,21 +245,6 @@ export const redirectToCurrentProtocol = (values, httpPort = 80) => {
export const normalizeTextarea = text => text && text.replace(/[;, ]/g, '\n').split('\n').filter(n => n); export const normalizeTextarea = text => text && text.replace(/[;, ]/g, '\n').split('\n').filter(n => n);
const formatWhois = (whois) => {
if (!whois) {
return '';
}
const keys = Object.keys(whois);
if (keys.length > 0) {
return (
keys.map(key => whois[key])
);
}
return '';
};
export const getClientInfo = (clients, ip) => { export const getClientInfo = (clients, ip) => {
const client = clients.find(item => ip === item.ip); const client = clients.find(item => ip === item.ip);
@ -268,8 +253,7 @@ export const getClientInfo = (clients, ip) => {
} }
const { name, whois_info } = client; const { name, whois_info } = client;
const formattedWhois = formatWhois(whois_info); const whois = Object.keys(whois_info).length > 0 ? whois_info : '';
const whois = formattedWhois && formattedWhois.length > 0 && formattedWhois.join(' | ');
return { name, whois }; return { name, whois };
}; };
@ -308,3 +292,29 @@ export const normalizeRulesTextarea = text => text && text.replace(/^\n/g, '').r
export const isVersionGreater = (currentVersion, previousVersion) => ( export const isVersionGreater = (currentVersion, previousVersion) => (
versionCompare(currentVersion, previousVersion) === -1 versionCompare(currentVersion, previousVersion) === -1
); );
export const normalizeWhois = (whois) => {
if (Object.keys(whois).length > 0) {
const {
city, country, ...values
} = whois;
let location = (country && country) || '';
if (city && location) {
location = `${location}, ${city}`;
} else if (city) {
location = city;
}
if (location) {
return {
location,
...values,
};
}
return { ...values };
}
return whois;
};