Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 4 additions & 5 deletions web/src/app/api/agents/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,9 @@ import { unstable_cache } from 'next/cache'

import { logger } from '@/util/logger'

// Enable static generation for API route
// ISR Configuration for API route
export const revalidate = 600 // Cache for 10 minutes
export const dynamic = 'force-static' // Force static generation
export const fetchCache = 'force-cache' // Use cached data when possible
export const dynamic = 'force-static'

// Cached function for expensive agent aggregations
const getCachedAgents = unstable_cache(
Expand Down Expand Up @@ -253,8 +252,8 @@ const getCachedAgents = unstable_cache(
},
['agents-data'],
{
revalidate,
tags: ['agents'],
revalidate: 600, // 10 minutes
tags: ['agents', 'api'],
}
)

Expand Down
71 changes: 71 additions & 0 deletions web/src/app/store/agents-data.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
import { unstable_cache } from 'next/cache'

// Types
interface AgentData {
id: string
name: string
description?: string
publisher: {
id: string
name: string
verified: boolean
avatar_url?: string | null
}
version: string
created_at: string
usage_count?: number
weekly_spent?: number
total_spent?: number
avg_cost_per_invocation?: number
unique_users?: number
last_used?: string
version_stats?: Record<string, any>
tags?: string[]
}

// Server-side data fetching function with ISR
export const getAgentsData = unstable_cache(
async (): Promise<AgentData[]> => {
const baseUrl =
process.env.NEXT_PUBLIC_CODEBUFF_APP_URL || 'http://localhost:3000'

try {
const response = await fetch(`${baseUrl}/api/agents`, {
headers: {
'User-Agent': 'Codebuff-Store-Static',
},
// Configure fetch-level caching
next: {
revalidate: 600, // 10 minutes
tags: ['agents', 'store'],
},
})

if (!response.ok) {
console.error(
'Failed to fetch agents:',
response.status,
response.statusText
)
return []
}

return await response.json()
} catch (error) {
console.error('Error fetching agents data:', error)
return []
}
},
['store-agents-data'],
{
revalidate: 600, // Cache for 10 minutes
tags: ['agents', 'store'],
}
)

// Helper function for on-demand revalidation (can be used in webhooks/admin actions)
export async function revalidateAgentsData() {
const { revalidateTag } = await import('next/cache')
revalidateTag('agents')
revalidateTag('store')
}
81 changes: 45 additions & 36 deletions web/src/app/store/page.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { Metadata } from 'next'
import { unstable_cache } from 'next/cache'
import { Suspense } from 'react'
import AgentStoreClient from './store-client'
import { getAgentsData } from './agents-data'

// Types
interface AgentData {
Expand Down Expand Up @@ -42,35 +43,6 @@ interface PublisherProfileResponse {
avatar_url?: string | null
}

// Cache the agents data with 60 second revalidation
const getCachedAgentsData = unstable_cache(
async (): Promise<AgentData[]> => {
const baseUrl =
process.env.NEXT_PUBLIC_CODEBUFF_APP_URL || 'http://localhost:3000'
const response = await fetch(`${baseUrl}/api/agents`, {
headers: {
'User-Agent': 'Codebuff-Store-Static',
},
})

if (!response.ok) {
console.error(
'Failed to fetch agents:',
response.status,
response.statusText
)
return []
}

return await response.json()
},
['store-agents-data'],
{
revalidate: 60, // Revalidate every 60 seconds
tags: ['agents', 'store'],
}
)

export const metadata: Metadata = {
title: 'Agent Store | Codebuff',
description: 'Browse all published AI agents. Run, compose, or fork them.',
Expand All @@ -81,18 +53,22 @@ export const metadata: Metadata = {
},
}

// Enable static site generation with ISR
export const revalidate = 60 * 10 // Revalidate every 10 minutes
// ISR Configuration - revalidate every 10 minutes
export const revalidate = 600
export const dynamic = 'force-static'
export const fetchCache = 'force-cache'

interface StorePageProps {
searchParams: { [key: string]: string | string[] | undefined }
}

export default async function StorePage({ searchParams }: StorePageProps) {
// Fetch agents data at build time
const agentsData = await getCachedAgentsData()
// Server Component for fetching and rendering agents data
async function AgentsDataProvider({
searchParams,
}: {
searchParams: StorePageProps['searchParams']
}) {
// Fetch agents data with ISR
const agentsData = await getAgentsData()

// For static generation, we don't pass session data
// The client will handle authentication state
Expand All @@ -107,3 +83,36 @@ export default async function StorePage({ searchParams }: StorePageProps) {
/>
)
}

// Loading component for better UX
function AgentsLoading() {
return (
<div className="container mx-auto py-8 px-4">
<div className="max-w-7xl mx-auto">
<div className="mb-12">
<h1 className="text-4xl font-bold text-white mb-2">Agent Store</h1>
<p className="text-xl text-muted-foreground">
Browse all published AI agents. Run, compose, or fork them.
</p>
</div>

<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
{Array.from({ length: 6 }).map((_, i) => (
<div
key={i}
className="h-[220px] bg-card/50 border rounded-lg animate-pulse"
/>
))}
</div>
</div>
</div>
)
}

export default async function StorePage({ searchParams }: StorePageProps) {
return (
<Suspense fallback={<AgentsLoading />}>
<AgentsDataProvider searchParams={searchParams} />
</Suspense>
)
}
96 changes: 0 additions & 96 deletions web/src/components/navbar/client-auth-nav.tsx

This file was deleted.

Loading