From d9e30133bc01354843635e7f42ece775b339f475 Mon Sep 17 00:00:00 2001 From: Eliezer Steinbock <3090527+elie222@users.noreply.github.com> Date: Wed, 26 Mar 2025 11:37:22 +0200 Subject: [PATCH] POC of the auto unsubscribe functionality --- apps/unsubscriber/src/main.ts | 8 +++++++- apps/unsubscriber/src/server.ts | 5 +++-- apps/web/app/(app)/bulk-unsubscribe/hooks.ts | 10 +++++++++- apps/web/env.ts | 2 ++ apps/web/utils/actions/unsubscriber.ts | 21 ++++++++++++++++++++ 5 files changed, 42 insertions(+), 4 deletions(-) diff --git a/apps/unsubscriber/src/main.ts b/apps/unsubscriber/src/main.ts index 73ef76ce11..1489bc83bc 100644 --- a/apps/unsubscriber/src/main.ts +++ b/apps/unsubscriber/src/main.ts @@ -203,7 +203,13 @@ async function performFallbackUnsubscribe(page: Page): Promise { return false; } -export async function autoUnsubscribe(url: string): Promise { +export async function autoUnsubscribe({ + url, + email, +}: { + url: string; + email: string; +}): Promise { if (!isValidUrl(url)) { console.error("Invalid URL provided:", url); return false; diff --git a/apps/unsubscriber/src/server.ts b/apps/unsubscriber/src/server.ts index 647b23ea26..270db35df6 100644 --- a/apps/unsubscriber/src/server.ts +++ b/apps/unsubscriber/src/server.ts @@ -16,6 +16,7 @@ if (env.CORS_ORIGIN) { const unsubscribeSchema = z.object({ url: z.string().url(), + email: z.string().email(), }); server.get("/", async (request, reply) => { @@ -24,8 +25,8 @@ server.get("/", async (request, reply) => { server.post("/unsubscribe", async (request, reply) => { try { - const { url } = unsubscribeSchema.parse(request.body); - const success = await autoUnsubscribe(url); + const { url, email } = unsubscribeSchema.parse(request.body); + const success = await autoUnsubscribe({ url, email }); return { success, message: success diff --git a/apps/web/app/(app)/bulk-unsubscribe/hooks.ts b/apps/web/app/(app)/bulk-unsubscribe/hooks.ts index 8bbdd7d464..3b80b27e23 100644 --- a/apps/web/app/(app)/bulk-unsubscribe/hooks.ts +++ b/apps/web/app/(app)/bulk-unsubscribe/hooks.ts @@ -4,7 +4,10 @@ import React, { useCallback, useState } from "react"; import { toast } from "sonner"; import type { PostHog } from "posthog-js/react"; import { onAutoArchive, onDeleteFilter } from "@/utils/actions/client"; -import { setNewsletterStatusAction } from "@/utils/actions/unsubscriber"; +import { + setNewsletterStatusAction, + unsubscribeAction, +} from "@/utils/actions/unsubscriber"; import { decrementUnsubscribeCreditAction } from "@/utils/actions/premium"; import { NewsletterStatus } from "@prisma/client"; import { cleanUnsubscribeLink } from "@/utils/parse/parseHtml.client"; @@ -54,6 +57,10 @@ export function useUnsubscribe({ posthog.capture("Clicked Unsubscribe"); if (item.status === NewsletterStatus.UNSUBSCRIBED) { + if (item.lastUnsubscribeLink) { + await unsubscribeAction({ url: item.lastUnsubscribeLink }); + } + await setNewsletterStatusAction({ newsletterEmail: item.name, status: null, @@ -72,6 +79,7 @@ export function useUnsubscribe({ hasUnsubscribeAccess, item.name, item.status, + item.lastUnsubscribeLink, mutate, refetchPremium, posthog, diff --git a/apps/web/env.ts b/apps/web/env.ts index 60307a6072..5fbed8cfe8 100644 --- a/apps/web/env.ts +++ b/apps/web/env.ts @@ -78,6 +78,8 @@ export const env = createEnv({ LICENSE_5_SEAT_VARIANT_ID: z.coerce.number().optional(), LICENSE_10_SEAT_VARIANT_ID: z.coerce.number().optional(), LICENSE_25_SEAT_VARIANT_ID: z.coerce.number().optional(), + + UNSUBSCRIBER_API_URL: z.string().optional(), }, client: { NEXT_PUBLIC_LEMON_STORE_ID: z.string().nullish().default("inboxzero"), diff --git a/apps/web/utils/actions/unsubscriber.ts b/apps/web/utils/actions/unsubscriber.ts index d09bb24e3d..8fd959e8a1 100644 --- a/apps/web/utils/actions/unsubscriber.ts +++ b/apps/web/utils/actions/unsubscriber.ts @@ -4,6 +4,7 @@ import { auth } from "@/app/api/auth/[...nextauth]/auth"; import prisma from "@/utils/prisma"; import type { NewsletterStatus } from "@prisma/client"; import { withActionInstrumentation } from "@/utils/actions/middleware"; +import { env } from "@/env"; export const setNewsletterStatusAction = withActionInstrumentation( "setNewsletterStatus", @@ -30,3 +31,23 @@ export const setNewsletterStatusAction = withActionInstrumentation( }); }, ); + +export const unsubscribeAction = withActionInstrumentation( + "unsubscribe", + async (options: { url: string }) => { + const session = await auth(); + if (!session?.user.email) return { error: "Not logged in" }; + + const { url } = options; + + const response = await fetch( + `${env.UNSUBSCRIBER_API_URL}/unsubscribe?url=${url}&email=${session.user.email}`, + ); + + if (!response.ok) { + return { error: "Failed to unsubscribe" }; + } + + return { success: true }; + }, +);