Created
July 17, 2024 21:28
-
-
Save lottamus/3a3bc7ac278672ce0150252585738c11 to your computer and use it in GitHub Desktop.
Use Cloudflare's Durable Object alarms to schedule outreach emails in the future
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| import { DurableObject } from "cloudflare:workers"; | |
| import * as zod from "zod"; | |
| interface Env { | |
| SCHEDULER: DurableObjectNamespace<Scheduler>; | |
| } | |
| const schema = zod.object({ | |
| outrCampaignOutreachId: zod.string(), | |
| outrCampaignOutreachSchedule: zod.number(), | |
| }); | |
| export default { | |
| async fetch(request: Request, env: Env) { | |
| const body = schema.safeParse(await request.json()); | |
| if (!body.success) { | |
| return new Response(JSON.stringify({ message: "Invalid request" }), { | |
| status: 400, | |
| }); | |
| } | |
| // Get the scheduler specificly for this outreach | |
| const id = env.SCHEDULER.idFromName(body.data.outrCampaignOutreachId); | |
| return await env.SCHEDULER.get(id).fetch(request); | |
| }, | |
| }; | |
| export class Scheduler extends DurableObject<Env> { | |
| state: DurableObjectState; | |
| storage: DurableObjectStorage; | |
| outrCampaignOutreachId?: string; | |
| constructor(state: DurableObjectState, env: Env) { | |
| super(state, env); | |
| this.state = state; | |
| this.storage = state.storage; | |
| this.state | |
| .blockConcurrencyWhile(async () => { | |
| // Initialization existing outrCampaignOutreachId from storage | |
| this.outrCampaignOutreachId = await this.storage.get<string>( | |
| "outrCampaignOutreachId", | |
| ); | |
| }) | |
| .catch(console.error); | |
| } | |
| async fetch(request: Request) { | |
| const body = schema.safeParse(await request.json()); | |
| if (!body.success) { | |
| return new Response(JSON.stringify({ error: "Invalid request" })); | |
| } | |
| // Store the outreach id, so we can access it later in the alarm() | |
| await this.storage.put( | |
| "outrCampaignOutreachId", | |
| body.data.outrCampaignOutreachId, | |
| ); | |
| // Schedule the outreach to be triggered in the future | |
| await this.storage.setAlarm(body.data.outrCampaignOutreachSchedule); | |
| return new Response( | |
| JSON.stringify({ | |
| message: `Email outreach scheduled for ${body.data.outrCampaignOutreachSchedule}`, | |
| }), | |
| ); | |
| } | |
| async alarm() { | |
| // Get outreach email list from outr API | |
| const body = await fetch( | |
| `https://outr.io/api/outreach/${this.outrCampaignOutreachId}`, | |
| ); | |
| const outreach = (await body.json()) as { | |
| emails: { from: string; to: string; subject: string; html: string }[]; | |
| }; | |
| // Send the emails | |
| await fetch("https://api.resend.com/emails/batch", { | |
| method: "POST", | |
| body: JSON.stringify(outreach.emails), | |
| }); | |
| await this.storage.deleteAll(); | |
| } | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment