171 lines
4.0 KiB
TypeScript
Raw Normal View History

import { METADATA_URL } from "@/constants"
import type { IconFile, IconWithName } from "@/types/icons"
2025-04-17 07:21:19 +02:00
/**
2025-04-17 07:21:19 +02:00
* Custom error class for API errors
*/
2025-04-17 07:21:19 +02:00
export class ApiError extends Error {
status: number
constructor(message: string, status = 500) {
super(message)
this.name = "ApiError"
this.status = status
}
}
2025-04-17 07:21:19 +02:00
/**
* Fetches all icon data from the metadata.json file
*/
export async function getAllIcons(): Promise<IconFile> {
2025-04-17 07:21:19 +02:00
try {
const response = await fetch(METADATA_URL)
if (!response.ok) {
throw new ApiError(`Failed to fetch icons: ${response.statusText}`, response.status)
}
return (await response.json()) as IconFile
} catch (error) {
if (error instanceof ApiError) {
throw error
}
console.error("Error fetching icons:", error)
throw new ApiError("Failed to fetch icons data. Please try again later.")
}
}
/**
* Gets a list of all icon names.
*/
export const getIconNames = async (): Promise<string[]> => {
2025-04-17 07:21:19 +02:00
try {
const iconsData = await getAllIcons()
return Object.keys(iconsData)
} catch (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<IconWithName[]> {
2025-04-17 07:21:19 +02:00
try {
const iconsData = await getAllIcons()
return Object.entries(iconsData)
.map(([name, data]) => ({
name,
data,
}))
.sort((a, b) => a.name.localeCompare(b.name))
} catch (error) {
console.error("Error getting icons array:", error)
throw error
}
}
/**
* Fetches data for a specific icon
*/
export async function getIconData(iconName: string): Promise<IconWithName | null> {
2025-04-17 07:21:19 +02:00
try {
const iconsData = await getAllIcons()
const iconData = iconsData[iconName]
2025-04-17 07:21:19 +02:00
if (!iconData) {
throw new ApiError(`Icon '${iconName}' not found`, 404)
}
2025-04-17 07:21:19 +02:00
return {
name: iconName,
data: iconData,
}
} catch (error) {
if (error instanceof ApiError && error.status === 404) {
return null
}
console.error("Error getting icon data:", error)
throw error
}
}
/**
* Fetches author data from GitHub API
*/
export async function getAuthorData(authorId: number) {
2025-04-17 07:21:19 +02:00
try {
const response = await fetch(`https://api.github.com/user/${authorId}`, {
headers: {
Authorization: `Bearer ${process.env.GITHUB_TOKEN}`,
"Cache-Control": "public, max-age=86400",
},
next: { revalidate: 86400 }, // Revalidate cache once a day
})
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}`)
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)
}
return response.json()
} catch (error) {
console.error("Error fetching author data:", error)
// Even for unexpected errors, return a default user to prevent page failures
return {
login: "unknown",
avatar_url: "https://avatars.githubusercontent.com/u/0",
html_url: "https://github.com",
name: "Unknown User",
bio: null,
}
}
}
/**
2025-04-17 07:21:19 +02:00
* Fetches total icon count
*/
export async function getTotalIcons() {
2025-04-17 07:21:19 +02:00
try {
const iconsData = await getAllIcons()
2025-04-17 07:21:19 +02:00
return {
totalIcons: Object.keys(iconsData).length,
}
} catch (error) {
console.error("Error getting total icons:", error)
throw error
}
}
/**
* Fetches recently added icons sorted by timestamp
*/
export async function getRecentlyAddedIcons(limit = 8): Promise<IconWithName[]> {
2025-04-17 07:21:19 +02:00
try {
const icons = await getIconsArray()
2025-04-17 07:21:19 +02:00
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()
})
.slice(0, limit)
} catch (error) {
console.error("Error getting recently added icons:", error)
throw error
}
}