143 lines
4.6 KiB
TypeScript
143 lines
4.6 KiB
TypeScript
import { Container } from "@/components/landing/container";
|
||
import { FadeIn } from "@/components/landing/fade-in";
|
||
import { MdxContent } from "@/components/landing/mdx-content";
|
||
// import { PageLinks } from "@/components/landing/page-links";
|
||
import { Avatar, AvatarImage } from "@/components/ui/avatar";
|
||
import { ScrollArea, ScrollBar } from "@/components/ui/scroll-area";
|
||
import { authors } from "@/content/blog/authors";
|
||
import type { GetServerSideProps, Metadata } from "next";
|
||
|
||
import Link from "next/link";
|
||
import { notFound } from "next/navigation";
|
||
|
||
import { BLOG_PATH, getContentData, getFilePaths, getPost, getQAContent } from "@/lib/mdx-helper";
|
||
import { getService } from "@/lib/http/service";
|
||
import { Header, NavBack, TimeP } from "@/components/header";
|
||
import { baseTitle, baseURL, keywordsRoot } from "@/lib/metadata";
|
||
import { getBaseUrl } from "@/lib/http/get-base-url"; // ✅ 不是 export 它,而是 import 用
|
||
|
||
export const runtime = "nodejs";
|
||
|
||
// // 推荐用法,async 获取
|
||
// export async function getBaseUrl() {
|
||
// let ip = await getRuntimeEnv("SUPABASE_URL");
|
||
// if (!ip) throw new Error("SUPABASE_URL 获取失败,无法构建 baseUrl");
|
||
|
||
// // 判断协议
|
||
// let protocol = "http";
|
||
// if (
|
||
// typeof window !== "undefined" &&
|
||
// window.location &&
|
||
// window.location.protocol === "https:"
|
||
// ) {
|
||
// protocol = "https";
|
||
// // ✅ 只在 HTTPS 客户端场景下用 hostname 替换 IP
|
||
// ip = window.location.hostname;
|
||
// }
|
||
|
||
// // 拼接
|
||
// return `${protocol}://${ip}`;
|
||
// }
|
||
|
||
type Props = {
|
||
params: { locale: string, slug: string; title: string; description: string; authorName: string };
|
||
searchParams: { [key: string]: string | string[] | undefined };
|
||
};
|
||
|
||
export async function generateMetadata({ params }: Props): Promise<Metadata> {
|
||
|
||
const { serialized, frontmatter, headings } = await getQAContent(params.locale, params.slug);
|
||
|
||
console.log("1------------------------generateMetadata----------------------------", headings)
|
||
|
||
if (!frontmatter) {
|
||
return notFound();
|
||
}
|
||
|
||
//const baseUrl = process.env.VERCEL_URL ? process.env.VERCEL_URL : baseURL;
|
||
const baseUrl = await getBaseUrl();
|
||
const ogUrl = new URL("/og/blog", baseUrl);
|
||
const author = authors[frontmatter.author];
|
||
ogUrl.searchParams.set("title", frontmatter.title ?? "");
|
||
return {
|
||
title: `${frontmatter.title}`,
|
||
description: frontmatter.description,
|
||
category: `${headings.text}`,
|
||
keywords: keywordsRoot(headings.text || ""),
|
||
openGraph: {
|
||
title: `${frontmatter.title} | ${baseTitle}`,
|
||
description: frontmatter.description,
|
||
type: "article",
|
||
images: [
|
||
{
|
||
url: ogUrl.toString(),
|
||
width: 1200,
|
||
height: 630,
|
||
alt: frontmatter.title,
|
||
},
|
||
],
|
||
},
|
||
twitter: {
|
||
card: "summary_large_image",
|
||
title: `${frontmatter.title} | ${baseTitle}`,
|
||
description: frontmatter.description,
|
||
images: [
|
||
{
|
||
url: ogUrl.toString(),
|
||
width: 1200,
|
||
height: 630,
|
||
alt: frontmatter.title,
|
||
},
|
||
],
|
||
},
|
||
icons: {
|
||
shortcut: "/favicon.png",
|
||
},
|
||
};
|
||
}
|
||
|
||
const BlogArticleWrapper = async ({ params }: { params: { slug: string, locale: string, } }) => {
|
||
|
||
const { serialized, frontmatter, headings } = await getQAContent(params.locale, params.slug);
|
||
|
||
console.log("frontmatter", frontmatter)
|
||
|
||
|
||
return (
|
||
<>
|
||
<NavBack />
|
||
<div className='mb-40 md:mb-60 w-11/12 lg:w-2/3 xl:w-3/5 mx-auto'>
|
||
<Container className="scroll-smooth">
|
||
<div className="relative mt-0 flex flex-col items-start space-y-8 lg:mt-32 lg:flex-row lg:space-y-0 ">
|
||
<div className="mx-auto ">
|
||
|
||
<div className="flex items-start justify-between space-x-[2rem] text-[1.125rem] text-[#1A1A1A] leading-[3rem] mt-[2rem]">
|
||
{/* <p className=" text-left text-[1rem] text-[#666666]">{frontmatter.date}</p>
|
||
<h6>JellyAI</h6> */}
|
||
<TimeP date={frontmatter.date} />
|
||
|
||
</div>
|
||
|
||
|
||
<h2 className="text-left text-[1.25rem] leading-[7.625rem] font-bold tracking-tight text-[#1A1A1A] sm:text-[1.5rem]">
|
||
{frontmatter.title}
|
||
</h2>
|
||
|
||
<div className="prose prose-neutral dark:prose-invert prose-pre:border prose-pre:border-border prose-pre:rounded-lg prose-img:rounded-lg prose-img:border prose-img:border-border mx-auto w-full max-w-max">
|
||
<MdxContent source={serialized} />
|
||
</div>
|
||
</div>
|
||
|
||
<div className="pt-16"></div>
|
||
</div>
|
||
</Container>
|
||
</div>
|
||
|
||
|
||
</>
|
||
);
|
||
|
||
};
|
||
|
||
export default BlogArticleWrapper;
|