From 5b183e67ed7b9134cd05577b2d417fc7d482534b Mon Sep 17 00:00:00 2001
From: Jay V
Date: Mon, 16 Sep 2024 20:03:57 -0400
Subject: [PATCH] docs: jsx email example
---
examples/aws-jsx-email/.gitignore | 15 +++
examples/aws-jsx-email/README.md | 1 +
examples/aws-jsx-email/index.ts | 37 ++++++
examples/aws-jsx-email/package.json | 22 ++++
examples/aws-jsx-email/sst-env.d.ts | 18 +++
examples/aws-jsx-email/sst.config.ts | 63 +++++++++++
examples/aws-jsx-email/templates/email.tsx | 96 ++++++++++++++++
examples/aws-jsx-email/tsconfig.json | 24 ++++
www/src/content/docs/docs/examples.mdx | 124 ++++++++++++++++++---
9 files changed, 384 insertions(+), 16 deletions(-)
create mode 100644 examples/aws-jsx-email/.gitignore
create mode 100644 examples/aws-jsx-email/README.md
create mode 100644 examples/aws-jsx-email/index.ts
create mode 100644 examples/aws-jsx-email/package.json
create mode 100644 examples/aws-jsx-email/sst-env.d.ts
create mode 100644 examples/aws-jsx-email/sst.config.ts
create mode 100644 examples/aws-jsx-email/templates/email.tsx
create mode 100644 examples/aws-jsx-email/tsconfig.json
diff --git a/examples/aws-jsx-email/.gitignore b/examples/aws-jsx-email/.gitignore
new file mode 100644
index 000000000..fccbe3fea
--- /dev/null
+++ b/examples/aws-jsx-email/.gitignore
@@ -0,0 +1,15 @@
+# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
+
+node_modules
+
+# env
+.env
+
+# debug
+npm-debug.log*
+yarn-debug.log*
+yarn-error.log*
+.pnpm-debug.log*
+
+# sst
+.sst
diff --git a/examples/aws-jsx-email/README.md b/examples/aws-jsx-email/README.md
new file mode 100644
index 000000000..a41fc0ef7
--- /dev/null
+++ b/examples/aws-jsx-email/README.md
@@ -0,0 +1 @@
+# aws-jsx-email
diff --git a/examples/aws-jsx-email/index.ts b/examples/aws-jsx-email/index.ts
new file mode 100644
index 000000000..907afeca8
--- /dev/null
+++ b/examples/aws-jsx-email/index.ts
@@ -0,0 +1,37 @@
+import { Resource } from "sst";
+import { render } from "jsx-email";
+import { Template } from "./templates/email";
+import { SESv2Client, SendEmailCommand } from "@aws-sdk/client-sesv2";
+
+const client = new SESv2Client();
+
+export const handler = async () => {
+ await client.send(
+ new SendEmailCommand({
+ FromEmailAddress: Resource.MyEmail.sender,
+ Destination: {
+ ToAddresses: [Resource.MyEmail.sender],
+ },
+ Content: {
+ Simple: {
+ Subject: {
+ Data: "Hello World!",
+ },
+ Body: {
+ Html: {
+ Data: await render(Template({
+ email: "spongebob@example.com",
+ name: "Spongebob Squarepants"
+ })),
+ },
+ },
+ },
+ },
+ })
+ );
+
+ return {
+ statusCode: 200,
+ body: "Sent!"
+ };
+};
diff --git a/examples/aws-jsx-email/package.json b/examples/aws-jsx-email/package.json
new file mode 100644
index 000000000..04b5e2995
--- /dev/null
+++ b/examples/aws-jsx-email/package.json
@@ -0,0 +1,22 @@
+{
+ "name": "aws-jsx-email",
+ "version": "0.0.0",
+ "private": true,
+ "description": "A simple starter for jsx-email",
+ "scripts": {
+ "build": "email build ./templates",
+ "create": "email create",
+ "dev": "email preview ./templates"
+ },
+ "dependencies": {
+ "@aws-sdk/client-sesv2": "^3.651.1",
+ "jsx-email": "^1.10.11",
+ "sst": "latest"
+ },
+ "devDependencies": {
+ "@types/aws-lambda": "8.10.145",
+ "@types/react": "^18.2.0",
+ "react": "^18.2.0",
+ "typescript": "^5.2.2"
+ }
+}
diff --git a/examples/aws-jsx-email/sst-env.d.ts b/examples/aws-jsx-email/sst-env.d.ts
new file mode 100644
index 000000000..04d80476f
--- /dev/null
+++ b/examples/aws-jsx-email/sst-env.d.ts
@@ -0,0 +1,18 @@
+/* This file is auto-generated by SST. Do not edit. */
+/* tslint:disable */
+/* eslint-disable */
+import "sst"
+export {}
+declare module "sst" {
+ export interface Resource {
+ "MyApi": {
+ "name": string
+ "type": "sst.aws.Function"
+ "url": string
+ }
+ "MyEmail": {
+ "sender": string
+ "type": "sst.aws.Email"
+ }
+ }
+}
diff --git a/examples/aws-jsx-email/sst.config.ts b/examples/aws-jsx-email/sst.config.ts
new file mode 100644
index 000000000..a17fd32f9
--- /dev/null
+++ b/examples/aws-jsx-email/sst.config.ts
@@ -0,0 +1,63 @@
+///
+
+/**
+ * ## AWS JSX Email
+ *
+ * Uses [JSX Email](https://jsx.email) and the `Email` component to design and send emails.
+ *
+ * To test this example, change the `sst.config.ts` to use your own email address.
+ *
+ * ```ts title="sst.config.ts"
+ * sender: "email@example.com"
+ * ```
+ *
+ * Then run.
+ *
+ * ```bash
+ * npm install
+ * npx sst dev
+ * ```
+ *
+ * You'll get an email from AWS asking you to confirm your email address. Click the link to
+ * verify it.
+ *
+ * Next, go to the URL in the `sst dev` CLI output. You should now receive an email rendered
+ * using JSX Email.
+ *
+ * ```ts title="index.ts"
+ * import { Template } from "./templates/email";
+ *
+ * await render(Template({
+ * email: "spongebob@example.com",
+ * name: "Spongebob Squarepants"
+ * }))
+ * ```
+ *
+ * Once you are ready to go to production, you can:
+ *
+ * - [Request production access](https://docs.aws.amazon.com/ses/latest/dg/request-production-access.html) for SES
+ * - And [use your domain](/docs/component/aws/email/) to send emails
+ */
+export default $config({
+ app(input) {
+ return {
+ name: "aws-jsx-email",
+ removal: input?.stage === "production" ? "retain" : "remove",
+ home: "aws",
+ };
+ },
+ async run() {
+ const email = new sst.aws.Email("MyEmail", {
+ sender: "email@example.com",
+ });
+ const api = new sst.aws.Function("MyApi", {
+ handler: "index.handler",
+ link: [email],
+ url: true,
+ });
+
+ return {
+ api: api.url,
+ };
+ },
+});
diff --git a/examples/aws-jsx-email/templates/email.tsx b/examples/aws-jsx-email/templates/email.tsx
new file mode 100644
index 000000000..4c4591a9e
--- /dev/null
+++ b/examples/aws-jsx-email/templates/email.tsx
@@ -0,0 +1,96 @@
+import {
+ Body,
+ Button,
+ Container,
+ Head,
+ Hr,
+ Html,
+ Link,
+ Preview,
+ Section,
+ Text
+} from 'jsx-email';
+
+
+export type TemplateProps = {
+ email: string;
+ name: string;
+}
+
+const main = {
+ backgroundColor: '#f6f9fc',
+ fontFamily:
+ '-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue",Ubuntu,sans-serif'
+};
+
+const container = {
+ backgroundColor: '#ffffff',
+ margin: '0 auto',
+ marginBottom: '64px',
+ padding: '20px 0 48px'
+};
+
+const box = {
+ padding: '0 48px'
+};
+
+const hr = {
+ borderColor: '#e6ebf1',
+ margin: '20px 0'
+};
+
+const paragraph = {
+ color: '#777',
+ fontSize: '16px',
+ lineHeight: '24px',
+ textAlign: 'left' as const
+};
+
+const anchor = {
+ color: '#777'
+};
+
+const button = {
+ backgroundColor: 'coral',
+ borderRadius: '5px',
+ color: '#fff',
+ display: 'block',
+ fontSize: '16px',
+ fontWeight: 'bold',
+ textAlign: 'center' as const,
+ textDecoration: 'none',
+ width: '100%',
+ padding: '10px'
+};
+
+export const defaultProps = {
+ email: 'batman@example.com',
+ name: 'Bruce Wayne'
+} as TemplateProps;
+
+export const templateName = 'aws-jsx-email';
+
+export const Template = ({ email, name }: TemplateProps) => (
+
+
+ This is our email preview text for {name} <{email}>
+
+
+
+ This is our email body text
+
+
+
+ This is text content with a{' '}
+
+ link
+
+ .
+
+
+
+
+
+);
diff --git a/examples/aws-jsx-email/tsconfig.json b/examples/aws-jsx-email/tsconfig.json
new file mode 100644
index 000000000..b49214f86
--- /dev/null
+++ b/examples/aws-jsx-email/tsconfig.json
@@ -0,0 +1,24 @@
+{
+ "$schema": "https://json.schemastore.org/tsconfig",
+ "compilerOptions": {
+ "declaration": true,
+ "declarationMap": true,
+ "esModuleInterop": true,
+ "jsx": "react-jsx",
+ "lib": ["ES2023"],
+ "module": "ESNext",
+ "moduleResolution": "node",
+ "noEmitOnError": true,
+ "noUnusedLocals": true,
+ "noUnusedParameters": true,
+ "preserveSymlinks": true,
+ "preserveWatchOutput": true,
+ "resolveJsonModule": true,
+ "skipLibCheck": true,
+ "strict": true,
+ "strictNullChecks": true,
+ "target": "ESNext"
+ },
+ "exclude": ["**/dist", "**/node_modules", "sst.config.ts"],
+ "include": ["templates", "index.ts", "sst-env.d.ts"]
+}
diff --git a/www/src/content/docs/docs/examples.mdx b/www/src/content/docs/docs/examples.mdx
index 15b45e523..07c8d5594 100644
--- a/www/src/content/docs/docs/examples.mdx
+++ b/www/src/content/docs/docs/examples.mdx
@@ -342,6 +342,61 @@ return {
View the [full example](https://github.com/sst/ion/tree/dev/examples/aws-info).
+---
+## AWS JSX Email
+
+Uses [JSX Email](https://jsx.email) and the `Email` component to design and send emails.
+
+To test this example, change the `sst.config.ts` to use your own email address.
+
+```ts title="sst.config.ts"
+sender: "email@example.com"
+```
+
+Then run.
+
+```bash
+npm install
+npx sst dev
+```
+
+You'll get an email from AWS asking you to confirm your email address. Click the link to
+verify it.
+
+Next, go to the URL in the `sst dev` CLI output. You should now receive an email rendered
+using JSX Email.
+
+```ts title="index.ts"
+import { Template } from "./templates/email";
+
+await render(Template({
+ email: "spongebob@example.com",
+ name: "Spongebob Squarepants"
+}))
+```
+
+Once you are ready to go to production, you can:
+
+- [Request production access](https://docs.aws.amazon.com/ses/latest/dg/request-production-access.html) for SES
+- And [use your domain](/docs/component/aws/email/) to send emails
+```ts title="sst.config.ts"
+const email = new sst.aws.Email("MyEmail", {
+ sender: "email@example.com",
+});
+const api = new sst.aws.Function("MyApi", {
+ handler: "index.handler",
+ link: [email],
+ url: true,
+});
+
+return {
+ api: api.url,
+};
+```
+
+View the [full example](https://github.com/sst/ion/tree/dev/examples/aws-jsx-email).
+
+
---
## Kinesis streams
@@ -465,7 +520,7 @@ the [`--fallback`](/docs/reference/cli#secret) flag.
```ts title="sst.config.ts"
const username = new sst.Secret("USERNAME");
const password = new sst.Secret("PASSWORD");
-const basicAuth = $output([username.value, password.value]).apply(
+const basicAuth = $resolve([username.value, password.value]).apply(
([username, password]) =>
Buffer.from(`${username}:${password}`).toString("base64")
);
@@ -711,6 +766,43 @@ return {
View the [full example](https://github.com/sst/ion/tree/dev/examples/aws-queue).
+---
+## AWS Router and bucket
+
+Creates a router that serves static files from the `public` folder in a bucket.
+```ts title="sst.config.ts"
+// Create a bucket that CloudFront can access
+const bucket = new sst.aws.Bucket("MyBucket", {
+ access: "cloudfront",
+});
+
+// Upload the image to the `public` folder
+new aws.s3.BucketObjectv2("MyImage", {
+ bucket: bucket.name,
+ key: "public/spongebob.svg",
+ contentType: "image/svg+xml",
+ source: new $util.asset.FileAsset(
+ path.join($cli.paths.root, "spongebob.svg")
+ ),
+});
+
+const router = new sst.aws.Router("MyRouter", {
+ routes: {
+ "/*": {
+ bucket,
+ rewrite: { regex: "^/(.*)$", to: "/public/$1" },
+ },
+ },
+});
+
+return {
+ image: $interpolate`${router.url}/spongebob.svg`,
+};
+```
+
+View the [full example](https://github.com/sst/ion/tree/dev/examples/aws-router-bucket).
+
+
---
## Router and function URL
@@ -827,7 +919,7 @@ the [`--fallback`](/docs/reference/cli#secret) flag.
```ts title="sst.config.ts"
const username = new sst.Secret("USERNAME");
const password = new sst.Secret("PASSWORD");
-const basicAuth = $output([username.value, password.value]).apply(
+const basicAuth = $resolve([username.value, password.value]).apply(
([username, password]) =>
Buffer.from(`${username}:${password}`).toString("base64")
);
@@ -837,20 +929,20 @@ new sst.aws.StaticSite("MySite", {
// Don't password protect prod
edge: $app.stage !== "production"
? {
- viewerRequest: {
- injection: $interpolate`
- if (
- !event.request.headers.authorization
- || event.request.headers.authorization.value !== "Basic ${basicAuth}"
- ) {
- return {
- statusCode: 401,
- headers: {
- "www-authenticate": { value: "Basic" }
- }
- };
- }`,
- },
+ viewerRequest: {
+ injection: $interpolate`
+ if (
+ !event.request.headers.authorization
+ || event.request.headers.authorization.value !== "Basic ${basicAuth}"
+ ) {
+ return {
+ statusCode: 401,
+ headers: {
+ "www-authenticate": { value: "Basic" }
+ }
+ };
+ }`,
+ },
}
: undefined,
});