/** * Beta Tester Feedback API * * Captures user bug reports, UI issues, search problems, and feature requests * enriched with client-safe diagnostic metadata (viewport, route, user agent). */ import { NextResponse } from "next/server"; import { getServerSession } from "next-auth/next"; import { authOptions } from "@/lib/auth"; import { prisma } from "@/lib/prisma"; import { logEvent } from "@/lib/logger"; import { rateLimit } from "@/lib/rateLimit"; import { sanitizeHtml } from "@/lib/validation"; export async function POST(req: Request) { // Rate limit: 10 feedback submissions per 10 minutes per IP const rateLimitRes = rateLimit(req, { limit: 10, windowMs: 10 * 60 * 1000, keyPrefix: "beta_feedback", }); if (rateLimitRes) return rateLimitRes; try { const session = await getServerSession(authOptions); const userId = (session?.user as any)?.id || null; const userEmail = session?.user?.email || null; const body = await req.json(); const { category, description, expectedBehavior, actualBehavior, route, pageUrl, viewport, browserInfo } = body; if (!description || typeof description !== "string" || description.trim().length < 5) { return NextResponse.json( { error: "Description is required (minimum 5 characters)." }, { status: 400 } ); } const cleanCategory = ["BUG", "UI_PROBLEM", "SEARCH_PROBLEM", "JOB_DATA_PROBLEM", "FEATURE_REQUEST", "OTHER"].includes(category) ? category : "OTHER"; const feedback = await prisma.betaFeedback.create({ data: { userId, userEmail, category: cleanCategory, description: sanitizeHtml(description.trim()), expectedBehavior: expectedBehavior ? sanitizeHtml(String(expectedBehavior).trim()) : null, actualBehavior: actualBehavior ? sanitizeHtml(String(actualBehavior).trim()) : null, route: route ? String(route).slice(0, 200) : null, pageUrl: pageUrl ? String(pageUrl).slice(0, 500) : null, viewport: viewport ? String(viewport).slice(0, 50) : null, browserInfo: browserInfo ? String(browserInfo).slice(0, 300) : null, }, }); logEvent({ level: "INFO", context: "BETA_FEEDBACK", message: `New beta feedback received [${cleanCategory}] from ${userEmail || "anonymous"}`, userId: userId || undefined, meta: { feedbackId: feedback.id, category: cleanCategory, route }, }); return NextResponse.json({ success: true, message: "Thank you for your feedback! Your report has been submitted to the engineering team.", feedbackId: feedback.id, }); } catch (err: any) { return NextResponse.json({ error: err.message || "Failed to submit feedback" }, { status: 500 }); } }