import { NextRequest } from 'next/server' import nodemailer from 'nodemailer' import { verifyApproveToken } from '@/lib/token' import { activateChannels } from '@/lib/threadfin' function htmlPage(title: string, body: string): Response { return new Response( ` ${title} — Jefflix
${body}
`, { status: 200, headers: { 'Content-Type': 'text/html; charset=utf-8' } }, ) } export async function GET(request: NextRequest) { const token = request.nextUrl.searchParams.get('token') if (!token) { return htmlPage('Invalid Link', `

Invalid Link

No approval token provided.

`) } const payload = verifyApproveToken(token) if (!payload) { return htmlPage('Expired or Invalid', `

Link Expired or Invalid

This approval link has expired or is invalid. Channel requests expire after 7 days.

Ask the user to submit a new request.

`) } try { const result = await activateChannels( payload.channels.map((ch) => ch.id), ) // Send confirmation email to the requester await sendConfirmationEmail(payload.email, result.activated, result.notFound, payload.channels) const activatedHtml = result.activated.length > 0 ? `

Activated (${result.activated.length})

` : '' const notFoundHtml = result.notFound.length > 0 ? `

Not Found in Playlists (${result.notFound.length})

These channels aren't in the current M3U playlists (english/news/sports). Add them to Threadfin's playlist sources first.

` : '' return htmlPage('Channels Approved', `

Channels Approved

Request from ${escapeHtml(payload.email)} has been processed.

${activatedHtml} ${notFoundHtml}

A confirmation email has been sent to the requester.

`) } catch (err) { console.error('Channel activation error:', err) return htmlPage('Activation Failed', `

Activation Failed

Could not connect to Threadfin or activate channels.

${escapeHtml(err instanceof Error ? err.message : String(err))}

Check Threadfin is running and credentials are correct.

`) } } async function sendConfirmationEmail( to: string, activated: string[], notFound: string[], channels: { id: string; name: string }[], ) { const smtpHost = process.env.SMTP_HOST const smtpUser = process.env.SMTP_USER const smtpPass = process.env.SMTP_PASS if (!smtpHost || !smtpUser || !smtpPass) return const transporter = nodemailer.createTransport({ host: smtpHost, port: Number(process.env.SMTP_PORT) || 587, secure: false, auth: { user: smtpUser, pass: smtpPass }, tls: { rejectUnauthorized: false }, }) const activatedList = activated.map((id) => { const ch = channels.find((c) => c.id === id) return `
  • ${escapeHtml(ch?.name ?? id)}
  • ` }).join('') const notFoundList = notFound.map((id) => { const ch = channels.find((c) => c.id === id) return `
  • ${escapeHtml(ch?.name ?? id)} — not available in current playlists
  • ` }).join('') await transporter.sendMail({ from: `Jefflix <${smtpUser}>`, to, subject: `[Jefflix] Your channel request has been processed`, html: `

    Your Channel Request Update

    ${activated.length > 0 ? `

    The following channels have been activated and should appear in your guide shortly:

    ` : ''} ${notFound.length > 0 ? `

    The following channels were not found in the current playlists:

    These may become available when new playlist sources are added.

    ` : ''} ${activated.length > 0 ? '

    It may take a few minutes for the channels to appear in your TV guide. If you don\'t see them after 15 minutes, try refreshing your IPTV app.

    ' : ''}

    Jefflix · ${new Date().toLocaleString()}

    `, }) } function escapeHtml(text: string): string { const map: Record = { '&': '&', '<': '<', '>': '>', '"': '"', "'": ''', } return text.replace(/[&<>"']/g, (char) => map[char]) }