Skip to content

Instantly share code, notes, and snippets.

@lottamus
Created July 17, 2024 21:28
Show Gist options
  • Select an option

  • Save lottamus/3a3bc7ac278672ce0150252585738c11 to your computer and use it in GitHub Desktop.

Select an option

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
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