diff --git a/src/app/api/og/user/route.tsx b/src/app/api/og/user/route.tsx new file mode 100644 index 00000000..8e010bd7 --- /dev/null +++ b/src/app/api/og/user/route.tsx @@ -0,0 +1,78 @@ +import { ImageResponse } from "@vercel/og"; +import { NextRequest } from "next/server"; + +export const runtime = "edge"; + +export async function GET(req: NextRequest) { + const { searchParams } = new URL(req.url); + + const username = searchParams.get("username") ?? "developer"; + const name = searchParams.get("name") ?? username; + const avatar = searchParams.get("avatar") ?? ""; + const topLang = searchParams.get("topLang") ?? "JavaScript"; + const streak = searchParams.get("streak") ?? "0"; + const commits = searchParams.get("commits") ?? "0"; + + return new ImageResponse( + ( +
+
+
+
+ +
+ +
+ {avatar ? ( + + ) : ( +
+ {username[0]?.toUpperCase()} +
+ )} +
+ {name} + @{username} +
+
+ +
+ +
+
+ 🔥 Streak + {streak} days +
+
+ 📦 Commits + {Number(commits).toLocaleString()} +
+
+ âš¡ Top Language + {topLang} +
+
+
+ +
+ DevTrack + · devtrack.app/u/{username} +
+
+ ), + { width: 1200, height: 630 } + ); +} \ No newline at end of file diff --git a/src/app/u/[username]/page.tsx b/src/app/u/[username]/page.tsx index 2a653574..18edb672 100644 --- a/src/app/u/[username]/page.tsx +++ b/src/app/u/[username]/page.tsx @@ -45,7 +45,6 @@ export async function generateMetadata({ params: Promise<{ username: string }>; }): Promise { const { username } = await params; - // Minimal lookup — avoids duplicating 3 GitHub API calls that the page already makes const user = await getUserByUsername(username); const profileUrl = getProfileUrl(username); @@ -56,24 +55,49 @@ export async function generateMetadata({ }; } + const baseUrl = + process.env.NEXT_PUBLIC_APP_URL || + process.env.NEXTAUTH_URL || + "http://localhost:3000"; + + // Build dynamic OG image URL + const ogImageUrl = new URL(`${baseUrl}/api/og/user`); +ogImageUrl.searchParams.set("username", username); +ogImageUrl.searchParams.set("name", username); +ogImageUrl.searchParams.set("avatar", `https://avatars.githubusercontent.com/${username}`); +ogImageUrl.searchParams.set("topLang", "Code"); +ogImageUrl.searchParams.set("streak", "0"); +ogImageUrl.searchParams.set("commits", "0"); + + const title = `${username}'s DevTrack Profile`; + const description = `GitHub stats and coding activity for ${username}. View commits, streaks, and top repositories.`; + return { - title: `${username}'s DevTrack Profile`, - description: `GitHub stats and coding activity for ${username}. View commits, streaks, and top repositories.`, + title, + description, openGraph: { - title: `${username}'s DevTrack Profile`, - description: `GitHub stats and coding activity for ${username}`, + title, + description, url: profileUrl, siteName: "DevTrack", type: "profile", + images: [ + { + url: ogImageUrl.toString(), + width: 1200, + height: 630, + alt: `${username}'s DevTrack profile`, + }, + ], }, twitter: { card: "summary_large_image", - title: `${username}'s DevTrack Profile`, - description: `GitHub stats and coding activity for ${username}`, + title, + description, + images: [ogImageUrl.toString()], }, }; } - export default async function PublicProfilePage({ params, }: {