import { S3Event , S3EventRecord , SQSEvent , SQSRecord } from 'aws-lambda' ; import { S3 , CloudFront , SQS } from 'aws-sdk' ; import { deployTrigger } from './deploy-trigger' ; import { ExpireValue } from './types' ; import { updateManifest } from './update-manifest' ; import { deploymentConfigurationKey } from './constants' ; import { getOrCreateManifest } from './get-or-create-manifest' ; import { createInvalidation , prepareInvalidations , } from './create-invalidation' ; import { generateRandomId } from './utils' ; interface InvalidationSQSMessage { id : string ; distributionId : string ; retries : number ; multiPaths : string [ ] ; singlePaths : string [ ] ; } const defaultExpireAfterDays = 30 ; const timeOutBetweenInvalidations = 60 ; const numberOfRetriesForInvalidation = 3 ; function parseExpireAfterDays ( ) { if ( process . env . EXPIRE_AFTER_DAYS ) { if ( process . env . EXPIRE_AFTER_DAYS === 'never' ) { return 'never' ; } try { const days = Number ( process . env . EXPIRE_AFTER_DAYS ) ; if ( days >= 0 ) { return days ; } return 'never' ; } catch ( err ) { return defaultExpireAfterDays ; } } return defaultExpireAfterDays ; } async function createCloudFrontInvalidation ( incomingMultiPaths , incomingSinglePaths , distributionId , incomingRetries ) { let retries = incomingRetries ; const cloudFrontClient = new CloudFront ( { apiVersion : '2020-05-31' , } ) ; const invalidationId = generateRandomId ( 4 ) ; let [ InvalidationBatch , multiPaths , singlePaths ] = createInvalidation ( invalidationId , incomingMultiPaths , incomingSinglePaths ) ; try { await cloudFrontClient . createInvalidation ( { DistributionId : distributionId , InvalidationBatch , } ) . promise ( ) ; } catch ( err ) { console . log ( err ) ; if ( err . code === 'TooManyInvalidationsInProgress' ) { if ( retries < numberOfRetriesForInvalidation ) { console . log ( 'Invalidation rescheduled' ) ; retries = retries + 1 ; multiPaths = incomingMultiPaths ; singlePaths = incomingSinglePaths ; } } } if ( multiPaths . length + singlePaths . length > 0 ) { const MessageBody = { id : invalidationId , distributionId , multiPaths , singlePaths , retries , } ; const sqsClient = new SQS ( ) ; try { await sqsClient . sendMessage ( { QueueUrl : process . env . SQS_QUEUE_URL , MessageBody : JSON . stringify ( MessageBody ) , DelaySeconds : timeOutBetweenInvalidations , } ) . promise ( ) ; } catch ( err ) { console . log ( err ) ; } } } param event export const handler = async function ( event ) { for ( const Record of event . Records ) { if ( 's3' in Record ) { await s3Handler ( Record ) ; } else { await sqsHandler ( Record ) ; } } } ; async function s3Handler ( Record ) { let s3 ; if ( process . env . __DEBUG__USE_LOCAL_BUCKET ) { s3 = new S3 ( JSON . parse ( process . env . __DEBUG__USE_LOCAL_BUCKET ) ) ; } else { s3 = new S3 ( { apiVersion : '2006-03-01' } ) ; } const deployBucket = process . env . TARGET_BUCKET ; const distributionId = process . env . DISTRIBUTION_ID ; const expireAfterDays = parseExpireAfterDays ( ) ; const { object } = Record . s3 ; const { versionId , key } = object ; const sourceBucket = Record . s3 . bucket . name ; const manifest = await getOrCreateManifest ( s3 , deployBucket , deploymentConfigurationKey ) ; const { files , buildId } = await deployTrigger ( { s3 , sourceBucket , deployBucket , key , versionId , } ) ; const { invalidate : invalidationPaths } = await updateManifest ( { s3 , bucket : deployBucket , expireAfterDays , files , buildId , deploymentConfigurationKey , manifest , } ) ; if ( ! process . env . __DEBUG__SKIP_INVALIDATIONS ) { const [ multiPaths , singlePaths ] = prepareInvalidations ( invalidationPaths ) ; await createCloudFrontInvalidation ( multiPaths , singlePaths , distributionId , 0 ) ; } } async function sqsHandler ( Record ) { const body = JSON . parse ( Record . body ) as InvalidationSQSMessage ; await createCloudFrontInvalidation ( body . multiPaths , body . singlePaths , body . distributionId , body . retries ) ; }