Files
AdGuardHome/client/src/components/Logs/Cells/index.js
Ainar Garipov 7c35d208b1 Pull request: querylog: search clients by name, enrich http resp
Updates #1273.

Squashed commit of the following:

commit 55b78153b1b775c855e759011141bbbe6d4b962c
Author: Artem Baskal <a.baskal@adguard.com>
Date:   Fri Apr 2 16:55:39 2021 +0300

    Update client_info in case of null

commit 5c80c1438ed9d961af11617831b704d6ae15cc34
Author: Ainar Garipov <A.Garipov@AdGuard.COM>
Date:   Fri Apr 2 16:24:14 2021 +0300

    querylog: always set client_info

commit b48efd64d757cc0bcf5b34de22fdd0b0464d98a6
Merge: 4ed7eab5 23c9f528
Author: Ainar Garipov <A.Garipov@AdGuard.COM>
Date:   Fri Apr 2 16:22:08 2021 +0300

    Merge branch 'master' into 1273-querylog-client-name

commit 4ed7eab52b6b5b0c0ddb5aa5a3225a62d1f9265b
Merge: dbf990eb 70d4c70e
Author: Ainar Garipov <A.Garipov@AdGuard.COM>
Date:   Fri Apr 2 12:57:17 2021 +0300

    Merge branch 'master' into 1273-querylog-client-name

commit dbf990eb881116754554270e7b691b5db8e9ee34
Author: Ainar Garipov <A.Garipov@AdGuard.COM>
Date:   Fri Apr 2 12:56:13 2021 +0300

    home: imp names

commit c2cfdef494ca26fff62b9fa008f1b389d9d4d46b
Author: Artem Baskal <a.baskal@adguard.com>
Date:   Thu Apr 1 19:26:04 2021 +0300

    Rename to whois

commit e3cc4a68ee576770b1922680155308e33bed31e8
Author: Ainar Garipov <A.Garipov@AdGuard.COM>
Date:   Thu Apr 1 19:03:42 2021 +0300

    home: imp whois more

commit 3b8ef8691c298aff35946b35923ef2e5b1f9bbbe
Author: Ainar Garipov <A.Garipov@AdGuard.COM>
Date:   Thu Apr 1 18:51:14 2021 +0300

    home: imp whois resp

commit fb97e0d74976723a512d6ff4c69e830fe59c8df8
Author: Artem Baskal <a.baskal@adguard.com>
Date:   Thu Apr 1 18:00:03 2021 +0300

    Fix client_info ids prop types

commit 298005189e372651ceff453e88aca19ee925a138
Author: Artem Baskal <a.baskal@adguard.com>
Date:   Thu Apr 1 17:58:14 2021 +0300

    Adapt changes on client

commit aa1769f64197d865478a66271da483babfc5dfd0
Author: Ainar Garipov <A.Garipov@AdGuard.COM>
Date:   Thu Apr 1 17:18:36 2021 +0300

    all: add more fields to querylog client

commit 4b2a2dbd380ec410f3068d15ea16430912e03e33
Merge: cda92c3f 2e4e2f62
Author: Ainar Garipov <A.Garipov@AdGuard.COM>
Date:   Thu Apr 1 16:57:26 2021 +0300

    Merge branch 'master' into 1273-querylog-client-name

commit cda92c3f0331cbac252f3163d31457f716bc7f2c
Author: Ainar Garipov <A.Garipov@AdGuard.COM>
Date:   Mon Mar 29 18:03:51 2021 +0300

    querylog: fix windows tests

commit 5a56f0a32608869ed93a38f18f63ea3a20f7bde2
Merge: 627e4958 e710ce11
Author: Ainar Garipov <A.Garipov@AdGuard.COM>
Date:   Mon Mar 29 17:45:53 2021 +0300

    Merge branch 'master' into 1273-querylog-client-name

commit 627e495828e82d44cc77aa393536479f23cc68b7
Author: Ainar Garipov <A.Garipov@AdGuard.COM>
Date:   Mon Mar 29 17:44:49 2021 +0300

    querylog: add tests, imp code, docs

commit 6dec468a2f0c29357875ff99458e0e8f8e580e6d
Author: Ainar Garipov <A.Garipov@AdGuard.COM>
Date:   Fri Mar 26 16:10:47 2021 +0300

    querylog: search clients by name, enrich http resp
2021-04-02 17:30:39 +03:00

259 lines
9.3 KiB
JavaScript

import React, { memo } from 'react';
import classNames from 'classnames';
import { useTranslation } from 'react-i18next';
import { shallowEqual, useDispatch, useSelector } from 'react-redux';
import propTypes from 'prop-types';
import {
captitalizeWords,
checkFiltered,
getRulesToFilterList,
formatDateTime,
formatElapsedMs,
formatTime,
getBlockingClientName,
getServiceName,
processContent,
} from '../../../helpers/helpers';
import {
BLOCK_ACTIONS,
DEFAULT_SHORT_DATE_FORMAT_OPTIONS,
FILTERED_STATUS,
FILTERED_STATUS_TO_META_MAP,
LONG_TIME_FORMAT,
QUERY_STATUS_COLORS,
SCHEME_TO_PROTOCOL_MAP,
} from '../../../helpers/constants';
import { getSourceData } from '../../../helpers/trackers/trackers';
import { toggleBlocking, toggleBlockingForClient } from '../../../actions';
import DateCell from './DateCell';
import DomainCell from './DomainCell';
import ResponseCell from './ResponseCell';
import ClientCell from './ClientCell';
import { toggleClientBlock } from '../../../actions/access';
import { getBlockClientInfo, BUTTON_PREFIX } from './helpers';
import { updateLogs } from '../../../actions/queryLogs';
import '../Logs.css';
const Row = memo(({
style,
rowProps,
rowProps: { reason },
isSmallScreen,
setDetailedDataCurrent,
setButtonType,
setModalOpened,
}) => {
const dispatch = useDispatch();
const { t } = useTranslation();
const dnssec_enabled = useSelector((state) => state.dnsConfig.dnssec_enabled);
const filters = useSelector((state) => state.filtering.filters, shallowEqual);
const whitelistFilters = useSelector((state) => state.filtering.whitelistFilters, shallowEqual);
const autoClients = useSelector((state) => state.dashboard.autoClients, shallowEqual);
const clients = useSelector((state) => state.dashboard.clients);
const onClick = () => {
if (!isSmallScreen) {
return;
}
const {
answer_dnssec,
client,
domain,
elapsedMs,
client_info,
response,
time,
tracker,
upstream,
type,
client_proto,
client_id,
rules,
originalResponse,
status,
service_name,
} = rowProps;
const hasTracker = !!tracker;
const autoClient = autoClients
.find((autoClient) => autoClient.name === client);
const source = autoClient?.source;
const formattedElapsedMs = formatElapsedMs(elapsedMs, t);
const isFiltered = checkFiltered(reason);
const isBlocked = reason === FILTERED_STATUS.FILTERED_BLACK_LIST
|| reason === FILTERED_STATUS.FILTERED_BLOCKED_SERVICE;
const buttonType = isFiltered ? BLOCK_ACTIONS.UNBLOCK : BLOCK_ACTIONS.BLOCK;
const onToggleBlock = () => {
dispatch(toggleBlocking(buttonType, domain));
};
const isBlockedByResponse = originalResponse.length > 0 && isBlocked;
const requestStatus = t(isBlockedByResponse ? 'blocked_by_cname_or_ip' : FILTERED_STATUS_TO_META_MAP[reason]?.LABEL || reason);
const protocol = t(SCHEME_TO_PROTOCOL_MAP[client_proto]) || '';
const sourceData = getSourceData(tracker);
const {
confirmMessage,
buttonKey: blockingClientKey,
isNotInAllowedList,
} = getBlockClientInfo(
client,
client_info?.disallowed || false,
client_info?.disallowed_rule || '',
);
const blockingForClientKey = isFiltered ? 'unblock_for_this_client_only' : 'block_for_this_client_only';
const clientNameBlockingFor = getBlockingClientName(clients, client);
const onBlockingForClientClick = () => {
dispatch(toggleBlockingForClient(buttonType, domain, clientNameBlockingFor));
};
const onBlockingClientClick = async () => {
if (window.confirm(confirmMessage)) {
await dispatch(
toggleClientBlock(
client,
client_info?.disallowed || false,
client_info?.disallowed_rule || '',
),
);
await dispatch(updateLogs());
setModalOpened(false);
}
};
const blockButton = <button
className={classNames('title--border text-center button-action--arrow-option', { 'bg--danger': !isBlocked })}
onClick={onToggleBlock}>
{t(buttonType)}
</button>;
const blockForClientButton = <button
className='text-center font-weight-bold py-2 button-action--arrow-option'
onClick={onBlockingForClientClick}>
{t(blockingForClientKey)}
</button>;
const blockClientButton = <button
className='text-center font-weight-bold py-2 button-action--arrow-option'
onClick={onBlockingClientClick}
disabled={isNotInAllowedList}>
{t(blockingClientKey)}
</button>;
const detailedData = {
time_table_header: formatTime(time, LONG_TIME_FORMAT),
date: formatDateTime(time, DEFAULT_SHORT_DATE_FORMAT_OPTIONS),
encryption_status: isBlocked
? <div className="bg--danger">{requestStatus}</div> : requestStatus,
...(FILTERED_STATUS.FILTERED_BLOCKED_SERVICE && service_name
&& { service_name: getServiceName(service_name) }),
domain,
type_table_header: type,
protocol,
known_tracker: hasTracker && 'title',
table_name: tracker?.name,
category_label: hasTracker && captitalizeWords(tracker.category),
tracker_source: hasTracker && sourceData
&& <a
href={sourceData.url}
target="_blank"
rel="noopener noreferrer"
className="link--green">{sourceData.name}
</a>,
response_details: 'title',
install_settings_dns: upstream,
elapsed: formattedElapsedMs,
...(rules.length > 0
&& { rule_label: getRulesToFilterList(rules, filters, whitelistFilters) }
),
response_table_header: response?.join('\n'),
response_code: status,
client_details: 'title',
ip_address: client,
name: client_info?.name || client_id,
country: client_info?.whois?.country,
city: client_info?.whois?.city,
network: client_info?.whois?.orgname,
source_label: source,
validated_with_dnssec: dnssec_enabled ? Boolean(answer_dnssec) : false,
original_response: originalResponse?.join('\n'),
[BUTTON_PREFIX + buttonType]: blockButton,
[BUTTON_PREFIX + blockingForClientKey]: blockForClientButton,
[BUTTON_PREFIX + blockingClientKey]: blockClientButton,
};
setDetailedDataCurrent(processContent(detailedData));
setButtonType(buttonType);
setModalOpened(true);
};
const isDetailed = useSelector((state) => state.queryLogs.isDetailed);
const className = classNames('d-flex px-5 logs__row',
`logs__row--${FILTERED_STATUS_TO_META_MAP?.[reason]?.COLOR ?? QUERY_STATUS_COLORS.WHITE}`, {
'logs__cell--detailed': isDetailed,
});
return <div style={style} className={className} onClick={onClick} role="row">
<DateCell {...rowProps} />
<DomainCell {...rowProps} />
<ResponseCell {...rowProps} />
<ClientCell {...rowProps} />
</div>;
});
Row.displayName = 'Row';
Row.propTypes = {
style: propTypes.object,
rowProps: propTypes.shape({
reason: propTypes.string.isRequired,
answer_dnssec: propTypes.bool.isRequired,
client: propTypes.string.isRequired,
domain: propTypes.string.isRequired,
elapsedMs: propTypes.string.isRequired,
response: propTypes.array.isRequired,
time: propTypes.string.isRequired,
tracker: propTypes.object,
upstream: propTypes.string.isRequired,
type: propTypes.string.isRequired,
client_proto: propTypes.string.isRequired,
client_id: propTypes.string,
client_info: propTypes.shape({
ids: propTypes.arrayOf(propTypes.string).isRequired,
name: propTypes.string.isRequired,
whois: propTypes.shape({
country: propTypes.string,
city: propTypes.string,
orgname: propTypes.string,
}).isRequired,
disallowed: propTypes.bool.isRequired,
disallowed_rule: propTypes.string.isRequired,
}),
rules: propTypes.arrayOf(propTypes.shape({
text: propTypes.string.isRequired,
filter_list_id: propTypes.number.isRequired,
})),
originalResponse: propTypes.array,
status: propTypes.string.isRequired,
service_name: propTypes.string,
}).isRequired,
isSmallScreen: propTypes.bool.isRequired,
setDetailedDataCurrent: propTypes.func.isRequired,
setButtonType: propTypes.func.isRequired,
setModalOpened: propTypes.func.isRequired,
};
export default Row;