Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 13 additions & 5 deletions infra/website/src/layouts/BaseLayout.astro
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,17 @@ import '../styles/global.css';
interface Props {
title: string;
description?: string;
socialImage?: string;
ogUrl?: string;
}

const { title, description = "Feast is an end-to-end open source feature store for machine learning. It allows teams to define, manage, discover, and serve features." } = Astro.props;
const defaultSocialImage = "https://feast.dev/wp-content/uploads/2023/01/feast-og@2x.png";
const {
title,
description = "Feast is an end-to-end open source feature store for machine learning. It allows teams to define, manage, discover, and serve features.",
socialImage = defaultSocialImage,
ogUrl = "https://feast.dev/",
} = Astro.props;
---

<!DOCTYPE html>
Expand All @@ -25,16 +33,16 @@ const { title, description = "Feast is an end-to-end open source feature store f
<meta property="og:type" content="website">
<meta property="og:title" content={title}>
<meta property="og:description" content={description}>
<meta property="og:url" content="https://feast.dev/">
<meta property="og:url" content={ogUrl}>
<meta property="og:site_name" content="Feast">
<meta property="og:image" content="https://feast.dev/wp-content/uploads/2023/01/feast-og@2x.png">
<meta property="og:image" content={socialImage}>
<meta property="og:image:width" content="1201">
<meta property="og:image:height" content="630">
<meta property="og:image:type" content="image/png">
Comment on lines +38 to 41

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Keep OG image metadata consistent with selected image

Now that og:image is dynamic on blog pages, these structured tags stay hard-coded to the default PNG dimensions/type, so posts whose hero image is JPEG/WEBP (e.g., several files under infra/website/docs/blog) publish contradictory metadata. Social crawlers can use these fields for validation/layout, so mismatches can lead to dropped or incorrectly rendered previews for affected posts; either compute these values per image or omit the structured fields when they are not accurate.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@copilot can you fix this in another PR?


<!-- Twitter Card Meta Tags -->
<meta name="twitter:card" content="summary_large_image">
<meta name="twitter:image" content="https://feast.dev/wp-content/uploads/2023/01/feast-og@2x.png">
<meta name="twitter:image" content={socialImage}>

<!-- Fonts -->
<link href="https://fonts.googleapis.com/css2?family=IBM+Plex+Mono:wght@400;700&family=IBM+Plex+Sans:wght@400&display=swap" rel="stylesheet">
Expand All @@ -51,4 +59,4 @@ const { title, description = "Feast is an end-to-end open source feature store f
</main>
<slot name="footer" />
</body>
</html>
</html>
27 changes: 25 additions & 2 deletions infra/website/src/pages/blog/[slug].astro
Original file line number Diff line number Diff line change
@@ -1,6 +1,25 @@
---
import BaseLayout from '../../layouts/BaseLayout.astro';
import Navigation from '../../components/Navigation.astro';
import { readFileSync } from 'node:fs';

const SITE_URL = 'https://feast.dev';

function toAbsoluteUrl(path: string): string {
if (/^https?:\/\//i.test(path)) {
return path;
}
return new URL(path, SITE_URL).toString();
}

function extractHeroImage(markdown: string): string | undefined {
const heroContainerMatch = markdown.match(/<div[^>]*class=["'][^"']*\bhero-image\b[^"']*["'][^>]*>([\s\S]*?)<\/div>/i);
if (!heroContainerMatch?.[1]) {
return undefined;
}
const imageMatch = heroContainerMatch[1].match(/<img[^>]*\bsrc=["']([^"']+)["'][^>]*>/i);
return imageMatch?.[1];
}

export async function getStaticPaths() {
const posts = await Astro.glob('../../../docs/blog/*.md');
Expand All @@ -17,9 +36,13 @@ export async function getStaticPaths() {

const { post } = Astro.props;
const { title, description, date, authors = [] } = post.frontmatter;
const blogUrl = `${SITE_URL}/blog/${Astro.params.slug}/`;
const frontmatterImage = post.frontmatter.hero_image || post.frontmatter.heroImage || post.frontmatter.image;
const heroImage = frontmatterImage || extractHeroImage(readFileSync(post.file, 'utf-8'));
const socialImage = heroImage ? toAbsoluteUrl(heroImage) : undefined;
---

<BaseLayout title={title} description={description}>
<BaseLayout title={title} description={description} socialImage={socialImage} ogUrl={blogUrl}>
<Navigation slot="header" />
<div class="blog-wrapper">
<header class="blog-hero">
Expand Down Expand Up @@ -316,4 +339,4 @@ const { title, description, date, authors = [] } = post.frontmatter;
font-size: 18px;
}
}
</style>
</style>
Loading