summaryrefslogtreecommitdiff
path: root/website/src/actions/otp
diff options
context:
space:
mode:
authorJoe Carstairs <me@joeac.net>2026-06-18 19:21:59 +0100
committerJoe Carstairs <me@joeac.net>2026-06-18 19:21:59 +0100
commitca8e6824eaa711105f4092365792720c9dde026b (patch)
tree2b6493941352d11b799ee63d3f1311c13de649b8 /website/src/actions/otp
parenta0f25057283a397f006127b6f582d190d2c4294e (diff)
remove Astro website
Diffstat (limited to 'website/src/actions/otp')
-rw-r--r--website/src/actions/otp/otp.ts7
-rw-r--r--website/src/actions/otp/send-otp.ts63
-rw-r--r--website/src/actions/otp/verify-otp.ts48
3 files changed, 0 insertions, 118 deletions
diff --git a/website/src/actions/otp/otp.ts b/website/src/actions/otp/otp.ts
deleted file mode 100644
index 73c322a..0000000
--- a/website/src/actions/otp/otp.ts
+++ /dev/null
@@ -1,7 +0,0 @@
-import send from "./send-otp";
-import verify from "./verify-otp";
-
-export default {
- send,
- verify,
-};
diff --git a/website/src/actions/otp/send-otp.ts b/website/src/actions/otp/send-otp.ts
deleted file mode 100644
index 2fbe2fc..0000000
--- a/website/src/actions/otp/send-otp.ts
+++ /dev/null
@@ -1,63 +0,0 @@
-import crypto from "node:crypto";
-import { z } from "astro/zod";
-import { defineAction } from "astro:actions";
-import { db, gte, Otp, SentEmails } from "astro:db";
-import { transporter } from "../sendmail";
-import { LOCAL_SMTP_ENVELOPE_FROM, MAX_DAILY_EMAILS } from "astro:env/server";
-
-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 emailsSentLast24Hours = await db.$count(
- SentEmails,
- gte(SentEmails.sentAt, Date.now() - 1000 * 60 * 60 * 24),
- );
- if (emailsSentLast24Hours >= MAX_DAILY_EMAILS) {
- console.warn(
- `${name} <${email}> requested an OTP, but ${emailsSentLast24Hours} have already been sent, whereas the max daily load is ${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: LOCAL_SMTP_ENVELOPE_FROM,
- 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(SentEmails)
- .values({ messageId: info.messageId, sentAt: Date.now() });
-
- await db.insert(Otp).values({
- userId: email,
- value: otp,
- createdAt: Date.now(),
- validUntil: Date.now() + 1000 * 60 * 5,
- });
-}
diff --git a/website/src/actions/otp/verify-otp.ts b/website/src/actions/otp/verify-otp.ts
deleted file mode 100644
index 726786e..0000000
--- a/website/src/actions/otp/verify-otp.ts
+++ /dev/null
@@ -1,48 +0,0 @@
-import { randomBytes } from "node:crypto";
-import { z } from "astro/zod";
-import { defineAction } from "astro:actions";
-import { and, db, eq, gte, Otp, SendmailToken } from "astro:db";
-
-export default defineAction({
- input: z.object({
- guess: z.string().length(6),
- lenient: z.boolean().default(false),
- userId: z.string().nonempty(),
- }),
- handler: verifyOtp,
-});
-
-async function verifyOtp({ guess, lenient, userId }: VerifyOtpParams) {
- const leniency = lenient ? 1000 * 60 : 0;
- const isOtpCorrect =
- (await db.$count(
- Otp,
- and(
- eq(Otp.userId, userId),
- eq(Otp.value, guess),
- gte(Otp.validUntil, Date.now() - leniency),
- ),
- )) > 0;
-
- if (!isOtpCorrect) {
- return false;
- }
-
- await db.delete(Otp).where(and(eq(Otp.userId, userId), eq(Otp.value, guess)));
-
- const token = randomBytes(256).toString("hex");
- await db.insert(SendmailToken).values({
- userId,
- value: token,
- createdAt: Date.now(),
- validUntil: Date.now() + 60_000,
- });
-
- return token;
-}
-
-type VerifyOtpParams = {
- guess: string;
- lenient: boolean;
- userId: string;
-};