Spaces:
Configuration error
Configuration error
| import { NextRequest, NextResponse } from "next/server" | |
| import prisma from "@/lib/prisma" | |
| import { getAuthUser } from "@/lib/auth" | |
| import { createForumPostSchema, parseBody } from "@/lib/validations" | |
| import { checkRateLimit, getClientIdentifier } from "@/lib/rate-limit" | |
| import { sanitizeText } from "@/lib/api-utils" | |
| const FORUM_CATEGORIES = [ | |
| "general", | |
| "help", | |
| "showcase", | |
| "feature-request", | |
| "tips", | |
| ] | |
| // GET - List forum posts (uses Prisma include instead of separate user query) | |
| export async function GET(request: NextRequest) { | |
| try { | |
| const { searchParams } = new URL(request.url) | |
| const category = searchParams.get("category") | |
| const search = searchParams.get("search") | |
| const sort = searchParams.get("sort") || "recent" | |
| const limit = Math.min(parseInt(searchParams.get("limit") || "20"), 100) | |
| const offset = Math.max(parseInt(searchParams.get("offset") || "0"), 0) | |
| // Build where clause | |
| const whereClause: Record<string, unknown> = {} | |
| if (category && category !== "all") { | |
| whereClause.category = category | |
| } | |
| if (search) { | |
| whereClause.OR = [ | |
| { title: { contains: search, mode: "insensitive" } }, | |
| { content: { contains: search, mode: "insensitive" } }, | |
| ] | |
| } | |
| // Build order by | |
| let orderBy: Record<string, string>[] = [{ createdAt: "desc" }] | |
| if (sort === "popular") { | |
| orderBy = [{ views: "desc" }, { likes: "desc" }] | |
| } else if (sort === "unanswered") { | |
| whereClause.replies = { none: {} } | |
| } | |
| // Use Prisma include to get user data in single query (fixes N+1) | |
| const [posts, total] = await Promise.all([ | |
| prisma.forumPost.findMany({ | |
| where: whereClause, | |
| orderBy, | |
| take: limit, | |
| skip: offset, | |
| include: { | |
| user: { | |
| select: { | |
| id: true, | |
| name: true, | |
| username: true, | |
| image: true, | |
| }, | |
| }, | |
| _count: { | |
| select: { replies: true } | |
| } | |
| } | |
| }), | |
| prisma.forumPost.count({ where: whereClause }), | |
| ]) | |
| const postsWithCounts = posts.map(post => ({ | |
| ...post, | |
| replyCount: post._count.replies, | |
| })) | |
| return NextResponse.json({ | |
| posts: postsWithCounts, | |
| total, | |
| categories: FORUM_CATEGORIES, | |
| hasMore: offset + posts.length < total, | |
| }, { | |
| headers: { | |
| 'Cache-Control': 'public, s-maxage=30, stale-while-revalidate=120', | |
| }, | |
| }) | |
| } catch (error) { | |
| console.error("Error fetching forum posts:", error) | |
| return NextResponse.json( | |
| { error: "Failed to fetch posts" }, | |
| { status: 500 } | |
| ) | |
| } | |
| } | |
| // POST - Create a new forum post — requires auth, rate limited, validated, sanitized | |
| export async function POST(request: NextRequest) { | |
| try { | |
| const authUser = await getAuthUser() | |
| if (!authUser) { | |
| return NextResponse.json( | |
| { error: "Authentication required" }, | |
| { status: 401 } | |
| ) | |
| } | |
| // Rate limit | |
| const identifier = getClientIdentifier(request, authUser.id) | |
| const rateLimit = await checkRateLimit(`forum:${identifier}`, true) | |
| if (!rateLimit.success) { | |
| return NextResponse.json({ error: rateLimit.error }, { status: 429 }) | |
| } | |
| const body = await request.json() | |
| const parsed = parseBody(createForumPostSchema, body) | |
| if (!parsed.success) return parsed.response | |
| const { title, content, category } = parsed.data | |
| // Sanitize content (strip HTML) | |
| const sanitizedContent = sanitizeText(content, 20000) | |
| const sanitizedTitle = sanitizeText(title, 300) | |
| const post = await prisma.forumPost.create({ | |
| data: { | |
| title: sanitizedTitle, | |
| content: sanitizedContent, | |
| category, | |
| userId: authUser.id, | |
| }, | |
| }) | |
| return NextResponse.json(post) | |
| } catch (error) { | |
| console.error("Error creating forum post:", error) | |
| return NextResponse.json( | |
| { error: "Failed to create post" }, | |
| { status: 500 } | |
| ) | |
| } | |
| } | |