Skip to content

Commit

Permalink
Merge pull request #1845 from dubinc/wrapped
Browse files Browse the repository at this point in the history
Dub Wrapped
  • Loading branch information
steven-tey authored Dec 30, 2024
2 parents 86648be + 9e23eb6 commit 7ea7c34
Show file tree
Hide file tree
Showing 7 changed files with 665 additions and 2 deletions.
158 changes: 158 additions & 0 deletions apps/web/app/api/cron/year-in-review/route.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,158 @@
import { handleAndReturnErrorResponse } from "@/lib/api/errors";
import { qstash } from "@/lib/cron";
import { resend } from "@/lib/resend";
import { prisma } from "@dub/prisma";
import { APP_DOMAIN_WITH_NGROK } from "@dub/utils";
import DubWrapped from "emails/dub-wrapped";

export const dynamic = "force-dynamic";

const BATCH_SIZE = 100;

// POST /api/cron/year-in-review
export async function POST(req: Request) {
try {
if (process.env.VERCEL === "1") {
return new Response("Not available in production.");
}

if (!resend) {
return new Response("Resend not initialized. Skipping...");
}

const yearInReviews = await prisma.yearInReview.findMany({
where: {
sentAt: null,
year: 2024,
},
select: {
id: true,
workspaceId: true,
topCountries: true,
topLinks: true,
totalClicks: true,
totalLinks: true,
workspace: {
select: {
id: true,
name: true,
slug: true,
logo: true,
users: {
select: {
user: {
select: {
email: true,
},
},
},
},
},
},
},
take: 100,
});

if (yearInReviews.length === 0) {
return new Response("No jobs found. Skipping...");
}

const emailData = yearInReviews.flatMap(
({ workspace, totalClicks, totalLinks, topCountries, topLinks }) =>
workspace.users
.map(({ user }) => {
if (!user.email) {
return null;
}

return {
workspaceId: workspace.id,
email: {
from: "Steven from Dub.co <[email protected]>",
to: user.email,
reply_to: "[email protected]",
subject: "Dub Year in Review 🎊",
text: "Thank you for your support and here's to another year of your activity on Dub! Here's a look back at your activity in 2024.",
react: DubWrapped({
email: user.email,
workspace: {
logo: workspace.logo,
name: workspace.name,
slug: workspace.slug,
},
stats: {
"Total Links": totalLinks,
"Total Clicks": totalClicks,
},
// @ts-ignore
topLinks,
// @ts-ignore
topCountries,
}),
},
};
})
.filter((data) => data !== null),
);

if (emailData.length === 0) {
return new Response("No email data found. Skipping...");
}

for (let i = 0; i < emailData.length; i += BATCH_SIZE) {
const batch = emailData.slice(i, i + BATCH_SIZE);

console.log(
`\n🚀 Sending batch ${Math.floor(i / BATCH_SIZE) + 1} of ${Math.ceil(emailData.length / BATCH_SIZE)}`,
);

console.log(
`📨 Recipients:`,
// @ts-ignore
batch.map((b) => b.email.to),
);

if (batch.length === 0) {
continue;
}

const { data, error } = await resend.batch.send(
// @ts-ignore
batch.map((b) => b.email),
);

console.log("🚀 ~ data:", data);
if (error) {
console.log("🚀 ~ error:", error);
}
}

await prisma.yearInReview.updateMany({
where: {
id: {
in: yearInReviews.map(({ id }) => id),
},
},
data: {
sentAt: new Date(),
},
});

console.log(
`Sent ${emailData.length} emails to ${yearInReviews.length} workspaces!`,
);

await qstash.publishJSON({
url: `${APP_DOMAIN_WITH_NGROK}/api/cron/year-in-review`,
delay: 3,
method: "POST",
body: {},
});

return new Response(
`Sent ${emailData.length} emails to ${yearInReviews.length} workspaces!`,
);
} catch (error) {
return handleAndReturnErrorResponse(error);
}
}
2 changes: 1 addition & 1 deletion apps/web/emails/clicks-summary.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -142,7 +142,7 @@ export default function ClicksSummary({
align="right"
className="text-sm text-gray-600"
>
{nFormatter(clicks)}
{nFormatter(clicks, { full: clicks < 99999 })}
</Column>
</Row>
{index !== topLinks.length - 1 && (
Expand Down
15 changes: 14 additions & 1 deletion apps/web/emails/components/footer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@ export default function Footer({
<Hr className="mx-0 my-6 w-full border border-gray-200" />
<Text className="text-[12px] leading-6 text-gray-500">
We send out product update emails once a month – no spam, no nonsense.
<br />
Don't want to get these emails?{" "}
<Link
className="text-gray-700 underline"
Expand All @@ -24,6 +23,13 @@ export default function Footer({
Unsubscribe here.
</Link>
</Text>
<Text className="text-[12px] text-gray-500">
Dub Technologies, Inc.
<br />
2261 Market Street STE 5906
<br />
San Francisco, CA 941114
</Text>
</Tailwind>
);
}
Expand All @@ -49,6 +55,13 @@ export default function Footer({
</Link>
</Text>
)}
<Text className="text-[12px] text-gray-500">
Dub Technologies, Inc.
<br />
2261 Market Street STE 5906
<br />
San Francisco, CA 941114
</Text>
</Tailwind>
);
}
Loading

0 comments on commit 7ea7c34

Please sign in to comment.