initial commit: rocket.new export of broadcastbeat
This commit is contained in:
76
src/app/api/newsletter/subscribe/route.ts
Normal file
76
src/app/api/newsletter/subscribe/route.ts
Normal file
@@ -0,0 +1,76 @@
|
||||
import { createClient } from '@/lib/supabase/server';
|
||||
import { NextRequest, NextResponse } from 'next/server';
|
||||
import nodemailer from 'nodemailer';
|
||||
|
||||
export async function POST(req: NextRequest) {
|
||||
try {
|
||||
const { email, topics } = await req.json();
|
||||
|
||||
if (!email || !/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email)) {
|
||||
return NextResponse.json({ error: 'Valid email is required' }, { status: 400 });
|
||||
}
|
||||
|
||||
const supabase = await createClient();
|
||||
|
||||
// Upsert subscriber (re-subscribe if previously unsubscribed)
|
||||
const { error } = await supabase
|
||||
.from('newsletter_subscribers')
|
||||
.upsert(
|
||||
{
|
||||
email: email.toLowerCase().trim(),
|
||||
topics: topics || [],
|
||||
status: 'active',
|
||||
subscribed_at: new Date().toISOString(),
|
||||
unsubscribed_at: null,
|
||||
},
|
||||
{ onConflict: 'email' }
|
||||
);
|
||||
|
||||
if (error) {
|
||||
console.error('Supabase error:', error);
|
||||
return NextResponse.json({ error: 'Failed to save subscription' }, { status: 500 });
|
||||
}
|
||||
|
||||
// Send confirmation email via SMTP
|
||||
const smtpHost = process.env.SMTP_HOST;
|
||||
const smtpPort = parseInt(process.env.SMTP_PORT || '587');
|
||||
const smtpUser = process.env.SMTP_USER;
|
||||
const smtpPass = process.env.SMTP_PASS;
|
||||
const fromEmail = process.env.SMTP_FROM_EMAIL || smtpUser;
|
||||
const fromName = process.env.SMTP_FROM_NAME || 'BroadcastBeat';
|
||||
|
||||
if (smtpHost && smtpUser && smtpPass) {
|
||||
const transporter = nodemailer.createTransport({
|
||||
host: smtpHost,
|
||||
port: smtpPort,
|
||||
secure: smtpPort === 465,
|
||||
auth: { user: smtpUser, pass: smtpPass },
|
||||
});
|
||||
|
||||
const topicsList = topics && topics.length > 0
|
||||
? `<p style="color:#aaa;font-size:14px;">You'll receive updates on: <strong style="color:#3b82f6;">${topics.join(', ')}</strong></p>`
|
||||
: '';
|
||||
|
||||
await transporter.sendMail({
|
||||
from: `"${fromName}" <${fromEmail}>`,
|
||||
to: email,
|
||||
subject: `Welcome to BroadcastBeat — You're subscribed!`,
|
||||
html: `
|
||||
<div style="background:#0d1520;padding:40px 20px;font-family:sans-serif;max-width:600px;margin:0 auto;">
|
||||
<h1 style="color:#3b82f6;font-size:24px;margin-bottom:8px;">You're in!</h1>
|
||||
<p style="color:#e0e0e0;font-size:16px;">Thanks for subscribing to <strong>BroadcastBeat</strong> — the digital platform for broadcast engineering.</p>
|
||||
${topicsList}
|
||||
<p style="color:#aaa;font-size:14px;margin-top:24px;">You'll be the first to know about the latest news, product launches, and industry insights.</p>
|
||||
<hr style="border:none;border-top:1px solid #1e3a5f;margin:32px 0;" />
|
||||
<p style="color:#555;font-size:12px;">You're receiving this because you signed up at broadcastbeat.com. <a href="${process.env.NEXT_PUBLIC_SITE_URL}/unsubscribe?email=${encodeURIComponent(email)}" style="color:#3b82f6;">Unsubscribe</a></p>
|
||||
</div>
|
||||
`,
|
||||
});
|
||||
}
|
||||
|
||||
return NextResponse.json({ success: true });
|
||||
} catch (err: unknown) {
|
||||
console.error('Subscribe error:', err);
|
||||
return NextResponse.json({ error: 'Internal server error' }, { status: 500 });
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user