diff --git a/apps/marketing/src/app/(marketing)/blog/[post]/opengraph/route.tsx b/apps/marketing/src/app/(marketing)/blog/[post]/opengraph/route.tsx
index 6f16b5092..f17a7931a 100644
--- a/apps/marketing/src/app/(marketing)/blog/[post]/opengraph/route.tsx
+++ b/apps/marketing/src/app/(marketing)/blog/[post]/opengraph/route.tsx
@@ -1,6 +1,8 @@
import { ImageResponse } from 'next/og';
import { NextResponse } from 'next/server';
+import { verify } from '@documenso/lib/server-only/crypto/verify';
+
export const runtime = 'edge';
const IMAGE_SIZE = {
@@ -8,16 +10,18 @@ const IMAGE_SIZE = {
height: 630,
};
-type BlogPostOpenGraphImageProps = {
- params: { post: string };
-};
+export async function GET(_request: Request) {
+ const url = new URL(_request.url);
-export async function GET(_request: Request, { params }: BlogPostOpenGraphImageProps) {
- const { allBlogPosts } = await import('contentlayer/generated');
+ const signature = url.searchParams.get('sig');
+ const title = url.searchParams.get('title');
+ const author = url.searchParams.get('author');
- const blogPost = allBlogPosts.find((post) => post._raw.flattenedPath === `blog/${params.post}`);
+ if (!title || !author || !signature) {
+ return NextResponse.json({ error: 'Not found' }, { status: 404 });
+ }
- if (!blogPost) {
+ if (!verify({ title, author }, signature)) {
return NextResponse.json({ error: 'Not found' }, { status: 404 });
}
@@ -48,10 +52,10 @@ export async function GET(_request: Request, { params }: BlogPostOpenGraphImageP
Written by {blogPost.authorName}
+Written by {author}
), { diff --git a/apps/marketing/src/app/(marketing)/blog/[post]/page.tsx b/apps/marketing/src/app/(marketing)/blog/[post]/page.tsx index fc65d9772..d8ef587c4 100644 --- a/apps/marketing/src/app/(marketing)/blog/[post]/page.tsx +++ b/apps/marketing/src/app/(marketing)/blog/[post]/page.tsx @@ -7,6 +7,8 @@ import { ChevronLeft } from 'lucide-react'; import type { MDXComponents } from 'mdx/types'; import { useMDXComponent } from 'next-contentlayer/hooks'; +import { sign } from '@documenso/lib/server-only/crypto/sign'; + import { CallToAction } from '~/components/(marketing)/call-to-action'; export const dynamic = 'force-dynamic'; @@ -20,16 +22,28 @@ export const generateMetadata = ({ params }: { params: { post: string } }) => { }; } + const signature = sign({ + title: blogPost.title, + author: blogPost.authorName, + }); + + // Use the url constructor to ensure that things are escaped as they should be + const openGraphImageUrl = new URL(`${blogPost.href}/opengraph`); + + openGraphImageUrl.searchParams.set('title', blogPost.title); + openGraphImageUrl.searchParams.set('author', blogPost.authorName); + openGraphImageUrl.searchParams.set('sig', signature); + return { title: { absolute: `${blogPost.title} - Documenso Blog`, }, description: blogPost.description, openGraph: { - images: [`${blogPost.href}/opengraph`], + images: [openGraphImageUrl.toString()], }, twitter: { - images: [`${blogPost.href}/opengraph`], + images: [openGraphImageUrl.toString()], }, }; };