diff --git a/first-cdk-deployment/first_cdk_deployment/first_cdk_deployment_stack.py b/first-cdk-deployment/first_cdk_deployment/first_cdk_deployment_stack.py
index 7a43f163c..773f33001 100644
--- a/first-cdk-deployment/first_cdk_deployment/first_cdk_deployment_stack.py
+++ b/first-cdk-deployment/first_cdk_deployment/first_cdk_deployment_stack.py
@@ -1,42 +1,33 @@
-# from aws_cdk import (
-# # Duration,
-# Stack,
-# # aws_sqs as sqs,
-# )
-# from constructs import Construct
-
-# class FirstCdkDeploymentStack(Stack):
-
-# def __init__(self, scope: Construct, construct_id: str, **kwargs) -> None:
-# super().__init__(scope, construct_id, **kwargs)
-
-# # The code that defines your stack goes here
-
-# # example resource
-# # queue = sqs.Queue(
-# # self, "FirstCdkDeploymentQueue",
-# # visibility_timeout=Duration.seconds(300),
-# # )
-
-
from aws_cdk import (
Stack,
aws_s3 as s3,
aws_cloudfront as cloudfront,
aws_cloudfront_origins as origins,
aws_s3_deployment as s3deploy,
+ aws_iam as iam,
CfnOutput,
RemovalPolicy
)
from constructs import Construct
-import os
class FirstCdkDeploymentStack(Stack):
def __init__(self, scope: Construct, construct_id: str, **kwargs) -> None:
super().__init__(scope, construct_id, **kwargs)
- # Create an S3 bucket to store the website files
- website_bucket = s3.Bucket(
+ # Create resources
+ website_bucket = self._create_website_bucket()
+ oac = self._create_origin_access_control()
+ distribution = self._create_cloudfront_distribution(website_bucket, oac)
+
+ # Add bucket policy after distribution is created
+ self._add_bucket_policy(website_bucket, distribution)
+
+ # Output CloudFront URL
+ self._create_cloudfront_output(distribution)
+
+ def _create_website_bucket(self) -> s3.Bucket:
+ """Create and configure S3 bucket for website hosting"""
+ return s3.Bucket(
self,
"ShopReactBucket",
block_public_access=s3.BlockPublicAccess.BLOCK_ALL,
@@ -44,38 +35,77 @@ def __init__(self, scope: Construct, construct_id: str, **kwargs) -> None:
auto_delete_objects=True
)
- # Create CloudFront distribution
- distribution = cloudfront.Distribution(
+ def _create_origin_access_control(self) -> cloudfront.CfnOriginAccessControl:
+ """Create Origin Access Control for CloudFront"""
+ return cloudfront.CfnOriginAccessControl(
+ self,
+ "OAC",
+ origin_access_control_config=cloudfront.CfnOriginAccessControl.OriginAccessControlConfigProperty(
+ name="S3OriginAccessControl",
+ origin_access_control_origin_type="s3",
+ signing_behavior="always",
+ signing_protocol="sigv4"
+ )
+ )
+
+ def _create_cloudfront_distribution(
+ self,
+ bucket: s3.Bucket,
+ oac: cloudfront.CfnOriginAccessControl
+ ) -> cloudfront.Distribution:
+ """Create and configure CloudFront distribution"""
+ return cloudfront.Distribution(
self,
"ShopReactDistribution",
default_behavior=cloudfront.BehaviorOptions(
- origin=origins.S3Origin(website_bucket),
- viewer_protocol_policy=cloudfront.ViewerProtocolPolicy.REDIRECT_TO_HTTPS,
- cache_policy=cloudfront.CachePolicy.CACHING_OPTIMIZED,
- ),
- default_root_object="index.html",
- error_responses=[
- cloudfront.ErrorResponse(
- http_status=404,
- response_http_status=200,
- response_page_path="/index.html"
+ origin=origins.S3BucketOrigin(
+ bucket,
+ origin_access_control_id=oac.ref
)
- ]
+ ),
+ default_root_object="index.html", # Added for SPA support
+ error_responses=self._get_error_responses() # Added for SPA support
)
- # Deploy site contents to S3
- s3deploy.BucketDeployment(
- self,
- "DeployShopReact",
- sources=[s3deploy.Source.asset(os.path.join(os.path.dirname(__file__), "..", "..", "dist"))],
- destination_bucket=website_bucket,
- distribution=distribution,
- distribution_paths=["/*"]
+ def _add_bucket_policy(
+ self,
+ bucket: s3.Bucket,
+ distribution: cloudfront.Distribution
+ ) -> None:
+ """Add bucket policy to allow CloudFront access"""
+ bucket.add_to_resource_policy(
+ iam.PolicyStatement(
+ actions=["s3:GetObject"],
+ resources=[bucket.arn_for_objects("*")],
+ principals=[iam.ServicePrincipal("cloudfront.amazonaws.com")],
+ conditions={
+ "StringEquals": {
+ "AWS:SourceArn": f"arn:aws:cloudfront::{Stack.of(self).account}:distribution/{distribution.distribution_id}"
+ }
+ }
+ )
)
- # Output the CloudFront URL
+ def _create_cloudfront_output(self, distribution: cloudfront.Distribution) -> None:
+ """Create CloudFront URL output"""
CfnOutput(
self,
"DistributionDomainName",
- value=distribution.distribution_domain_name
+ value=distribution.distribution_domain_name,
+ description="CloudFront Distribution Domain Name"
)
+
+ def _get_error_responses(self) -> list[cloudfront.ErrorResponse]:
+ """Configure error responses for SPA support"""
+ return [
+ cloudfront.ErrorResponse(
+ http_status=403,
+ response_http_status=200,
+ response_page_path="/index.html"
+ ),
+ cloudfront.ErrorResponse(
+ http_status=404,
+ response_http_status=200,
+ response_page_path="/index.html"
+ )
+ ]
diff --git a/src/components/pages/PageProducts/components/Products.tsx b/src/components/pages/PageProducts/components/Products.tsx
index 68eec6d69..9f986554d 100755
--- a/src/components/pages/PageProducts/components/Products.tsx
+++ b/src/components/pages/PageProducts/components/Products.tsx
@@ -32,6 +32,7 @@ export default function Products() {
{product.title}
+ Available: {count}
{formatAsPrice(product.price)}
diff --git a/src/queries/products.ts b/src/queries/products.ts
index b24ce6d54..dd8f8651a 100644
--- a/src/queries/products.ts
+++ b/src/queries/products.ts
@@ -50,7 +50,7 @@ export function useRemoveProductCache() {
export function useUpsertAvailableProduct() {
return useMutation((values: AvailableProduct) =>
- axios.put(`${API_PATHS.bff}/product`, values, {
+ axios.put(`${API_PATHS.product}/product`, values, {
headers: {
Authorization: `Basic ${localStorage.getItem("authorization_token")}`,
},
@@ -60,7 +60,7 @@ export function useUpsertAvailableProduct() {
export function useDeleteAvailableProduct() {
return useMutation((id: string) =>
- axios.delete(`${API_PATHS.bff}/product/${id}`, {
+ axios.delete(`${API_PATHS.product}/product/${id}`, {
headers: {
Authorization: `Basic ${localStorage.getItem("authorization_token")}`,
},