diff --git a/server/lib/resend.ts b/server/lib/resend.ts deleted file mode 100644 index 0c21b1be..00000000 --- a/server/lib/resend.ts +++ /dev/null @@ -1,16 +0,0 @@ -export enum AudienceIds { - SignUps = "", - Subscribed = "", - Churned = "", - Newsletter = "" -} - -let resend; -export default resend; - -export async function moveEmailToAudience( - email: string, - audienceId: AudienceIds -) { - return; -} diff --git a/server/private/lib/resend.ts b/server/private/lib/resend.ts deleted file mode 100644 index 42a11c15..00000000 --- a/server/private/lib/resend.ts +++ /dev/null @@ -1,127 +0,0 @@ -/* - * This file is part of a proprietary work. - * - * Copyright (c) 2025 Fossorial, Inc. - * All rights reserved. - * - * This file is licensed under the Fossorial Commercial License. - * You may not use this file except in compliance with the License. - * Unauthorized use, copying, modification, or distribution is strictly prohibited. - * - * This file is not licensed under the AGPLv3. - */ - -import { Resend } from "resend"; -import privateConfig from "#private/lib/config"; -import logger from "@server/logger"; - -export enum AudienceIds { - SignUps = "6c4e77b2-0851-4bd6-bac8-f51f91360f1a", - Subscribed = "870b43fd-387f-44de-8fc1-707335f30b20", - Churned = "f3ae92bd-2fdb-4d77-8746-2118afd62549", - Newsletter = "5500c431-191c-42f0-a5d4-8b6d445b4ea0" -} - -const resend = new Resend( - privateConfig.getRawPrivateConfig().server.resend_api_key || "missing" -); - -export default resend; - -export async function moveEmailToAudience( - email: string, - audienceId: AudienceIds -) { - if (process.env.ENVIRONMENT !== "prod") { - logger.debug( - `Skipping moving email ${email} to audience ${audienceId} in non-prod environment` - ); - return; - } - const { error, data } = await retryWithBackoff(async () => { - const { data, error } = await resend.contacts.create({ - email, - unsubscribed: false, - audienceId - }); - if (error) { - throw new Error( - `Error adding email ${email} to audience ${audienceId}: ${error}` - ); - } - return { error, data }; - }); - - if (error) { - logger.error( - `Error adding email ${email} to audience ${audienceId}: ${error}` - ); - return; - } - - if (data) { - logger.debug( - `Added email ${email} to audience ${audienceId} with contact ID ${data.id}` - ); - } - - const otherAudiences = Object.values(AudienceIds).filter( - (id) => id !== audienceId - ); - - for (const otherAudienceId of otherAudiences) { - const { error, data } = await retryWithBackoff(async () => { - const { data, error } = await resend.contacts.remove({ - email, - audienceId: otherAudienceId - }); - if (error) { - throw new Error( - `Error removing email ${email} from audience ${otherAudienceId}: ${error}` - ); - } - return { error, data }; - }); - - if (error) { - logger.error( - `Error removing email ${email} from audience ${otherAudienceId}: ${error}` - ); - } - - if (data) { - logger.info( - `Removed email ${email} from audience ${otherAudienceId}` - ); - } - } -} - -type RetryOptions = { - retries?: number; - initialDelayMs?: number; - factor?: number; -}; - -export async function retryWithBackoff( - fn: () => Promise, - options: RetryOptions = {} -): Promise { - const { retries = 5, initialDelayMs = 500, factor = 2 } = options; - - let attempt = 0; - let delay = initialDelayMs; - - while (true) { - try { - return await fn(); - } catch (err) { - attempt++; - - if (attempt > retries) throw err; - - await new Promise((resolve) => setTimeout(resolve, delay)); - delay *= factor; - } - } -} diff --git a/server/private/routers/billing/hooks/handleSubscriptionCreated.ts b/server/private/routers/billing/hooks/handleSubscriptionCreated.ts index 1152f223..a4014252 100644 --- a/server/private/routers/billing/hooks/handleSubscriptionCreated.ts +++ b/server/private/routers/billing/hooks/handleSubscriptionCreated.ts @@ -24,7 +24,6 @@ import { eq, and } from "drizzle-orm"; import logger from "@server/logger"; import stripe from "#private/lib/stripe"; import { handleSubscriptionLifesycle } from "../subscriptionLifecycle"; -import { AudienceIds, moveEmailToAudience } from "#private/lib/resend"; import { getSubType } from "./getSubType"; import privateConfig from "#private/lib/config"; import { getLicensePriceSet, LicenseId } from "@server/lib/billing/licenses"; @@ -172,7 +171,7 @@ export async function handleSubscriptionCreated( const email = orgUserRes.user.email; if (email) { - moveEmailToAudience(email, AudienceIds.Subscribed); + // TODO: update user in Sendy } } } else if (type === "license") { diff --git a/server/private/routers/billing/hooks/handleSubscriptionDeleted.ts b/server/private/routers/billing/hooks/handleSubscriptionDeleted.ts index d92741be..a029fc5c 100644 --- a/server/private/routers/billing/hooks/handleSubscriptionDeleted.ts +++ b/server/private/routers/billing/hooks/handleSubscriptionDeleted.ts @@ -23,7 +23,6 @@ import { import { eq, and } from "drizzle-orm"; import logger from "@server/logger"; import { handleSubscriptionLifesycle } from "../subscriptionLifecycle"; -import { AudienceIds, moveEmailToAudience } from "#private/lib/resend"; import { getSubType } from "./getSubType"; import stripe from "#private/lib/stripe"; import privateConfig from "#private/lib/config"; @@ -109,7 +108,7 @@ export async function handleSubscriptionDeleted( const email = orgUserRes.user.email; if (email) { - moveEmailToAudience(email, AudienceIds.Churned); + // TODO: update user in Sendy } } } else if (type === "license") { diff --git a/server/routers/auth/signup.ts b/server/routers/auth/signup.ts index 93403a50..af3c670d 100644 --- a/server/routers/auth/signup.ts +++ b/server/routers/auth/signup.ts @@ -22,7 +22,6 @@ import { checkValidInvite } from "@server/auth/checkValidInvite"; import { passwordSchema } from "@server/auth/passwordSchema"; import { UserType } from "@server/types/UserTypes"; import { build } from "@server/build"; -import resend, { AudienceIds, moveEmailToAudience } from "#dynamic/lib/resend"; export const signupBodySchema = z.object({ email: z.email().toLowerCase(), @@ -213,7 +212,7 @@ export async function signup( logger.debug( `User ${email} opted in to marketing emails during signup.` ); - moveEmailToAudience(email, AudienceIds.SignUps); + // TODO: update user in Sendy } if (config.getRawConfig().flags?.require_email_verification) {