Migrate to astro

This commit is contained in:
2026-06-15 13:13:57 +03:00
parent 1a844c57df
commit 8c4c4af56a
81 changed files with 6200 additions and 5069 deletions

128
src/utils/seo.ts Normal file
View File

@@ -0,0 +1,128 @@
import type { CollectionEntry } from "astro:content";
export const SITE_URL = "https://mariosant.dev";
export const TWITTER_HANDLE = "@marios_ant";
export const AUTHOR_NAME = "Marios Antonoudiou";
export const AUTHOR_JOB_TITLE = "AI Product Engineer";
export const SOCIALS = {
github: "https://github.com/mariosant",
linkedin: "https://www.linkedin.com/in/mariosant/",
bluesky: "https://bsky.app/profile/mariosant.bsky.social",
email: "mailto:mariosant@sent.com",
calendar: "https://cal.com/mariosant/30min",
} as const;
export const PERSON_ID = `${SITE_URL}/#person`;
export const AUTHOR_DESCRIPTION =
"AI product engineer building AI-powered products that feel simple, useful, and ready for real users.";
export const stripMarkdown = (body: string): string =>
body
.replace(/```[\s\S]*?```/g, " ")
.replace(/`[^`]*`/g, " ")
.replace(/^#{1,6}\s+.*$/gm, "")
.replace(/\[([^\]]+)\]\([^)]+\)/g, "$1")
.replace(/!\[[^\]]*\]\([^)]+\)/g, "")
.replace(/^>\s*/gm, "")
.replace(/^\s*[-*+]\s+/gm, "")
.replace(/^\s*\d+\.\s+/gm, "")
.replace(/[*_~]+/g, "")
.replace(/\s+/g, " ")
.trim();
export const deriveDescription = (body: string, max = 155): string => {
const plain = stripMarkdown(body);
if (plain.length <= max) return plain;
const cut = plain.slice(0, max);
const lastSpace = cut.lastIndexOf(" ");
const safeCut = lastSpace > 80 ? cut.slice(0, lastSpace) : cut;
return `${safeCut.trimEnd()}`;
};
type ArticleEntry = CollectionEntry<"articles">;
export const personJsonLd = (): string =>
JSON.stringify({
"@context": "https://schema.org",
"@type": "Person",
"@id": PERSON_ID,
name: AUTHOR_NAME,
jobTitle: AUTHOR_JOB_TITLE,
url: SITE_URL,
image: `${SITE_URL}/mariosant.webp`,
description: AUTHOR_DESCRIPTION,
sameAs: [SOCIALS.github, SOCIALS.linkedin, SOCIALS.bluesky],
});
export const articleJsonLd = (entry: ArticleEntry): string => {
const description = entry.data.description ?? deriveDescription(entry.body ?? "");
return JSON.stringify({
"@context": "https://schema.org",
"@type": "Article",
headline: entry.data.title,
description,
image: entry.data.coverImage.url,
datePublished: entry.data.date.toISOString(),
author: {
"@id": PERSON_ID,
"@type": "Person",
name: AUTHOR_NAME,
},
publisher: {
"@id": PERSON_ID,
"@type": "Person",
name: AUTHOR_NAME,
},
mainEntityOfPage: {
"@type": "WebPage",
"@id": `${SITE_URL}/articles/${entry.id}`,
},
});
};
export const breadcrumbJsonLd = (entry: ArticleEntry): string =>
JSON.stringify({
"@context": "https://schema.org",
"@type": "BreadcrumbList",
itemListElement: [
{
"@type": "ListItem",
position: 1,
name: "Home",
item: SITE_URL,
},
{
"@type": "ListItem",
position: 2,
name: "Articles",
item: `${SITE_URL}/articles`,
},
{
"@type": "ListItem",
position: 3,
name: entry.data.title,
item: `${SITE_URL}/articles/${entry.id}`,
},
],
});
export const articleListJsonLd = (entries: readonly ArticleEntry[]): string =>
JSON.stringify({
"@context": "https://schema.org",
"@type": "ItemList",
name: "Articles by Marios Antonoudiou",
itemListElement: entries.map((entry, index) => ({
"@type": "ListItem",
position: index + 1,
url: `${SITE_URL}/articles/${entry.id}`,
name: entry.data.title,
})),
});
export const renderJsonLd = (data: unknown): string => {
if (Array.isArray(data)) {
return data.map((item) => JSON.stringify(item)).join("\n");
}
return JSON.stringify(data);
};