mirror of
https://github.com/MODSetter/SurfSense.git
synced 2026-04-24 16:26:32 +02:00
Some checks failed
Build and Push Docker Images / tag_release (push) Has been cancelled
Build and Push Docker Images / build (./surfsense_backend, ./surfsense_backend/Dockerfile, backend, surfsense-backend, ubuntu-24.04-arm, linux/arm64, arm64) (push) Has been cancelled
Build and Push Docker Images / build (./surfsense_backend, ./surfsense_backend/Dockerfile, backend, surfsense-backend, ubuntu-latest, linux/amd64, amd64) (push) Has been cancelled
Build and Push Docker Images / build (./surfsense_web, ./surfsense_web/Dockerfile, web, surfsense-web, ubuntu-24.04-arm, linux/arm64, arm64) (push) Has been cancelled
Build and Push Docker Images / build (./surfsense_web, ./surfsense_web/Dockerfile, web, surfsense-web, ubuntu-latest, linux/amd64, amd64) (push) Has been cancelled
Build and Push Docker Images / create_manifest (backend, surfsense-backend) (push) Has been cancelled
Build and Push Docker Images / create_manifest (web, surfsense-web) (push) Has been cancelled
- Added multiple new skills to skills-lock.json from the repository `aaron-he-zhu/seo-geo-claude-skills`. - Introduced `fuzzy-search` dependency in package.json for improved search functionality. - Updated pnpm-lock.yaml to include the new `fuzzy-search` package. - Enhanced SEO metadata across various pages, including canonical links and descriptions for better search visibility. - Improved layout and structure of several components, including the homepage and changelog, to enhance user experience.
163 lines
4.5 KiB
TypeScript
163 lines
4.5 KiB
TypeScript
import { loader } from "fumadocs-core/source";
|
|
import type { Metadata } from "next";
|
|
import Image from "next/image";
|
|
import { notFound } from "next/navigation";
|
|
import { blog } from "@/.source/server";
|
|
import { BreadcrumbNav } from "@/components/seo/breadcrumb-nav";
|
|
import { ArticleJsonLd } from "@/components/seo/json-ld";
|
|
import { formatDate } from "@/lib/utils";
|
|
import { getMDXComponents } from "@/mdx-components";
|
|
|
|
const source = loader({
|
|
baseUrl: "/blog",
|
|
source: blog.toFumadocsSource(),
|
|
});
|
|
|
|
interface BlogData {
|
|
title: string;
|
|
description: string;
|
|
date: string;
|
|
image?: string;
|
|
author?: string;
|
|
authorAvatar?: string;
|
|
tags?: string[];
|
|
body: React.ComponentType<{
|
|
components?: Record<string, React.ComponentType>;
|
|
}>;
|
|
}
|
|
|
|
interface BlogPageItem {
|
|
url: string;
|
|
slugs: string[];
|
|
data: BlogData;
|
|
}
|
|
|
|
export async function generateStaticParams() {
|
|
return source.getPages().map((page) => ({
|
|
slug: (page as BlogPageItem).slugs.join("/"),
|
|
}));
|
|
}
|
|
|
|
export async function generateMetadata(props: {
|
|
params: Promise<{ slug: string }>;
|
|
}): Promise<Metadata> {
|
|
const { slug } = await props.params;
|
|
const page = (source.getPages() as BlogPageItem[]).find(
|
|
(p) => p.slugs.join("/") === slug,
|
|
);
|
|
|
|
if (!page) return {};
|
|
|
|
return {
|
|
title: `${page.data.title} | SurfSense Blog`,
|
|
description: page.data.description,
|
|
alternates: {
|
|
canonical: `https://surfsense.com/blog/${slug}`,
|
|
},
|
|
openGraph: {
|
|
title: page.data.title,
|
|
description: page.data.description,
|
|
type: "article",
|
|
publishedTime: page.data.date,
|
|
authors: [page.data.author ?? "SurfSense Team"],
|
|
tags: page.data.tags,
|
|
images: page.data.image ? [{ url: page.data.image }] : [{ url: "/og-image.png" }],
|
|
},
|
|
twitter: {
|
|
card: "summary_large_image",
|
|
title: page.data.title,
|
|
description: page.data.description,
|
|
images: page.data.image ? [page.data.image] : ["/og-image.png"],
|
|
},
|
|
};
|
|
}
|
|
|
|
export default async function BlogPostPage(props: {
|
|
params: Promise<{ slug: string }>;
|
|
}) {
|
|
const { slug } = await props.params;
|
|
const page = (source.getPages() as BlogPageItem[]).find(
|
|
(p) => p.slugs.join("/") === slug,
|
|
);
|
|
|
|
if (!page) notFound();
|
|
|
|
const MDX = page.data.body;
|
|
const date = new Date(page.data.date);
|
|
|
|
return (
|
|
<div className="min-h-screen relative pt-20">
|
|
<ArticleJsonLd
|
|
title={page.data.title}
|
|
description={page.data.description}
|
|
url={`https://surfsense.com/blog/${slug}`}
|
|
datePublished={page.data.date}
|
|
author={page.data.author ?? "SurfSense Team"}
|
|
image={page.data.image ? `https://surfsense.com${page.data.image}` : undefined}
|
|
/>
|
|
<div className="max-w-3xl mx-auto px-6 lg:px-10 pt-10 pb-20">
|
|
<BreadcrumbNav
|
|
items={[
|
|
{ name: "Home", href: "/" },
|
|
{ name: "Blog", href: "/blog" },
|
|
{ name: page.data.title, href: `/blog/${slug}` },
|
|
]}
|
|
className="mb-8"
|
|
/>
|
|
|
|
{page.data.image && (
|
|
<div className="relative aspect-2/1 overflow-hidden rounded-2xl mb-8">
|
|
<Image
|
|
src={page.data.image}
|
|
alt={page.data.title}
|
|
fill
|
|
className="object-cover"
|
|
priority
|
|
sizes="(max-width: 768px) 100vw, 768px"
|
|
/>
|
|
</div>
|
|
)}
|
|
|
|
<div className="space-y-4 mb-10">
|
|
<h1 className="text-3xl md:text-4xl font-bold tracking-tight text-balance">
|
|
{page.data.title}
|
|
</h1>
|
|
|
|
{page.data.tags && page.data.tags.length > 0 && (
|
|
<div className="flex flex-wrap gap-2">
|
|
{page.data.tags.map((tag: string) => (
|
|
<span
|
|
key={tag}
|
|
className="h-6 w-fit px-2.5 text-xs font-medium bg-muted text-muted-foreground rounded-full border flex items-center justify-center"
|
|
>
|
|
{tag}
|
|
</span>
|
|
))}
|
|
</div>
|
|
)}
|
|
|
|
<div className="flex items-center gap-3 text-sm text-muted-foreground">
|
|
{page.data.authorAvatar && (
|
|
<Image
|
|
src={page.data.authorAvatar}
|
|
alt={page.data.author ?? "SurfSense Team"}
|
|
width={32}
|
|
height={32}
|
|
className="h-8 w-8 rounded-full object-cover"
|
|
/>
|
|
)}
|
|
<span className="font-medium text-foreground">
|
|
{page.data.author ?? "SurfSense Team"}
|
|
</span>
|
|
<span>·</span>
|
|
<time dateTime={page.data.date}>{formatDate(date)}</time>
|
|
</div>
|
|
</div>
|
|
|
|
<div className="prose dark:prose-invert max-w-none prose-headings:scroll-mt-8 prose-headings:font-semibold prose-a:no-underline prose-headings:tracking-tight prose-headings:text-balance prose-p:tracking-tight prose-p:text-balance prose-img:rounded-xl prose-img:shadow-lg">
|
|
<MDX components={getMDXComponents()} />
|
|
</div>
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|