mirror of
https://github.com/walkxcode/dashboard-icons.git
synced 2025-11-19 18:11:14 +01:00
refactor(api): improve API error handling and revalidation
- Update API utilities to use new ApiError class - Improve error handling and status code management - Enhance revalidation logic for better cache management
This commit is contained in:
@@ -1,25 +1,17 @@
|
|||||||
|
import { unstable_cache } from "next/cache"
|
||||||
import { METADATA_URL } from "@/constants"
|
import { METADATA_URL } from "@/constants"
|
||||||
import type { IconFile, IconWithName } from "@/types/icons"
|
import { ApiError } from "@/lib/errors"
|
||||||
|
import type { AuthorData, IconFile, IconWithName } from "@/types/icons"
|
||||||
/**
|
|
||||||
* Custom error class for API errors
|
|
||||||
*/
|
|
||||||
export class ApiError extends Error {
|
|
||||||
status: number
|
|
||||||
|
|
||||||
constructor(message: string, status = 500) {
|
|
||||||
super(message)
|
|
||||||
this.name = "ApiError"
|
|
||||||
this.status = status
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Fetches all icon data from the metadata.json file
|
* Fetches all icon data from the metadata.json file
|
||||||
|
* Uses fetch with revalidate for caching
|
||||||
*/
|
*/
|
||||||
export async function getAllIcons(): Promise<IconFile> {
|
export async function getAllIcons(): Promise<IconFile> {
|
||||||
try {
|
try {
|
||||||
const response = await fetch(METADATA_URL)
|
const response = await fetch(METADATA_URL, {
|
||||||
|
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)
|
||||||
@@ -93,16 +85,14 @@ export async function getIconData(iconName: string): Promise<IconWithName | null
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Fetches author data from GitHub API
|
* Fetch author data from GitHub API (raw function without caching)
|
||||||
*/
|
*/
|
||||||
export async function getAuthorData(authorId: number) {
|
async function fetchAuthorData(authorId: number) {
|
||||||
try {
|
try {
|
||||||
const response = await fetch(`https://api.github.com/user/${authorId}`, {
|
const response = await fetch(`https://api.github.com/user/${authorId}`, {
|
||||||
headers: {
|
headers: {
|
||||||
Authorization: `Bearer ${process.env.GITHUB_TOKEN}`,
|
Authorization: `Bearer ${process.env.GITHUB_TOKEN}`,
|
||||||
"Cache-Control": "public, max-age=86400",
|
|
||||||
},
|
},
|
||||||
next: { revalidate: 86400 }, // Revalidate cache once a day
|
|
||||||
})
|
})
|
||||||
|
|
||||||
if (!response.ok) {
|
if (!response.ok) {
|
||||||
@@ -134,6 +124,26 @@ export async function getAuthorData(authorId: number) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Cached version of fetchAuthorData
|
||||||
|
* Uses unstable_cache with tags for on-demand revalidation
|
||||||
|
* Revalidates every 86400 seconds (24 hours)
|
||||||
|
* Cache key: author-{authorId}
|
||||||
|
*
|
||||||
|
* This prevents hitting GitHub API rate limits by caching author data
|
||||||
|
* across multiple page builds and requests.
|
||||||
|
*/
|
||||||
|
export async function getAuthorData(authorId: number): Promise<AuthorData> {
|
||||||
|
return unstable_cache(
|
||||||
|
async () => await fetchAuthorData(authorId),
|
||||||
|
[`author-${authorId}`],
|
||||||
|
{
|
||||||
|
revalidate: 86400,
|
||||||
|
tags: ["authors", `author-${authorId}`],
|
||||||
|
}
|
||||||
|
)()
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Fetches total icon count
|
* Fetches total icon count
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -1,21 +1,37 @@
|
|||||||
"use server"
|
"use server"
|
||||||
|
|
||||||
import { revalidatePath, revalidateTag } from "next/cache"
|
import { revalidatePath, updateTag } from "next/cache"
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Revalidate the community page cache
|
* Revalidate the community page cache
|
||||||
* Can be called from server actions after submission approval/rejection
|
* Can be called from server actions after submission approval/rejection
|
||||||
|
* Uses updateTag for immediate updates (read-your-writes semantics)
|
||||||
*/
|
*/
|
||||||
export async function revalidateCommunityPage() {
|
export async function revalidateCommunityPage() {
|
||||||
revalidatePath("/community")
|
revalidatePath("/community")
|
||||||
revalidateTag("community-gallery")
|
updateTag("community-gallery")
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Revalidate a specific community icon page
|
||||||
|
* Use this when a specific submission's status changes
|
||||||
|
* Uses updateTag for immediate updates (read-your-writes semantics)
|
||||||
|
*/
|
||||||
|
export async function revalidateCommunityIcon(iconName: string) {
|
||||||
|
revalidatePath(`/community/${iconName}`)
|
||||||
|
updateTag("community-gallery")
|
||||||
|
updateTag("community-submission")
|
||||||
|
updateTag("community-gallery-record")
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Revalidate all submission-related caches
|
* Revalidate all submission-related caches
|
||||||
|
* Uses updateTag for immediate updates (read-your-writes semantics)
|
||||||
*/
|
*/
|
||||||
export async function revalidateSubmissions() {
|
export async function revalidateSubmissions() {
|
||||||
revalidateTag("community-gallery")
|
updateTag("community-gallery")
|
||||||
|
updateTag("community-submission")
|
||||||
|
updateTag("community-gallery-record")
|
||||||
revalidatePath("/community")
|
revalidatePath("/community")
|
||||||
revalidatePath("/dashboard")
|
revalidatePath("/dashboard")
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user