mirror of
https://github.com/Lissy93/web-check.git
synced 2025-02-17 02:40:46 +01:00
Adds new Result cards, and updates params in old ones
This commit is contained in:
parent
4005b9c49d
commit
c9c462f2a1
49
src/components/Results/CarbonFootprint.tsx
Normal file
49
src/components/Results/CarbonFootprint.tsx
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
import { useEffect, useState } from 'react';
|
||||||
|
import styled from 'styled-components';
|
||||||
|
import { Card } from 'components/Form/Card';
|
||||||
|
import Row from 'components/Form/Row';
|
||||||
|
import colors from 'styles/colors';
|
||||||
|
|
||||||
|
const LearnMoreInfo = styled.p`
|
||||||
|
font-size: 0.8rem;
|
||||||
|
margin-top: 0.5rem;
|
||||||
|
opacity: 0.75;
|
||||||
|
a { color: ${colors.primary}; }
|
||||||
|
`;
|
||||||
|
|
||||||
|
const CarbonCard = (props: { data: any, title: string, actionButtons: any }): JSX.Element => {
|
||||||
|
const carbons = props.data.statistics;
|
||||||
|
const initialUrl = props.data.scanUrl;
|
||||||
|
|
||||||
|
const [carbonData, setCarbonData] = useState<{c?: number, p?: number}>({});
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const fetchCarbonData = async () => {
|
||||||
|
try {
|
||||||
|
const response = await fetch(`https://api.websitecarbon.com/b?url=${encodeURIComponent(initialUrl)}`);
|
||||||
|
const data = await response.json();
|
||||||
|
setCarbonData(data);
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error fetching carbon data:', error);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
fetchCarbonData();
|
||||||
|
}, [initialUrl]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Card heading={props.title} actionButtons={props.actionButtons}>
|
||||||
|
{ (!carbons?.adjustedBytes && !carbonData.c) && <p>Unable to calculate carbon footprint for host</p>}
|
||||||
|
{ carbons?.adjustedBytes > 0 && <>
|
||||||
|
<Row lbl="HTML Initial Size" val={`${carbons.adjustedBytes} bytes`} />
|
||||||
|
<Row lbl="CO2 for Initial Load" val={`${(carbons.co2.grid.grams * 1000).toPrecision(4)} grams`} />
|
||||||
|
<Row lbl="Energy Usage for Load" val={`${(carbons.energy * 1000).toPrecision(4)} KWg`} />
|
||||||
|
</>}
|
||||||
|
{carbonData.c && <Row lbl="CO2 Emitted" val={`${carbonData.c} grams`} />}
|
||||||
|
{carbonData.p && <Row lbl="Better than average site by" val={`${carbonData.p}%`} />}
|
||||||
|
<br />
|
||||||
|
<LearnMoreInfo>Learn more at <a href="https://www.websitecarbon.com/">websitecarbon.com</a></LearnMoreInfo>
|
||||||
|
</Card>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default CarbonCard;
|
@ -3,7 +3,7 @@ import Row, { ListRow } from 'components/Form/Row';
|
|||||||
|
|
||||||
const styles = `
|
const styles = `
|
||||||
.content {
|
.content {
|
||||||
max-height: 28rem;
|
max-height: 32rem;
|
||||||
overflow-y: auto;
|
overflow-y: auto;
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
210
src/components/Results/DnsSec.tsx
Normal file
210
src/components/Results/DnsSec.tsx
Normal file
@ -0,0 +1,210 @@
|
|||||||
|
import { Card } from 'components/Form/Card';
|
||||||
|
import Row, { ExpandableRow, RowProps } from 'components/Form/Row';
|
||||||
|
import Heading from 'components/Form/Heading';
|
||||||
|
import colors from 'styles/colors';
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
const parseDNSKeyData = (data: string) => {
|
||||||
|
const dnsKey = data.split(' ');
|
||||||
|
|
||||||
|
const flags = parseInt(dnsKey[0]);
|
||||||
|
const protocol = parseInt(dnsKey[1]);
|
||||||
|
const algorithm = parseInt(dnsKey[2]);
|
||||||
|
|
||||||
|
let flagMeaning = '';
|
||||||
|
let protocolMeaning = '';
|
||||||
|
let algorithmMeaning = '';
|
||||||
|
|
||||||
|
// Flags
|
||||||
|
if (flags === 256) {
|
||||||
|
flagMeaning = 'Zone Signing Key (ZSK)';
|
||||||
|
} else if (flags === 257) {
|
||||||
|
flagMeaning = 'Key Signing Key (KSK)';
|
||||||
|
} else {
|
||||||
|
flagMeaning = 'Unknown';
|
||||||
|
}
|
||||||
|
|
||||||
|
// Protocol
|
||||||
|
protocolMeaning = protocol === 3 ? 'DNSSEC' : 'Unknown';
|
||||||
|
|
||||||
|
// Algorithm
|
||||||
|
switch (algorithm) {
|
||||||
|
case 5:
|
||||||
|
algorithmMeaning = 'RSA/SHA-1';
|
||||||
|
break;
|
||||||
|
case 7:
|
||||||
|
algorithmMeaning = 'RSASHA1-NSEC3-SHA1';
|
||||||
|
break;
|
||||||
|
case 8:
|
||||||
|
algorithmMeaning = 'RSA/SHA-256';
|
||||||
|
break;
|
||||||
|
case 10:
|
||||||
|
algorithmMeaning = 'RSA/SHA-512';
|
||||||
|
break;
|
||||||
|
case 13:
|
||||||
|
algorithmMeaning = 'ECDSA Curve P-256 with SHA-256';
|
||||||
|
break;
|
||||||
|
case 14:
|
||||||
|
algorithmMeaning = 'ECDSA Curve P-384 with SHA-384';
|
||||||
|
break;
|
||||||
|
case 15:
|
||||||
|
algorithmMeaning = 'Ed25519';
|
||||||
|
break;
|
||||||
|
case 16:
|
||||||
|
algorithmMeaning = 'Ed448';
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
algorithmMeaning = 'Unknown';
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
flags: flagMeaning,
|
||||||
|
protocol: protocolMeaning,
|
||||||
|
algorithm: algorithmMeaning,
|
||||||
|
publicKey: dnsKey[3]
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
const getRecordTypeName = (typeCode: number): string => {
|
||||||
|
switch(typeCode) {
|
||||||
|
case 1: return 'A';
|
||||||
|
case 2: return 'NS';
|
||||||
|
case 5: return 'CNAME';
|
||||||
|
case 6: return 'SOA';
|
||||||
|
case 12: return 'PTR';
|
||||||
|
case 13: return 'HINFO';
|
||||||
|
case 15: return 'MX';
|
||||||
|
case 16: return 'TXT';
|
||||||
|
case 28: return 'AAAA';
|
||||||
|
case 33: return 'SRV';
|
||||||
|
case 35: return 'NAPTR';
|
||||||
|
case 39: return 'DNAME';
|
||||||
|
case 41: return 'OPT';
|
||||||
|
case 43: return 'DS';
|
||||||
|
case 46: return 'RRSIG';
|
||||||
|
case 47: return 'NSEC';
|
||||||
|
case 48: return 'DNSKEY';
|
||||||
|
case 50: return 'NSEC3';
|
||||||
|
case 51: return 'NSEC3PARAM';
|
||||||
|
case 52: return 'TLSA';
|
||||||
|
case 53: return 'SMIMEA';
|
||||||
|
case 55: return 'HIP';
|
||||||
|
case 56: return 'NINFO';
|
||||||
|
case 57: return 'RKEY';
|
||||||
|
case 58: return 'TALINK';
|
||||||
|
case 59: return 'CDS';
|
||||||
|
case 60: return 'CDNSKEY';
|
||||||
|
case 61: return 'OPENPGPKEY';
|
||||||
|
case 62: return 'CSYNC';
|
||||||
|
case 63: return 'ZONEMD';
|
||||||
|
default: return 'Unknown';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const parseDSData = (dsData: string) => {
|
||||||
|
const parts = dsData.split(' ');
|
||||||
|
|
||||||
|
const keyTag = parts[0];
|
||||||
|
const algorithm = getAlgorithmName(parseInt(parts[1], 10));
|
||||||
|
const digestType = getDigestTypeName(parseInt(parts[2], 10));
|
||||||
|
const digest = parts[3];
|
||||||
|
|
||||||
|
return {
|
||||||
|
keyTag,
|
||||||
|
algorithm,
|
||||||
|
digestType,
|
||||||
|
digest
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
const getAlgorithmName = (code: number) => {
|
||||||
|
switch(code) {
|
||||||
|
case 1: return 'RSA/MD5';
|
||||||
|
case 2: return 'Diffie-Hellman';
|
||||||
|
case 3: return 'DSA/SHA1';
|
||||||
|
case 5: return 'RSA/SHA1';
|
||||||
|
case 6: return 'DSA/NSEC3/SHA1';
|
||||||
|
case 7: return 'RSASHA1/NSEC3/SHA1';
|
||||||
|
case 8: return 'RSA/SHA256';
|
||||||
|
case 10: return 'RSA/SHA512';
|
||||||
|
case 12: return 'ECC/GOST';
|
||||||
|
case 13: return 'ECDSA/CurveP256/SHA256';
|
||||||
|
case 14: return 'ECDSA/CurveP384/SHA384';
|
||||||
|
case 15: return 'Ed25519';
|
||||||
|
case 16: return 'Ed448';
|
||||||
|
default: return 'Unknown';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const getDigestTypeName = (code: number) => {
|
||||||
|
switch(code) {
|
||||||
|
case 1: return 'SHA1';
|
||||||
|
case 2: return 'SHA256';
|
||||||
|
case 3: return 'GOST R 34.11-94';
|
||||||
|
case 4: return 'SHA384';
|
||||||
|
default: return 'Unknown';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const makeResponseList = (response: any): RowProps[] => {
|
||||||
|
const result = [] as RowProps[];
|
||||||
|
if (!response) return result;
|
||||||
|
if (typeof response.RD === 'boolean') result.push({ lbl: 'Recursion Desired (RD)', val: response.RD });
|
||||||
|
if (typeof response.RA === 'boolean') result.push({ lbl: 'Recursion Available (RA)', val: response.RA });
|
||||||
|
if (typeof response.TC === 'boolean') result.push({ lbl: 'TrunCation (TC)', val: response.TC });
|
||||||
|
if (typeof response.AD === 'boolean') result.push({ lbl: 'Authentic Data (AD)', val: response.AD });
|
||||||
|
if (typeof response.CD === 'boolean') result.push({ lbl: 'Checking Disabled (CD)', val: response.CD });
|
||||||
|
return result;
|
||||||
|
};
|
||||||
|
|
||||||
|
const makeAnswerList = (recordData: any): RowProps[] => {
|
||||||
|
return [
|
||||||
|
{ lbl: 'Domain', val: recordData.name },
|
||||||
|
{ lbl: 'Type', val: `${getRecordTypeName(recordData.type)} (${recordData.type})` },
|
||||||
|
{ lbl: 'TTL', val: recordData.TTL },
|
||||||
|
{ lbl: 'Algorithm', val: recordData.algorithm },
|
||||||
|
{ lbl: 'Flags', val: recordData.flags },
|
||||||
|
{ lbl: 'Protocol', val: recordData.protocol },
|
||||||
|
{ lbl: 'Public Key', val: recordData.publicKey },
|
||||||
|
{ lbl: 'Key Tag', val: recordData.keyTag },
|
||||||
|
{ lbl: 'Digest Type', val: recordData.digestType },
|
||||||
|
{ lbl: 'Digest', val: recordData.digest },
|
||||||
|
].filter((rowData) => rowData.val && rowData.val !== 'Unknown');
|
||||||
|
};
|
||||||
|
|
||||||
|
const DnsSecCard = (props: { data: any, title: string, actionButtons: any }): JSX.Element => {
|
||||||
|
const dnsSec = props.data;
|
||||||
|
return (
|
||||||
|
<Card heading={props.title} actionButtons={props.actionButtons}>
|
||||||
|
{
|
||||||
|
['DNSKEY', 'DS', 'RRSIG'].map((key: string, index: number) => {
|
||||||
|
const record = dnsSec[key];
|
||||||
|
return (<div key={`${key}-${index}`}>
|
||||||
|
<Heading as="h3" size="small" color={colors.primary}>{key}</Heading>
|
||||||
|
{(record.isFound && record.answer) && (<>
|
||||||
|
<Row lbl={`${key} - Present?`} val="✅ Yes" />
|
||||||
|
{
|
||||||
|
record.answer.map((answer: any, index: number) => {
|
||||||
|
const keyData = parseDNSKeyData(answer.data);
|
||||||
|
const dsData = parseDSData(answer.data);
|
||||||
|
const label = (keyData.flags && keyData.flags !== 'Unknown') ? keyData.flags : key;
|
||||||
|
return (
|
||||||
|
<ExpandableRow lbl={`Record #${index+1}`} val={label} rowList={makeAnswerList({ ...answer, ...keyData, ...dsData })} open />
|
||||||
|
);
|
||||||
|
})
|
||||||
|
}
|
||||||
|
</>)}
|
||||||
|
|
||||||
|
{(!record.isFound && record.response) && (
|
||||||
|
<ExpandableRow lbl={`${key} - Present?`} val={record.isFound ? '✅ Yes' : '❌ No'} rowList={makeResponseList(record.response)} />
|
||||||
|
)}
|
||||||
|
</div>)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
</Card>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default DnsSecCard;
|
@ -28,7 +28,7 @@ const HostListSection = (props: { list: string[], title: string }) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const cardStyles = `
|
const cardStyles = `
|
||||||
max-height: 28rem;
|
max-height: 32rem;
|
||||||
overflow: auto;
|
overflow: auto;
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
@ -3,8 +3,9 @@ import { Card } from 'components/Form/Card';
|
|||||||
import Row, { RowProps } from 'components/Form/Row';
|
import Row, { RowProps } from 'components/Form/Row';
|
||||||
|
|
||||||
const cardStyles = `
|
const cardStyles = `
|
||||||
|
grid-row: span 2;
|
||||||
.content {
|
.content {
|
||||||
max-height: 28rem;
|
max-height: 40rem;
|
||||||
overflow-y: auto;
|
overflow-y: auto;
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
58
src/components/Results/SiteFeatures.tsx
Normal file
58
src/components/Results/SiteFeatures.tsx
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
import { Card } from 'components/Form/Card';
|
||||||
|
import colors from 'styles/colors';
|
||||||
|
import Row, { ListRow } from 'components/Form/Row';
|
||||||
|
import Heading from 'components/Form/Heading';
|
||||||
|
|
||||||
|
const styles = `
|
||||||
|
.content {
|
||||||
|
max-height: 32rem;
|
||||||
|
overflow-y: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.scan-date {
|
||||||
|
font-size: 0.8rem;
|
||||||
|
margin-top: 0.5rem;
|
||||||
|
opacity: 0.75;
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
const formatDate = (timestamp: number): string => {
|
||||||
|
const date = new Date(timestamp * 1000);
|
||||||
|
const formatter = new Intl.DateTimeFormat('en-GB', {
|
||||||
|
day: 'numeric',
|
||||||
|
month: 'long',
|
||||||
|
year: 'numeric',
|
||||||
|
hour: '2-digit',
|
||||||
|
minute: '2-digit',
|
||||||
|
hour12: true
|
||||||
|
});
|
||||||
|
return formatter.format(date);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
const SiteFeaturesCard = (props: { data: any, title: string, actionButtons: any }): JSX.Element => {
|
||||||
|
const features = props.data;
|
||||||
|
return (
|
||||||
|
<Card heading={props.title} actionButtons={props.actionButtons} styles={styles}>
|
||||||
|
<div className="content">
|
||||||
|
{ features.groups.filter((group: any) => group.categories.length > 0).map((group: any, index: number) => (
|
||||||
|
<div key={`${group.name}-${index}`}>
|
||||||
|
<Heading as="h4" size="small" color={colors.primary}>{group.name}</Heading>
|
||||||
|
{ group.categories.map((category: any, subIndex: number) => (
|
||||||
|
// <Row lbl={category.name} val={category.live} />
|
||||||
|
<Row lbl="" val="" key={`${category.name}-${subIndex}`}>
|
||||||
|
<span className="lbl">{category.name}</span>
|
||||||
|
<span className="val">{category.live} Live {category.dead ? `(${category.dead} dead)` : ''}</span>
|
||||||
|
</Row>
|
||||||
|
))
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
))
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
<p className="scan-date">Last scanned on {formatDate(features.last)}</p>
|
||||||
|
</Card>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default SiteFeaturesCard;
|
@ -1,7 +1,6 @@
|
|||||||
import styled from 'styled-components';
|
import styled from 'styled-components';
|
||||||
import colors from 'styles/colors';
|
import colors from 'styles/colors';
|
||||||
import { Card } from 'components/Form/Card';
|
import { Card } from 'components/Form/Card';
|
||||||
import Heading from 'components/Form/Heading';
|
|
||||||
|
|
||||||
const RouteRow = styled.div`
|
const RouteRow = styled.div`
|
||||||
text-align: center;
|
text-align: center;
|
||||||
@ -31,14 +30,17 @@ p {
|
|||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
const cardStyles = `
|
||||||
|
grid-row: span 2;
|
||||||
|
`;
|
||||||
|
|
||||||
const TraceRouteCard = (props: { data: any, title: string, actionButtons: any }): JSX.Element => {
|
const TraceRouteCard = (props: { data: any, title: string, actionButtons: any }): JSX.Element => {
|
||||||
const traceRouteResponse = props.data;
|
const traceRouteResponse = props.data;
|
||||||
const routes = traceRouteResponse.result;
|
const routes = traceRouteResponse.result;
|
||||||
return (
|
return (
|
||||||
<Card heading={props.title} actionButtons={props.actionButtons}>
|
<Card heading={props.title} actionButtons={props.actionButtons} styles={cardStyles}>
|
||||||
{routes.filter((x: any) => x).map((route: any, index: number) => (
|
{routes.filter((x: any) => x).map((route: any, index: number) => (
|
||||||
<RouteRow key={index}>
|
<RouteRow key={index}>
|
||||||
{/* <span>{route}</span> */}
|
|
||||||
<span className="ipName">{Object.keys(route)[0]}</span>
|
<span className="ipName">{Object.keys(route)[0]}</span>
|
||||||
<RouteTimings>
|
<RouteTimings>
|
||||||
{route[Object.keys(route)[0]].map((time: any, packetIndex: number) => (
|
{route[Object.keys(route)[0]].map((time: any, packetIndex: number) => (
|
||||||
|
Loading…
Reference in New Issue
Block a user