feat: add og image

This commit is contained in:
KazooTTT
2025-01-30 20:26:14 +08:00
parent 5fcbb79ee4
commit f742a42d72
12 changed files with 144 additions and 15612 deletions

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -3,29 +3,13 @@ import type { HTMLAttributes } from 'astro/types'
interface Props extends HTMLAttributes<'time'> {
date: Date
coverImage: string
}
const { date, coverImage: coverImage, ...attrs } = Astro.props
console.log('coverImage', coverImage)
const { date, ...attrs } = Astro.props
const formattedDate = date.toISOString().slice(0, 10).replace(/-/g, '')
---
{
coverImage ? (
<div class='relative'>
<time datetime={date.toISOString()} {...attrs}>
{formattedDate}
</time>
<img
src={coverImage}
class='absolute left-0 top-0 -z-10 h-full w-full object-cover opacity-10'
/>
</div>
) : (
<time datetime={date.toISOString()} {...attrs}>
{formattedDate}
</time>
)
}
<time datetime={date.toISOString()} {...attrs}>
{formattedDate}
</time>

View File

@ -8,25 +8,22 @@ import Label from '../Label.astro'
interface Props {
content: CollectionEntry<'post'>
simple?: boolean
socialImage?: string
}
const {
content: { data, render },
simple = false
simple = false,
socialImage
} = Astro.props
const { remarkPluginFrontmatter } = await render()
const dateTimeOptions: Intl.DateTimeFormatOptions = {
month: 'long'
}
---
{
data.coverImage && (
<div class='aspect-h-9 aspect-w-16 mb-6'>
socialImage && (
<div class='mb-6'>
<img
src={data.coverImage}
src={socialImage}
class='rounded-2xl object-cover'
fetchpriority='high'
loading='eager'
@ -34,11 +31,12 @@ const dateTimeOptions: Intl.DateTimeFormatOptions = {
</div>
)
}
{data.draft ? <span class='text-red-500'>(Draft)</span> : null}
<div class='flex flex-wrap items-center gap-x-3 gap-y-2'>
<p class='text-xs'>
<FormattedDate date={data.date} dateTimeOptions={dateTimeOptions} /> /{' '}
<FormattedDate date={data.date} /> /{' '}
{remarkPluginFrontmatter.minutesRead}
</p>
{
@ -106,12 +104,3 @@ const dateTimeOptions: Intl.DateTimeFormatOptions = {
</Card>
)
}
<!-- {
data.date && (
<p class='mt-6 text-base'>
Last Updated:
<FormattedDate class='ms-1' date={data.date} dateTimeOptions={dateTimeOptions} />
</p>
)
} -->

View File

@ -19,7 +19,7 @@ const coverImage = post.data.coverImage
{showYear && <h2 class='my-2 text-xl font-semibold'>{year}</h2>}
<li class='flex flex-col gap-2 sm:flex-row sm:gap-x-4 [&_q]:basis-full'>
<FormattedDate class='min-w-[80px]' date={postDate} coverImage={coverImage} />
<FormattedDate class='min-w-[80px]' date={postDate} />
<Tag>
{post.data.draft && <span class='text-red-500'>(Draft) </span>}

View File

@ -15,10 +15,11 @@ interface Props {
const { post, simple = false, backHref = '/blog' } = Astro.props
const {
data: { description, ogImage, title, date }
data: { description, ogImage, title, date },
slug
} = post
const socialImage = ogImage
const socialImage = ogImage ? ogImage : `/og-image/${slug}.png`
const articleDate = date?.toISOString()
const { headings } = await post.render()
---
@ -51,7 +52,7 @@ const { headings } = await post.render()
{!!headings.length && <TOC headings={headings} />}
<article class='flex-1 flex-grow break-words' data-pagefind-body>
<div id='blog-hero'>
<BlogHero content={post} simple={simple} />
<BlogHero content={post} simple={simple} socialImage={socialImage} />
</div>
<div
id='blog-gallery'

View File

@ -0,0 +1,88 @@
import NotoSansSC from "@/assets/NotoSansHans-Regular-2.ttf";
import RobotoMonoBold from "@/assets/roboto-mono-700.ttf";
import RobotoMono from "@/assets/roboto-mono-regular.ttf";
import { siteConfig } from "@/site-config";
import { Resvg } from "@resvg/resvg-js";
import type { APIContext, InferGetStaticPropsType } from "astro";
import satori, { type SatoriOptions } from "satori";
import { html } from "satori-html";
import { getSortedAllPostsAndDiaries } from "src/utils/post";
const ogOptions: SatoriOptions = {
// debug: true,
fonts: [
{
data: Buffer.from(RobotoMono),
name: "Roboto Mono",
style: "normal",
weight: 400,
},
{
data: Buffer.from(RobotoMonoBold),
name: "Roboto Mono",
style: "normal",
weight: 700,
},
{
data: Buffer.from(NotoSansSC),
name: "Noto Sans SC",
style: "normal",
weight: 400,
},
],
height: 630,
width: 1200,
};
const markup = (title: string, pubDate: string) =>
html`<div tw="flex flex-col w-full h-full bg-[#1d1f21] text-[#c9cacc] font-['Noto Sans SC']">
<div tw="flex flex-col flex-1 w-full p-10 justify-center">
<p tw="text-2xl mb-6">${pubDate}</p>
<h1 tw="text-6xl font-bold leading-snug text-white">${title}</h1>
</div>
<div tw="flex items-center justify-between w-full p-10 border-t border-[#2bbc89] text-xl">
<div tw="flex items-center">
<img tw="w-14 h-14 rounded-full" src="https://pictures.kazoottt.top/2025/01/20250130-121E9E4A-39FB-46DD-8D64-EAA3C77C6503.jpeg" alt="avatar" />
<p tw="ml-3 font-semibold">${siteConfig.title}</p>
</div>
<p>by ${siteConfig.author}</p>
</div>
</div>`;
type Props = InferGetStaticPropsType<typeof getStaticPaths>;
export async function GET(context: APIContext) {
const { pubDate, title } = context.props as Props;
console.log("context.props", context.props)
const postDate = pubDate instanceof Date
? pubDate.toLocaleDateString('zh-CN', {
year: 'numeric',
month: 'long',
day: 'numeric'
})
: pubDate;
const htmlElement = markup(title, postDate);
const svg = await satori(htmlElement, ogOptions);
const png = new Resvg(svg).render().asPng();
return new Response(png, {
headers: {
"Cache-Control": "public, max-age=31536000, immutable",
"Content-Type": "image/png",
},
});
}
export async function getStaticPaths() {
const posts = await getSortedAllPostsAndDiaries();
return posts
.filter(({ data }) => !data.ogImage)
.map((post) => ({
params: { slug: post.id },
props: {
pubDate: post.data?.date,
title: post.data.title,
},
}));
}