import { S3 } from 'aws-sdk' ; import unzipper from 'unzipper' ; import { lookup as mimeLookup , contentType as mimeContentType , } from 'mime-types' ; import { deploymentConfigurationKey } from './constants' ; import { generateRandomBuildId } from './utils' ; import { FileResult } from './types' ; const BuildIdMetaDataKey = 'x-amz-meta-tf-next-build-id' ; /**
 * Cache control header for immutable files that are stored in _next/static
 */ const CacheControlImmutable = 'public,max-age=31536000,immutable' ; /**
 * Static files that have no hashed filenames
 *
 * Must be refetched by the browser every time (max-age=0).
 * But CloudFront CDN can hold the copy infinite time until a invalidation
 * removes it (s-maxage=31536000).
 * https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/Expiration.html#ExpirationDownloadDist
 */ const CacheControlStatic = 'public,max-age=0,must-revalidate,s-maxage=31536000' ; interface Props { s3 : S3 ; sourceBucket : string ; deployBucket : string ; key : string ; versionId ? : string ; } interface Response { files : FileResult [ ] ; buildId : string ; } export async function deployTrigger ( { s3 , key , sourceBucket , deployBucket , versionId , } ) { let buildId = '' ; const params = { Key : key , Bucket : sourceBucket , VersionId : versionId , } ; const zipHeaders = await s3 . headObject ( params ) . promise ( ) ; if ( zipHeaders . Metadata && BuildIdMetaDataKey in zipHeaders . Metadata ) { buildId = zipHeaders . Metadata [ BuildIdMetaDataKey ] ; } else if ( zipHeaders . ETag ) { buildId = zipHeaders . ETag ; } else { buildId = generateRandomBuildId ( ) ; } const zip = s3 . getObject ( params ) . createReadStream ( ) . pipe ( unzipper . Parse ( { forceStream : true } ) ) ; const uploads = [ ] ; for await ( const e of zip ) { const entry = e as unzipper . Entry ; const fileName = entry . path ; const type = entry . type ; if ( type === 'File' ) { const mimeType = mimeLookup ( fileName ) ; const contentType = typeof mimeType === 'string' ? mimeContentType ( mimeType ) : false ; const cacheControl = fileName . startsWith ( '_next/' ) ? CacheControlImmutable : CacheControlStatic ; const uploadParams = { Bucket : deployBucket , Key : fileName , Body : entry , ContentType : typeof contentType === 'string' ? contentType : 'text/html; charset=utf-8' , CacheControl : cacheControl , } ; if ( fileName !== deploymentConfigurationKey ) { uploads . push ( s3 . upload ( uploadParams ) . promise ( ) ) ; } } else { entry . autodrain ( ) ; } } const files = ( await Promise . all ( uploads ) ) . map ( ( obj ) => { return { key : obj . Key , eTag : obj . ETag } ; } ) ; await s3 . deleteObject ( params ) . promise ( ) ; return { files , buildId , } ; }