From 40d6c7f248c3fe506690ee3ccb3894f952e164ed Mon Sep 17 00:00:00 2001 From: Joe Carstairs Date: Thu, 18 Dec 2025 11:03:32 +0000 Subject: otp actions --- website/src/actions/otp/send-otp.ts | 51 +++++++++++++++++++++++++++++++++++++ 1 file changed, 51 insertions(+) create mode 100644 website/src/actions/otp/send-otp.ts (limited to 'website/src/actions/otp/send-otp.ts') diff --git a/website/src/actions/otp/send-otp.ts b/website/src/actions/otp/send-otp.ts new file mode 100644 index 0000000..b349feb --- /dev/null +++ b/website/src/actions/otp/send-otp.ts @@ -0,0 +1,51 @@ +import crypto from "node:crypto"; +import { SENDMAIL_BIN } from "astro:env/server"; +import nodemailer from "nodemailer"; +import { z } from "astro/zod"; +import { defineAction } from "astro:actions"; +import { db, Otp } from "astro:db"; + +export default defineAction({ + input: z.object({ + email: z.string().email(), + name: z.string().optional(), + type: z.enum(["email"]), + }), + handler: sendOtp, +}); + +type OtpParams = { + email: string; + name?: string; + type: "email"; +}; + +async function sendOtp({ email, name }: OtpParams) { + const otp = crypto.randomBytes(3).toString("hex").toLocaleUpperCase(); + const otpPretty = `${otp.slice(0, 3)}-${otp.slice(3)}`; + + const info = await transporter.sendMail({ + from: `"Joe Carstairs" `, + to: `${name ? `"${name}" ` : ""}<${email}>`, + subject: `joeac.net: your OTP is ${otpPretty}`, + text: ` +Someone tried to use this email address on joeac.net. If this was you, +your one-time passcode is ${otpPretty}. If this wasn't you, you don't need +to do anything.`, + }); + console.log( + `Sent OTP (${otpPretty}) to ${email}. Message ID: ${info.messageId}`, + ); + + await db.insert(Otp).values({ + userId: email, + value: otp, + createdAt: Date.now(), + validUntil: Date.now() + 1000 * 60 * 5, + }); +} + +const transporter = nodemailer.createTransport({ + sendmail: true, + path: SENDMAIL_BIN, +}); -- cgit v1.2.3