VIbe-code some optimizations

This commit is contained in:
Thomas Camlong
2025-11-17 13:36:00 +01:00
parent 4b0a9313a5
commit 2e20511872

View File

@@ -1,8 +1,8 @@
import { unstable_cache } from "next/cache" import { unstable_cache } from "next/cache";
import { cache } from "react" import { cache } from "react";
import { METADATA_URL } from "@/constants" import { METADATA_URL } from "@/constants";
import { ApiError } from "@/lib/errors" import { ApiError } from "@/lib/errors";
import type { AuthorData, IconFile, IconWithName } from "@/types/icons" import type { AuthorData, IconFile, IconWithName } from "@/types/icons";
/** /**
* Raw fetch function for icon data (without caching) * Raw fetch function for icon data (without caching)
@@ -11,19 +11,22 @@ async function fetchAllIconsRaw(): Promise<IconFile> {
try { try {
const response = await fetch(METADATA_URL, { const response = await fetch(METADATA_URL, {
next: { revalidate: 3600 }, next: { revalidate: 3600 },
}) });
if (!response.ok) { 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) { } catch (error) {
if (error instanceof ApiError) { if (error instanceof ApiError) {
throw error throw error;
} }
console.error("Error fetching icons:", error) console.error("Error fetching icons:", error);
throw new ApiError("Failed to fetch icons data. Please try again later.") throw new ApiError("Failed to fetch icons data. Please try again later.");
} }
} }
@@ -31,10 +34,14 @@ async function fetchAllIconsRaw(): Promise<IconFile> {
* Cached version using unstable_cache for build-time caching * Cached version using unstable_cache for build-time caching
* Revalidates every hour (3600 seconds) * Revalidates every hour (3600 seconds)
*/ */
const getAllIconsCached = unstable_cache(async () => fetchAllIconsRaw(), ["all-icons"], { const getAllIconsCached = unstable_cache(
async () => fetchAllIconsRaw(),
["all-icons"],
{
revalidate: 3600, revalidate: 3600,
tags: ["icons"], tags: ["icons"],
}) },
);
/** /**
* Fetches all icon data from the metadata.json file * Fetches all icon data from the metadata.json file
@@ -42,63 +49,65 @@ const getAllIconsCached = unstable_cache(async () => fetchAllIconsRaw(), ["all-i
* This prevents duplicate fetches within the same request and across builds * This prevents duplicate fetches within the same request and across builds
*/ */
export const getAllIcons = cache(async (): Promise<IconFile> => { export const getAllIcons = cache(async (): Promise<IconFile> => {
return getAllIconsCached() return getAllIconsCached();
}) });
/** /**
* Gets a list of all icon names. * Gets a list of all icon names.
*/ */
export const getIconNames = async (): Promise<string[]> => { export const getIconNames = async (): Promise<string[]> => {
try { try {
const iconsData = await getAllIcons() const iconsData = await getAllIcons();
return Object.keys(iconsData) return Object.keys(iconsData);
} catch (error) { } catch (error) {
console.error("Error getting icon names:", error) console.error("Error getting icon names:", error);
throw error throw error;
} }
} };
/** /**
* Converts icon data to an array format for easier rendering * Converts icon data to an array format for easier rendering
*/ */
export async function getIconsArray(): Promise<IconWithName[]> { export async function getIconsArray(): Promise<IconWithName[]> {
try { try {
const iconsData = await getAllIcons() const iconsData = await getAllIcons();
return Object.entries(iconsData) return Object.entries(iconsData)
.map(([name, data]) => ({ .map(([name, data]) => ({
name, name,
data, data,
})) }))
.sort((a, b) => a.name.localeCompare(b.name)) .sort((a, b) => a.name.localeCompare(b.name));
} catch (error) { } catch (error) {
console.error("Error getting icons array:", error) console.error("Error getting icons array:", error);
throw error throw error;
} }
} }
/** /**
* Fetches data for a specific icon * Fetches data for a specific icon
*/ */
export async function getIconData(iconName: string): Promise<IconWithName | null> { export async function getIconData(
iconName: string,
): Promise<IconWithName | null> {
try { try {
const iconsData = await getAllIcons() const iconsData = await getAllIcons();
const iconData = iconsData[iconName] const iconData = iconsData[iconName];
if (!iconData) { if (!iconData) {
throw new ApiError(`Icon '${iconName}' not found`, 404) throw new ApiError(`Icon '${iconName}' not found`, 404);
} }
return { return {
name: iconName, name: iconName,
data: iconData, data: iconData,
} };
} catch (error) { } catch (error) {
if (error instanceof ApiError && error.status === 404) { if (error instanceof ApiError && error.status === 404) {
return null return null;
} }
console.error("Error getting icon data:", error) console.error("Error getting icon data:", error);
throw error throw error;
} }
} }
@@ -111,26 +120,31 @@ async function fetchAuthorData(authorId: number) {
headers: { headers: {
Authorization: `Bearer ${process.env.GITHUB_TOKEN}`, Authorization: `Bearer ${process.env.GITHUB_TOKEN}`,
}, },
}) });
if (!response.ok) { if (!response.ok) {
// If unauthorized or other error, return a default user object // If unauthorized or other error, return a default user object
if (response.status === 401 || response.status === 403) { 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 { return {
login: "unknown", login: "unknown",
avatar_url: "https://avatars.githubusercontent.com/u/0", avatar_url: "https://avatars.githubusercontent.com/u/0",
html_url: "https://github.com", html_url: "https://github.com",
name: "Unknown User", name: "Unknown User",
bio: null, bio: null,
};
} }
} throw new ApiError(
throw new ApiError(`Failed to fetch author data: ${response.statusText}`, response.status) `Failed to fetch author data: ${response.statusText}`,
response.status,
);
} }
return response.json() return response.json();
} catch (error) { } 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 // Even for unexpected errors, return a default user to prevent page failures
return { return {
login: "unknown", login: "unknown",
@@ -138,11 +152,11 @@ async function fetchAuthorData(authorId: number) {
html_url: "https://github.com", html_url: "https://github.com",
name: "Unknown User", name: "Unknown User",
bio: null, bio: null,
} };
} }
} }
const authorDataCache: Record<number, AuthorData> = {} const authorDataCache: Record<number, AuthorData> = {};
/** /**
* Cached version of fetchAuthorData * Cached version of fetchAuthorData
@@ -155,12 +169,12 @@ const authorDataCache: Record<number, AuthorData> = {}
*/ */
export async function getAuthorData(authorId: number): Promise<AuthorData> { export async function getAuthorData(authorId: number): Promise<AuthorData> {
if (authorDataCache[authorId]) { if (authorDataCache[authorId]) {
return authorDataCache[authorId] return authorDataCache[authorId];
} }
const data = await fetchAuthorData(authorId) const data = await fetchAuthorData(authorId);
authorDataCache[authorId] = data authorDataCache[authorId] = data;
return data return data;
} }
/** /**
@@ -168,32 +182,37 @@ export async function getAuthorData(authorId: number): Promise<AuthorData> {
*/ */
export async function getTotalIcons() { export async function getTotalIcons() {
try { try {
const iconsData = await getAllIcons() const iconsData = await getAllIcons();
return { return {
totalIcons: Object.keys(iconsData).length, totalIcons: Object.keys(iconsData).length,
} };
} catch (error) { } catch (error) {
console.error("Error getting total icons:", error) console.error("Error getting total icons:", error);
throw error throw error;
} }
} }
/** /**
* Fetches recently added icons sorted by timestamp * Fetches recently added icons sorted by timestamp
*/ */
export async function getRecentlyAddedIcons(limit = 8): Promise<IconWithName[]> { export async function getRecentlyAddedIcons(
limit = 8,
): Promise<IconWithName[]> {
try { try {
const icons = await getIconsArray() const icons = await getIconsArray();
return icons return icons
.sort((a, b) => { .sort((a, b) => {
// Sort by timestamp in descending order (newest first) // 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) { } catch (error) {
console.error("Error getting recently added icons:", error) console.error("Error getting recently added icons:", error);
throw error throw error;
} }
} }