Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix/autoupdating cdn downtime #2467

Merged
merged 4 commits into from
Dec 16, 2024
Merged
Show file tree
Hide file tree
Changes from all 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
6 changes: 6 additions & 0 deletions .github/workflows/publish.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,12 @@ jobs:
with:
subject-path: './sdk'

- name: Generate last_updated.json
if: (env.DRY_RUN) == 'false'
run: |
echo "{\"last_updated\": \"$(date -u +"%Y-%m-%dT%H:%M:%SZ")\"}" > ./sdk/last_updated.json
cp ./sdk/last_updated.json ./sdk/dist/
- name: Authenticate NPM
if: contains(env.RELEASE_TYPE, 'release')
run: npm config set //registry.npmjs.org/:_authToken ${{ secrets.TS_IMMUTABLE_SDK_NPM_TOKEN }}
Expand Down
68 changes: 38 additions & 30 deletions packages/checkout/sdk/src/widgets/version.ts
Original file line number Diff line number Diff line change
Expand Up @@ -84,33 +84,6 @@ export async function getLatestVersionFromNpm(): Promise<string> {
}
}

/**
* Checks if the provided version is available on the CDN.
* @param {string} version - The version to check.
* @returns {Promise<boolean>} A promise resolving to a boolean indicating if the version is available on the CDN.
*/
async function isVersionAvailableOnCDN(version: string): Promise<boolean> {
const files = ['widgets-esm.js', 'widgets.js'];
const baseUrl = `https://cdn.jsdelivr.net/npm/@imtbl/sdk@${version}/dist/browser/checkout/`;

try {
const checks = files.map(async (file) => {
const response = await fetch(`${baseUrl}${file}`, { method: 'HEAD' });
if (!response.ok) {
return false;
}
return true;
});

const results = await Promise.all(checks);
const allFilesAvailable = results.every((isAvailable) => isAvailable);

return allFilesAvailable;
} catch {
return false;
}
}

/**
* Returns the latest compatible version based on the provided checkout version config.
* If no compatible version markers are provided, it returns 'latest'.
Expand All @@ -127,6 +100,40 @@ function latestCompatibleVersion(
return 'latest';
}

/**
* Checks if the last_updated.json file exists on the CDN and validates its timestamp.
* @param {string} version - The version to check.
* @returns {Promise<boolean>} A promise resolving to `true` if last_updated.json exists and is older than 15 minutes, `false` otherwise.
*/
async function checkLastUpdatedTimestamp(version: string): Promise<boolean> {
const WAIT_TIME_IN_MINUTES = 20;

const lastUpdatedJsonUrl = `https://cdn.jsdelivr.net/npm/@imtbl/sdk@${version}/dist/last_updated.json`;

try {
const response = await fetch(lastUpdatedJsonUrl);

if (!response.ok) {
return false;
}

const lastUpdatedData = await response.json();

if (lastUpdatedData.timestamp) {
const timestamp = new Date(lastUpdatedData.timestamp);
const now = new Date();
const diffInMs = now.getTime() - timestamp.getTime();
const diffInMinutes = diffInMs / (1000 * 60);

return diffInMinutes > WAIT_TIME_IN_MINUTES;
}
} catch (error) {
return false;
}

return false;
}

/**
* Determines the version of the widgets to use based on the provided validated build version and checkout version config.
* If a version is provided in the widget init parameters, it uses that version.
Expand Down Expand Up @@ -158,13 +165,14 @@ export async function determineWidgetsVersion(
versionConfig.compatibleVersionMarkers,
);

// If `latest` is returned, query NPM registry for the actual latest version and check if it's available on the CDN
// If `latest` is returned, query NPM registry for the actual latest version and check timestamp
if (compatibleVersion === 'latest') {
const latestVersion = await getLatestVersionFromNpm();
const isAvailable = await isVersionAvailableOnCDN(latestVersion);
if (isAvailable) {

if (await checkLastUpdatedTimestamp(latestVersion)) {
return latestVersion;
}

return 'latest';
}

Expand Down
Loading