import { NextRequest, NextResponse } from 'next/server'; import { createClient } from '@/lib/supabase/server'; export async function GET(req: NextRequest) { try { const supabase = await createClient(); const { searchParams } = new URL(req.url); const categoryId = searchParams.get('category_id'); const page = parseInt(searchParams.get('page') || '1'); const limit = parseInt(searchParams.get('limit') || '20'); const offset = (page - 1) * limit; const search = searchParams.get('search') || ''; const sort = searchParams.get('sort') || 'newest'; const unanswered = searchParams.get('unanswered') === 'true'; let query = supabase .from('forum_threads') .select('*, forum_categories(name, slug)', { count: 'exact' }) .range(offset, offset + limit - 1); if (categoryId) { query = query.eq('category_id', categoryId); } if (search.trim()) { query = query.ilike('title', `%${search.trim()}%`); } if (unanswered) { query = query.eq('reply_count', 0); } if (sort === 'top-voted') { query = query.order('upvote_count', { ascending: false }).order('created_at', { ascending: false }); } else if (sort === 'most-viewed') { query = query.order('view_count', { ascending: false }).order('created_at', { ascending: false }); } else if (sort === 'most-replied') { query = query.order('reply_count', { ascending: false }).order('created_at', { ascending: false }); } else if (sort === 'unanswered') { query = query.eq('reply_count', 0).order('created_at', { ascending: false }); } else { // newest — default query = query.order('status', { ascending: false }).order('last_reply_at', { ascending: false }); } const { data, error, count } = await query; if (error) throw error; return NextResponse.json({ threads: data, total: count, page, limit }); } catch (err: any) { return NextResponse.json({ error: err.message }, { status: 500 }); } } export async function POST(req: NextRequest) { try { const supabase = await createClient(); const { data: { user } } = await supabase.auth.getUser(); const body = await req.json(); const { category_id, title, body: threadBody, author_name } = body; if (!category_id || !title || !threadBody) { return NextResponse.json({ error: 'category_id, title, and body are required' }, { status: 400 }); } // Check if authenticated user is suspended or banned if (user) { const { data: suspension } = await supabase .from('user_suspensions') .select('suspension_type, reason, expires_at') .eq('user_id', user.id) .eq('is_active', true) .or('expires_at.is.null,expires_at.gt.' + new Date().toISOString()) .order('created_at', { ascending: false }) .limit(1) .maybeSingle(); if (suspension) { const isBanned = suspension.suspension_type === 'banned'; const expiryText = suspension.expires_at ? ` until ${new Date(suspension.expires_at).toLocaleDateString('en-US', { month: 'short', day: 'numeric', year: 'numeric' })}` : ''; return NextResponse.json( { error: isBanned ? `Your account has been permanently banned. Reason: ${suspension.reason}` : `Your account is suspended${expiryText}. Reason: ${suspension.reason}`, suspended: true, suspension_type: suspension.suspension_type, }, { status: 403 } ); } } // Fetch category name for better moderation context const { data: categoryData } = await supabase .from('forum_categories') .select('name') .eq('id', category_id) .single(); const { data, error } = await supabase .from('forum_threads') .insert({ category_id, title: title.trim(), body: threadBody.trim(), author_id: user?.id || null, author_name: author_name?.trim() || (user ? 'Community Member' : 'Anonymous'), is_ai_seeded: false, }) .select() .single(); if (error) throw error; // Auto-moderate with Claude (fire-and-forget style, non-blocking on failure) try { const baseUrl = process.env.NEXT_PUBLIC_SITE_URL || 'http://localhost:3000'; const modRes = await fetch(`${baseUrl}/api/forum/auto-moderate`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ content_type: 'thread', title: title.trim(), body: threadBody.trim(), category_name: categoryData?.name || '', }), }); if (modRes.ok) { const { moderation } = await modRes.json(); if (moderation?.flagged) { await supabase .from('forum_threads') .update({ is_flagged: true, flag_reason: `[AI] ${moderation.reason || 'Auto-flagged by moderation system'}`, }) .eq('id', data.id); return NextResponse.json({ thread: { ...data, is_flagged: true, flag_reason: `[AI] ${moderation.reason}` }, }, { status: 201 }); } } } catch { // AI moderation failure must not block thread creation } return NextResponse.json({ thread: data }, { status: 201 }); } catch (err: any) { return NextResponse.json({ error: err.message }, { status: 500 }); } }