90 lines
3.0 KiB
TypeScript
90 lines
3.0 KiB
TypeScript
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 userId = searchParams.get('user_id');
|
|
|
|
if (!userId) {
|
|
return NextResponse.json({ error: 'user_id is required' }, { status: 400 });
|
|
}
|
|
|
|
// Fetch user profile
|
|
const { data: profile, error: profileError } = await supabase
|
|
.from('user_profiles')
|
|
.select('id, full_name, avatar_url, bio, website, location, reputation, created_at')
|
|
.eq('id', userId)
|
|
.single();
|
|
|
|
if (profileError || !profile) {
|
|
return NextResponse.json({ error: 'User not found' }, { status: 404 });
|
|
}
|
|
|
|
// Fetch user's threads (post history)
|
|
const { data: threads } = await supabase
|
|
.from('forum_threads')
|
|
.select('id, title, body, vote_score, upvotes, reply_count, view_count, created_at, forum_categories(id, name, slug)')
|
|
.eq('author_id', userId)
|
|
.order('created_at', { ascending: false })
|
|
.limit(50);
|
|
|
|
// Fetch user's replies
|
|
const { data: replies } = await supabase
|
|
.from('forum_replies')
|
|
.select('id, body, vote_score, upvotes, created_at, thread_id, forum_threads(id, title)')
|
|
.eq('author_id', userId)
|
|
.order('created_at', { ascending: false })
|
|
.limit(50);
|
|
|
|
// Top threads by vote score
|
|
const topThreads = [...(threads || [])]
|
|
.sort((a, b) => (b.vote_score || 0) - (a.vote_score || 0))
|
|
.slice(0, 5);
|
|
|
|
// Contributions by category
|
|
const categoryMap: Record<string, { name: string; slug: string; count: number }> = {};
|
|
(threads || []).forEach((t: any) => {
|
|
const cat = t.forum_categories;
|
|
if (cat) {
|
|
if (!categoryMap[cat.id]) {
|
|
categoryMap[cat.id] = { name: cat.name, slug: cat.slug, count: 0 };
|
|
}
|
|
categoryMap[cat.id].count++;
|
|
}
|
|
});
|
|
const contributionsByCategory = Object.values(categoryMap).sort((a, b) => b.count - a.count);
|
|
|
|
// Stats
|
|
const totalThreads = (threads || []).length;
|
|
const totalReplies = (replies || []).length;
|
|
const totalUpvotesReceived = (threads || []).reduce((sum: number, t: any) => sum + (t.upvotes || 0), 0)
|
|
+ (replies || []).reduce((sum: number, r: any) => sum + (r.upvotes || 0), 0);
|
|
|
|
// Fetch earned badges
|
|
const { data: badges } = await supabase
|
|
.from('user_badges')
|
|
.select('badge_type, awarded_at, awarded_reason')
|
|
.eq('user_id', userId)
|
|
.order('awarded_at', { ascending: true });
|
|
|
|
return NextResponse.json({
|
|
profile,
|
|
threads: threads || [],
|
|
replies: replies || [],
|
|
topThreads,
|
|
contributionsByCategory,
|
|
badges: badges || [],
|
|
stats: {
|
|
totalThreads,
|
|
totalReplies,
|
|
totalPosts: totalThreads + totalReplies,
|
|
totalUpvotesReceived,
|
|
},
|
|
});
|
|
} catch (err: any) {
|
|
return NextResponse.json({ error: err.message }, { status: 500 });
|
|
}
|
|
}
|