diff --git a/dashboard/app/api/badge/[owner]/[name]/route.ts b/dashboard/app/api/badge/[owner]/[name]/route.ts index 4de3d98..97d91c0 100644 --- a/dashboard/app/api/badge/[owner]/[name]/route.ts +++ b/dashboard/app/api/badge/[owner]/[name]/route.ts @@ -1,6 +1,5 @@ import { NextRequest, NextResponse } from "next/server"; import { analyzeRepo } from "@/lib/scorer"; -import { auth } from "@/lib/auth"; type Params = { params: Promise<{ owner: string; name: string }> }; @@ -11,15 +10,32 @@ function scoreColor(score: number): string { return "dc2626"; } -function buildSvg(owner: string, name: string, score: number): string { +function escapeXml(value: string): string { + return value.replace(/[&<>"']/g, (char) => { + const entities: Record = { + "&": "&", + "<": "<", + ">": ">", + '"': """, + "'": "'", + }; + return entities[char]; + }); +} + +function buildSvg(value: string, color: string): string { const label = "DevLens"; - const value = `${score}/100`; - const color = scoreColor(score); + const safeValue = escapeXml(value); const lw = 68; - const vw = 52; + const vw = Math.max(52, Math.min(110, safeValue.length * 7 + 14)); const tw = lw + vw; - return ` - ${label}: ${value} + const labelX = Math.round(lw / 2) * 10; + const valueX = (lw + Math.round(vw / 2)) * 10; + const labelLength = (lw - 10) * 10; + const valueLength = (vw - 10) * 10; + + return ` + ${label}: ${safeValue} @@ -31,29 +47,37 @@ function buildSvg(owner: string, name: string, score: number): string { - ${label} - ${label} - ${value} - ${value} + ${label} + ${label} + ${safeValue} + ${safeValue} `; } export async function GET(_req: NextRequest, { params }: Params) { const { owner, name } = await params; - let score = 0; + try { - const session = await auth(); - const token = (session as any)?.accessToken ?? process.env.GITHUB_TOKEN; - const report = await analyzeRepo(owner, name, token); - score = report.healthScore ?? 0; - } catch { /* return 0-score badge on error */ } - - const svg = buildSvg(owner, name, score); - return new NextResponse(svg, { - headers: { - "Content-Type": "image/svg+xml", - "Cache-Control": "s-maxage=3600, stale-while-revalidate=86400", - }, - }); + const report = await analyzeRepo(owner, name, process.env.GITHUB_TOKEN); + const score = report.healthScore; + + if (!Number.isFinite(score) || score < 0 || score > 100) { + throw new Error("Analysis returned an invalid health score"); + } + + return new NextResponse(buildSvg(`${score}/100`, scoreColor(score)), { + headers: { + "Content-Type": "image/svg+xml; charset=utf-8", + "Cache-Control": "s-maxage=3600, stale-while-revalidate=86400", + }, + }); + } catch { + return new NextResponse(buildSvg("unavailable", "6b7280"), { + headers: { + "Content-Type": "image/svg+xml; charset=utf-8", + "Cache-Control": "no-store", + }, + }); + } } diff --git a/dashboard/package.json b/dashboard/package.json index d9718a1..896b1fe 100644 --- a/dashboard/package.json +++ b/dashboard/package.json @@ -15,7 +15,7 @@ "@vercel/speed-insights": "^1.1.0", "clsx": "2.1.1", "lucide-react": "0.479.0", - "next": "16.3.1", + "next": "16.3.3", "next-auth": "5.0.0-beta.32", "react": "19.2.4", "react-dom": "19.2.4", @@ -26,7 +26,7 @@ "@types/react": "19", "@types/react-dom": "19", "eslint": "9", - "eslint-config-next": "16.3.1", + "eslint-config-next": "16.3.3", "typescript": "5" } }