diff --git a/app/devices/[slug]/opengraph-image.tsx b/app/devices/[slug]/opengraph-image.tsx new file mode 100644 index 0000000..4f9c7ba --- /dev/null +++ b/app/devices/[slug]/opengraph-image.tsx @@ -0,0 +1,120 @@ +import { ImageResponse } from "next/og" +import { db } from "@/lib/db/index" +import { hardware, performanceEntries, gameVersions, games } from "@/lib/db/schema" +import { eq, and, sql } from "drizzle-orm" + +export const runtime = "edge" +export const alt = "Device benchmarks on DeckyVault" +export const size = { width: 1200, height: 630 } +export const contentType = "image/png" + +export default async function Image({ + params, +}: { + params: Promise<{ slug: string }> +}) { + const { slug } = await params + + const [device] = await db + .select({ + name: hardware.name, + deviceType: hardware.deviceType, + }) + .from(hardware) + .where(eq(hardware.slug, slug)) + .limit(1) + + if (!device) { + return new ImageResponse( + ( +
+
DeckyVault
+
+ Device Not Found +
+
+ ), + { ...size } + ) + } + + const [stats] = await db + .select({ + totalBenchmarks: sql`count(*)::int`, + avgFps: sql`round(avg(${performanceEntries.fpsAvg})::numeric, 1)`, + gameCount: sql`count(distinct ${gameVersions.gameId})::int`, + }) + .from(performanceEntries) + .innerJoin(gameVersions, eq(performanceEntries.versionId, gameVersions.id)) + .innerJoin(games, eq(gameVersions.gameId, games.id)) + .where( + and( + eq(performanceEntries.hardwareSlug, slug), + eq(performanceEntries.isRemoved, false) + ) + ) + + const typeLabel = device.deviceType === "handheld" ? "Handheld" : "Console" + const avgFpsStr = stats?.avgFps ? String(Math.round(Number(stats.avgFps))) : "—" + + return new ImageResponse( + ( +
+
+
+ {typeLabel} +
+
+
+ {device.name} +
+
+
+ {stats?.totalBenchmarks ?? 0} Benchmarks +
+
+ {avgFpsStr} Avg FPS +
+
+ {stats?.gameCount ?? 0} Games +
+
+
+ deckyvault.xyz +
+
+ ), + { ...size } + ) +} diff --git a/app/sitemap.ts b/app/sitemap.ts index 58f7e41..7d5ae60 100644 --- a/app/sitemap.ts +++ b/app/sitemap.ts @@ -26,7 +26,6 @@ export default async function sitemap(): Promise { return [ { url: BASE_URL, lastModified: new Date(), changeFrequency: "weekly", priority: 1 }, - { url: `${BASE_URL}/search`, lastModified: new Date(), changeFrequency: "weekly", priority: 0.7 }, { url: `${BASE_URL}/devices`, lastModified: new Date(), changeFrequency: "monthly", priority: 0.6 }, ...gameEntries, ...deviceEntries,