email marketing
This commit is contained in:
51
src/app/(main)/api/marketing/designer-broadcast/route.ts
Normal file
51
src/app/(main)/api/marketing/designer-broadcast/route.ts
Normal file
@@ -0,0 +1,51 @@
|
||||
import { NextResponse } from 'next/server';
|
||||
import { cookies } from 'next/headers';
|
||||
import { db } from '@/lib/db';
|
||||
import { sendDesignerAnnouncementEmail } from '@/lib/email';
|
||||
import { createMarketingUnsubscribeUrl } from '@/lib/marketingEmail';
|
||||
|
||||
export const maxDuration = 300;
|
||||
|
||||
/** Sends the Designer announcement to account holders who have not opted out. */
|
||||
export async function POST() {
|
||||
if (cookies().get('newsletter-admin')?.value !== 'authenticated') {
|
||||
return NextResponse.json({ error: 'Unauthorized.' }, { status: 401 });
|
||||
}
|
||||
|
||||
try {
|
||||
const suppressions = await db.newsletterSubscription.findMany({
|
||||
where: { status: 'unsubscribed' },
|
||||
select: { email: true },
|
||||
});
|
||||
const suppressedEmails = new Set(suppressions.map((entry) => entry.email.toLowerCase()));
|
||||
const accountUsers = await db.user.findMany({
|
||||
select: { email: true },
|
||||
orderBy: { createdAt: 'asc' },
|
||||
});
|
||||
const recipients = accountUsers.filter(
|
||||
(recipient) => !suppressedEmails.has(recipient.email.toLowerCase())
|
||||
);
|
||||
|
||||
let sent = 0;
|
||||
const failed: string[] = [];
|
||||
|
||||
// Sequential sending respects the configured Resend free-tier rate limit.
|
||||
for (const recipient of recipients) {
|
||||
try {
|
||||
await sendDesignerAnnouncementEmail(
|
||||
recipient.email,
|
||||
createMarketingUnsubscribeUrl(recipient.email)
|
||||
);
|
||||
sent++;
|
||||
} catch (error) {
|
||||
failed.push(recipient.email);
|
||||
console.error('Designer announcement failed:', error);
|
||||
}
|
||||
}
|
||||
|
||||
return NextResponse.json({ sent, failed: failed.length, total: recipients.length });
|
||||
} catch (error) {
|
||||
console.error('Designer announcement broadcast error:', error);
|
||||
return NextResponse.json({ error: 'Unable to send Designer announcement.' }, { status: 500 });
|
||||
}
|
||||
}
|
||||
25
src/app/(main)/api/marketing/unsubscribe/route.ts
Normal file
25
src/app/(main)/api/marketing/unsubscribe/route.ts
Normal file
@@ -0,0 +1,25 @@
|
||||
import { NextRequest, NextResponse } from 'next/server';
|
||||
import { db } from '@/lib/db';
|
||||
import { getUnsubscribeEmail } from '@/lib/marketingEmail';
|
||||
|
||||
export async function POST(request: NextRequest) {
|
||||
try {
|
||||
const { token } = await request.json();
|
||||
const email = getUnsubscribeEmail(typeof token === 'string' ? token : null);
|
||||
|
||||
if (!email) {
|
||||
return NextResponse.json({ error: 'This unsubscribe link is invalid or expired.' }, { status: 400 });
|
||||
}
|
||||
|
||||
await db.newsletterSubscription.upsert({
|
||||
where: { email },
|
||||
update: { status: 'unsubscribed', source: 'marketing-unsubscribe' },
|
||||
create: { email, status: 'unsubscribed', source: 'marketing-unsubscribe' },
|
||||
});
|
||||
|
||||
return NextResponse.json({ success: true });
|
||||
} catch (error) {
|
||||
console.error('Marketing unsubscribe error:', error);
|
||||
return NextResponse.json({ error: 'Unable to update your email preferences.' }, { status: 500 });
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user