Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 2 additions & 25 deletions first-cdk-deployment/app.py
Original file line number Diff line number Diff line change
@@ -1,31 +1,8 @@
#!/usr/bin/env python3
import os

import aws_cdk as cdk

from first_cdk_deployment.first_cdk_deployment_stack import FirstCdkDeploymentStack

from first_cdk_deployment.first_cdk_deployment_stack import WebsiteStack

app = cdk.App()
FirstCdkDeploymentStack(app, "FirstCdkDeploymentStack",
# If you don't specify 'env', this stack will be environment-agnostic.
# Account/Region-dependent features and context lookups will not work,
# but a single synthesized template can be deployed anywhere.

# Uncomment the next line to specialize this stack for the AWS Account
# and Region that are implied by the current CLI configuration.

#env=cdk.Environment(account=os.getenv('CDK_DEFAULT_ACCOUNT'), region=os.getenv('CDK_DEFAULT_REGION')),

# Uncomment the next line if you know exactly what Account and Region you
# want to deploy the stack to. */

#env=cdk.Environment(account='123456789012', region='us-east-1'),

# For more information, see https://docs.aws.amazon.com/cdk/latest/guide/environments.html
)

WebsiteStack(app, "WebsiteStack")
app.synth()


#!/usr/bin/env python3
5 changes: 5 additions & 0 deletions first-cdk-deployment/cdk.context.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"acknowledged-issue-numbers": [
32775
]
}
Original file line number Diff line number Diff line change
@@ -1,24 +1,3 @@
# 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,
Expand All @@ -29,32 +8,49 @@
RemovalPolicy
)
from constructs import Construct
import os

class FirstCdkDeploymentStack(Stack):
class WebsiteStack(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
# Create an S3 bucket to host the website
website_bucket = s3.Bucket(
self,
"ShopReactBucket",
block_public_access=s3.BlockPublicAccess.BLOCK_ALL,
removal_policy=RemovalPolicy.DESTROY,
auto_delete_objects=True
"WebsiteBucket",
removal_policy=RemovalPolicy.DESTROY, # NOT recommended for production
auto_delete_objects=True, # NOT recommended for production
block_public_access=s3.BlockPublicAccess.BLOCK_ALL
)

# Create CloudFront distribution
distribution = cloudfront.Distribution(
# Create Origin Access Identity for CloudFront
origin_access_identity = cloudfront.OriginAccessIdentity(
self,
"ShopReactDistribution",
"OriginAccessIdentity",
comment="CloudFront access to S3"
)

# Grant read permissions for CloudFront
website_bucket.grant_read(origin_access_identity)

# Create CloudFront Distribution
distribution = cloudfront.Distribution(
self,
"Distribution",
default_behavior=cloudfront.BehaviorOptions(
origin=origins.S3Origin(website_bucket),
origin=origins.S3Origin(
website_bucket,
origin_access_identity=origin_access_identity
),
viewer_protocol_policy=cloudfront.ViewerProtocolPolicy.REDIRECT_TO_HTTPS,
cache_policy=cloudfront.CachePolicy.CACHING_OPTIMIZED,
cache_policy=cloudfront.CachePolicy.CACHING_OPTIMIZED
),
default_root_object="index.html",
error_responses=[
cloudfront.ErrorResponse(
http_status=403,
response_http_status=200,
response_page_path="/index.html"
),
cloudfront.ErrorResponse(
http_status=404,
response_http_status=200,
Expand All @@ -63,19 +59,22 @@ def __init__(self, scope: Construct, construct_id: str, **kwargs) -> None:
]
)

# Deploy site contents to S3
# Deploy site contents to S3 bucket
s3deploy.BucketDeployment(
self,
"DeployShopReact",
sources=[s3deploy.Source.asset(os.path.join(os.path.dirname(__file__), "..", "..", "dist"))],
self,
"DeployWebsite",
sources=[s3deploy.Source.asset("../dist")], # Adjust this path
destination_bucket=website_bucket,
distribution=distribution,
distribution_paths=["/*"]
)

# Output the CloudFront URL
CfnOutput(
self,
"DistributionDomainName",
value=distribution.distribution_domain_name
self,
"CloudFrontURL",
value=f"https://{distribution.distribution_domain_name}",
description="Website URL"
)


3 changes: 2 additions & 1 deletion first-cdk-deployment/requirements.txt
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
aws-cdk-lib==2.178.1
aws-cdk-lib==2.124.0
constructs>=10.0.0,<11.0.0
typeguard>=2.13.3
32 changes: 32 additions & 0 deletions src/components/ToastProvider/ToastProvider.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import React, { useState, useEffect } from "react";
import { Snackbar, Alert } from "@mui/material";

const ToastProvider: React.FC<{ children: React.ReactNode }> = ({ children }) => {
const [open, setOpen] = useState(false);
const [message, setMessage] = useState("");
const [severity, setSeverity] = useState<"error" | "warning" | "info" | "success">("error");

useEffect(() => {
const handleToast = (event: CustomEvent) => {
setMessage(event.detail.message);
setSeverity(event.detail.severity || "error");
setOpen(true);
};

window.addEventListener("global-toast", handleToast as EventListener);
return () => window.removeEventListener("global-toast", handleToast as EventListener);
}, []);

return (
<>
{children}
<Snackbar open={open} autoHideDuration={3000} onClose={() => setOpen(false)}>
<Alert severity={severity} onClose={() => setOpen(false)}>
{message}
</Alert>
</Snackbar>
</>
);
};

export default ToastProvider;
1 change: 1 addition & 0 deletions src/components/pages/PageProducts/components/Products.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ export default function Products() {
<Typography gutterBottom variant="h5" component="h2">
{product.title}
</Typography>
<Typography> Available: {count}</Typography>
<Typography>{formatAsPrice(product.price)}</Typography>
</CardContent>
<CardActions>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,20 +1,25 @@
import React from "react";
import Typography from "@mui/material/Typography";
import Box from "@mui/material/Box";
import axios from "axios";
import { Button, Snackbar, Alert } from "@mui/material";

type CSVFileImportProps = {
url: string;
title: string;
};

export default function CSVFileImport({ url, title }: CSVFileImportProps) {
const [file, setFile] = React.useState<File>();
const [file, setFile] = React.useState<File | undefined>(undefined);
const [error, setError] = React.useState<{ message: string; open: boolean }>({
message: "",
open: false,
});

const onFileChange = (e: React.ChangeEvent<HTMLInputElement>) => {
const files = e.target.files;
if (files && files.length > 0) {
const file = files[0];
setFile(file);
setFile(files[0]);
}
};

Expand All @@ -23,25 +28,61 @@ export default function CSVFileImport({ url, title }: CSVFileImportProps) {
};

const uploadFile = async () => {
console.log("uploadFile to", url);

// Get the presigned URL
// const response = await axios({
// method: "GET",
// url,
// params: {
// name: encodeURIComponent(file.name),
// },
// });
// console.log("File to upload: ", file.name);
// console.log("Uploading to: ", response.data);
// const result = await fetch(response.data, {
// method: "PUT",
// body: file,
// });
// console.log("Result: ", result);
// setFile("");
if (!file) {
console.error("No file selected.");
return;
}

// Retrieve the token from local storage
const token = localStorage.getItem("authorization_token");
console.log("Authorization Token:", token); // Log the token

// Set up the headers with an empty authorization if token is null
const headers = {
Authorization: token ? `Basic ${token}` : "", // Allow empty header
};

console.log("Uploading file to", url);

try {
// Get the presigned URL
const response = await axios.get(url, {
params: { name: encodeURIComponent(file.name) },
headers,
});

console.log("File to upload: ", file.name);
console.log("Uploading to: ", response.data);

// Upload file to the signed URL
const result = await fetch(response.data, {
method: "PUT",
body: file,
headers: {
"Content-Type": "text/csv",
},
});

console.log("Result: ", result);
setFile(undefined); // Clear the file after successful upload
} catch (error: any) {
if (axios.isAxiosError(error)) {
// Handle specific status codes
const status = error.response?.status;
if (status === 401) {
setError({ message: "Unauthorized. Please sign in.", open: true });
} else if (status === 403) {
setError({ message: "You don't have permission to upload this file.", open: true });
} else {
setError({ message: "An error occurred while uploading the file.", open: true });
}
} else {
setError({ message: "An unexpected error occurred.", open: true });
}
console.error("Error uploading file:", error);
}
};

return (
<Box>
<Typography variant="h6" gutterBottom>
Expand All @@ -51,10 +92,29 @@ export default function CSVFileImport({ url, title }: CSVFileImportProps) {
<input type="file" onChange={onFileChange} />
) : (
<div>
<button onClick={removeFile}>Remove file</button>
<button onClick={uploadFile}>Upload file</button>
<Button variant="contained" color="secondary" onClick={removeFile}>
Remove file
</Button>
<Button variant="contained" color="primary" onClick={uploadFile}>
Upload file
</Button>
</div>
)}

{/* Error Snackbar */}
<Snackbar
open={error.open}
autoHideDuration={6000}
onClose={() => setError({ ...error, open: false })}
anchorOrigin={{ vertical: "top", horizontal: "center" }}
>
<Alert
onClose={() => setError({ ...error, open: false })}
severity="error"
>
{error.message}
</Alert>
</Snackbar>
</Box>
);
}
6 changes: 4 additions & 2 deletions src/constants/apiPaths.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
const API_PATHS = {
//product: "https://.execute-api.eu-west-1.amazonaws.com/dev",https://pj8m5nmg5m.execute-api.us-east-1.amazonaws.com/prod/products/
//product: "https://i58jmttgyj.execute-api.us-east-1.amazonaws.com/prod/products",https://pj8m5nmg5m.execute-api.us-east-1.amazonaws.com/prod/products
product: "https://pj8m5nmg5m.execute-api.us-east-1.amazonaws.com/prod",
// product: "https://pj8m5nmg5m.execute-api.us-east-1.amazonaws.com/prod",https://c00aj28k2m.execute-api.us-east-1.amazonaws.com/prod/products
// import: "https://5uj67empu0.execute-api.us-east-1.amazonaws.com/prod",
product: "https://c00aj28k2m.execute-api.us-east-1.amazonaws.com/prod",
order: "https://.execute-api.eu-west-1.amazonaws.com/dev",
import: "https://.execute-api.eu-west-1.amazonaws.com/dev",
import: "https://tpv5ydqmbg.execute-api.us-east-1.amazonaws.com/prod",
bff: "https://.execute-api.eu-west-1.amazonaws.com/dev",
cart: "https://.execute-api.eu-west-1.amazonaws.com/dev",
};
Expand Down
Loading