Skip to content

Commit

Permalink
Add Preview deployments + Cloudfront distro (#6)
Browse files Browse the repository at this point in the history
  • Loading branch information
lukqw committed Nov 23, 2023
1 parent 022a747 commit 3f608fe
Show file tree
Hide file tree
Showing 31 changed files with 1,137 additions and 951 deletions.
38 changes: 38 additions & 0 deletions .github/workflows/preview.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
name: Create PR Preview

on:
pull_request:
types: [opened, synchronize, reopened]

jobs:
preview:
permissions: write-all
runs-on: ubuntu-latest
timeout-minutes: 15
steps:
- name: Checkout
uses: actions/checkout@v4

- name: Deploy Preview
uses: LocalStack/setup-localstack/preview@main
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
localstack-api-key: ${{ secrets.LOCALSTACK_API_KEY }}
preview-cmd: |
npm install -g aws-cdk-local aws-cdk
pip install awscli-local[ver1]
make build
make bootstrap
make deploy
make prepare-frontend-local
make build-frontend
make bootstrap-frontend
make deploy-frontend
distributionId=$(awslocal cloudfront list-distributions | jq -r '.DistributionList.Items[0].Id')
echo "Open URL: $AWS_ENDPOINT_URL/cloudfront/$AWS_DEFAULT_REGION/$distributionId/"
- name: Finalize PR comment
uses: LocalStack/setup-localstack/finish@main
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
include-preview: true
50 changes: 35 additions & 15 deletions README.md

Large diffs are not rendered by default.

Binary file removed aws-sdk-js-notes.png
Binary file not shown.
Binary file added images/aws-sdk-js-notes.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
3 changes: 2 additions & 1 deletion packages/backend/src/createNote.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { success, failure } from "./libs/response";

// eslint-disable-next-line no-unused-vars
import { APIGatewayEvent } from "aws-lambda";
import { endpoint } from "./libs/endpoint";

export const handler = async (event: APIGatewayEvent) => {
const data = JSON.parse(event.body || "{}");
Expand All @@ -19,7 +20,7 @@ export const handler = async (event: APIGatewayEvent) => {
};

try {
const client = new DynamoDBClient({});
const client = new DynamoDBClient({endpoint});
await client.send(new PutItemCommand(params));
return success(params.Item);
} catch (e) {
Expand Down
3 changes: 2 additions & 1 deletion packages/backend/src/deleteNote.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { success, failure } from "./libs/response";

// eslint-disable-next-line no-unused-vars
import { APIGatewayEvent } from "aws-lambda";
import { endpoint } from "./libs/endpoint";

export const handler = async (event: APIGatewayEvent) => {
const params = {
Expand All @@ -14,7 +15,7 @@ export const handler = async (event: APIGatewayEvent) => {
};

try {
const client = new DynamoDBClient({});
const client = new DynamoDBClient({endpoint});
await client.send(new DeleteItemCommand(params));
return success({ status: true });
} catch (e) {
Expand Down
3 changes: 2 additions & 1 deletion packages/backend/src/getNote.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { success, failure } from "./libs/response";

// eslint-disable-next-line no-unused-vars
import { APIGatewayEvent } from "aws-lambda";
import { endpoint } from "./libs/endpoint";

export const handler = async (event: APIGatewayEvent) => {
const params = {
Expand All @@ -14,7 +15,7 @@ export const handler = async (event: APIGatewayEvent) => {
};

try {
const client = new DynamoDBClient({});
const client = new DynamoDBClient({endpoint});
const result = await client.send(new GetItemCommand(params));
if (result.Item) {
// Return the retrieved item
Expand Down
1 change: 1 addition & 0 deletions packages/backend/src/libs/endpoint.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export const endpoint = process.env.AWS_ENDPOINT_URL || '';
4 changes: 3 additions & 1 deletion packages/backend/src/listNotes.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,16 @@
import { DynamoDBClient, ScanCommand } from "@aws-sdk/client-dynamodb";
import { unmarshall } from "@aws-sdk/util-dynamodb";
import { success, failure } from "./libs/response";
import { endpoint } from "./libs/endpoint";

export const handler = async () => {
const params = {
TableName: process.env.NOTES_TABLE_NAME || "",
};


try {
const client = new DynamoDBClient({});
const client = new DynamoDBClient({endpoint});
const result = await client.send(new ScanCommand(params));
// Return the matching list of items in response body
return success(result.Items.map((Item) => unmarshall(Item)));
Expand Down
3 changes: 2 additions & 1 deletion packages/backend/src/updateNote.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { success, failure } from "./libs/response";

// eslint-disable-next-line no-unused-vars
import { APIGatewayEvent } from "aws-lambda";
import { endpoint } from "./libs/endpoint";

export const handler = async (event: APIGatewayEvent) => {
const data = JSON.parse(event.body || "{}");
Expand All @@ -23,7 +24,7 @@ export const handler = async (event: APIGatewayEvent) => {
};

try {
const client = new DynamoDBClient({});
const client = new DynamoDBClient({endpoint});
await client.send(new UpdateItemCommand(params));
return success({ status: true });
} catch (e) {
Expand Down
2 changes: 1 addition & 1 deletion packages/frontend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,14 +13,14 @@
"@aws-sdk/s3-request-presigner": "3.245.0",
"@aws-sdk/util-create-request": "3.234.0",
"@aws-sdk/util-format-url": "3.226.0",
"@reach/router": "1.3.4",
"buffer": "6.0.3",
"events": "^3.3.0",
"microphone-stream": "6.0.1",
"process": "0.11.10",
"react": "18.2.0",
"react-bootstrap": "1.4.0",
"react-dom": "18.2.0",
"react-router-dom": "^6.20.0",
"util": "^0.12.5"
},
"scripts": {
Expand Down
30 changes: 17 additions & 13 deletions packages/frontend/src/Routes.tsx
Original file line number Diff line number Diff line change
@@ -1,20 +1,24 @@
import React, { lazy, Suspense } from "react";
import { Router } from "@reach/router";
import React from "react";
import { Suspense } from "react";
import { BrowserRouter, Route, Routes as RouterRoutes } from "react-router-dom";
import ListNotes from "./content/ListNotes";
import CreateNote from "./content/CreateNote";
import ShowNote from "./content/ShowNote";
import NotFound from "./content/NotFound";

const ListNotes = lazy(() => import("./content/ListNotes"));
const CreateNote = lazy(() => import("./content/CreateNote"));
const ShowNote = lazy(() => import("./content/ShowNote"));
const NotFound = lazy(() => import("./content/NotFound"));
import { BASE_URL } from "./config";

const Routes = () => (
const Routes = () => (
<div className="mt-md-4 d-flex flex-column justify-content-center">
<Suspense fallback={<div>Loading...</div>}>
<Router>
<ListNotes path="/" />
<CreateNote path="/note/new" />
<ShowNote path="/notes/:noteId" />
<NotFound default />
</Router>
<BrowserRouter basename={BASE_URL}>
<RouterRoutes>
<Route path="/" element={<ListNotes/>} />
<Route path="/note/new" element={<CreateNote/>} />
<Route path="/notes/:noteId" element={<ShowNote/>} />
<Route element={<NotFound/>} />
</RouterRoutes>
</BrowserRouter>
</Suspense>
</div>
);
Expand Down
4 changes: 3 additions & 1 deletion packages/frontend/src/components/HomeButton.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
import React from "react";
import { Button } from "react-bootstrap";
import { BASE_URL } from "../config";


const HomeButton = () => (
<Button href="/" variant="light" className="border border-secondary">
<Button href={BASE_URL} variant="light" className="border border-secondary">
&lt; Home
</Button>
);
Expand Down
7 changes: 0 additions & 7 deletions packages/frontend/src/config.json

This file was deleted.

15 changes: 15 additions & 0 deletions packages/frontend/src/config.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
const getOrigin = () => {
const origin = window.location.origin;
if (origin.indexOf('localhost') !== -1) {
return 'http://localhost:4566';
}
return origin;
}


export const GATEWAY_URL = `${getOrigin()}/restapis/${import.meta.env.VITE_GATEWAY_ID}/prod/_user_request_/`;
export const MAX_FILE_SIZE = 500000;
export const FILES_BUCKET = import.meta.env.VITE_FILES_BUCKET;
export const REGION = import.meta.env.VITE_REGION;
export const IDENTITY_POOL_ID = import.meta.env.VITE_IDENTITY_POOL_ID;
export const BASE_URL = import.meta.env.BASE_URL;
8 changes: 5 additions & 3 deletions packages/frontend/src/content/CreateNote.tsx
Original file line number Diff line number Diff line change
@@ -1,16 +1,18 @@
import React, { useState, FormEvent } from "react";
import { Form, Button, Alert } from "react-bootstrap";
import { navigate, RouteComponentProps } from "@reach/router";
import { GATEWAY_URL, MAX_FILE_SIZE } from "../config.json";
import { GATEWAY_URL, MAX_FILE_SIZE } from "../config";
import { putObject } from "../libs";
import { HomeButton, ButtonSpinner, PageContainer } from "../components";
import { useNavigate } from "react-router-dom";

const CreateNote = (props: RouteComponentProps) => {
const CreateNote = (): JSX.Element => {
const [isLoading, setIsLoading] = useState(false);
const [errorMsg, setErrorMsg] = useState("");
const [noteContent, setNoteContent] = useState("");
const [file, setFile] = useState();

const navigate = useNavigate();

const handleSubmit = async (event: FormEvent<HTMLFormElement>) => {
event.preventDefault();

Expand Down
6 changes: 4 additions & 2 deletions packages/frontend/src/content/DeleteNoteButton.tsx
Original file line number Diff line number Diff line change
@@ -1,15 +1,17 @@
import React, { useState } from "react";
import { Button, Alert } from "react-bootstrap";
import { GATEWAY_URL } from "../config.json";
import { navigate } from "@reach/router";
import { GATEWAY_URL } from "../config";
import { deleteObject } from "../libs";
import { ButtonSpinner } from "../components";
import { useNavigate } from "react-router-dom";

const DeleteNoteButton = (props: { noteId: string; attachment?: string }) => {
const { noteId, attachment } = props;
const [isDeleting, setIsDeleting] = useState(false);
const [errorMsg, setErrorMsg] = useState("");

const navigate = useNavigate();

const handleDelete = async (event: any) => {
event.preventDefault();
setIsDeleting(true);
Expand Down
6 changes: 3 additions & 3 deletions packages/frontend/src/content/ListNotes.tsx
Original file line number Diff line number Diff line change
@@ -1,16 +1,16 @@
import React, { useState, useEffect } from "react";
import { Link, RouteComponentProps } from "@reach/router";
import { GATEWAY_URL } from "../config.json";
import { GATEWAY_URL } from "../config";
import { Card, Alert, CardColumns, Button } from "react-bootstrap";
import { Loading, PageContainer } from "../components";
import { Link } from "react-router-dom";
interface Note {
noteId: string;
createdAt: string;
content: string;
attachment: boolean;
}

const ListNotes = (props: RouteComponentProps) => {
const ListNotes = (): JSX.Element => {
const [isLoading, setIsLoading] = useState(true);
const [errorMsg, setErrorMsg] = useState("");
const [notes, setNotes] = useState([]);
Expand Down
4 changes: 1 addition & 3 deletions packages/frontend/src/content/NotFound.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
import React from "react";
import { RouteComponentProps } from "@reach/router";
import { HomeButton, PageContainer } from "../components";

const NotFound = (props: RouteComponentProps) => (
const NotFound = (): JSX.Element => (
<PageContainer header={<HomeButton />}>404 Page Not Found</PageContainer>
);

Expand Down
6 changes: 4 additions & 2 deletions packages/frontend/src/content/SaveNoteButton.tsx
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
import React, { useState } from "react";
import { Button, Alert } from "react-bootstrap";
import { GATEWAY_URL } from "../config.json";
import { navigate } from "@reach/router";
import { GATEWAY_URL } from "../config";
import { ButtonSpinner } from "../components";
import { useNavigate } from "react-router-dom";

const SaveNoteButton = (props: { noteId: string; noteContent: string }) => {
const [isSaving, setIsSaving] = useState(false);
const [errorMsg, setErrorMsg] = useState("");

const navigate = useNavigate();

const handleSave = async (event: any) => {
event.preventDefault();
setIsSaving(true);
Expand Down
9 changes: 5 additions & 4 deletions packages/frontend/src/content/ShowNote.tsx
Original file line number Diff line number Diff line change
@@ -1,17 +1,18 @@
import React, { useState, useEffect } from "react";
import { RouteComponentProps, navigate } from "@reach/router";
import { Form, Card } from "react-bootstrap";
import { GATEWAY_URL } from "../config.json";
import { GATEWAY_URL } from "../config";
import { DeleteNoteButton, SaveNoteButton } from "./";
import { getObjectUrl } from "../libs";
import { HomeButton, Loading, PageContainer } from "../components";
import { useNavigate, useParams } from "react-router-dom";

const ShowNote = (props: RouteComponentProps<{ noteId: string }>) => {
const { noteId } = props;
const ShowNote = () => {
const { noteId } = useParams<'noteId'>();
const [isLoading, setIsLoading] = useState(true);
const [noteContent, setNoteContent] = useState("");
const [attachment, setAttachment] = useState("");
const [attachmentURL, setAttachmentURL] = useState("");
const navigate = useNavigate();

useEffect(() => {
const fetchNote = async (noteId: string) => {
Expand Down
2 changes: 1 addition & 1 deletion packages/frontend/src/libs/deleteObject.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { s3Client } from "./s3Client";
import { FILES_BUCKET } from "../config.json";
import { FILES_BUCKET } from "../config";
import { DeleteObjectCommand } from "@aws-sdk/client-s3";

const deleteObject = async (fileName: string) =>
Expand Down
2 changes: 1 addition & 1 deletion packages/frontend/src/libs/getObjectUrl.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { s3Client } from "./s3Client";
import { FILES_BUCKET } from "../config.json";
import { FILES_BUCKET } from "../config";

import { createRequest } from "@aws-sdk/util-create-request";
import { GetObjectCommand } from "@aws-sdk/client-s3";
Expand Down
2 changes: 1 addition & 1 deletion packages/frontend/src/libs/putObject.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { s3Client } from "./s3Client";
import { FILES_BUCKET } from "../config.json";
import { FILES_BUCKET } from "../config";
import { PutObjectCommand } from "@aws-sdk/client-s3";

const putObject = async (file: File) => {
Expand Down
2 changes: 1 addition & 1 deletion packages/frontend/src/libs/s3Client.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { S3Client } from "@aws-sdk/client-s3";
import { fromCognitoIdentityPool } from "@aws-sdk/credential-provider-cognito-identity";
import { CognitoIdentityClient } from "@aws-sdk/client-cognito-identity";
import { IDENTITY_POOL_ID, REGION } from "../config.json";
import { IDENTITY_POOL_ID, REGION } from "../config";

const s3Client = new S3Client({
region: REGION,
Expand Down
1 change: 1 addition & 0 deletions packages/frontend/tsconfig.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
{
"compilerOptions": {
"target": "ESNext",
"types": ["vite/client"],
"lib": ["dom", "dom.iterable", "esnext"],
"allowJs": true,
"skipLibCheck": true,
Expand Down
21 changes: 13 additions & 8 deletions packages/frontend/vite.config.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,17 @@
import { defineConfig } from "vite";
import { defineConfig, loadEnv } from "vite";
import reactRefresh from "@vitejs/plugin-react-refresh";

// https://vitejs.dev/config/
export default defineConfig({
plugins: [reactRefresh()],
resolve: {
alias: {
"./runtimeConfig": "./runtimeConfig.browser",
export default ({mode}) => {
console.log(process.env.VITE_BASE_URL)
process.env = {...process.env, ...loadEnv(mode, process.cwd())};
return defineConfig({
plugins: [reactRefresh()],
base: process.env.VITE_BASE_URL,
resolve: {
alias: {
"./runtimeConfig": "./runtimeConfig.browser",
},
},
},
});
});
}
Loading

0 comments on commit 3f608fe

Please sign in to comment.