mirror of
https://github.com/Lissy93/web-check.git
synced 2024-11-22 16:23:56 +01:00
Adds Lighthouse reporting
This commit is contained in:
parent
920ab64410
commit
f139256736
@ -39,6 +39,11 @@
|
||||
to = "/.netlify/functions/find-url-ip"
|
||||
status = 301
|
||||
force = true
|
||||
[[redirects]]
|
||||
from = "/lighthouse-report"
|
||||
to = "/.netlify/functions/lighthouse-report"
|
||||
status = 301
|
||||
force = true
|
||||
|
||||
# For router history mode, ensure pages land on index
|
||||
[[redirects]]
|
||||
|
28019
package-lock.json
generated
Normal file
28019
package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
@ -3,6 +3,7 @@
|
||||
"version": "0.1.0",
|
||||
"private": true,
|
||||
"dependencies": {
|
||||
"@netlify/functions": "^1.6.0",
|
||||
"@testing-library/jest-dom": "^5.16.4",
|
||||
"@testing-library/react": "^13.3.0",
|
||||
"@testing-library/user-event": "^13.5.0",
|
||||
@ -12,8 +13,12 @@
|
||||
"@types/react-dom": "^18.0.5",
|
||||
"@types/react-router-dom": "^5.3.3",
|
||||
"@types/styled-components": "^5.1.25",
|
||||
"axios": "^1.4.0",
|
||||
"chrome-aws-lambda": "^10.1.0",
|
||||
"jest-styled-components": "^7.0.8",
|
||||
"lighthouse": "^10.3.0",
|
||||
"node-fetch": "2.6.1",
|
||||
"puppeteer-core": "^20.7.2",
|
||||
"react": "^18.1.0",
|
||||
"react-dom": "^18.1.0",
|
||||
"react-router-dom": "^6.3.0",
|
||||
@ -51,5 +56,9 @@
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/react-simple-maps": "^1.0.8"
|
||||
},
|
||||
"compilerOptions": {
|
||||
"allowJs": true,
|
||||
"outDir": "./dist"
|
||||
}
|
||||
}
|
||||
|
33
server/lambda/lighthouse-report.js
Normal file
33
server/lambda/lighthouse-report.js
Normal file
@ -0,0 +1,33 @@
|
||||
const fetch = require('node-fetch');
|
||||
|
||||
exports.handler = function(event, context, callback) {
|
||||
const { url } = event.queryStringParameters;
|
||||
|
||||
if (!url) {
|
||||
callback(null, {
|
||||
statusCode: 400,
|
||||
body: JSON.stringify({ error: 'URL param is required'}),
|
||||
});
|
||||
}
|
||||
|
||||
const apiKey = process.env.GOOGLE_CLOUD_API_KEY;
|
||||
const endpoint = `https://www.googleapis.com/pagespeedonline/v5/runPagespeed?url=${encodeURIComponent(url)}&category=PERFORMANCE&category=ACCESSIBILITY&category=BEST_PRACTICES&category=SEO&category=PWA&strategy=mobile&key=${apiKey}`;
|
||||
fetch(endpoint)
|
||||
.then((res) => res.json() )
|
||||
.then(
|
||||
(data) => {
|
||||
callback(null, {
|
||||
statusCode: 200,
|
||||
body: JSON.stringify(data),
|
||||
});
|
||||
}
|
||||
).catch(
|
||||
() => {
|
||||
callback(null, {
|
||||
statusCode: 500,
|
||||
body: JSON.stringify({ error: 'Error running Lighthouse'}),
|
||||
});
|
||||
}
|
||||
);
|
||||
};
|
||||
|
125
src/components/Results/Lighthouse.tsx
Normal file
125
src/components/Results/Lighthouse.tsx
Normal file
@ -0,0 +1,125 @@
|
||||
|
||||
import styled from 'styled-components';
|
||||
|
||||
import colors from 'styles/colors';
|
||||
import Card from 'components/Form/Card';
|
||||
import Heading from 'components/Form/Heading';
|
||||
|
||||
const processScore = (percentile: number) => {
|
||||
return `${Math.round(percentile * 100)}%`;
|
||||
}
|
||||
|
||||
const Outer = styled(Card)``;
|
||||
|
||||
const Row = styled.div`
|
||||
details summary {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
padding: 0.25rem;
|
||||
cursor: pointer;
|
||||
}
|
||||
&:not(:last-child) { border-bottom: 1px solid ${colors.primary}; }
|
||||
span.lbl {
|
||||
font-weight: bold;
|
||||
text-transform: capitalize;
|
||||
}
|
||||
span.val {
|
||||
max-width: 200px;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
`;
|
||||
|
||||
const ScoreRow = styled.li`
|
||||
list-style: none;
|
||||
margin: 0;
|
||||
display: flex;
|
||||
gap: 0.25rem;
|
||||
padding: 0.25rem 0;
|
||||
justify-content: space-between;
|
||||
span { min-width: 5rem; max-width: 8rem; }
|
||||
border-bottom: 1px dashed ${colors.primaryTransparent};
|
||||
`;
|
||||
|
||||
const ScoreList = styled.ul`
|
||||
margin: 0;
|
||||
padding: 0.25rem;
|
||||
border-top: 1px solid ${colors.primary};
|
||||
background: ${colors.primaryTransparent};
|
||||
`;
|
||||
|
||||
interface Audit {
|
||||
id: string,
|
||||
score?: number | string,
|
||||
scoreDisplayMode?: string,
|
||||
title?: string,
|
||||
description?: string,
|
||||
displayValue?: string,
|
||||
};
|
||||
|
||||
const ScoreItem = (props: { scoreId: any, audits: Audit[] }) => {
|
||||
const { scoreId, audits } = props;
|
||||
console.log('Audits: ', props.audits)
|
||||
const audit = audits[scoreId];
|
||||
if (!audit.score) return null;
|
||||
|
||||
let score = audit.score;
|
||||
if (audit.displayValue) {
|
||||
score = audit.displayValue;
|
||||
} else if (audit.scoreDisplayMode) {
|
||||
score = audit.score === 1 ? '✅ Pass' : '❌ Fail';
|
||||
}
|
||||
|
||||
return (
|
||||
<ScoreRow title={audit.description}>
|
||||
<b>{ audit.title }</b>
|
||||
<span>{score}</span>
|
||||
</ScoreRow>
|
||||
);
|
||||
};
|
||||
|
||||
const ExpandableRow = (props: { lbl: string, val: string, list: string[], audits: Audit[] }) => {
|
||||
const { lbl, val, list, audits } = props;
|
||||
return (
|
||||
<Row>
|
||||
<details>
|
||||
<summary>
|
||||
<span className="lbl">{lbl}</span>
|
||||
<span className="val" title={val}>{val}</span>
|
||||
</summary>
|
||||
<ScoreList>
|
||||
{ list.map((li: string) => {
|
||||
return <ScoreItem scoreId={li} audits={audits} />
|
||||
}) }
|
||||
</ScoreList>
|
||||
</details>
|
||||
</Row>
|
||||
);
|
||||
};
|
||||
|
||||
const LighthouseCard = (props: { lighthouse: any }): JSX.Element => {
|
||||
console.log('Props:', props.lighthouse);
|
||||
const categories = props.lighthouse?.categories || {};
|
||||
const audits = props.lighthouse?.audits || [];
|
||||
|
||||
return (
|
||||
<Outer>
|
||||
<Heading as="h3" size="small" align="left" color={colors.primary}>Performance</Heading>
|
||||
{ Object.keys(categories).map((title: string, index: number) => {
|
||||
const scoreIds = categories[title].auditRefs.map((ref: { id: string }) => ref.id);
|
||||
return (
|
||||
<ExpandableRow
|
||||
key={`lighthouse-${index}`}
|
||||
lbl={title}
|
||||
val={processScore(categories[title].score)}
|
||||
list={scoreIds}
|
||||
audits={audits}
|
||||
/>
|
||||
);
|
||||
}) }
|
||||
</Outer>
|
||||
);
|
||||
}
|
||||
|
||||
export default LighthouseCard;
|
22
src/components/Results/Screenshot.tsx
Normal file
22
src/components/Results/Screenshot.tsx
Normal file
@ -0,0 +1,22 @@
|
||||
|
||||
import styled from 'styled-components';
|
||||
import colors from 'styles/colors';
|
||||
import Card from 'components/Form/Card';
|
||||
import Heading from 'components/Form/Heading';
|
||||
|
||||
const Outer = styled(Card)`
|
||||
overflow: auto;
|
||||
max-height: 20rem;
|
||||
`;
|
||||
|
||||
const ScreenshotCard = (props: { screenshot: string }): JSX.Element => {
|
||||
console.log('Props:', props.screenshot);
|
||||
return (
|
||||
<Outer>
|
||||
<Heading as="h3" size="small" align="left" color={colors.primary}>Screenshot</Heading>
|
||||
<img src={props.screenshot} alt="Full page screenshot" />
|
||||
</Outer>
|
||||
);
|
||||
}
|
||||
|
||||
export default ScreenshotCard;
|
@ -12,6 +12,7 @@ body {
|
||||
sans-serif;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
background: #141d2b;
|
||||
}
|
||||
|
||||
code {
|
||||
|
@ -10,6 +10,8 @@ import ServerInfoCard from 'components/Results/ServerInfo';
|
||||
import HostNamesCard from 'components/Results/HostNames';
|
||||
import WhoIsCard from 'components/Results/WhoIs';
|
||||
import BuiltWithCard from 'components/Results/BuiltWith';
|
||||
import LighthouseCard from 'components/Results/Lighthouse';
|
||||
import ScreenshotCard from 'components/Results/Screenshot';
|
||||
import keys from 'utils/get-keys';
|
||||
import { determineAddressType, AddressType } from 'utils/address-type-checker';
|
||||
|
||||
@ -57,6 +59,8 @@ const Results = (): JSX.Element => {
|
||||
const [ locationResults, setLocationResults ] = useState<ServerLocation>();
|
||||
const [ whoIsResults, setWhoIsResults ] = useState<Whois>();
|
||||
const [ technologyResults, setTechnologyResults ] = useState<TechnologyGroup[]>();
|
||||
const [ lighthouseResults, setLighthouseResults ] = useState<any>();
|
||||
const [ screenshotResult, setScreenshotResult ] = useState<string>();
|
||||
const [ ipAddress, setIpAddress ] = useState<undefined | string>(undefined);
|
||||
const [ addressType, setAddressType ] = useState<AddressType>('empt');
|
||||
const { address } = useParams();
|
||||
@ -75,7 +79,6 @@ const Results = (): JSX.Element => {
|
||||
fetch(`/find-url-ip?address=${address}`)
|
||||
.then(function(response) {
|
||||
response.json().then(jsonData => {
|
||||
console.log('Get IP Address', jsonData);
|
||||
setIpAddress(jsonData.ip);
|
||||
});
|
||||
})
|
||||
@ -88,6 +91,17 @@ const Results = (): JSX.Element => {
|
||||
}
|
||||
}, [address]);
|
||||
|
||||
/* Get Lighthouse report */
|
||||
useEffect(() => {
|
||||
fetch(`/lighthouse-report?url=${address}`)
|
||||
.then(response => response.json())
|
||||
.then(response => {
|
||||
setLighthouseResults(response.lighthouseResult);
|
||||
setScreenshotResult(response.lighthouseResult?.fullPageScreenshot?.screenshot?.data);
|
||||
})
|
||||
.catch(err => console.error(err));
|
||||
}, [address])
|
||||
|
||||
/* Get IP address location info */
|
||||
useEffect(() => {
|
||||
const fetchIpLocation = () => {
|
||||
@ -118,7 +132,6 @@ const Results = (): JSX.Element => {
|
||||
fetch(`https://api.shodan.io/shodan/host/${ipAddress}?key=${apiKey}`)
|
||||
.then(response => response.json())
|
||||
.then(response => {
|
||||
console.log(response);
|
||||
if (!response.error) applyShodanResults(response)
|
||||
})
|
||||
.catch(err => console.error(err));
|
||||
@ -137,7 +150,6 @@ const Results = (): JSX.Element => {
|
||||
fetch(endpoint)
|
||||
.then(response => response.json())
|
||||
.then(response => {
|
||||
console.log(response);
|
||||
setTechnologyResults(makeTechnologies(response));
|
||||
});
|
||||
}, [address]);
|
||||
@ -179,6 +191,8 @@ const Results = (): JSX.Element => {
|
||||
{ results.serverInfo && <ServerInfoCard {...results.serverInfo} />}
|
||||
{ results.hostNames && <HostNamesCard hosts={results.hostNames} />}
|
||||
{ whoIsResults && <WhoIsCard {...whoIsResults} />}
|
||||
{ lighthouseResults && <LighthouseCard lighthouse={lighthouseResults} />}
|
||||
{ screenshotResult && <ScreenshotCard screenshot={screenshotResult} />}
|
||||
{ technologyResults && <BuiltWithCard technologies={technologyResults} />}
|
||||
</ResultsContent>
|
||||
</ResultsOuter>
|
||||
|
@ -9,6 +9,7 @@ const colors = {
|
||||
backgroundLighter: '#1a2332',
|
||||
bgShadowColor: '#0f1620',
|
||||
fgShadowColor: '#456602',
|
||||
primaryTransparent: '#9fef0012',
|
||||
|
||||
// Action Colors
|
||||
info: '#04e4f4',
|
||||
|
Loading…
Reference in New Issue
Block a user