diff --git a/web/biome.jsonc b/web/biome.jsonc
index 498148cf..aa97117f 100644
--- a/web/biome.jsonc
+++ b/web/biome.jsonc
@@ -1,5 +1,5 @@
{
- "$schema": "https://biomejs.dev/schemas/2.2.4/schema.json",
+ "$schema": "https://biomejs.dev/schemas/2.3.5/schema.json",
"vcs": {
"enabled": true,
"clientKind": "git",
diff --git a/web/src/app/community/[icon]/opengraph-image.tsx b/web/src/app/community/[icon]/opengraph-image.tsx
index 61da7c4c..94f17d72 100644
--- a/web/src/app/community/[icon]/opengraph-image.tsx
+++ b/web/src/app/community/[icon]/opengraph-image.tsx
@@ -2,26 +2,16 @@ import { permanentRedirect, redirect } from "next/navigation"
import { ImageResponse } from "next/og"
import { getCommunityGalleryRecord, getCommunitySubmissionByName, getCommunitySubmissions } from "@/lib/community"
-export const revalidate = 21600 // 6 hours
-
-export async function generateStaticParams() {
- const icons = await getCommunitySubmissions()
- const validIcons = icons.filter((icon) => icon.name)
- if (process.env.CI_MODE === "false") {
- return validIcons.slice(0, 5).map((icon) => ({
- icon: icon.name,
- }))
- }
- return validIcons.map((icon) => ({
- icon: icon.name,
- }))
-}
+export const dynamic = "force-dynamic";
export const size = {
width: 1200,
height: 630,
}
+export const alt = "Community Icon Open Graph Image";
+export const contentType = "image/png";
+
export default async function Image({ params }: { params: Promise<{ icon: string }> }) {
const { icon } = await params
@@ -137,7 +127,7 @@ export default async function Image({ params }: { params: Promise<{ icon: string
const iconUrl = iconDataBuffer
? `data:image/png;base64,${iconDataBuffer.toString("base64")}`
- : `https://placehold.co/600x400?text=${formattedIconName}`
+ : `https://placehold.co/600x400?text=${formattedIconName}`;
return new ImageResponse(
({
- icon,
- }))
- }
- return Object.keys(iconsData).map((icon) => ({
- icon,
- }))
-}
+export const dynamic = "force-dynamic";
export const size = {
width: 1200,
height: 630,
-}
-export default async function Image({ params }: { params: Promise<{ icon: string }> }) {
- const { icon } = await params
+};
+
+export const alt = "Icon Open Graph Image";
+export const contentType = "image/png";
+export default async function Image({
+ params,
+}: {
+ params: Promise<{ icon: string }>;
+}) {
+ const { icon } = await params;
if (!icon) {
- console.error(`[Opengraph Image] Icon not found for ${icon}`)
+ console.error(`[Opengraph Image] Icon not found for ${icon}`);
return new ImageResponse(
,
{ ...size },
- )
+ );
}
- const iconsData = await getAllIcons()
- const totalIcons = Object.keys(iconsData).length
- const index = Object.keys(iconsData).indexOf(icon)
+ const iconsData = await getAllIcons();
+ const totalIcons = Object.keys(iconsData).length;
+ const index = Object.keys(iconsData).indexOf(icon);
// Format the icon name for display
const formattedIconName = icon
.split("-")
.map((word) => word.charAt(0).toUpperCase() + word.slice(1))
- .join(" ")
+ .join(" ");
// Read the icon file from local filesystem
- let iconData: Buffer | null = null
+ let iconData: Buffer | null = null;
try {
- const iconPath = join(process.cwd(), `../png/${icon}.png`)
- console.log(`Generating opengraph image for ${icon} (${index + 1} / ${totalIcons}) from path ${iconPath}`)
- iconData = await readFile(iconPath)
+ const iconPath = join(process.cwd(), `../png/${icon}.png`);
+ console.log(
+ `Generating opengraph image for ${icon} (${index + 1} / ${totalIcons}) from path ${iconPath}`,
+ );
+ iconData = await readFile(iconPath);
} catch (_error) {
- console.error(`Icon ${icon} was not found locally`)
+ console.error(`Icon ${icon} was not found locally`);
}
// Convert the image data to a data URL or use placeholder
- const iconUrl = iconData ? `data:image/png;base64,${iconData.toString("base64")}` : null
+ const iconUrl = iconData
+ ? `data:image/png;base64,${iconData.toString("base64")}`
+ : null;
return new ImageResponse(

{
try {
- const response = await fetch(METADATA_URL)
+ const response = await fetch(METADATA_URL);
if (!response.ok) {
- throw new ApiError(`Failed to fetch icons: ${response.statusText}`, response.status)
+ throw new ApiError(
+ `Failed to fetch icons: ${response.statusText}`,
+ response.status,
+ );
}
- return (await response.json()) as IconFile
+ return (await response.json()) as IconFile;
} catch (error) {
if (error instanceof ApiError) {
- throw error
+ throw error;
}
- console.error("Error fetching icons:", error)
- throw new ApiError("Failed to fetch icons data. Please try again later.")
+ console.error("Error fetching icons:", error);
+ throw new ApiError("Failed to fetch icons data. Please try again later.");
}
}
@@ -30,55 +33,57 @@ export async function getAllIcons(): Promise
{
*/
export const getIconNames = async (): Promise => {
try {
- const iconsData = await getAllIcons()
- return Object.keys(iconsData)
+ const iconsData = await getAllIcons();
+ return Object.keys(iconsData);
} catch (error) {
- console.error("Error getting icon names:", error)
- throw error
+ console.error("Error getting icon names:", error);
+ throw error;
}
-}
+};
/**
* Converts icon data to an array format for easier rendering
*/
export async function getIconsArray(): Promise {
try {
- const iconsData = await getAllIcons()
+ const iconsData = await getAllIcons();
return Object.entries(iconsData)
.map(([name, data]) => ({
name,
data,
}))
- .sort((a, b) => a.name.localeCompare(b.name))
+ .sort((a, b) => a.name.localeCompare(b.name));
} catch (error) {
- console.error("Error getting icons array:", error)
- throw error
+ console.error("Error getting icons array:", error);
+ throw error;
}
}
/**
* Fetches data for a specific icon
*/
-export async function getIconData(iconName: string): Promise {
+export async function getIconData(
+ iconName: string,
+): Promise {
try {
- const iconsData = await getAllIcons()
- const iconData = iconsData[iconName]
+ const iconsData = await getAllIcons();
+ const iconData = iconsData[iconName];
if (!iconData) {
- throw new ApiError(`Icon '${iconName}' not found`, 404)
+ throw new ApiError(`Icon '${iconName}' not found`, 404);
}
return {
name: iconName,
data: iconData,
- }
+ };
} catch (error) {
if (error instanceof ApiError && error.status === 404) {
- return null
+ return null;
}
- console.error("Error getting icon data:", error)
- throw error
+ console.error("Error getting icon data:", error);
+ throw error;
}
}
@@ -91,26 +96,31 @@ async function fetchAuthorData(authorId: number) {
headers: {
Authorization: `Bearer ${process.env.GITHUB_TOKEN}`,
},
- })
+ });
if (!response.ok) {
// If unauthorized or other error, return a default user object
if (response.status === 401 || response.status === 403) {
- console.warn(`GitHub API rate limit or authorization issue: ${response.statusText}`)
+ console.warn(
+ `GitHub API rate limit or authorization issue: ${response.statusText}`,
+ );
return {
login: "unknown",
avatar_url: "https://avatars.githubusercontent.com/u/0",
html_url: "https://github.com",
name: "Unknown User",
bio: null,
- }
+ };
}
- throw new ApiError(`Failed to fetch author data: ${response.statusText}`, response.status)
+ throw new ApiError(
+ `Failed to fetch author data: ${response.statusText}`,
+ response.status,
+ );
}
- return response.json()
+ return response.json();
} catch (error) {
- console.error("Error fetching author data:", error)
+ console.error("Error fetching author data:", error);
// Even for unexpected errors, return a default user to prevent page failures
return {
login: "unknown",
@@ -118,7 +128,7 @@ async function fetchAuthorData(authorId: number) {
html_url: "https://github.com",
name: "Unknown User",
bio: null,
- }
+ };
}
}
@@ -148,32 +158,37 @@ export async function getAuthorData(authorId: number): Promise {
*/
export async function getTotalIcons() {
try {
- const iconsData = await getAllIcons()
+ const iconsData = await getAllIcons();
return {
totalIcons: Object.keys(iconsData).length,
- }
+ };
} catch (error) {
- console.error("Error getting total icons:", error)
- throw error
+ console.error("Error getting total icons:", error);
+ throw error;
}
}
/**
* Fetches recently added icons sorted by timestamp
*/
-export async function getRecentlyAddedIcons(limit = 8): Promise {
+export async function getRecentlyAddedIcons(
+ limit = 8,
+): Promise {
try {
- const icons = await getIconsArray()
+ const icons = await getIconsArray();
return icons
.sort((a, b) => {
// Sort by timestamp in descending order (newest first)
- return new Date(b.data.update.timestamp).getTime() - new Date(a.data.update.timestamp).getTime()
+ return (
+ new Date(b.data.update.timestamp).getTime() -
+ new Date(a.data.update.timestamp).getTime()
+ );
})
- .slice(0, limit)
+ .slice(0, limit);
} catch (error) {
- console.error("Error getting recently added icons:", error)
- throw error
+ console.error("Error getting recently added icons:", error);
+ throw error;
}
}
diff --git a/web/src/lib/errors.ts b/web/src/lib/errors.ts
index 7c7b203a..e1f6eceb 100644
--- a/web/src/lib/errors.ts
+++ b/web/src/lib/errors.ts
@@ -10,6 +10,3 @@ export class ApiError extends Error {
this.status = status
}
}
-
-
-