/* * 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 = "5cfbf99b-c592-40a9-9b8a-577a4681c158", 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; } } }