Skip to content

Commit c5b28d2

Browse files
committed
test: og
1 parent d7b7721 commit c5b28d2

File tree

4 files changed

+230
-0
lines changed

4 files changed

+230
-0
lines changed

config/seo.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
export const seo = {
22
title: 'home | zhw',
3+
ogTitle: 'blog',
34
description: '我是zhw,欢迎来到我的博客',
5+
word: '沉淀',
46
url: new URL(
57
process.env.NODE_ENV === 'production'
68
? 'https://blog-rbtb.vercel.app/'

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@
4545
"tailwind-variants": "^0.2.1",
4646
"tailwindcss-animate": "^1.0.7",
4747
"tailwindcss-animated": "^1.1.2",
48+
"uniqolor": "^1.1.1",
4849
"vaul": "^1.1.1",
4950
"zustand": "^5.0.0"
5051
},

pnpm-lock.yaml

Lines changed: 8 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/app/api/og/router.tsx

Lines changed: 219 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,219 @@
1+
import { ImageResponse } from 'next/og';
2+
import { ImageResponseOptions, NextRequest } from 'next/server';
3+
4+
import { seo } from '~/index';
5+
import { buildPostData } from '@/core';
6+
7+
export const runtime = 'edge';
8+
9+
const { postDataMap } = buildPostData();
10+
11+
const resOptions = {
12+
width: 1200,
13+
height: 600,
14+
emoji: 'twemoji',
15+
headers: new Headers([
16+
['cache-control', 'max-age=3600, s-maxage=3600, stale-while-revalidate=600'],
17+
['cdn-cache-control', 'max-age=3600, stale-while-revalidate=600'],
18+
]),
19+
} as ImageResponseOptions;
20+
const HomeOGImage = () => {
21+
const seed = Math.random().toString(36).substring(7);
22+
const bgAccent = uniqolor(seed, {
23+
saturation: [30, 35],
24+
lightness: [60, 70],
25+
}).color;
26+
27+
const bgAccentLight = uniqolor(seed, {
28+
saturation: [30, 35],
29+
lightness: [80, 90],
30+
}).color;
31+
32+
const bgAccentUltraLight = uniqolor(seed, {
33+
saturation: [30, 35],
34+
lightness: [95, 96],
35+
}).color;
36+
37+
return (
38+
<div
39+
style={{
40+
display: 'flex',
41+
height: '100%',
42+
width: '100%',
43+
44+
background: `linear-gradient(37deg, ${bgAccent} 27.82%, ${bgAccentLight} 79.68%, ${bgAccentUltraLight} 100%)`,
45+
46+
fontFamily: 'Noto Sans, Inter, "Material Icons"',
47+
48+
padding: '80px 15rem',
49+
alignItems: 'center',
50+
justifyContent: 'space-between',
51+
}}
52+
>
53+
<img
54+
src="/image/owner.jpg"
55+
style={{
56+
borderRadius: '50%',
57+
}}
58+
height={256}
59+
width={256}
60+
/>
61+
62+
<div
63+
style={{
64+
display: 'flex',
65+
flexDirection: 'column',
66+
marginLeft: '3rem',
67+
width: '500px',
68+
overflow: 'hidden',
69+
alignItems: 'center',
70+
justifyContent: 'center',
71+
}}
72+
>
73+
<h3
74+
style={{
75+
color: '#ffffff99',
76+
fontSize: '3.5rem',
77+
whiteSpace: 'nowrap',
78+
}}
79+
>
80+
{seo.ogTitle}
81+
</h3>
82+
<p
83+
style={{
84+
fontSize: '1.8rem',
85+
height: '5.2rem',
86+
overflow: 'hidden',
87+
lineClamp: 2,
88+
color: '#ffffff89',
89+
}}
90+
>
91+
{seo.description}
92+
</p>
93+
</div>
94+
</div>
95+
);
96+
};
97+
98+
export const GET = async (req: NextRequest) => {
99+
try {
100+
const { searchParams } = req.nextUrl;
101+
102+
const dataString = searchParams.get('nid') as string;
103+
104+
if (!dataString) {
105+
return new ImageResponse(<HomeOGImage />, resOptions);
106+
}
107+
108+
try {
109+
if (!(dataString in postDataMap)) {
110+
throw new Error('No such post');
111+
}
112+
} catch {
113+
return new Response('Failed to parse the data.', { status: 400 });
114+
}
115+
116+
const postName = postDataMap[dataString].title;
117+
118+
const bgAccent = uniqolor(postName, {
119+
saturation: [30, 35],
120+
lightness: [60, 70],
121+
}).color;
122+
123+
const bgAccentLight = uniqolor(postName, {
124+
saturation: [30, 35],
125+
lightness: [80, 90],
126+
}).color;
127+
128+
const bgAccentUltraLight = uniqolor(postName, {
129+
saturation: [30, 35],
130+
lightness: [95, 96],
131+
}).color;
132+
133+
return new ImageResponse(
134+
(
135+
<div
136+
style={{
137+
display: 'flex',
138+
height: '100%',
139+
width: '100%',
140+
141+
background: `linear-gradient(37deg, ${bgAccent} 27.82%, ${bgAccentLight} 79.68%, ${bgAccentUltraLight} 100%)`,
142+
143+
fontFamily: 'Inter, Noto Sans, Inter, "Material Icons"',
144+
145+
padding: '80px',
146+
alignItems: 'flex-end',
147+
justifyContent: 'flex-end',
148+
}}
149+
>
150+
<div
151+
style={{
152+
display: 'flex',
153+
154+
position: 'absolute',
155+
left: '5rem',
156+
top: '5rem',
157+
alignItems: 'center',
158+
justifyContent: 'center',
159+
}}
160+
>
161+
<img
162+
src="/image/owner.jpg"
163+
style={{
164+
borderRadius: '50%',
165+
}}
166+
height={128}
167+
width={128}
168+
/>
169+
170+
<span
171+
style={{
172+
marginLeft: '3rem',
173+
color: '#ffffff99',
174+
fontSize: '2rem',
175+
}}
176+
>
177+
<h3>{seo.ogTitle}</h3>
178+
</span>
179+
</div>
180+
<div
181+
style={{
182+
display: 'flex',
183+
justifyContent: 'flex-end',
184+
alignItems: 'flex-end',
185+
flexDirection: 'column',
186+
textAlign: 'right',
187+
}}
188+
>
189+
<h1
190+
style={{
191+
color: 'rgba(255, 255, 255, 0.92)',
192+
fontSize: '50px',
193+
overflow: 'hidden',
194+
maxHeight: '150px',
195+
fontWeight: 'bold',
196+
}}
197+
>
198+
{postName}
199+
</h1>
200+
<h2
201+
style={{
202+
color: 'rgba(255, 255, 255, 0.85)',
203+
fontSize: '38px',
204+
fontWeight: 'lighter',
205+
}}
206+
>
207+
{seo.word}
208+
</h2>
209+
</div>
210+
</div>
211+
),
212+
resOptions,
213+
);
214+
} catch (e: any) {
215+
return new Response(`Failed to generate the OG image. Error ${e.message}`, {
216+
status: 500,
217+
});
218+
}
219+
};

0 commit comments

Comments
 (0)