diff --git a/gatsby-config.js b/gatsby-config.js index 19c2baad345d46..9345aedbef1675 100644 --- a/gatsby-config.js +++ b/gatsby-config.js @@ -42,6 +42,49 @@ collectionIgnoreGlobs.length > 0 : console.info("Build Scope includes all collections"); module.exports = { ...(pathPrefix != null ? { pathPrefix } : {}), + // cloud.layer5.io's CORS allowlist only permits https://layer5.io/https://www.layer5.io, + // so browser fetches from any local `gatsby develop` session are blocked cross-origin. + // This dev-only proxy (no effect on `gatsby build`) routes those requests through the + // same origin as the dev server so they succeed locally too. + // + // Gatsby's built-in `proxy` config uses `got`, whose `req.pipe(got.stream(...))` wiring + // has a bug that breaks TLS certificate verification for bodiless GET requests in this + // Gatsby/Node version combination (reproduced in isolation, confirmed deterministic). + // `developMiddleware` with Node's built-in `https` module sidesteps it entirely. + ...(isDevelopment + ? { + developMiddleware: (app) => { + app.get("/api/*", (req, res) => { + const https = require("https"); + const proxyReq = https.request( + `https://cloud.layer5.io${req.originalUrl}`, + { + method: req.method, + headers: { accept: req.headers.accept || "*/*" }, + }, + (proxyRes) => { + res.writeHead(proxyRes.statusCode, proxyRes.headers); + proxyRes.pipe(res); + }, + ); + proxyReq.setTimeout(10000, () => { + proxyReq.destroy(new Error("Proxy request timed out")); + }); + proxyReq.on("error", (err) => { + if (!res.headersSent) { + res + .status(502) + .json({ + error: "Proxy request failed", + message: err.message, + }); + } + }); + proxyReq.end(); + }); + }, + } + : {}), siteMetadata: { title: "Layer5 - Expect more from your infrastructure", description: diff --git a/src/sections/Counters/index.js b/src/sections/Counters/index.js index 7f90f142f3e0d8..6fe78fd8d0d358 100644 --- a/src/sections/Counters/index.js +++ b/src/sections/Counters/index.js @@ -5,15 +5,33 @@ import Counter from "../../reusecore/Counter"; import CounterSectionWrapper from "./counterSection.style"; -export const URL = "https://cloud.layer5.io/api/performance/results/total"; +// cloud.layer5.io's CORS allowlist only permits https://layer5.io/https://www.layer5.io, +// so local `gatsby develop` sessions use the dev-only proxy from gatsby-config.js instead. +export const URL = + process.env.NODE_ENV === "development" + ? "/api/performance/results/total" + : "https://cloud.layer5.io/api/performance/results/total"; const Counters = () => { const [performanceCount, setPerformanceCount] = useState(0); useEffect(() => { fetch(URL) - .then(response => response.json()) - .then(result => setPerformanceCount(result.total_runs)); + .then((response) => { + if (!response.ok) { + throw new Error(`Request failed with status ${response.status}`); + } + return response.json(); + }) + .then((result) => { + if (!Number.isFinite(result.totalRuns)) { + throw new Error("Invalid performance count received"); + } + setPerformanceCount(result.totalRuns); + }) + .catch((error) => { + console.log("Failed to fetch performance count:", error.message); + }); }, []); return ( diff --git a/src/sections/Meshery/Features-Col/index.js b/src/sections/Meshery/Features-Col/index.js index 9b33e6683ad9b9..789444bed386ed 100644 --- a/src/sections/Meshery/Features-Col/index.js +++ b/src/sections/Meshery/Features-Col/index.js @@ -18,7 +18,22 @@ function getServiceFeature(service, index) { - + + + + {service.content} @@ -38,7 +53,7 @@ function getFeatureBlock(feature, index, performanceCount) { {feature.services.map((service, index) => - getServiceFeature(service, index) + getServiceFeature(service, index), )} @@ -47,9 +62,16 @@ function getFeatureBlock(feature, index, performanceCount) { duration={5} separator="," end={ - feature.count.value !== 0 ? feature.count.value : performanceCount + feature.count.description === "performance tests run" + ? performanceCount + : feature.count.value + } + suffix={ + feature.count.description == "components" || + feature.count.description == "cloud native integrations" + ? "+" + : " " } - suffix= {(feature.count.description == "components" || feature.count.description == "cloud native integrations") ? "+" : " "} />

{feature.count.description}

@@ -60,25 +82,29 @@ function getFeatureBlock(feature, index, performanceCount) { const Features = () => { const [performanceCount, setPerformanceCount] = useState(0); - const performanceCountEndpoint = "https://cloud.layer5.io/api/performance/results/total"; + // cloud.layer5.io's CORS allowlist only permits https://layer5.io/https://www.layer5.io, + // so local `gatsby develop` sessions use the dev-only proxy from gatsby-config.js instead. + const performanceCountEndpoint = + process.env.NODE_ENV === "development" + ? "/api/performance/results/total" + : "https://cloud.layer5.io/api/performance/results/total"; useEffect(() => { fetch(performanceCountEndpoint) .then((response) => { if (!response.ok) { - setPerformanceCount(250000); throw new Error(`HTTP error! status: ${response.status}`); } return response.json(); }) .then((resultcount) => { - if (resultcount && typeof resultcount.total_runs === "number") { - setPerformanceCount(resultcount.total_runs); + if (resultcount && typeof resultcount.totalRuns === "number") { + setPerformanceCount(resultcount.totalRuns); } }) .catch((error) => { console.log("Failed to fetch performance count:", error.message); - // Keep default value of 0 if fetch fails + // Keep default value of 0 if fetch fails }); }, []); @@ -94,12 +120,11 @@ const Features = () => { {data.map((feature, index) => - getFeatureBlock(feature, index, performanceCount) + getFeatureBlock(feature, index, performanceCount), )} ); }; - export default Features; diff --git a/src/sections/Meshery/How-meshery-works/specs/data-card.js b/src/sections/Meshery/How-meshery-works/specs/data-card.js index 1925ab2a72e0ce..a948b87d7f2aae 100644 --- a/src/sections/Meshery/How-meshery-works/specs/data-card.js +++ b/src/sections/Meshery/How-meshery-works/specs/data-card.js @@ -8,41 +8,39 @@ import Counter from "../../../../reusecore/Counter"; import { URL } from "../../../Counters/index"; const DataCardWrapper = styled.div` - background: ${props => props.theme.grey222222ToWhite}; + background: ${(props) => props.theme.grey222222ToWhite}; border-radius: 10px; - color: ${props => props.theme.text}; + color: ${(props) => props.theme.text}; padding: 2rem; transition: 0.8s cubic-bezier(0.2, 0.8, 0.2, 1); - - ul{ + + ul { list-style: none; padding: 0; } - .col-1 li{ - display: flex; - align-items: center; - vertical-align: center; - margin-bottom: 1.5rem; - img{ - margin-right: 1rem; - } - h5{ - font-weight: 600; - transition: 0.8s cubic-bezier(0.2, 0.8, 0.2, 1); - } - } - - .col-2 li { - h3{ - color: ${props => props.theme.secondaryColor}; - font-weight: 700; - } - p { - font-size: 16px; - } - } - - + .col-1 li { + display: flex; + align-items: center; + vertical-align: center; + margin-bottom: 1.5rem; + img { + margin-right: 1rem; + } + h5 { + font-weight: 600; + transition: 0.8s cubic-bezier(0.2, 0.8, 0.2, 1); + } + } + + .col-2 li { + h3 { + color: ${(props) => props.theme.secondaryColor}; + font-weight: 700; + } + p { + font-size: 16px; + } + } `; const DataCard = () => { @@ -50,8 +48,21 @@ const DataCard = () => { useEffect(() => { fetch(URL) - .then((response) => response.json()) - .then((result) => setPerformanceCount(result.total_runs)); + .then((response) => { + if (!response.ok) { + throw new Error(`Request failed with status ${response.status}`); + } + return response.json(); + }) + .then((result) => { + if (!Number.isInteger(result.totalRuns) || result.totalRuns < 0) { + throw new Error("Invalid performance count received"); + } + setPerformanceCount(result.totalRuns); + }) + .catch((error) => { + console.log("Failed to fetch performance count:", error.message); + }); }, []); return ( @@ -77,22 +88,13 @@ const DataCard = () => {