Skip to content
Merged
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
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