Skip to content
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 5 additions & 4 deletions apps/site/next-data/generators/supportersData.mjs
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
import { OPENCOLLECTIVE_MEMBERS_URL } from '#site/next.constants.mjs';
import { fetchWithRetry } from '#site/util/fetch';

/**
* Fetches supporters data from Open Collective API, filters active backers,
* and maps it to the Supporters type.
*
* @returns {Promise<Array<import('#site/types/supporters')>>} Array of supporters
* @returns {Promise<Array<import('#site/types/supporters').OpenCollectiveSupporter>>} Array of supporters
*/
async function fetchOpenCollectiveData() {
const endpoint = 'https://opencollective.com/nodejs/members/all.json';

const response = await fetch(endpoint);
const response = await fetchWithRetry(OPENCOLLECTIVE_MEMBERS_URL);

const payload = await response.json();

Expand Down
28 changes: 10 additions & 18 deletions apps/site/next-data/generators/vulnerabilities.mjs
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
import { VULNERABILITIES_URL } from '#site/next.constants.mjs';
import { fetchWithRetry } from '#site/util/fetch';

const RANGE_REGEX = /([<>]=?)\s*(\d+)(?:\.(\d+))?/;
const V0_REGEX = /^0\.\d+(\.x)?$/;
const VER_REGEX = /^\d+\.x$/;

/**
* Fetches vulnerability data from the Node.js Security Working Group repository,
Expand All @@ -9,7 +12,7 @@ const RANGE_REGEX = /([<>]=?)\s*(\d+)(?:\.(\d+))?/;
* @returns {Promise<import('#site/types/vulnerabilities').GroupedVulnerabilities>} Grouped vulnerabilities
*/
export default async function generateVulnerabilityData() {
const response = await fetch(VULNERABILITIES_URL);
const response = await fetchWithRetry(VULNERABILITIES_URL);
Comment thread
araujogui marked this conversation as resolved.

/** @type {Array<import('#site/types/vulnerabilities').RawVulnerability>} */
const data = Object.values(await response.json());
Expand All @@ -26,14 +29,14 @@ export default async function generateVulnerabilityData() {
// Helper function to process version patterns
const processVersion = (version, vulnerability) => {
// Handle 0.X versions (pre-semver)
if (/^0\.\d+(\.x)?$/.test(version)) {
if (V0_REGEX.test(version)) {
addToGroup('0', vulnerability);

return;
}

// Handle simple major.x patterns (e.g., 12.x)
if (/^\d+\.x$/.test(version)) {
if (VER_REGEX.test(version)) {
const majorVersion = version.split('.')[0];

addToGroup(majorVersion, vulnerability);
Expand Down Expand Up @@ -67,25 +70,14 @@ export default async function generateVulnerabilityData() {
}
};

for (const vulnerability of Object.values(data)) {
const parsedVulnerability = {
cve: vulnerability.cve,
url: vulnerability.ref,
vulnerable: vulnerability.vulnerable,
patched: vulnerability.patched,
description: vulnerability.description,
overview: vulnerability.overview,
affectedEnvironments: vulnerability.affectedEnvironments,
severity: vulnerability.severity,
};
for (const { ref, ...vulnerability } of Object.values(data)) {
vulnerability.url = ref;

// Process all potential versions from the vulnerable field
const versions = parsedVulnerability.vulnerable
.split(' || ')
.filter(Boolean);
const versions = vulnerability.vulnerable.split(' || ').filter(Boolean);

for (const version of versions) {
processVersion(version, parsedVulnerability);
processVersion(version, vulnerability);
}
}

Expand Down
3 changes: 2 additions & 1 deletion apps/site/next.calendar.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import {
BASE_CALENDAR_URL,
SHARED_CALENDAR_KEY,
} from './next.calendar.constants.mjs';
import { fetchWithRetry } from './util/fetch';

/**
*
Expand Down Expand Up @@ -33,7 +34,7 @@ export const getCalendarEvents = async (calendarId = '', maxResults = 20) => {
calendarQueryUrl.searchParams.append(key, value)
);

return fetch(calendarQueryUrl)
return fetchWithRetry(calendarQueryUrl)
.then(response => response.json())
.then(calendar => calendar.items ?? []);
};
6 changes: 6 additions & 0 deletions apps/site/next.constants.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -213,3 +213,9 @@ export const EOL_VERSION_IDENTIFIER = 'End-of-life';
*/
export const VULNERABILITIES_URL =
'https://raw.githubusercontent.com/nodejs/security-wg/main/vuln/core/index.json';

/**
* The location of the OpenCollective data
*/
export const OPENCOLLECTIVE_MEMBERS_URL =
'https://opencollective.com/nodejs/members/all.json';
1 change: 1 addition & 0 deletions apps/site/types/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,3 +16,4 @@ export * from './download';
export * from './userAgent';
export * from './vulnerabilities';
export * from './page';
export * from './supporters';
9 changes: 9 additions & 0 deletions apps/site/types/supporters.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
export type Supporter<T extends string> = {
name: string;
image: string;
url: string;
profile: string;
source: T;
};

export type OpenCollectiveSupporter = Supporter<'opencollective'>;
35 changes: 35 additions & 0 deletions apps/site/util/fetch.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import { setTimeout } from 'node:timers/promises';

type RetryOptions = RequestInit & {
maxRetry?: number;
delay?: number;
};

type FetchError = {
cause: {
code: string;
};
};

export const fetchWithRetry = async (
url: string,
{ maxRetry = 3, delay = 100, ...options }: RetryOptions = {}
) => {
for (let i = 1; i <= maxRetry; i++) {
try {
return fetch(url, options);
} catch (e) {
console.debug(
`fetch of ${url} failed at ${Date.now()}, attempt ${i}/${maxRetry}`,
e
);

if (i === maxRetry || (e as FetchError).cause.code !== 'ETIMEDOUT') {
throw e;
}

await setTimeout(delay);
Comment thread
araujogui marked this conversation as resolved.
Outdated
Comment thread
avivkeller marked this conversation as resolved.
Outdated
continue;
Comment thread
avivkeller marked this conversation as resolved.
Outdated
}
}
};
Loading