|
1 | 1 | import db from '@codebuff/internal/db' |
2 | 2 | import * as schema from '@codebuff/internal/db/schema' |
| 3 | +import { env } from '@codebuff/common/env' |
3 | 4 | import { and, eq } from 'drizzle-orm' |
4 | 5 | import { Calendar } from 'lucide-react' |
5 | 6 | import Link from 'next/link' |
@@ -34,6 +35,7 @@ export async function generateMetadata({ params }: AgentDetailPageProps) { |
34 | 35 | .select({ |
35 | 36 | data: schema.agentConfig.data, |
36 | 37 | version: schema.agentConfig.version, |
| 38 | + created_at: schema.agentConfig.created_at, |
37 | 39 | }) |
38 | 40 | .from(schema.agentConfig) |
39 | 41 | .innerJoin( |
@@ -85,6 +87,61 @@ export async function generateMetadata({ params }: AgentDetailPageProps) { |
85 | 87 | } |
86 | 88 | } |
87 | 89 |
|
| 90 | +// JSON-LD structured data for SEO |
| 91 | +function AgentJsonLd({ |
| 92 | + agentName, |
| 93 | + agentId, |
| 94 | + version, |
| 95 | + description, |
| 96 | + publisherId, |
| 97 | + publisherName, |
| 98 | + createdAt, |
| 99 | +}: { |
| 100 | + agentName: string |
| 101 | + agentId: string |
| 102 | + version: string |
| 103 | + description?: string |
| 104 | + publisherId: string |
| 105 | + publisherName: string |
| 106 | + createdAt: Date |
| 107 | +}) { |
| 108 | + const jsonLd = { |
| 109 | + '@context': 'https://schema.org', |
| 110 | + '@type': 'SoftwareApplication', |
| 111 | + name: agentName, |
| 112 | + applicationCategory: 'DeveloperApplication', |
| 113 | + operatingSystem: 'Cross-platform', |
| 114 | + softwareVersion: version, |
| 115 | + description: |
| 116 | + description || `AI agent ${agentName} for code assistance and automation`, |
| 117 | + url: `${env.NEXT_PUBLIC_CODEBUFF_APP_URL}/publishers/${publisherId}/agents/${agentId}/${version}`, |
| 118 | + datePublished: createdAt.toISOString(), |
| 119 | + author: { |
| 120 | + '@type': 'Organization', |
| 121 | + name: publisherName, |
| 122 | + url: `${env.NEXT_PUBLIC_CODEBUFF_APP_URL}/publishers/${publisherId}`, |
| 123 | + }, |
| 124 | + provider: { |
| 125 | + '@type': 'Organization', |
| 126 | + name: 'Codebuff', |
| 127 | + url: env.NEXT_PUBLIC_CODEBUFF_APP_URL, |
| 128 | + }, |
| 129 | + offers: { |
| 130 | + '@type': 'Offer', |
| 131 | + price: '0', |
| 132 | + priceCurrency: 'USD', |
| 133 | + availability: 'https://schema.org/InStock', |
| 134 | + }, |
| 135 | + } |
| 136 | + |
| 137 | + return ( |
| 138 | + <script |
| 139 | + type="application/ld+json" |
| 140 | + dangerouslySetInnerHTML={{ __html: JSON.stringify(jsonLd) }} |
| 141 | + /> |
| 142 | + ) |
| 143 | +} |
| 144 | + |
88 | 145 | const AgentDetailPage = async ({ params }: AgentDetailPageProps) => { |
89 | 146 | const { id, agentId, version } = await params |
90 | 147 | // Get publisher info |
@@ -148,7 +205,17 @@ const AgentDetailPage = async ({ params }: AgentDetailPageProps) => { |
148 | 205 | const fullAgentId = `${id}/${agentId}@${latestVersion}` |
149 | 206 |
|
150 | 207 | return ( |
151 | | - <div className="container mx-auto py-6 px-4"> |
| 208 | + <> |
| 209 | + <AgentJsonLd |
| 210 | + agentName={agentName} |
| 211 | + agentId={agentId} |
| 212 | + version={version} |
| 213 | + description={agentData.description} |
| 214 | + publisherId={id} |
| 215 | + publisherName={publisherData.name} |
| 216 | + createdAt={new Date(agent[0].created_at)} |
| 217 | + /> |
| 218 | + <div className="container mx-auto py-6 px-4"> |
152 | 219 | <div className="max-w-4xl mx-auto"> |
153 | 220 | {' '} |
154 | 221 | {/* Navigation */} |
@@ -332,6 +399,7 @@ const AgentDetailPage = async ({ params }: AgentDetailPageProps) => { |
332 | 399 | </div> |
333 | 400 | </div> |
334 | 401 | </div> |
| 402 | + </> |
335 | 403 | ) |
336 | 404 | } |
337 | 405 |
|
|
0 commit comments