import { NextRequest } from 'next/server'; import { db } from '@/lib/db'; import { auth } from '@/lib/auth'; import { rateLimit } from '@/lib/rate-limit'; import nodemailer from 'nodemailer'; import { testEmailHtml } from '@/lib/notifications'; import { apiErrors, successResponse } from '@/lib/api-response'; // GET /api/settings/notifications — Fetch current notification preferences export async function GET() { try { const session = await auth(); if (!session?.user?.id) { return apiErrors.unauthorized(); } const settings = await db.notificationSetting.findUnique({ where: { userId: session.user.id }, }); // Return defaults if no settings exist yet return successResponse( settings ?? { telegramBotToken: null, telegramChatId: null, telegramEnabled: false, emailEnabled: false, onNewVideo: true, onNewComment: true, onNewReply: true, timezone: 'UTC', } ); } catch (error) { console.error('Error fetching notification settings:', error); return apiErrors.internalError('Failed to fetch settings'); } } // PUT /api/settings/notifications — Update notification preferences export async function PUT(request: NextRequest) { try { const limited = await rateLimit(request, 'mutate'); if (limited) return limited; const session = await auth(); if (!session?.user?.id) { return apiErrors.unauthorized(); } const body = await request.json(); const { telegramBotToken, telegramChatId, telegramEnabled, emailEnabled, onNewVideo, onNewComment, onNewReply, timezone, } = body; // Validate: if enabling Telegram, both token and chatId are required if (telegramEnabled && (!telegramBotToken || !telegramChatId)) { return apiErrors.badRequest('Telegram Bot Token and Chat ID are required to enable Telegram notifications'); } const settings = await db.notificationSetting.upsert({ where: { userId: session.user.id }, create: { userId: session.user.id, telegramBotToken: telegramBotToken || null, telegramChatId: telegramChatId || null, telegramEnabled: !!telegramEnabled, emailEnabled: !!emailEnabled, onNewVideo: onNewVideo ?? true, onNewComment: onNewComment ?? true, onNewReply: onNewReply ?? true, timezone: timezone || 'UTC', }, update: { telegramBotToken: telegramBotToken || null, telegramChatId: telegramChatId || null, telegramEnabled: !!telegramEnabled, emailEnabled: !!emailEnabled, onNewVideo: onNewVideo ?? true, onNewComment: onNewComment ?? true, onNewReply: onNewReply ?? true, timezone: timezone || 'UTC', }, }); return successResponse(settings); } catch (error) { console.error('Error updating notification settings:', error); return apiErrors.internalError('Failed to update settings'); } } // POST /api/settings/notifications — Test a notification channel export async function POST(request: NextRequest) { try { const limited = await rateLimit(request, 'mutate'); if (limited) return limited; const session = await auth(); if (!session?.user?.id) { return apiErrors.unauthorized(); } const body = await request.json(); const { channel, telegramBotToken, telegramChatId } = body; if (channel === 'telegram') { if (!telegramBotToken || !telegramChatId) { return apiErrors.badRequest('Bot Token and Chat ID are required'); } const settingsUrl = `${process.env.NEXTAUTH_URL || ''}/settings`; const telegramPayload: Record = { chat_id: telegramChatId, text: '✅ OpenFrame notifications connected successfully!\n\nYou will receive notifications here when activity happens on your projects.', link_preview_options: { is_disabled: true }, }; // Telegram inline keyboard buttons require HTTPS URLs if (settingsUrl.startsWith('https://')) { telegramPayload.reply_markup = { inline_keyboard: [[{ text: 'Open Settings', url: settingsUrl }]], }; } const res = await fetch(`https://api.telegram.org/bot${telegramBotToken}/sendMessage`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(telegramPayload), }); if (!res.ok) { const data = await res.json().catch(() => ({})); const desc = (data as { description?: string }).description || 'Unknown error'; return apiErrors.badRequest(`Telegram test failed: ${desc}`); } return successResponse({ message: 'Test message sent to Telegram' }); } if (channel === 'email') { const user = await db.user.findUnique({ where: { id: session.user.id }, select: { email: true }, }); if (!user?.email) { return apiErrors.badRequest('No email address on your account'); } const smtpHost = process.env.SMTP_HOST; const smtpPort = Number(process.env.SMTP_PORT || '587'); const smtpUser = process.env.SMTP_USER; const smtpPass = process.env.SMTP_PASSWORD; if (!smtpHost || !smtpUser || !smtpPass) { return apiErrors.internalError('Email service not configured (SMTP settings missing)'); } const transporter = nodemailer.createTransport({ host: smtpHost, port: smtpPort, secure: smtpPort === 465, auth: { user: smtpUser, pass: smtpPass }, }); const fromAddress = process.env.SMTP_FROM || process.env.EMAIL_FROM || 'OpenFrame '; try { await transporter.sendMail({ from: fromAddress, to: user.email, subject: '[OpenFrame] Test notification', html: testEmailHtml(), }); } catch (emailErr) { console.error('SMTP test email failed:', emailErr); return apiErrors.internalError('Failed to send test email — check SMTP settings'); } return successResponse({ message: `Test email sent to ${user.email}` }); } return apiErrors.badRequest('Unknown channel'); } catch (error) { console.error('Error testing notification:', error); return apiErrors.internalError('Failed to test notification'); } }