diff --git a/serverless.yml b/serverless.yml index d5dac92..4fad0e2 100644 --- a/serverless.yml +++ b/serverless.yml @@ -1,9 +1,9 @@ -service: 'sveltekit-app' +service: "sveltekit-app" frameworkVersion: "3" plugins: - - '@silvermine/serverless-plugin-cloudfront-lambda-edge' + - "@silvermine/serverless-plugin-cloudfront-lambda-edge" - serverless-s3-deploy provider: @@ -25,23 +25,22 @@ custom: assets: auto: true targets: - - bucket: + - bucket: Ref: StaticAssets files: - source: ./build/assets/ - globs: - - '**' + globs: + - "**" empty: true headers: CacheControl: max-age=31104000 - source: ./build/prerendered/ - globs: - - '**' + globs: + - "**" empty: true headers: CacheControl: max-age=60 - functions: #SSR Function svelte: @@ -56,7 +55,7 @@ functions: memorySize: 128 timeout: 1 lambdaAtEdge: - distribution: 'WebsiteDistribution' + distribution: "WebsiteDistribution" eventType: origin-request resources: @@ -65,14 +64,14 @@ resources: Type: AWS::S3::Bucket Properties: BucketName: ${self:provider.stage}-${self:service}-static-assets + PublicAccessBlockConfiguration: + BlockPublicAcls: true + BlockPublicPolicy: true + IgnorePublicAcls: true + RestrictPublicBuckets: true OwnershipControls: Rules: - ObjectOwnership: BucketOwnerPreferred - PublicAccessBlockConfiguration: - BlockPublicAcls: false - BlockPublicPolicy: false - IgnorePublicAcls: false - RestrictPublicBuckets: false StaticAssetsS3BucketPolicy: Type: AWS::S3::BucketPolicy @@ -81,35 +80,54 @@ resources: Ref: StaticAssets PolicyDocument: Statement: - - Sid: PublicReadGetObject + - Sid: PolicyForCloudFrontPrivateContent Effect: Allow - Principal: "*" + Principal: + AWS: + Fn::Join: + [ + "", + ["arn:aws:iam::cloudfront:user/CloudFront Origin Access Identity ", { "Ref": "StaticAssetsOAI" }], + ] Action: - s3:GetObject Resource: Fn::Join: ["", ["arn:aws:s3:::", { "Ref": "StaticAssets" }, "/*"]] + StaticAssetsOAI: + Type: AWS::CloudFront::CloudFrontOriginAccessIdentity + Properties: + CloudFrontOriginAccessIdentityConfig: + Comment: "Access Identity for ${self:service} (${self:provider.stage})" + WebsiteDistribution: - Type: 'AWS::CloudFront::Distribution' + Type: "AWS::CloudFront::Distribution" Properties: DistributionConfig: Origins: - - - DomainName: !Select [2, !Split ["/", !GetAtt ["SvelteLambdaFunctionUrl", "FunctionUrl"]]] - Id: default - OriginCustomHeaders: + - DomainName: !Select [2, !Split ["/", !GetAtt ["SvelteLambdaFunctionUrl", "FunctionUrl"]]] + Id: svelte-server + OriginCustomHeaders: #Lambda@edge does not support ENV vars, so instead we have to pass in a customHeaders. - - - HeaderName: 's3-host' - HeaderValue: '${self:provider.stage}-${self:service}-static-assets.s3.amazonaws.com' + - HeaderName: "s3-host" + HeaderValue: "${self:provider.stage}-${self:service}-static-assets.s3.amazonaws.com" CustomOriginConfig: HTTPPort: 80 HTTPSPort: 443 - OriginProtocolPolicy: 'https-only' + OriginProtocolPolicy: "https-only" + - DomainName: !GetAtt ["StaticAssets", "DomainName"] + Id: static-assets + OriginCustomHeaders: + #Lambda@edge does not support ENV vars, so instead we have to pass in a customHeaders. + - HeaderName: "lambda-domain" + HeaderValue: !Select [2, !Split ["/", !GetAtt ["SvelteLambdaFunctionUrl", "FunctionUrl"]]] + S3OriginConfig: + OriginAccessIdentity: + Fn::Join: ["", ["origin-access-identity/cloudfront/", { "Ref": "StaticAssetsOAI" }]] Enabled: true - Comment: '${self:service}_${self:provider.stage}' + Comment: "${self:service}_${self:provider.stage}" DefaultCacheBehavior: - TargetOriginId: default + TargetOriginId: static-assets Compress: true AllowedMethods: - DELETE @@ -146,4 +164,4 @@ resources: } FunctionConfig: Comment: 'Add x-forwarded-host' - Runtime: cloudfront-js-1.0 \ No newline at end of file + Runtime: cloudfront-js-1.0 diff --git a/src/router.js b/src/router.js index 74579a9..b6b1360 100644 --- a/src/router.js +++ b/src/router.js @@ -1,47 +1,35 @@ -'use strict'; +"use strict"; -import staticFiles from './static.js' +import staticFiles from "./static.js"; exports.handler = (event, context, callback) => { const request = event.Records[0].cf.request; - //Only send GET request to S3 - if (request.method !== 'GET') { - callback(null, request); - return; - } - let uri = request.uri; - //If our path matches a static file, perfrom an origin re-write to S3; - if (staticFiles.includes(uri)) { - callback(null, performReWrite(uri, request)); - return; + if (!uri.includes(".") && uri.slice(-1) !== "/") { + uri += "/"; } - - //Remove the leading slash (if any) to normalise the path if (uri.slice(-1) === "/") { - uri = uri.substring(0, uri.length - 1); + uri += "index.html"; } - - //Pre-rendered pages could be named `/index.html` or `route/name.html` lets try looking for those as well - if (staticFiles.includes(uri + '/index.html')) { - callback(null, performReWrite(uri + '/index.html', request)); - return; - } - if (staticFiles.includes(uri + '.html')) { - callback(null, performReWrite(uri + '.html', request)); - return; + if (staticFiles.includes(uri)) { + request.uri = uri; + } else { + const domainName = request.origin.s3.customHeaders["lambda-domain"][0].value; + request.headers["host"] = [{ key: "Host", value: domainName }]; + request.origin.custom = { + domainName: domainName, + keepaliveTimeout: 5, + path: "", + port: 443, + protocol: "https", + readTimeout: 30, + sslProtocols: ["TLSv1", "SSLv3"], + }; + + // Cleanup request origin to only have custom configuration + delete request.origin.s3; } callback(null, request); }; - -function performReWrite(uri, request) { - request.uri = uri; - //Lambda@edge does not support ENV vars, so instead we have to pass in a customHeaders. - const domainName = request.origin.custom.customHeaders["s3-host"][0].value; - request.origin.custom.domainName = domainName; - request.origin.custom.path = ""; - request.headers["host"] = [{ key: "host", value: domainName }]; - return request; -} \ No newline at end of file