diff --git a/src/app/api/public/[username]/route.ts b/src/app/api/public/[username]/route.ts index 3b8c23086..7692a4f0e 100644 --- a/src/app/api/public/[username]/route.ts +++ b/src/app/api/public/[username]/route.ts @@ -1,6 +1,6 @@ // @ts-nocheck import { NextRequest, NextResponse } from "next/server"; -import { fetchPublicProfile } from "@/lib/public-profile-data"; +import { fetchPublicProfile, filterPublicProfileData } from "@/lib/public-profile-data"; import { getUpstashConfig, upstashRateLimitFixedWindow } from "@/lib/upstash-rest"; import { createMemoryFixedWindowRateLimiter, getClientIp } from "@/lib/rate-limit"; @@ -89,5 +89,5 @@ export async function GET( ); } - return NextResponse.json(profile); -} \ No newline at end of file + return NextResponse.json(filterPublicProfileData(profile)); +} diff --git a/src/lib/public-profile-data.ts b/src/lib/public-profile-data.ts index 7558c9563..0fc7bf9f8 100644 --- a/src/lib/public-profile-data.ts +++ b/src/lib/public-profile-data.ts @@ -59,6 +59,21 @@ export interface PublicProfileData { publicWidgets: PublicWidgetKey[]; } +/** Remove disabled widget data before a public profile leaves the server. */ +export function filterPublicProfileData( + profile: PublicProfileData +): Partial { + const visibleProfile: Partial = { ...profile }; + const publicWidgets = profile.publicWidgets ?? ["streak", "contributions"]; + + if (!publicWidgets.includes("streak")) delete visibleProfile.streak; + if (!publicWidgets.includes("contributions")) delete visibleProfile.contributions; + if (!publicWidgets.includes("languages")) delete visibleProfile.topLanguages; + if (!publicWidgets.includes("prs")) delete visibleProfile.pullRequests; + + return visibleProfile; +} + async function ghFetch(url: string, token?: string): Promise { const headers: Record = { Accept: "application/vnd.github+json", @@ -453,4 +468,4 @@ export async function fetchPublicProfile( weeklyGoalProgress, publicWidgets, }; -} \ No newline at end of file +} diff --git a/test/public-profile-data.test.ts b/test/public-profile-data.test.ts index f217b64f7..10eebaed5 100644 --- a/test/public-profile-data.test.ts +++ b/test/public-profile-data.test.ts @@ -5,6 +5,8 @@ import { fetchPublicStreak, fetchTopLanguage, fetchPublicGists, + filterPublicProfileData, + type PublicProfileData, } from "../src/lib/public-profile-data"; describe("public-profile-data", () => { @@ -47,6 +49,25 @@ describe("public-profile-data", () => { }); }); + describe("filterPublicProfileData", () => { + it("removes disabled widget data from the public response", () => { + const profile = { + publicWidgets: ["contributions"], + streak: { current: 4 }, + contributions: { days: 30, total: 12, data: {} }, + topLanguages: [{ name: "TypeScript", count: 1, percentage: 100 }], + pullRequests: 8, + } as unknown as PublicProfileData; + + const filtered = filterPublicProfileData(profile); + + expect(filtered.contributions).toEqual(profile.contributions); + expect(filtered).not.toHaveProperty("streak"); + expect(filtered).not.toHaveProperty("topLanguages"); + expect(filtered).not.toHaveProperty("pullRequests"); + }); + }); + describe("fetchPublicGists", () => { it("should return the public gist count on successful API response", async () => { const mockFetch = vi.fn().mockResolvedValue({