From bd84f29b968cb75da996b220049d066a2f30c54e Mon Sep 17 00:00:00 2001 From: Ali HD <85040609+alihd-tech@users.noreply.github.com> Date: Tue, 22 Sep 2026 00:17:41 +0330 Subject: [PATCH 1/2] Decouple public analysis from hosted sessions --- app/api/analyze/route.ts | 65 +++++++++++++++++++++++++++------------- 1 file changed, 44 insertions(+), 21 deletions(-) diff --git a/app/api/analyze/route.ts b/app/api/analyze/route.ts index 8d94dab..d6cf13a 100644 --- a/app/api/analyze/route.ts +++ b/app/api/analyze/route.ts @@ -1,5 +1,5 @@ import { NextRequest, NextResponse } from "next/server" -import { getSession } from "@/lib/session" +import { getOptionalSession, getSession } from "@/lib/session" import { analyzeUser, GitHubError } from "@/lib/github" /** @@ -8,37 +8,60 @@ import { analyzeUser, GitHubError } from "@/lib/github" * /api/analyze?username=octocat public data only, no authorization needed */ export async function GET(request: NextRequest) { - const requested = request.nextUrl.searchParams.get("username")?.trim() - const session = await getSession() + try { + const requested = request.nextUrl.searchParams.get("username")?.trim() - let token: string | null = null - let username: string + let token: string | null = null + let username: string + + if (requested) { + if (!/^[a-zA-Z0-9](?:[a-zA-Z0-9]|-(?=[a-zA-Z0-9])){0,38}$/.test(requested)) { + return NextResponse.json( + { error: "That is not a valid GitHub username." }, + { status: 400 } + ) + } + + username = requested + + // Public username analysis must remain available even when the hosted + // OAuth/session layer is intentionally not configured. + const session = await getOptionalSession() + + // Only reuse a hosted session token when the visitor is analyzing + // themselves. Otherwise public GitHub data is used without a token. + if ( + session?.accessToken && + session.user?.login.toLowerCase() === requested.toLowerCase() + ) { + token = session.accessToken + } + } else { + // The private dashboard is an authenticated hosted feature, so keep its + // session requirement strict. + const session = await getSession() + + if (!session.accessToken || !session.user) { + return NextResponse.json({ error: "Not authenticated" }, { status: 401 }) + } - if (requested) { - if (!/^[a-zA-Z0-9](?:[a-zA-Z0-9]|-(?=[a-zA-Z0-9])){0,38}$/.test(requested)) { - return NextResponse.json({ error: "That is not a valid GitHub username." }, { status: 400 }) - } - username = requested - // Only reuse the session token when the visitor is asking about themselves. - if (session.accessToken && session.user?.login.toLowerCase() === requested.toLowerCase()) { token = session.accessToken + username = session.user.login } - } else { - if (!session.accessToken || !session.user) { - return NextResponse.json({ error: "Not authenticated" }, { status: 401 }) - } - token = session.accessToken - username = session.user.login - } - try { const data = await analyzeUser(token, username) return NextResponse.json(data) } catch (err) { if (err instanceof GitHubError) { return NextResponse.json({ error: err.message }, { status: err.status }) } - const message = err instanceof Error ? err.message : "Unknown error" + + const message = + err instanceof Error && err.message + ? err.message + : "Public GitHub analysis failed unexpectedly." + + console.error("GitHub analysis request failed", err) return NextResponse.json({ error: message }, { status: 500 }) } } From 176083eeaadde96d45088fb478624074551f092f Mon Sep 17 00:00:00 2001 From: Ali HD <85040609+alihd-tech@users.noreply.github.com> Date: Tue, 22 Sep 2026 00:17:43 +0330 Subject: [PATCH 2/2] Surface actionable public analysis errors --- components/dashboard-client.tsx | 30 +++++++++++++++++++++++------- 1 file changed, 23 insertions(+), 7 deletions(-) diff --git a/components/dashboard-client.tsx b/components/dashboard-client.tsx index 7608fea..dda2066 100644 --- a/components/dashboard-client.tsx +++ b/components/dashboard-client.tsx @@ -12,14 +12,30 @@ import Link from "next/link" import { Loader2, AlertCircle, RefreshCcw, Lock, ShieldCheck } from "lucide-react" import type { AnalysisData } from "@/lib/github" -const fetcher = (url: string) => - fetch(url).then(async (r) => { - if (!r.ok) { - const err = await r.json().catch(() => ({ error: "Unknown error" })) - throw new Error(err.error ?? "Failed to fetch") +const fetcher = async (url: string) => { + const response = await fetch(url) + const contentType = response.headers.get("content-type") ?? "" + + if (!response.ok) { + if (contentType.includes("application/json")) { + const payload = (await response.json().catch(() => null)) as + | { error?: string } + | null + throw new Error( + payload?.error || `GitHub analysis request failed (${response.status}).` + ) } - return r.json() - }) + + const text = await response.text().catch(() => "") + throw new Error( + text.trim() + ? `GitHub analysis request failed (${response.status}): ${text.slice(0, 160)}` + : `GitHub analysis request failed (${response.status}).` + ) + } + + return response.json() +} interface DashboardClientProps { /** Set for a public lookup; omit to analyze the signed-in user. */