ai-wpa/lib/structured-data.ts

163 lines
4.1 KiB
TypeScript

import { ITopic } from '@/models/topic'
interface ArticleStructuredData {
title: string
description: string
image: string
publishedTime: string
authorName: string
url: string
readingTime?: string
tags?: string[]
}
export function generateArticleStructuredData({
title,
description,
image,
publishedTime,
authorName,
url,
readingTime,
tags = [],
}: ArticleStructuredData): string {
const baseUrl = process.env.NEXT_PUBLIC_APP_URL || 'https://siliconpin.com'
const structuredData = {
'@context': 'https://schema.org',
'@type': 'Article',
headline: title,
description: description,
image: {
'@type': 'ImageObject',
url: image.startsWith('http') ? image : `${baseUrl}${image}`,
width: 800,
height: 450,
},
author: {
'@type': 'Person',
name: authorName,
},
publisher: {
'@type': 'Organization',
name: 'SiliconPin',
logo: {
'@type': 'ImageObject',
url: `${baseUrl}/favicon.ico`,
width: 32,
height: 32,
},
},
datePublished: publishedTime,
dateModified: publishedTime,
mainEntityOfPage: {
'@type': 'WebPage',
'@id': `${baseUrl}${url}`,
},
url: `${baseUrl}${url}`,
...(readingTime && {
timeRequired: readingTime,
}),
...(tags.length > 0 && {
keywords: tags.join(', '),
about: tags.map(tag => ({
'@type': 'Thing',
name: tag,
})),
}),
}
return JSON.stringify(structuredData, null, 2)
}
export function generateTopicListingStructuredData(topics: ITopic[]): string {
const baseUrl = process.env.NEXT_PUBLIC_APP_URL || 'https://siliconpin.com'
const structuredData = {
'@context': 'https://schema.org',
'@type': 'Topic',
name: 'SiliconPin Topics',
description: 'Technical articles, tutorials, and insights from the SiliconPin community',
url: `${baseUrl}/topics`,
publisher: {
'@type': 'Organization',
name: 'SiliconPin',
logo: {
'@type': 'ImageObject',
url: `${baseUrl}/favicon.ico`,
width: 32,
height: 32,
},
},
topicPost: topics.slice(0, 10).map(topic => ({
'@type': 'TopicPosting',
headline: topic.title,
description: topic.excerpt,
url: `${baseUrl}/topics/${topic.slug}`,
datePublished: new Date(topic.publishedAt).toISOString(),
author: {
'@type': 'Person',
name: topic.author,
},
image: {
'@type': 'ImageObject',
url: topic.coverImage.startsWith('http') ? topic.coverImage : `${baseUrl}${topic.coverImage}`,
width: 800,
height: 450,
},
keywords: topic.tags.map(tag => tag.name).join(', '),
})),
}
return JSON.stringify(structuredData, null, 2)
}
export function generateWebsiteStructuredData(): string {
const baseUrl = process.env.NEXT_PUBLIC_APP_URL || 'https://siliconpin.com'
const structuredData = {
'@context': 'https://schema.org',
'@type': 'WebSite',
name: 'SiliconPin',
description: 'Modern web hosting and cloud solutions with integrated topic platform',
url: baseUrl,
potentialAction: {
'@type': 'SearchAction',
target: {
'@type': 'EntryPoint',
urlTemplate: `${baseUrl}/search?q={search_term_string}`,
},
'query-input': 'required name=search_term_string',
},
publisher: {
'@type': 'Organization',
name: 'SiliconPin',
logo: {
'@type': 'ImageObject',
url: `${baseUrl}/favicon.ico`,
width: 32,
height: 32,
},
},
}
return JSON.stringify(structuredData, null, 2)
}
export function generateBreadcrumbStructuredData(items: Array<{ name: string; url: string }>): string {
const baseUrl = process.env.NEXT_PUBLIC_APP_URL || 'https://siliconpin.com'
const structuredData = {
'@context': 'https://schema.org',
'@type': 'BreadcrumbList',
itemListElement: items.map((item, index) => ({
'@type': 'ListItem',
position: index + 1,
name: item.name,
item: item.url.startsWith('http') ? item.url : `${baseUrl}${item.url}`,
})),
}
return JSON.stringify(structuredData, null, 2)
}