Skip to content

Commit 75d529e

Browse files
committed
edit membership renewl cron job and email template
1 parent 120049a commit 75d529e

4 files changed

Lines changed: 40 additions & 123 deletions

File tree

Lines changed: 31 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,17 @@
11
import * as Sentry from "@sentry/nextjs"
2-
import { eq } from "drizzle-orm"
2+
import { format } from "date-fns"
3+
import { and, eq, lte } from "drizzle-orm"
34
import type { NextRequest } from "next/server"
5+
import { Resend } from "resend"
6+
7+
import { MembershipRenewalReminderEmail } from "~/components/email-template"
48

59
import { env } from "~/env"
610
import { db } from "~/server/db"
711
import { User } from "~/server/db/schema"
812

913
export const dynamic = "force-dynamic"
14+
const resend = new Resend(process.env.RESEND_API_KEY)
1015

1116
export async function GET(request: NextRequest) {
1217
const authHeader = request.headers.get("authorization")
@@ -17,23 +22,39 @@ export async function GET(request: NextRequest) {
1722
}
1823

1924
let dbRes: (typeof User.$inferSelect)[] = []
25+
const now = new Date()
26+
const today = new Date(now)
2027

2128
await Sentry.withMonitor("cycle-memberships", async () => {
22-
// TODO backup with xata cli and put into aws bucket
23-
await db.update(User).set({ reminder_pending: true }).where(eq(User.role, "member"))
24-
dbRes = await db.update(User).set({ role: null }).where(eq(User.role, "member")).returning()
25-
console.log(dbRes.length)
29+
// TODO: backup db and put into aws bucket
30+
dbRes = await db
31+
.update(User)
32+
.set({ role: null, membership_expiry: null, reminder_pending: true })
33+
.where(and(eq(User.role, "member"), lte(User.membership_expiry, today)))
34+
.returning()
2635
})
2736

28-
if (!dbRes.length) {
29-
return new Response("Internal Server Error", {
30-
status: 500,
31-
})
37+
for (const user of dbRes) {
38+
try {
39+
const formattedToday = format(today, "dd MMM yyyy")
40+
await resend.emails.send({
41+
from: "Coders for Causes <noreply@codersforcauses.org>",
42+
to: user.email,
43+
subject: "Reminder of your membership renewal",
44+
react: MembershipRenewalReminderEmail({
45+
firstname: user.preferred_name,
46+
membershipEndDate: formattedToday,
47+
}),
48+
})
49+
} catch (err) {
50+
console.error(`Failed to send email to ${user.email}`, err)
51+
Sentry.captureException(err, { extra: { memberId: user.id } })
52+
}
3253
}
3354

3455
return Response.json({
3556
success: true,
36-
message: `Memberships for ${new Date().getFullYear()} have been cycled.`,
57+
message: `${dbRes.length} Memberships for ${today.toISOString().slice(0, 10)} have been cycled.`,
3758
count: dbRes.length,
3859
})
3960
}

src/app/api/cron/membership-renewal-reminder/route.ts

Lines changed: 0 additions & 86 deletions
This file was deleted.

src/components/email-template.tsx

Lines changed: 9 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,15 @@ import { Body, Button, Container, Head, Html, Img, Preview, Section, Text } from
22
import * as React from "react"
33

44
interface MembershipRenewalReminderEmailProps {
5-
Firstname?: string
6-
WebsiteLink?: string
5+
firstname?: string
6+
membershipEndDate?: string
7+
websiteLink?: string
78
}
89

910
export const MembershipRenewalReminderEmail = ({
10-
Firstname,
11-
WebsiteLink = "https://codersforcauses.org/join",
11+
firstname,
12+
membershipEndDate,
13+
websiteLink = "https://codersforcauses.org/join",
1214
}: MembershipRenewalReminderEmailProps) => {
1315
// let year = new Date().getFullYear()
1416
// const month = new Date().getMonth()
@@ -27,17 +29,13 @@ export const MembershipRenewalReminderEmail = ({
2729
<Container style={container}>
2830
<Text style={title}>Your membership has expired</Text>
2931
<Section>
30-
<Text style={text}>Hi {Firstname},</Text>
32+
<Text style={text}>Hi {firstname},</Text>
3133
<Text style={text}>
32-
{`Your membership of Coders for Causes ends on 31st Dec 2025. To keep your membership active, please renew it by clicking the button below.`}
34+
{`Your membership of Coders for Causes ends on ${membershipEndDate}. To keep your membership active, please renew it by clicking the button below.`}
3335
</Text>
34-
<Text
35-
style={text}
36-
>{`Please note that, following an amendment to our constitution last year, subscriptions will expire one year from the date of purchase, effective from 2026.`}</Text>
37-
<Button style={button} href={WebsiteLink}>
36+
<Button style={button} href={websiteLink}>
3837
Login to my account
3938
</Button>
40-
<Text style={text}>Happy new year and happy coding!</Text>
4139
<Img
4240
src="https://codersforcauses.org/logo/cfc_logo_white_circle.png"
4341
width="50"

vercel.json

Lines changed: 0 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -3,22 +3,6 @@
33
{
44
"path": "/api/cron/cycle-memberships",
55
"schedule": "59 15 31 12 *"
6-
},
7-
{
8-
"path": "/api/cron/membership-renewal-reminder",
9-
"schedule": "0 3 1 1 *"
10-
},
11-
{
12-
"path": "/api/cron/membership-renewal-reminder",
13-
"schedule": "0 3 2 1 *"
14-
},
15-
{
16-
"path": "/api/cron/membership-renewal-reminder",
17-
"schedule": "0 3 3 1 *"
18-
},
19-
{
20-
"path": "/api/cron/membership-renewal-reminder",
21-
"schedule": "0 3 4 1 *"
226
}
237
]
248
}

0 commit comments

Comments
 (0)