summaryrefslogtreecommitdiff
path: root/website/src/actions
diff options
context:
space:
mode:
Diffstat (limited to 'website/src/actions')
-rw-r--r--website/src/actions/index.ts7
-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
-rw-r--r--website/src/actions/sendmail.ts97
5 files changed, 0 insertions, 222 deletions
diff --git a/website/src/actions/index.ts b/website/src/actions/index.ts
deleted file mode 100644
index a2dcded..0000000
--- a/website/src/actions/index.ts
+++ /dev/null
@@ -1,7 +0,0 @@
-import otp from "./otp/otp";
-import sendmail from "./sendmail";
-
-export const server = {
- otp,
- sendmail,
-};
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;
-};
diff --git a/website/src/actions/sendmail.ts b/website/src/actions/sendmail.ts
deleted file mode 100644
index bc3a2fa..0000000
--- a/website/src/actions/sendmail.ts
+++ /dev/null
@@ -1,97 +0,0 @@
-import { z } from "astro/zod";
-import { defineAction } from "astro:actions";
-import nodemailer from "nodemailer";
-import {
- MAX_DAILY_EMAILS,
- LOCAL_SMTP_HOST,
- LOCAL_SMTP_PASSWORD,
- LOCAL_SMTP_PORT,
- LOCAL_SMTP_USER,
- CONTACT_MAILBOX,
- LOCAL_SMTP_ENVELOPE_FROM,
-} from "astro:env/server";
-import { and, db, eq, gte, SendmailToken, SentEmails } from "astro:db";
-
-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) {
- console.warn(
- `${name} <${email}> tried to send an email, 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: CONTACT_MAILBOX,
- subject: `joeac.net: ${name} 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);
-}
-
-export const transporter = nodemailer.createTransport({
- host: LOCAL_SMTP_HOST,
- from: LOCAL_SMTP_ENVELOPE_FROM,
- port: LOCAL_SMTP_PORT,
- secure: false,
- authMethod: "PLAIN",
- auth: {
- type: "login",
- user: LOCAL_SMTP_USER,
- pass: LOCAL_SMTP_PASSWORD,
- },
-});