eip-cloud-services
Version:
Houses a collection of helpers for connecting with Cloud services.
114 lines (97 loc) • 5.17 kB
JavaScript
const { CloudFrontClient, CreateInvalidationCommand } = require ( '@aws-sdk/client-cloudfront' );
const { GoogleAuth } = require ( 'google-auth-library' );
const { initialiseGoogleAuth } = require ( './gcp' );
const fs = require ( 'fs' );
let config = {};
const configDirPath = `${ process.cwd ()}/config`;
if ( fs.existsSync ( configDirPath ) && fs.statSync ( configDirPath ).isDirectory () ) {
config = require ( 'config' ); // require the config directory if it exists
}
const packageJson = require ( '../package.json' );
const { cwd } = require ( 'process' );
const { log } = config?.s3?.logsFunction ? require ( `${ cwd ()}/${config.s3.logsFunction}` ) : console;
const redis = require('./redis');
/**
* Create a CDN invalidation for the specified key(s) and environment.
*
* @param {string} cdn - The CDN provider to be used (e.g., 'google', 'amazon').
* @param {string|string[]} key - The key(s) representing the file(s) to invalidate.
* @param {string} environment - The environment (e.g., 'staging', 'production').
* @returns {Promise<void>} A promise that resolves when the invalidation is created.
* @description Creates a CDN invalidation for the specified key(s) in the specified environment.
* - The `cdn` parameter specifies the CDN provider (e.g., 'google', 'amazon').
* - The `key` parameter can be a single string or an array of strings representing the file(s) to invalidate.
* - The `environment` parameter specifies the environment (e.g., 'staging', 'production').
* - The function validates the `key` argument and throws an error if it is not a string or an array of strings.
* - The function constructs the invalidation paths based on the provided keys.
* - The CDN client (either Google or Amazon CloudFront) is used to send the invalidation command.
* - Returns a promise that resolves when the invalidation is created.
* - The function initializes Google Auth if Google CDN is used.
* - If Google CDN is used, the function makes a POST request to Google's 'invalidateCache' endpoint for each provided path.
* - If Amazon CloudFront is used, the function sends an invalidation command to CloudFront.
* - If the CDN type is not 'google' or 'amazon', the function throws an error.
* - The function uses the 'config' module to access the CDN settings based on the CDN provider and environment.
*/
exports.createInvalidation = async ( cdn, key, environment = 'production' ) => {
const cdnSettings = config.cdn[ cdn ][ environment ];
// Ensure paths is an array and sanitize paths
const paths = ( Array.isArray ( key ) ? key : [ key ] )
.filter ( item => typeof item === 'string' )
.map ( item => item.charAt ( 0 ) !== '/' ? '/' + item : item );
if ( paths.length === 0 ) {
throw new Error ( 'Invalid key argument. Expected a string or an array of strings.' );
}
if ( config?.redis?.host ) {
const redisKey = `cdn-invalidation:${cdn}:${environment}:${key}`;
const redisValue = await redis.get(redisKey);
if(redisValue) {
if ( config.cdn.log )
log ( `CDN [INVALIDATE]: Invalidation already in progress - skipping: ${paths.map ( path => `https://${cdn}${environment !== 'production' ? '-test' : ''}.eip.telegraph.co.uk${path}` ).join ( ', ' )}\n` );
await redis.increment(redisKey);
return;
}
else{
await redis.set(redisKey, 1, cdnSettings.type === 'google' ? 300 : 120); // 5 minutes for google, 2 minutes for amazon
}
}
if ( config.cdn.log )
log ( `CDN [INVALIDATE]: ${paths.map ( path => `https://${cdn}${environment !== 'production' ? '-test' : ''}.eip.telegraph.co.uk${path}` ).join ( ', ' )}\n` );
switch ( cdnSettings.type ) {
case 'google':
await invalidateGoogleCDN ( cdnSettings, paths );
break;
case 'amazon':
await invalidateAmazonCDN ( cdnSettings, paths );
break;
default:
throw new Error ( `Invalid cdn type: ${cdnSettings.type}` );
}
};
async function invalidateGoogleCDN ( cdnSettings, paths ) {
await initialiseGoogleAuth ();
const auth = new GoogleAuth ( {
scopes: 'https://www.googleapis.com/auth/cloud-platform'
} );
const client = await auth.getClient ();
const url = `https://compute.googleapis.com/compute/v1/projects/${cdnSettings.projectId}/global/urlMaps/${cdnSettings.urlMapName}/invalidateCache`;
const headers = await client.getRequestHeaders ( url );
await Promise.all ( paths.map ( path => fetch ( url, {
method: 'POST',
headers,
body: JSON.stringify ( { path } )
} ) ) );
}
async function invalidateAmazonCDN ( cdnSettings, paths ) {
const client = new CloudFrontClient ();
const command = new CreateInvalidationCommand ( {
DistributionId: cdnSettings.distributionId,
InvalidationBatch: {
CallerReference: `${packageJson.name}-${Date.now ()}`,
Paths: {
Quantity: paths.length,
Items: paths,
},
},
} );
await client.send ( command );
}