fix(forum): complete member profiles — counts, avatars, strip AI metadata
This commit is contained in:
@@ -6,7 +6,7 @@ export const revalidate = 120;
|
||||
|
||||
const SUPABASE_URL = process.env.NEXT_PUBLIC_SUPABASE_URL!;
|
||||
const SUPABASE_ANON = process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!;
|
||||
const SCHEMA = process.env.NEXT_PUBLIC_SUPABASE_SCHEMA || "bb";
|
||||
const SCHEMA = process.env.NEXT_PUBLIC_SUPABASE_SCHEMA || "av";
|
||||
|
||||
function client() {
|
||||
return createClient(SUPABASE_URL, SUPABASE_ANON, {
|
||||
@@ -16,24 +16,19 @@ function client() {
|
||||
}
|
||||
|
||||
export async function GET(
|
||||
req: Request,
|
||||
_req: Request,
|
||||
{ params }: { params: Promise<{ username: string }> },
|
||||
) {
|
||||
const { username } = await params;
|
||||
const sb = client();
|
||||
|
||||
// 1) Profile — accept EITHER username OR display_name (URL-encoded).
|
||||
// forum_threads/forum_replies store author_name = display_name,
|
||||
// so the link in the UI uses the display name; we resolve it here.
|
||||
// Multiple profiles can share a display_name (Michael Carter, Alex Rivera,
|
||||
// etc.) — pick the earliest-created profile deterministically rather than
|
||||
// erroring out with maybeSingle().
|
||||
const decoded = decodeURIComponent(username);
|
||||
|
||||
let { data: profile } = await sb
|
||||
.from("forum_user_profiles")
|
||||
.select("*")
|
||||
.eq("username", decoded)
|
||||
.maybeSingle();
|
||||
|
||||
if (!profile) {
|
||||
const r2 = await sb
|
||||
.from("forum_user_profiles")
|
||||
@@ -48,51 +43,79 @@ export async function GET(
|
||||
return NextResponse.json({ error: "not found" }, { status: 404 });
|
||||
}
|
||||
|
||||
// 2) Their threads (use display_name match since virtual users live
|
||||
// outside the auth.users / bb.user_profiles graph and the
|
||||
// forum_threads.author_id is NULL for them).
|
||||
const displayName = (profile as any).display_name as string;
|
||||
const { data: threads } = await sb
|
||||
.from("forum_threads")
|
||||
.select("id,title,reply_count,view_count,vote_score,created_at,category_id,forum_categories(name,slug)")
|
||||
.eq("author_name", displayName)
|
||||
.order("created_at", { ascending: false })
|
||||
.limit(50);
|
||||
const displayName = profile.display_name as string;
|
||||
|
||||
// 3) Recent replies
|
||||
const { data: replies } = await sb
|
||||
.from("forum_replies")
|
||||
.select("id,thread_id,body,vote_score,created_at,forum_threads(title)")
|
||||
.eq("author_name", displayName)
|
||||
.order("created_at", { ascending: false })
|
||||
.limit(20);
|
||||
const [
|
||||
{ count: threadCount },
|
||||
{ count: replyCount },
|
||||
{ data: threads },
|
||||
{ data: replies },
|
||||
] = await Promise.all([
|
||||
sb
|
||||
.from("forum_threads")
|
||||
.select("*", { count: "exact", head: true })
|
||||
.eq("author_name", displayName),
|
||||
sb
|
||||
.from("forum_replies")
|
||||
.select("*", { count: "exact", head: true })
|
||||
.eq("author_name", displayName),
|
||||
sb
|
||||
.from("forum_threads")
|
||||
.select(
|
||||
"id,title,reply_count,view_count,vote_score,created_at,category_id,forum_categories(name,slug)",
|
||||
)
|
||||
.eq("author_name", displayName)
|
||||
.order("created_at", { ascending: false })
|
||||
.limit(50),
|
||||
sb
|
||||
.from("forum_replies")
|
||||
.select("id,thread_id,body,vote_score,created_at")
|
||||
.eq("author_name", displayName)
|
||||
.order("created_at", { ascending: false })
|
||||
.limit(20),
|
||||
]);
|
||||
|
||||
// 4) Computed counts
|
||||
const threadCount = (threads || []).length;
|
||||
const replyCount = (replies || []).length;
|
||||
const threadIds = [...new Set((replies || []).map((r) => r.thread_id).filter(Boolean))];
|
||||
let titleByThread: Record<string, string> = {};
|
||||
if (threadIds.length > 0) {
|
||||
const { data: threadTitles } = await sb
|
||||
.from("forum_threads")
|
||||
.select("id,title")
|
||||
.in("id", threadIds);
|
||||
titleByThread = Object.fromEntries(
|
||||
(threadTitles || []).map((t) => [t.id, t.title]),
|
||||
);
|
||||
}
|
||||
|
||||
const repliesWithTitles = (replies || []).map((r) => ({
|
||||
...r,
|
||||
forum_threads: { title: titleByThread[r.thread_id] || "(thread)" },
|
||||
}));
|
||||
|
||||
const totalThreads = threadCount ?? 0;
|
||||
const totalReplies = replyCount ?? 0;
|
||||
|
||||
return NextResponse.json({
|
||||
profile: {
|
||||
username: (profile as any).username,
|
||||
display_name: (profile as any).display_name,
|
||||
bio: (profile as any).bio || null,
|
||||
avatar_url: (profile as any).avatar_url || null,
|
||||
role_title: (profile as any).role_title || null,
|
||||
company: (profile as any).company || null,
|
||||
location_city: (profile as any).location_city || null,
|
||||
location_country: (profile as any).location_country || null,
|
||||
years_experience: (profile as any).years_experience || null,
|
||||
ai_persona_archetype: (profile as any).ai_persona_archetype || null,
|
||||
expertise_level: (profile as any).expertise_level || null,
|
||||
expertise_tags: (profile as any).expertise_tags || [],
|
||||
writing_voice: (profile as any).writing_voice || null,
|
||||
signature: (profile as any).signature || null,
|
||||
join_date: (profile as any).join_date || null,
|
||||
last_seen_at: (profile as any).last_seen_at || null,
|
||||
thread_count: threadCount,
|
||||
reply_count: replyCount,
|
||||
username: profile.username,
|
||||
display_name: profile.display_name,
|
||||
bio: profile.bio || null,
|
||||
avatar_url: profile.avatar_url || null,
|
||||
role_title: profile.role_title || null,
|
||||
company: profile.company || null,
|
||||
location_city: profile.location_city || null,
|
||||
location_country: profile.location_country || null,
|
||||
years_experience: profile.years_experience || null,
|
||||
expertise_level: profile.expertise_level || null,
|
||||
expertise_tags: profile.expertise_tags || [],
|
||||
signature: profile.signature || null,
|
||||
join_date: profile.join_date || null,
|
||||
last_seen_at: profile.last_seen_at || null,
|
||||
thread_count: totalThreads,
|
||||
reply_count: totalReplies,
|
||||
post_count: totalThreads + totalReplies,
|
||||
},
|
||||
threads: threads || [],
|
||||
replies: replies || [],
|
||||
replies: repliesWithTitles,
|
||||
});
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user