@@ -619,6 +619,9 @@ Resources:
619619 Type : AWS::S3::Bucket
620620 Properties :
621621 BucketName : !Sub ${S3BucketPrefix}-ui
622+ WebsiteConfiguration :
623+ IndexDocument : index.html
624+ ErrorDocument : index.html
622625
623626 AppCloudfrontS3OAC :
624627 Type : AWS::CloudFront::OriginAccessControl
@@ -672,9 +675,10 @@ Resources:
672675 Cookies :
673676 Forward : none
674677 CachePolicyId : 658327ea-f89d-4fab-a63d-7e88639e58f6 # caching-optimized
675- LambdaFunctionAssociations :
676- - EventType : origin-request
677- LambdaFunctionARN : !Ref AppFrontendEdgeLambdaVersion
678+ FunctionAssociations :
679+ - EventType : viewer-request
680+ FunctionARN : !GetAtt AppFrontendViewerRequestFunction.FunctionARN
681+
678682 CacheBehaviors :
679683 - PathPattern : " /api/v1/events*"
680684 TargetOriginId : LambdaOrigin
@@ -747,11 +751,21 @@ Resources:
747751 - Effect : Allow
748752 Principal :
749753 Service : cloudfront.amazonaws.com
750- Action : s3:GetObject
754+ Action :
755+ - s3:GetObject
751756 Resource : !Sub "${AppFrontendS3Bucket.Arn}/*"
752757 Condition :
753758 StringEquals :
754759 AWS:SourceArn : !Sub "arn:aws:cloudfront::${AWS::AccountId}:distribution/${AppFrontendCloudfrontDistribution}"
760+ - Effect : Allow
761+ Principal :
762+ Service : cloudfront.amazonaws.com
763+ Action :
764+ - s3:ListBucket
765+ Resource : !Sub "${AppFrontendS3Bucket.Arn}"
766+ Condition :
767+ StringEquals :
768+ AWS:SourceArn : !Sub "arn:aws:cloudfront::${AWS::AccountId}:distribution/${AppFrontendCloudfrontDistribution}"
755769
756770 CloudfrontNoCachePolicy :
757771 Type : AWS::CloudFront::CachePolicy
@@ -796,36 +810,32 @@ Resources:
796810 CookiesConfig :
797811 CookieBehavior : none
798812
799- AppFrontendEdgeLambda :
800- Type : AWS::Lambda::Function
801- DependsOn :
802- - AppLogGroups
813+ AppFrontendViewerRequestFunction :
814+ Type : AWS::CloudFront::Function
803815 Properties :
804- FunctionName : !Sub ${ApplicationPrefix}-lambda-edge
805- Handler : " index.handler"
806- Role : !GetAtt AppSecurityRoles.Outputs.EdgeFunctionRoleArn
807- Runtime : nodejs22.x
808- Code :
809- ZipFile : |
810- 'use strict';
811- exports.handler = async (event) => {
812- const request = event.Records[0].cf.request;
813- const uri = request.uri;
814- if (uri === '/docs') {
815- request.uri = "/docs/index.html";
816- }
817- if (!uri.startsWith('/api') && !uri.match(/\.\w+$/)) {
818- request.uri = "/index.html";
819- }
820- return request;
821- };
822- MemorySize : 128
823- Timeout : 5
816+ Name : !Sub ${ApplicationPrefix}-url-rewrite-function
817+ AutoPublish : true
818+ FunctionConfig :
819+ Comment : " Handles SPA routing by rewriting URIs to index.html"
820+ Runtime : cloudfront-js-2.0
821+ FunctionCode : |
822+ function handler(event) {
823+ var request = event.request;
824+ var uri = request.uri;
825+
826+ // Rewrite /docs or /docs/ to the documentation index file
827+ if (uri === '/docs' || uri === '/docs/') {
828+ request.uri = '/docs/index.html';
829+ return request;
830+ }
824831
825- AppFrontendEdgeLambdaVersion :
826- Type : AWS::Lambda::Version
827- Properties :
828- FunctionName : !Ref AppFrontendEdgeLambda
832+ // Rewrite paths for the SPA, excluding /api and files with extensions
833+ if (!uri.startsWith('/api') && !uri.includes('.')) {
834+ request.uri = '/index.html';
835+ }
836+
837+ return request;
838+ }
829839
830840 AppIcalCloudfrontDistribution :
831841 Type : AWS::CloudFront::Distribution
@@ -897,40 +907,40 @@ Resources:
897907 KeyValueStoreAssociations :
898908 - KeyValueStoreARN : !Sub '${LinkryRecordsCloudfrontStore.Arn}'
899909 FunctionCode : !Sub |
900- import cf from 'cloudfront';
901- const kvsId = '${LinkryRecordsCloudfrontStore.Id}';
902- const kvs = cf.kvs(kvsId);
903-
904- async function handler(event) {
905- const request = event.request;
906- const path = request.uri.replace(/^\/+/, '');
907- if (path === "") {
908- return {
909- statusCode: 301,
910- statusDescription: 'Found',
911- headers: {
912- 'location': { value: "https://core.acm.illinois.edu/linkry" }
910+ import cf from 'cloudfront';
911+ const kvsId = '${LinkryRecordsCloudfrontStore.Id}';
912+ const kvs = cf.kvs(kvsId);
913+
914+ async function handler(event) {
915+ const request = event.request;
916+ const path = request.uri.replace(/^\/+/, '');
917+ if (path === "") {
918+ return {
919+ statusCode: 301,
920+ statusDescription: 'Found',
921+ headers: {
922+ 'location': { value: "https://core.acm.illinois.edu/linkry" }
923+ }
913924 }
914925 }
915- }
916- let redirectUrl = "https://acm.illinois.edu/404";
917- try {
918- const value = await kvs.get(path);
919- if (value) {
920- redirectUrl = value;
926+ let redirectUrl = "https://acm.illinois.edu/404";
927+ try {
928+ const value = await kvs.get(path);
929+ if (value) {
930+ redirectUrl = value;
931+ }
932+ } catch (err) {
933+ console.log(`KVS key lookup failed for $!{path}: $!{err}`);
921934 }
922- } catch (err) {
923- console.log('KVS key lookup failed');
935+ var response = {
936+ statusCode: 302,
937+ statusDescription: 'Found',
938+ headers: {
939+ 'location': { value: redirectUrl }
940+ }
941+ };
942+ return response;
924943 }
925- var response = {
926- statusCode: 302,
927- statusDescription: 'Found',
928- headers: {
929- 'location': { value: redirectUrl }
930- }
931- };
932- return response;
933- }
934944 AutoPublish : true
935945
936946 AppLinkryCloudfrontDistribution :
0 commit comments