Skip to content

Commit

Permalink
refactor(api): ✨ replace Brevo with Resend
Browse files Browse the repository at this point in the history
As a mail sender for contact form, switch to Resend

✅ Closes: #14
  • Loading branch information
brklntmhwk committed Aug 6, 2024
1 parent 1939033 commit a181b41
Show file tree
Hide file tree
Showing 9 changed files with 115 additions and 153 deletions.
3 changes: 0 additions & 3 deletions .devcontainer/postCreateCommand.sh
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,3 @@ git config --global user.name "brklntmhwk_dev"

# Make the settings in ".git/hooks" consistent with your lefthook.yml
lefthook install

# # Install tools specified in the .tool-versions file
# mise install
20 changes: 1 addition & 19 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -71,17 +71,6 @@ RUN apt-get update \
&& apt-get auto-remove -y \
&& apt-get clean -y

# # Define the base path for Bun related bins
ARG ORIGINAL_BUN_PATH=/root/.bun
ARG BIN_BASE_PATH=/usr/local/bin

# # Install Bun
# RUN curl -fsSL https://bun.sh/install | bash \
# && mv $ORIGINAL_BUN_PATH/bin/bun $BIN_BASE_PATH/bun \
# && rm -rf $ORIGINAL_BUN_PATH \
# && ln -s $BIN_BASE_PATH/bun $BIN_BASE_PATH/bunx \
# && chmod a+x $BIN_BASE_PATH/bun

# Install Lefthook
# (Must be installed here otherwise it'll be unavailable in the script for the "postCreateCommand" in devcontainer and thereafter)
RUN curl -1sLf 'https://dl.cloudsmith.io/public/evilmartians/lefthook/setup.deb.sh' | bash \
Expand All @@ -94,6 +83,7 @@ RUN apt-get update \
&& apt-get -y install nodejs

# Set the pnpm bin dir
ARG BIN_BASE_PATH=/usr/local/bin
ENV PNPM_HOME="$BIN_BASE_PATH/pnpm"
ENV PATH="$PNPM_HOME:$PATH"

Expand All @@ -103,14 +93,6 @@ RUN corepack enable pnpm
# Install Git-cz globally
RUN pnpm install -g git-cz

## Git-cz could also be available via another route below but the "commit init git-cz ..." command took a large amount of time

# Install Commitizen & Git-cz globally
# RUN pnpm install -g commitizen

# Init git-cz
# RUN commitizen init git-cz --save-dev --save-exact

# Execute as a non-root user hereafter
USER $USERNAME

Expand Down
Binary file modified bun.lockb
Binary file not shown.
8 changes: 5 additions & 3 deletions docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,11 @@ services:
environment:
- COMPOSE_PROJECT_NAME=app
working_dir: /workspace
# volumes:
# - .:/workspace:cached
volumes:
- project-data:/workspace:consistent
# - .:/workspace:cached
# - node-modules-data:/workspace/node_modules
user: dev
# volumes:
volumes:
project-data:
# node-modules-data:
20 changes: 10 additions & 10 deletions drizzle.config.ts
Original file line number Diff line number Diff line change
@@ -1,25 +1,25 @@
import type { Config } from 'drizzle-kit'
import type { Config } from "drizzle-kit";

const { CLOUDFLARE_ACCOUNT_ID, D1_DB_ID, D1_DB_API_TOKEN, LOCAL_DB_PATH } =
import.meta.env
process.env;

export default LOCAL_DB_PATH
? ({
schema: './src/db/schema/index.ts',
out: './src/db/migrations',
dialect: 'sqlite',
schema: "./src/db/schema/index.ts",
out: "./src/db/migrations",
dialect: "sqlite",
dbCredentials: {
url: LOCAL_DB_PATH,
},
} satisfies Config)
: ({
schema: './src/db/schema/index.ts',
out: './src/db/migrations',
dialect: 'sqlite',
driver: 'd1-http',
schema: "./src/db/schema/index.ts",
out: "./src/db/migrations",
dialect: "sqlite",
driver: "d1-http",
dbCredentials: {
accountId: CLOUDFLARE_ACCOUNT_ID!,
databaseId: D1_DB_ID!,
token: D1_DB_API_TOKEN!,
},
} satisfies Config)
} satisfies Config);
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -42,8 +42,8 @@
"astro": "^4.12.2",
"astro-purgecss": "4.1.0",
"drizzle-orm": "^0.31.1",
"nodemailer": "^6.9.14",
"purgecss": "5.0.0",
"resend": "^3.5.0",
"solid-js": "^1.8.17",
"solid-motionone": "^1.0.0",
"solid-toast": "^0.5.0",
Expand Down
102 changes: 48 additions & 54 deletions src/pages/[locale]/api/form.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,13 @@ export const prerender = false;

import { getEntry } from 'astro:content';
import {
BREVO_FORM_URL,
CONTACT_NOTIFICATION_SUBJECT,
FORM_TEXTAREA_MINLENGTH,
TURNSTILE_SITE_VERIFICATION_URL,
} from '@/lib/consts';
import type { Language } from '@/utils/i18n/data';
import type { APIContext, APIRoute } from 'astro';
import { Resend } from 'resend';
import {
type InferOutput,
boolean,
Expand All @@ -22,31 +22,6 @@ import {
string,
} from 'valibot';

type TurnstileErrorCode =
| 'missing-input-secret'
| 'invalid-input-secret'
| 'missing-input-response'
| 'invalid-input-response'
| 'invalid-widget-id'
| 'invalid-parsed-secret'
| 'bad-request'
| 'timeout-or-duplicate'
| 'internal-error';

type TurnstileResponse =
| {
success: true;
'error-codes': [];
challenge_ts: string;
hostname: string;
action: string;
cdata: string;
}
| {
success: false;
'error-codes': TurnstileErrorCode[];
};

export const POST: APIRoute = async ({
request,
redirect,
Expand Down Expand Up @@ -96,12 +71,39 @@ export const POST: APIRoute = async ({
if (!vResult.success) {
return new Response(
JSON.stringify({
message: `Missing or invalid field input(s): ${vResult.issues.map((issue) => `message: ${issue.message}\n input: ${issue.input}`)}`,
message: `Missing or invalid field input(s): ${vResult.issues.map(
(issue) => `message: ${issue.message}\n input: ${issue.input}`,
)}`,
}),
{ status: 422 },
);
}

type TurnstileErrorCode =
| 'missing-input-secret'
| 'invalid-input-secret'
| 'missing-input-response'
| 'invalid-input-response'
| 'invalid-widget-id'
| 'invalid-parsed-secret'
| 'bad-request'
| 'timeout-or-duplicate'
| 'internal-error';

type TurnstileResponse =
| {
success: true;
'error-codes': [];
challenge_ts: string;
hostname: string;
action: string;
cdata: string;
}
| {
success: false;
'error-codes': TurnstileErrorCode[];
};

const turnstileToken = data['cf-turnstile-response'];
const secretKey = locals.runtime.env.TURNSTILE_SECRET_KEY;

Expand All @@ -115,48 +117,40 @@ export const POST: APIRoute = async ({
response: turnstileToken,
}),
});

const outcome = (await turnstileResult.json()) as TurnstileResponse;
if (!outcome.success) {
return new Response(
JSON.stringify({
message: `Turnstile verification failed: ${outcome['error-codes']}`,
message: `Failed to verify Turnstile due to the error "${outcome['error-codes']}"`,
}),
{ status: 500 },
);
}

const myEmail = locals.runtime.env.MY_CUSTOM_EMAIL_ADDRESS;
const {
MY_CUSTOM_EMAIL_ADDRESS: myCustomAddress,
RESEND_API_KEY: resendApiKey,
} = locals.runtime.env;

const resend = new Resend(resendApiKey);

const mailContent = {
sender: { email: myEmail, name: meta.data.site.title },
to: [
{
email: myEmail,
name: t.data.author_name,
},
],
subject: CONTACT_NOTIFICATION_SUBJECT,
textContent: `お問い合わせ内容 \n --- \n 名前: ${data.name} \n メールアドレス: ${data.email} \n メッセージ: ${data.message} \n ---`,
replyTo: {
email: data.email,
name: data.name,
},
from: `${meta.data.site.title} <${myCustomAddress}>`,
to: myCustomAddress,
subject: `${CONTACT_NOTIFICATION_SUBJECT} from ${data.name}`,
text:
`Name:\n${data.name}\n\n` +
`Email:\n${data.email}\n\n` +
`Message:\n${data.message}\n\n`,
reply_to: `${data.name} <${data.email}>`,
};
const brevoApiKey = locals.runtime.env.BREVO_API_KEY;

const response = await fetch(BREVO_FORM_URL, {
method: 'POST',
headers: {
Accept: 'application/json',
'Content-Type': 'application/json',
'api-key': brevoApiKey,
},
body: JSON.stringify(mailContent),
});
if (!response.ok) {
const resendResult = await resend.emails.send(mailContent);
if (resendResult.error) {
return new Response(
JSON.stringify({
message: `Failed to submit form data: ${response.status} ${response.statusText}`,
message: `Failed to send email due to the error "${resendResult.error.name}: ${resendResult.error.message}"`,
}),
{ status: 500 },
);
Expand Down
Loading

0 comments on commit a181b41

Please sign in to comment.