Skip to content

Instantly share code, notes, and snippets.

@crazy4groovy
Last active March 16, 2023 03:30
Show Gist options
  • Save crazy4groovy/f48092b79320f24bbea94f6c8be1b987 to your computer and use it in GitHub Desktop.
Save crazy4groovy/f48092b79320f24bbea94f6c8be1b987 to your computer and use it in GitHub Desktop.
Astro Endpoint for sending emails via Gmail SMTP username + app password, via nodemailer (NodeJS)
import type { APIRoute } from "astro";
import type SMTPTransport from "nodemailer/lib/smtp-transport";
import nodemailer from "nodemailer";
/****
envs:
GMAIL_APP_PW=
GMAIL_ACCOUNT=
GMAIL_TO=
*/
/****
Re: GMail SMTP, see:
- https://community.cloudflare.com/t/solved-how-to-use-gmail-smtp-to-send-from-an-email-address-which-uses-cloudflare-email-routing/382769
- https://myaccount.google.com/apppasswords
*/
interface PostBody {
from: string;
name: string;
text: string;
answer?: string;
}
function makeResponse200(message?: string) {
if (message) {
return new Response(JSON.stringify({ message }), { status: 200 });
}
return new Response(JSON.stringify({ ok: true }), { status: 200 });
}
const makeMessage = (
from: string,
name: string,
text: string,
to = import.meta.env.GMAIL_TO
) => ({
from: `"${from}" <${import.meta.env.GMAIL_ACCOUNT}>`,
to,
subject: `Website Email from: ${name}`,
text,
html: `<p>${text.replaceAll(/\r?\n/g, "<br>")}</p>`,
});
const smtpConfig = "smtps://" + import.meta.env.GMAIL_ACCOUNT + ":" + import.meta.env.GMAIL_APP_PW + "@smtp.gmail.com/`;
const _post = async ({ from, name, text }: PostBody): Promise<SMTPTransport.SentMessageInfo> => {
const transporter = nodemailer.createTransport(smtpConfig);
return transporter.sendMail(makeMessage(from, name, text));
};
export const post: APIRoute = async function post({ request }) {
let doSend = request.headers.get("Content-Type") === "application/json";
if (!doSend) {
return makeResponse200("json");
}
const body = (await request.json()) as PostBody;
// validate honeypot
// Challenge question: "Today's Day of Month Is..."
const { answer } = body;
// Note: buffer by +/-1 day to handle diff time zones
doSend = Math.abs(new Date().getDate() - Number(answer)) <= 1;
if (!doSend) {
return makeResponse200(answer);
}
let { from, name, text } = body;
doSend = Boolean(from && name && text);
if (!doSend) {
return makeResponse200("body");
}
[from, name, text] = [from, name, text].map(
(t, i) => t.slice(0, 50 + 200 * i) // do some VERY basic input sanitization
);
await _post({ from, name, text }).catch((err) => {
// TODO: needs better error handling!
console.error({ smtpConfig, err: err.message });
});
return makeResponse200(`${from} : ${name}`);
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment