@@ -619,6 +619,9 @@ Resources:
619
619
Type : AWS::S3::Bucket
620
620
Properties :
621
621
BucketName : !Sub ${S3BucketPrefix}-ui
622
+ WebsiteConfiguration :
623
+ IndexDocument : index.html
624
+ ErrorDocument : index.html
622
625
623
626
AppCloudfrontS3OAC :
624
627
Type : AWS::CloudFront::OriginAccessControl
@@ -672,9 +675,10 @@ Resources:
672
675
Cookies :
673
676
Forward : none
674
677
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
+
678
682
CacheBehaviors :
679
683
- PathPattern : " /api/v1/events*"
680
684
TargetOriginId : LambdaOrigin
@@ -747,11 +751,21 @@ Resources:
747
751
- Effect : Allow
748
752
Principal :
749
753
Service : cloudfront.amazonaws.com
750
- Action : s3:GetObject
754
+ Action :
755
+ - s3:GetObject
751
756
Resource : !Sub "${AppFrontendS3Bucket.Arn}/*"
752
757
Condition :
753
758
StringEquals :
754
759
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}"
755
769
756
770
CloudfrontNoCachePolicy :
757
771
Type : AWS::CloudFront::CachePolicy
@@ -796,36 +810,32 @@ Resources:
796
810
CookiesConfig :
797
811
CookieBehavior : none
798
812
799
- AppFrontendEdgeLambda :
800
- Type : AWS::Lambda::Function
801
- DependsOn :
802
- - AppLogGroups
813
+ AppFrontendViewerRequestFunction :
814
+ Type : AWS::CloudFront::Function
803
815
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
+ }
824
831
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
+ }
829
839
830
840
AppIcalCloudfrontDistribution :
831
841
Type : AWS::CloudFront::Distribution
@@ -897,40 +907,40 @@ Resources:
897
907
KeyValueStoreAssociations :
898
908
- KeyValueStoreARN : !Sub '${LinkryRecordsCloudfrontStore.Arn}'
899
909
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
+ }
913
924
}
914
925
}
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}`);
921
934
}
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;
924
943
}
925
- var response = {
926
- statusCode: 302,
927
- statusDescription: 'Found',
928
- headers: {
929
- 'location': { value: redirectUrl }
930
- }
931
- };
932
- return response;
933
- }
934
944
AutoPublish : true
935
945
936
946
AppLinkryCloudfrontDistribution :
0 commit comments