initial commit: rocket.new export of broadcastbeat
This commit is contained in:
153
src/app/api/admin/forum-moderation/route.ts
Normal file
153
src/app/api/admin/forum-moderation/route.ts
Normal file
@@ -0,0 +1,153 @@
|
||||
import { NextRequest, NextResponse } from 'next/server';
|
||||
import { createClient } from '@/lib/supabase/server';
|
||||
|
||||
// GET: Fetch threads and replies for moderation
|
||||
export async function GET(req: NextRequest) {
|
||||
try {
|
||||
const supabase = await createClient();
|
||||
const { data: { user } } = await supabase.auth.getUser();
|
||||
if (!user) return NextResponse.json({ error: 'Unauthorized' }, { status: 401 });
|
||||
|
||||
const { searchParams } = new URL(req.url);
|
||||
const tab = searchParams.get('tab') || 'threads';
|
||||
const filter = searchParams.get('filter') || 'all';
|
||||
const page = parseInt(searchParams.get('page') || '1');
|
||||
const limit = 20;
|
||||
const offset = (page - 1) * limit;
|
||||
|
||||
if (tab === 'replies') {
|
||||
let query = supabase
|
||||
.from('forum_replies')
|
||||
.select('*, forum_threads(id, title)', { count: 'exact' })
|
||||
.order('created_at', { ascending: false })
|
||||
.range(offset, offset + limit - 1);
|
||||
|
||||
if (filter === 'flagged') query = query.eq('is_flagged', true);
|
||||
if (filter === 'ai_flagged') query = query.eq('is_flagged', true).like('flag_reason', '[AI]%');
|
||||
if (filter === 'hidden') query = query.eq('is_hidden', true);
|
||||
|
||||
const { data, error, count } = await query;
|
||||
if (error) throw error;
|
||||
return NextResponse.json({ replies: data, total: count, page, limit });
|
||||
}
|
||||
|
||||
// threads tab
|
||||
let query = supabase
|
||||
.from('forum_threads')
|
||||
.select('*, forum_categories(name, slug)', { count: 'exact' })
|
||||
.order('created_at', { ascending: false })
|
||||
.range(offset, offset + limit - 1);
|
||||
|
||||
if (filter === 'flagged') query = query.eq('is_flagged', true);
|
||||
if (filter === 'ai_flagged') query = query.eq('is_flagged', true).like('flag_reason', '[AI]%');
|
||||
if (filter === 'pinned') query = query.eq('is_pinned', true);
|
||||
if (filter === 'featured') query = query.eq('is_featured', true);
|
||||
if (filter === 'locked') query = query.eq('is_locked', true);
|
||||
|
||||
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 });
|
||||
}
|
||||
}
|
||||
|
||||
// PATCH: Apply moderation action
|
||||
export async function PATCH(req: NextRequest) {
|
||||
try {
|
||||
const supabase = await createClient();
|
||||
const { data: { user } } = await supabase.auth.getUser();
|
||||
if (!user) return NextResponse.json({ error: 'Unauthorized' }, { status: 401 });
|
||||
|
||||
const body = await req.json();
|
||||
const { type, id, action, flag_reason } = body;
|
||||
|
||||
if (!type || !id || !action) {
|
||||
return NextResponse.json({ error: 'type, id, and action are required' }, { status: 400 });
|
||||
}
|
||||
|
||||
const now = new Date().toISOString();
|
||||
|
||||
if (type === 'thread') {
|
||||
let updateData: Record<string, any> = { moderated_by: user.id, moderated_at: now };
|
||||
|
||||
switch (action) {
|
||||
case 'pin':
|
||||
updateData.is_pinned = true;
|
||||
break;
|
||||
case 'unpin':
|
||||
updateData.is_pinned = false;
|
||||
break;
|
||||
case 'feature':
|
||||
updateData.is_featured = true;
|
||||
break;
|
||||
case 'unfeature':
|
||||
updateData.is_featured = false;
|
||||
break;
|
||||
case 'lock':
|
||||
updateData.is_locked = true;
|
||||
break;
|
||||
case 'unlock':
|
||||
updateData.is_locked = false;
|
||||
break;
|
||||
case 'flag':
|
||||
updateData.is_flagged = true;
|
||||
updateData.flag_reason = flag_reason || null;
|
||||
break;
|
||||
case 'unflag':
|
||||
updateData.is_flagged = false;
|
||||
updateData.flag_reason = null;
|
||||
break;
|
||||
default:
|
||||
return NextResponse.json({ error: 'Invalid action' }, { status: 400 });
|
||||
}
|
||||
|
||||
const { data, error } = await supabase
|
||||
.from('forum_threads')
|
||||
.update(updateData)
|
||||
.eq('id', id)
|
||||
.select()
|
||||
.single();
|
||||
|
||||
if (error) throw error;
|
||||
return NextResponse.json({ thread: data });
|
||||
}
|
||||
|
||||
if (type === 'reply') {
|
||||
let updateData: Record<string, any> = { moderated_by: user.id, moderated_at: now };
|
||||
|
||||
switch (action) {
|
||||
case 'hide':
|
||||
updateData.is_hidden = true;
|
||||
break;
|
||||
case 'unhide':
|
||||
updateData.is_hidden = false;
|
||||
break;
|
||||
case 'flag':
|
||||
updateData.is_flagged = true;
|
||||
updateData.flag_reason = flag_reason || null;
|
||||
break;
|
||||
case 'unflag':
|
||||
updateData.is_flagged = false;
|
||||
updateData.flag_reason = null;
|
||||
break;
|
||||
default:
|
||||
return NextResponse.json({ error: 'Invalid action' }, { status: 400 });
|
||||
}
|
||||
|
||||
const { data, error } = await supabase
|
||||
.from('forum_replies')
|
||||
.update(updateData)
|
||||
.eq('id', id)
|
||||
.select()
|
||||
.single();
|
||||
|
||||
if (error) throw error;
|
||||
return NextResponse.json({ reply: data });
|
||||
}
|
||||
|
||||
return NextResponse.json({ error: 'Invalid type' }, { status: 400 });
|
||||
} catch (err: any) {
|
||||
return NextResponse.json({ error: err.message }, { status: 500 });
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user