Skip to content

Commit

Permalink
feat: ✨ implement cronjob and db logic for sending emails (#25)
Browse files Browse the repository at this point in the history
* feat: ✨ implement cronjob and db logic for sending emails

* fix: 🐛 email database schema

* fix: 🐛 update mimetext for Mailbox class

---------

Co-authored-by: Alexander Liu <[email protected]>
  • Loading branch information
ecxyzzy and alexanderl19 authored Mar 7, 2024
1 parent 9166365 commit 78ee809
Show file tree
Hide file tree
Showing 7 changed files with 56 additions and 44 deletions.
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@
"googleapis": "128.0.0",
"lucia": "2.7.4",
"lucide-svelte": "0.292.0",
"mimetext": "3.0.16",
"mimetext": "3.0.21",
"postgres": "3.4.3",
"zod": "3.22.4"
},
Expand Down
25 changes: 4 additions & 21 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

30 changes: 30 additions & 0 deletions services/send-email.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import { asc, eq } from "drizzle-orm";
import { drizzle } from "drizzle-orm/postgres-js";
import { google } from "googleapis";
import postgres from "postgres";

import { email } from "../src/lib/db/schema";

async function main() {
const { DATABASE_URL, GOOGLE_OAUTH_CLIENT_ID, GOOGLE_OAUTH_CLIENT_SECRET, MAILER_REFRESH_TOKEN } =
process.env;
const db = drizzle(postgres(DATABASE_URL, { max: 1, ssl: { rejectUnauthorized: false } }), {
schema: { email },
});
const auth = new google.auth.OAuth2(GOOGLE_OAUTH_CLIENT_ID, GOOGLE_OAUTH_CLIENT_SECRET);
auth.setCredentials({ refresh_token: MAILER_REFRESH_TOKEN });
const gmail = google.gmail({ version: "v1", auth });
const messages = await db
.select({ id: email.id, raw: email.raw })
.from(email)
.orderBy(asc(email.submittedAt))
.limit(15);
for (const { id, raw } of messages) {
await gmail.users.messages.send({ userId: "me", requestBody: { raw } });
await db.delete(email).where(eq(email.id, id));
await new Promise((_) => setTimeout(_, 1000));
}
process.exit(0);
}

main().then();
8 changes: 7 additions & 1 deletion src/lib/db/schema.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { relations } from "drizzle-orm";
import { pgSchema, pgEnum, text, timestamp, bigint, varchar } from "drizzle-orm/pg-core";
import { pgSchema, pgEnum, text, timestamp, bigint, varchar, uuid } from "drizzle-orm/pg-core";

export const schema = pgSchema("dev");

Expand Down Expand Up @@ -98,3 +98,9 @@ export const contactRelations = relations(contact, ({ one }) => ({
references: [user.id],
}),
}));

export const email = schema.table("email", {
id: uuid("id").primaryKey().defaultRandom(),
submittedAt: timestamp("submitted_at").defaultNow(),
raw: text("raw").notNull(),
});
11 changes: 0 additions & 11 deletions src/lib/server/gmail.ts

This file was deleted.

14 changes: 5 additions & 9 deletions src/routes/api/email/send/+server.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import { error, json } from "@sveltejs/kit";
import { createMimeMessage } from "mimetext";
import { createMimeMessage, Mailbox } from "mimetext";

import type { RequestHandler } from "./$types";

import { gmail } from "$lib/server/gmail";
import { email } from "$lib/db/schema";
import { drizzle } from "$lib/server/drizzle";
import { auth } from "$lib/server/lucia";
import { sleep } from "$lib/util/sleep";

type AttachmentBase = {
filename: string;
Expand Down Expand Up @@ -43,7 +43,7 @@ export const POST: RequestHandler = async (event) => {
({ attachments, htmlBody, plaintextBody, recipient, replyTo, subject }) => {
const msg = createMimeMessage();
msg.setSender({ name: "ICS Student Council", addr: "[email protected]" });
msg.setHeader("Reply-To", { addr: replyTo });
msg.setHeader("Reply-To", new Mailbox(replyTo));
msg.setRecipient(recipient);
msg.setSubject(subject);
msg.addMessage({ contentType: "text/plain", data: plaintextBody });
Expand All @@ -61,9 +61,5 @@ export const POST: RequestHandler = async (event) => {
return { raw: msg.asEncoded() };
},
);
for (const requestBody of messages) {
await gmail.users.messages.send({ userId: "me", requestBody });
await sleep(1000);
}
return json({});
return json(await drizzle.insert(email).values(messages).returning({ id: email.id }));
};
10 changes: 9 additions & 1 deletion sst.config.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import {} from "aws-cdk-lib";
import type { SSTConfig } from "sst";
import { SvelteKitSite } from "sst/constructs";
import { Cron, SvelteKitSite } from "sst/constructs";

export default {
config(_input) {
Expand All @@ -25,5 +25,13 @@ export default {
if (app.stage !== "prod") {
app.setDefaultRemovalPolicy("destroy");
}
if (app.stage === "prod") {
app.stack(function SendEmail({ stack }) {
new Cron(stack, "sendEmail", {
schedule: "rate(10 minutes)",
job: "services/send-email.main",
});
});
}
},
} satisfies SSTConfig;

0 comments on commit 78ee809

Please sign in to comment.