Skip to content

Commit

Permalink
Feature/resend (#339)
Browse files Browse the repository at this point in the history
* Trigger build

* Use Resend

* Use Resend
  • Loading branch information
pontusab authored Dec 5, 2024
1 parent 98c50d1 commit 25bce89
Show file tree
Hide file tree
Showing 20 changed files with 46 additions and 112 deletions.
3 changes: 1 addition & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -68,13 +68,12 @@ We are working on the documentation to get started with Midday for local develop
### Services

- Trigger.dev (background jobs)
- Resend (email)
- Resend (Transactional & Marketing)
- Novu (notifications)
- Github Actions (CI/CD)
- GoCardLess (Bank connection EU)
- Plaid (Bank connection in Canada and US)
- Teller (Bank connection in the US)
- Loops (Marketing email)
- OpenPanel (Events and Analytics)
- Dub (Short URLs)
- Polar (Payment processing)
Expand Down
11 changes: 2 additions & 9 deletions apps/dashboard/.env-example
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,7 @@ SUPABASE_SERVICE_KEY=

# Resend
RESEND_API_KEY=

# Loops
LOOPS_ENDPOINT=
LOOPS_API_KEY=
RESEND_AUDIENCE_ID=

# GoCardLess
GOCARDLESS_SECRET_ID=
Expand Down Expand Up @@ -60,10 +57,6 @@ MISTRAL_API_KEY=
NEXT_PUBLIC_OPENPANEL_CLIENT_ID=
OPENPANEL_SECRET_KEY=

# Engine
ENGINE_API_ENDPOINT=http://localhost:3002
ENGINE_API_SECRET=secret

# Webhook
WEBHOOK_SECRET_KEY=6c369443-1a88-444e-b459-7e662c1fff9e

Expand All @@ -75,7 +68,7 @@ SENTRY_PROJECT=

# Engine
MIDDAY_ENGINE_API_KEY=secret
MIDDAY_BASE_URL=http://localhost:3002
NEXT_PUBLIC_ENGINE_API_URL=http://localhost:3002

# Azure
AZURE_DOCUMENT_INTELLIGENCE_ENDPOINT=
Expand Down
2 changes: 1 addition & 1 deletion apps/dashboard/README.md
Original file line number Diff line number Diff line change
@@ -1 +1 @@
## Dashboard
## Dashboard
3 changes: 1 addition & 2 deletions apps/dashboard/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,6 @@
"framer-motion": "^11.12.0",
"geist": "^1.3.1",
"little-date": "^1.0.0",
"loops": "1.0.1",
"lottie-react": "^2.4.0",
"ms": "^2.1.3",
"next": "14.2.1",
Expand All @@ -70,7 +69,7 @@
"react-plaid-link": "^3.6.0",
"react-use-draggable-scroll": "^0.4.7",
"recharts": "^2.12.7",
"resend": "^3.5.0",
"resend": "^4.0.1",
"sharp": "^0.33.5",
"tus-js-client": "4.1.0",
"use-long-press": "^3.2.0",
Expand Down
9 changes: 5 additions & 4 deletions apps/dashboard/src/actions/delete-user-action.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,13 @@
"use server";

import { resend } from "@/utils/resend";
import { LogEvents } from "@midday/events/events";
import { setupAnalytics } from "@midday/events/server";
import { getUser } from "@midday/supabase/cached-queries";
import { deleteUser } from "@midday/supabase/mutations";
import { createClient } from "@midday/supabase/server";
import { LoopsClient } from "loops";
import { redirect } from "next/navigation";

const loops = new LoopsClient(process.env.LOOPS_API_KEY!);

export const deleteUserAction = async () => {
const supabase = createClient();
const user = await getUser();
Expand All @@ -30,7 +28,10 @@ export const deleteUserAction = async () => {

const userId = await deleteUser(supabase);

await loops.deleteContact({ userId });
await resend.contacts.remove({
email: user.data?.email!,
audienceId: process.env.RESEND_AUDIENCE_ID!,
});

const analytics = await setupAnalytics({
userId,
Expand Down
1 change: 0 additions & 1 deletion apps/dashboard/src/actions/schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,6 @@ export type UpdateTeamFormValues = z.infer<typeof updateTeamSchema>;

export const subscribeSchema = z.object({
email: z.string().email(),
userGroup: z.string(),
});

export const deleteBankAccountSchema = z.object({
Expand Down
22 changes: 6 additions & 16 deletions apps/dashboard/src/actions/subscribe-action.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
"use server";

import { resend } from "@/utils/resend";
import { authActionClient } from "./safe-action";
import { subscribeSchema } from "./schema";

Expand All @@ -8,20 +9,9 @@ export const subscribeAction = authActionClient
.metadata({
name: "subscribe",
})
.action(async ({ parsedInput: { email, userGroup } }) => {
const res = await fetch(
"https://app.loops.so/api/newsletter-form/clna1p09j00d3l60og56gj3u1",
{
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({
email,
userGroup,
}),
},
);

return res.json();
.action(async ({ parsedInput: { email } }) => {
return resend.contacts.create({
email,
audienceId: process.env.RESEND_AUDIENCE_ID!,
});
});
39 changes: 9 additions & 30 deletions apps/dashboard/src/app/api/webhook/registered/route.ts
Original file line number Diff line number Diff line change
@@ -1,21 +1,17 @@
import * as crypto from "node:crypto";
import { env } from "@/env.mjs";
import { logger } from "@/utils/logger";
import { resend } from "@/utils/resend";
import WelcomeEmail from "@midday/email/emails/welcome";
import { LogEvents } from "@midday/events/events";
import { setupAnalytics } from "@midday/events/server";
import { render } from "@react-email/render";
import { LoopsClient } from "loops";
import { nanoid } from "nanoid";
import { headers } from "next/headers";
import { NextResponse } from "next/server";
import { Resend } from "resend";

export const dynamic = "force-dynamic";

const loops = new LoopsClient(env.LOOPS_API_KEY);
const resend = new Resend(env.RESEND_API_KEY);

// NOTE: This is trigger from supabase database webhook
export async function POST(req: Request) {
const text = await req.clone().text();
Expand Down Expand Up @@ -74,34 +70,17 @@ export async function POST(req: Request) {
}

try {
const found = await loops.findContact(email);
const [firstName, lastName] = fullName?.split(" ") ?? [];

if (found.length > 0) {
const userId = found?.at(0)?.id;

if (!userId) {
return null;
}

await loops.updateContact(email, {
userId,
userGroup: "registered",
firstName,
lastName,
});
} else {
await loops.createContact(email, {
userId: body.record.id,
userGroup: "registered",
firstName,
lastName,
});
}
await resend.contacts.create({
email,
firstName,
lastName,
unsubscribed: false,
audienceId: env.RESEND_AUDIENCE_ID,
});
} catch (error) {
const message = error instanceof Error ? error.message : "Unknown error";

logger(message);
logger(error as string);
}

return NextResponse.json({ success: true });
Expand Down
6 changes: 2 additions & 4 deletions apps/dashboard/src/env.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,11 @@ export const env = createEnv({
SUPABASE_SERVICE_KEY: z.string(),
UPSTASH_REDIS_REST_TOKEN: z.string(),
UPSTASH_REDIS_REST_URL: z.string(),
LOOPS_ENDPOINT: z.string(),
LOOPS_API_KEY: z.string(),
GOCARDLESS_SECRET_ID: z.string(),
GOCARDLESS_SECRET_KEY: z.string(),
NOVU_API_KEY: z.string(),
RESEND_API_KEY: z.string(),
RESEND_AUDIENCE_ID: z.string(),
OPENPANEL_SECRET_KEY: z.string(),
MIDDAY_ENGINE_API_KEY: z.string(),
MIDDAY_CACHE_API_SECRET: z.string(),
Expand Down Expand Up @@ -61,9 +60,8 @@ export const env = createEnv({
NEXT_PUBLIC_TELLER_ENVIRONMENT: process.env.NEXT_PUBLIC_TELLER_ENVIRONMENT,
NEXT_PUBLIC_PLAID_ENVIRONMENT: process.env.NEXT_PUBLIC_PLAID_ENVIRONMENT,
RESEND_API_KEY: process.env.RESEND_API_KEY,
RESEND_AUDIENCE_ID: process.env.RESEND_AUDIENCE_ID,
PORT: process.env.PORT,
LOOPS_ENDPOINT: process.env.LOOPS_ENDPOINT,
LOOPS_API_KEY: process.env.LOOPS_API_KEY,
GOCARDLESS_SECRET_ID: process.env.GOCARDLESS_SECRET_ID,
GOCARDLESS_SECRET_KEY: process.env.GOCARDLESS_SECRET_KEY,
NOVU_API_KEY: process.env.NOVU_API_KEY,
Expand Down
9 changes: 0 additions & 9 deletions apps/dashboard/src/utils/engine.ts

This file was deleted.

4 changes: 3 additions & 1 deletion apps/website/.env-template
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
UPSTASH_REDIS_REST_TOKEN=
UPSTASH_REDIS_REST_URL=
NEXT_PUBLIC_OPENPANEL_CLIENT_ID=
NEXT_PUBLIC_OPENPANEL_CLIENT_ID=
RESEND_API_KEY=
RESEND_AUDIENCE_ID=
1 change: 1 addition & 0 deletions apps/website/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
"react-dom": "19.0.0-rc-66855b96-20241106",
"react-hls-player": "^3.0.7",
"react-use-draggable-scroll": "^0.4.7",
"resend": "^4.0.1",
"server-only": "^0.0.1",
"sharp": "^0.33.5",
"sugar-high": "^0.7.5"
Expand Down
27 changes: 6 additions & 21 deletions apps/website/src/actions/subscribe-action.ts
Original file line number Diff line number Diff line change
@@ -1,27 +1,12 @@
"use server";

import { getCountryCode } from "@midday/location";
import { resend } from "@/utils/resend";

export async function subscribeAction(formData: FormData, userGroup: string) {
export async function subscribeAction(formData: FormData) {
const email = formData.get("email") as string;
const country = await getCountryCode();

const res = await fetch(
"https://app.loops.so/api/newsletter-form/clna1p09j00d3l60og56gj3u1",
{
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({
email,
userGroup,
country,
}),
},
);

const json = await res.json();

return json;
return resend.contacts.create({
email,
audienceId: process.env.RESEND_AUDIENCE_ID!,
});
}
2 changes: 1 addition & 1 deletion apps/website/src/app/engine/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ export default function Page() {
</p>
</div>

<SubscribeInput group="engine" />
<SubscribeInput />

<div className="text-center flex flex-col items-center mt-[140px]">
<h3 className="mb-4 text-2xl font-medium">
Expand Down
2 changes: 1 addition & 1 deletion apps/website/src/components/footer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,7 @@ export function Footer() {
</div>

<div className="mb-8">
<SubscribeInput group="news" />
<SubscribeInput />
</div>
<div className="md:mr-0 mt-auto mr-auto">
<StatusWidget />
Expand Down
8 changes: 2 additions & 6 deletions apps/website/src/components/subscribe-input.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -26,11 +26,7 @@ function SubmitButton() {
);
}

type Props = {
group: string;
};

export function SubscribeInput({ group }: Props) {
export function SubscribeInput() {
const [isSubmitted, setSubmitted] = useState(false);

return (
Expand All @@ -57,7 +53,7 @@ export function SubscribeInput({ group }: Props) {
<form
action={async (formData) => {
setSubmitted(true);
await subscribeAction(formData, group);
await subscribeAction(formData);

setTimeout(() => {
setSubmitted(false);
Expand Down
3 changes: 3 additions & 0 deletions apps/website/src/utils/resend.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import { Resend } from "resend";

export const resend = new Resend(process.env.RESEND_API_KEY!);
Binary file modified bun.lockb
Binary file not shown.
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@
"turbo": "2.3.3",
"typescript": "^5.7.2"
},
"packageManager": "[email protected].27",
"packageManager": "[email protected].38",
"resolutions": {
"jackspeak": "2.1.1"
}
Expand Down
4 changes: 1 addition & 3 deletions turbo.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,7 @@
"SUPABASE_SERVICE_KEY",
"SUPABASE_API_KEY",
"RESEND_API_KEY",
"LOOPS_ENDPOINT",
"LOOPS_API_KEY",
"RESEND_AUDIENCE_ID",
"GOCARDLESS_SECRET_ID",
"GOCARDLESS_SECRET_KEY",
"UPSTASH_REDIS_REST_URL",
Expand All @@ -22,7 +21,6 @@
"API_ROUTE_SECRET",
"TELLER_CERTIFICATE",
"TELLER_CERTIFICATE_PRIVATE_KEY",
"MIDDAY_ENGINE_ENVIRONMENT",
"MIDDAY_ENGINE_API_KEY",
"PLAID_CLIENT_ID",
"PLAID_SECRET",
Expand Down

0 comments on commit 25bce89

Please sign in to comment.