jefflix-website/app/api/request-channel/route.ts

112 lines
4.1 KiB
TypeScript

import { NextRequest, NextResponse } from 'next/server'
import nodemailer from 'nodemailer'
export async function POST(request: NextRequest) {
try {
const body = await request.json()
const { channelName, category, url, notes, email } = body
// Validate required fields
if (!channelName || !email) {
return NextResponse.json(
{ error: 'Channel name and email are required' },
{ status: 400 }
)
}
// Email validation
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/
if (!emailRegex.test(email)) {
return NextResponse.json(
{ error: 'Invalid email format' },
{ status: 400 }
)
}
const smtpHost = process.env.SMTP_HOST
const smtpUser = process.env.SMTP_USER
const smtpPass = process.env.SMTP_PASS
if (!smtpHost || !smtpUser || !smtpPass) {
console.error('SMTP credentials not configured')
return NextResponse.json(
{ error: 'Email service not configured' },
{ status: 500 }
)
}
const adminEmail = process.env.ADMIN_EMAIL || 'jeff@jeffemmett.com'
const transporter = nodemailer.createTransport({
host: smtpHost,
port: Number(process.env.SMTP_PORT) || 587,
secure: false,
auth: { user: smtpUser, pass: smtpPass },
tls: { rejectUnauthorized: false },
})
await transporter.sendMail({
from: `Jefflix <${smtpUser}>`,
to: adminEmail,
subject: `[Jefflix] Channel Request: ${escapeHtml(channelName)}`,
html: `
<h2>New Channel Request</h2>
<p>Someone has requested a new Live TV channel:</p>
<table style="border-collapse: collapse; margin: 20px 0;">
<tr>
<td style="padding: 8px; font-weight: bold; border: 1px solid #ddd;">Channel:</td>
<td style="padding: 8px; border: 1px solid #ddd;">${escapeHtml(channelName)}</td>
</tr>
<tr>
<td style="padding: 8px; font-weight: bold; border: 1px solid #ddd;">Category:</td>
<td style="padding: 8px; border: 1px solid #ddd;">${escapeHtml(category || 'Not specified')}</td>
</tr>
<tr>
<td style="padding: 8px; font-weight: bold; border: 1px solid #ddd;">URL/Link:</td>
<td style="padding: 8px; border: 1px solid #ddd;">${url ? `<a href="${escapeHtml(url)}">${escapeHtml(url)}</a>` : 'Not provided'}</td>
</tr>
<tr>
<td style="padding: 8px; font-weight: bold; border: 1px solid #ddd;">Notes:</td>
<td style="padding: 8px; border: 1px solid #ddd;">${escapeHtml(notes || 'None')}</td>
</tr>
<tr>
<td style="padding: 8px; font-weight: bold; border: 1px solid #ddd;">Email:</td>
<td style="padding: 8px; border: 1px solid #ddd;"><a href="mailto:${escapeHtml(email)}">${escapeHtml(email)}</a></td>
</tr>
<tr>
<td style="padding: 8px; font-weight: bold; border: 1px solid #ddd;">Requested:</td>
<td style="padding: 8px; border: 1px solid #ddd;">${new Date().toLocaleString()}</td>
</tr>
</table>
<p><strong>To add this channel:</strong></p>
<ol>
<li>Check <a href="https://iptv-org.github.io">iptv-org</a> for an existing stream</li>
<li>If found, add to Threadfin channel list and map the stream</li>
<li>If not in iptv-org, add as a custom M3U source in Threadfin</li>
<li>Reply to ${escapeHtml(email)} to let them know it's been added</li>
</ol>
<hr style="margin: 20px 0; border: none; border-top: 1px solid #ddd;" />
<p style="color: #666; font-size: 12px;">This is an automated message from Jefflix.</p>
`,
})
return NextResponse.json({ success: true })
} catch (error) {
console.error('Channel request error:', error)
return NextResponse.json(
{ error: 'Internal server error' },
{ status: 500 }
)
}
}
function escapeHtml(text: string): string {
const map: Record<string, string> = {
'&': '&amp;',
'<': '&lt;',
'>': '&gt;',
'"': '&quot;',
"'": '&#039;',
}
return text.replace(/[&<>"']/g, (char) => map[char])
}