| --- |
| import type { APIContext } from 'astro'; |
|
|
| interface Props { |
| title: string; |
| description?: string; |
| authors?: string[]; |
| published?: string; // ISO or human-readable |
| tags?: string[]; |
| image?: string; // Recommended absolute URL |
| } |
|
|
| const { |
| title, |
| description = '', |
| authors = [], |
| published, |
| tags = [], |
| image, |
| } = Astro.props as Props; |
|
|
| const url = Astro.url?.toString?.() ?? ''; |
| const site = (Astro.site ? String(Astro.site) : '') as string; |
| const ogImage = image && image.length > 0 |
| ? (image.startsWith('http') ? image : (site ? new URL(image, site).toString() : image)) |
| : undefined; |
|
|
| const jsonLd = { |
| '@context': 'https://schema.org', |
| '@type': 'Article', |
| headline: title, |
| description: description || undefined, |
| datePublished: published || undefined, |
| author: authors.map((name) => ({ '@type': 'Person', name })), |
| keywords: tags.length ? tags.join(', ') : undefined, |
| mainEntityOfPage: url || undefined, |
| image: ogImage ? [ogImage] : undefined, |
| }; |
| --- |
| <html lang="en"> |
| <head> |
| <meta charset="utf-8" /> |
| <meta name="viewport" content="width=device-width, initial-scale=1" /> |
| |
| <title>{title}</title> |
| {description && <meta name="description" content={description} />} |
| |
| <link rel="canonical" href={url} /> |
| |
| <meta property="og:type" content="article" /> |
| <meta property="og:title" content={title} /> |
| {description && <meta property="og:description" content={description} />} |
| <meta property="og:url" content={url} /> |
| {ogImage && <meta property="og:image" content={ogImage} />} |
| {published && <meta property="article:published_time" content={published} />} |
| {authors.map(a => <meta property="article:author" content={a} />)} |
| |
| <meta name="twitter:card" content={ogImage ? 'summary_large_image' : 'summary'} /> |
| <meta name="twitter:title" content={title} /> |
| {description && <meta name="twitter:description" content={description} />} |
| {ogImage && <meta name="twitter:image" content={ogImage} />} |
| |
| <script type="application/ld+json" set:html={JSON.stringify(jsonLd)} /> |
| </head> |
| <body> |
| <slot /> |
| </body> |
| </html> |
|
|
|
|
|
|