Files
avbeat-com/src/app/api/forum/user-by-username/[username]/route.ts

121 lines
3.6 KiB
TypeScript

import { NextResponse } from "next/server";
import { createClient } from "@supabase/supabase-js";
export const runtime = "nodejs";
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 || "av";
function client() {
return createClient(SUPABASE_URL, SUPABASE_ANON, {
db: { schema: SCHEMA as "public" },
auth: { persistSession: false },
});
}
export async function GET(
_req: Request,
{ params }: { params: Promise<{ username: string }> },
) {
const { username } = await params;
const sb = client();
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")
.select("*")
.eq("display_name", decoded)
.order("created_at", { ascending: true })
.limit(1);
profile = (r2.data && r2.data[0]) || null;
}
if (!profile) {
return NextResponse.json({ error: "not found" }, { status: 404 });
}
const displayName = profile.display_name as string;
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),
]);
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.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: repliesWithTitles,
});
}