summaryrefslogtreecommitdiff
path: root/website/src/actions/sendmail.ts
diff options
context:
space:
mode:
authorJoe Carstairs <me@joeac.net>2025-12-18 11:00:49 +0000
committerJoe Carstairs <me@joeac.net>2025-12-18 11:03:12 +0000
commita81d1de1e58ee2c314dbbea941dcb4e4a776ed84 (patch)
treea4ce457cb78f0bfbd689ce52aca32327bc7bd862 /website/src/actions/sendmail.ts
parent46387b41ce8d8dbba424af57187b6705c4a1c0e9 (diff)
sendmail action
Diffstat (limited to 'website/src/actions/sendmail.ts')
-rw-r--r--website/src/actions/sendmail.ts78
1 files changed, 78 insertions, 0 deletions
diff --git a/website/src/actions/sendmail.ts b/website/src/actions/sendmail.ts
new file mode 100644
index 0000000..3b2d242
--- /dev/null
+++ b/website/src/actions/sendmail.ts
@@ -0,0 +1,78 @@
+import { z } from "astro/zod";
+import { actions, defineAction } from "astro:actions";
+import nodemailer from "nodemailer";
+import { MAX_DAILY_EMAILS, SENDMAIL_BIN } from "astro:env/server";
+import { and, db, eq, gte, lte, SendmailToken, SentEmails } from "astro:db";
+import { isToken } from "typescript";
+
+export default defineAction({
+ input: z.object({
+ email: z.string().email(),
+ message: z.string().nonempty(),
+ name: z.string().nonempty(),
+ userId: z.string().nonempty(),
+ token: z.string().nonempty(),
+ }),
+ handler: sendmail,
+});
+
+type SendEmailParams = {
+ email: string;
+ message: string;
+ name: string;
+ token: string;
+ userId: string;
+};
+
+async function sendmail({
+ name,
+ email,
+ message,
+ token,
+ userId,
+}: SendEmailParams) {
+ const isTokenCorrect =
+ (await db.$count(
+ SendmailToken,
+ and(
+ eq(SendmailToken.userId, userId),
+ eq(SendmailToken.value, token),
+ gte(SendmailToken.validUntil, Date.now()),
+ ),
+ )) > 0;
+ if (!isTokenCorrect) {
+ return false;
+ }
+ await db
+ .delete(SendmailToken)
+ .where(
+ and(eq(SendmailToken.userId, userId), eq(SendmailToken.value, token)),
+ );
+
+ const emailsSentLast24Hours = await db.$count(
+ SentEmails,
+ gte(SentEmails.sentAt, Date.now() - 1000 * 60 * 60 * 24),
+ );
+ if (emailsSentLast24Hours > MAX_DAILY_EMAILS) {
+ throw new Error(
+ `${emailsSentLast24Hours} emails have been sent in the last 24 hours, but the max daily load is ${MAX_DAILY_EMAILS}.`,
+ );
+ }
+
+ const info = await transporter.sendMail({
+ from: `"Joe Carstairs" <me@joeac.net>`,
+ to: `"Joe Carstairs" <me@joeac.net>`,
+ subject: "joeac.net: a visitor left a message",
+ text: `${name} <${email}> sent you a message:\n\n\n${message}`,
+ });
+ await db
+ .insert(SentEmails)
+ .values({ messageId: info.messageId, sentAt: Date.now() });
+
+ console.log("Sent an email to Joe. Message ID: ", info.messageId);
+}
+
+const transporter = nodemailer.createTransport({
+ sendmail: true,
+ path: SENDMAIL_BIN,
+});