364 lines
14 KiB
TypeScript
364 lines
14 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, getPostContent } from "@/lib/mdx-helper";
|
|
import service from "@/lib/http/service";
|
|
import { Header, NavBack, TimeP } from "@/components/header";
|
|
import { baseTitle, baseURL, keywordsRoot } from "@/lib/metadata";
|
|
import { useTranslation } from "react-i18next";
|
|
|
|
export const runtime = "nodejs";
|
|
|
|
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> {
|
|
// export async function generateMetadata(ctx: any): Promise<Metadata> {
|
|
// read route params
|
|
// const { frontmatter } = await getPost(params.slug);
|
|
|
|
// const { t, i18n } = useTranslation()
|
|
|
|
const { serialized, frontmatter, headings } = await getPostContent(params.locale, params.slug);
|
|
|
|
console.log("---generateMetadata---", headings)
|
|
|
|
// const {
|
|
// serialized,
|
|
// frontmatter,
|
|
// headings,
|
|
// } = ctx
|
|
|
|
if (!frontmatter) {
|
|
return notFound();
|
|
}
|
|
|
|
// const baseUrl = process.env.VERCEL_URL ? "https://jellyai.xyz" : "http://localhost:3000";
|
|
const baseUrl = process.env.VERCEL_URL ? process.env.VERCEL_URL : baseURL;
|
|
const ogUrl = new URL("/og/blog", baseUrl);
|
|
const author = authors[frontmatter.author];
|
|
ogUrl.searchParams.set("title", frontmatter.title ?? "");
|
|
// ogUrl.searchParams.set("author", author.name ?? "");
|
|
// if (author.image.src) {
|
|
// ogUrl.searchParams.set("image", new URL(author.image.src, baseUrl).toString());
|
|
// }
|
|
|
|
return {
|
|
// title: {
|
|
// default: 'JellyAI',
|
|
// template: `${frontmatter.title} | JellyAI`
|
|
// },
|
|
title: `${frontmatter.title}`,
|
|
description: frontmatter.description,
|
|
keywords: keywordsRoot(headings.text || ""),
|
|
openGraph: {
|
|
title: `${frontmatter.title} | ${baseTitle}`,
|
|
description: frontmatter.description,
|
|
// url: `https://unkey.dev/blog/${params.slug}`,
|
|
type: "article",
|
|
images: [
|
|
{
|
|
url: ogUrl.toString(),
|
|
width: 1200,
|
|
height: 630,
|
|
alt: frontmatter.title,
|
|
},
|
|
],
|
|
// siteName: "unkey.dev",
|
|
},
|
|
twitter: {
|
|
card: "summary_large_image",
|
|
title: `${frontmatter.title} | ${baseTitle}`,
|
|
description: frontmatter.description,
|
|
images: [
|
|
{
|
|
url: ogUrl.toString(),
|
|
width: 1200,
|
|
height: 630,
|
|
alt: frontmatter.title,
|
|
},
|
|
],
|
|
// site: "@aigxion",
|
|
// creator: "@aigxion",
|
|
},
|
|
icons: {
|
|
shortcut: "/favicon.png",
|
|
},
|
|
};
|
|
}
|
|
|
|
// export const generateStaticParams = async () => {
|
|
// const posts = await getFilePaths(BLOG_PATH);
|
|
// // Remove file extensions for page paths
|
|
// posts.map((path) => path.replace(/\.mdx?$/, "")).map((slug) => ({ params: { slug } }));
|
|
// return posts;
|
|
// };
|
|
|
|
// export async function getStaticProps() {
|
|
// const res = await fetch(`https://...`)
|
|
// const projects = await res.json()
|
|
|
|
// return { props: { projects } }
|
|
// }
|
|
|
|
// export default function Index({ projects }) {
|
|
// return projects.map((project) => <div>{project.name}</div>)
|
|
// }
|
|
|
|
// getServerSideProps generateStaticParams getStaticProps
|
|
// export async function getStaticProps({ params }: { params: { slug: string } }) {
|
|
// export async function getServerSideProps({ params }: { params: { slug: string } }) {
|
|
// const { serialized, frontmatter, headings } = await getPostContent(params.slug);
|
|
// // 使用 params.id 查询数据库获取数据
|
|
// // const post = await getPostById(params.id);
|
|
|
|
|
|
// console.log("generateStaticParams", headings, params, params.slug)
|
|
// return {
|
|
// props: {
|
|
// ...params,
|
|
// serialized,
|
|
// frontmatter,
|
|
// headings
|
|
// },
|
|
// };
|
|
// }
|
|
|
|
|
|
const BlogArticleWrapper = async ({ params }: { params: { slug: string, locale: string, } }) => {
|
|
// const BlogArticleWrapper = async (ctx: any) => {
|
|
// console.log(ctx)
|
|
// const {
|
|
// serialized,
|
|
// frontmatter,
|
|
// headings,
|
|
// } = ctx
|
|
|
|
|
|
const { serialized, frontmatter, headings } = await getPostContent(params.locale, params.slug);
|
|
|
|
// console.log(serialized, frontmatter, headings, params.slug)
|
|
// console.log(params.slug)
|
|
|
|
console.log("frontmatter", frontmatter)
|
|
|
|
const defaultImage = "https://gimg3.baidu.com/search/src=http%3A%2F%2Fpics3.baidu.com%2Ffeed%2F4ec2d5628535e5dd7c7cdc23847e08e2cc1b62c4.jpeg%40f_auto%3Ftoken%3D38327d5cbefb2cd45af58f8d47c0a0b5&refer=http%3A%2F%2Fwww.baidu.com&app=2021&size=f360,240&n=0&g=0n&q=75&fmt=auto?sec=1710435600&t=eafc0a27ff3295bb5b0b9065fc33a523"
|
|
|
|
|
|
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-2 lg:flex-row lg:space-y-0 ">
|
|
<div className="mx-auto pt-[3rem] lg:pt-[0] ">
|
|
<div className="flex items-start space-x-[0.2rem] text-left text-[0.8rem] lg:text-[1rem] mb-8 text-[#808080] leading-[1.4rem] lg:leading-[4rem]">
|
|
|
|
<Link
|
|
href="/"
|
|
className=" hover:text-black focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-white"
|
|
>
|
|
Blog
|
|
</Link>
|
|
<p className=" ">> </p>
|
|
<p className="">{frontmatter.title} </p>
|
|
|
|
</div>
|
|
|
|
<div className="flex items-start font-bold space-x-[2rem] text-[0.8rem] lg:text-[1.125rem] text-[#1A1A1A] leading-[1.5rem] lg:leading-[3rem] mb-[2rem]">
|
|
{/* <h4 className="border rounded-full bg-[#E5E5E5] px-[1.8rem]">All Blogs </h4> */}
|
|
{
|
|
frontmatter.tags && frontmatter.tags?.length > 0 && frontmatter.tags?.map((item, index) => {
|
|
return <h4 className="border rounded-full bg-[#E5E5E5] px-[1.8rem]">{item}</h4>
|
|
})
|
|
}
|
|
</div>
|
|
|
|
{/* <p className=" text-left text-[1rem] text-[#808080]">{} {t("JellyAI")}</p> */}
|
|
<TimeP date={frontmatter.date} />
|
|
<h2 className="text-left text-[1rem] leading-[1.625rem] mb-[1rem] font-bold tracking-tight text-[#1A1A1A] sm:text-[1rem] lg:text-[1.5rem] lg:leading-[7.625rem]">
|
|
{frontmatter.title}
|
|
</h2>
|
|
|
|
<figure
|
|
className="w-full min-h-[146px] lg:min-h-[246px]"
|
|
// style={{ width: '100%', minHeight: "246px" }}
|
|
>
|
|
<img src={
|
|
(
|
|
frontmatter.image_url.includes("http")
|
|
? frontmatter.image_url
|
|
: process.env.NEXT_PUBLIC_CLIENT_IMAGE_URL + frontmatter.image_url
|
|
) || defaultImage
|
|
|
|
} alt={frontmatter.title} style={{ width: '100%', marginBottom: '8px' }} />
|
|
</figure>
|
|
|
|
<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;
|
|
|
|
|
|
|
|
// const BlogArticleWrapper_bak = async ({ params }: { params: { slug: string } }) => {
|
|
// const { serialized, frontmatter, headings } = await getPost(params.slug);
|
|
// console.log(serialized, frontmatter, headings, params.slug)
|
|
|
|
|
|
// const author = authors[frontmatter.author];
|
|
// const moreArticles = await getContentData({
|
|
// contentPath: BLOG_PATH,
|
|
// filepath: params.slug,
|
|
// });
|
|
|
|
|
|
// return (
|
|
// <>
|
|
// <Container className="scroll-smooth">
|
|
// <div className="relative mt-16 flex flex-col items-start space-y-8 lg:mt-32 lg:flex-row lg:space-y-0 ">
|
|
// <div className="mx-auto w-full lg:pl-8 ">
|
|
// <h2 className="text-center text-3xl font-bold tracking-tight text-gray-900 sm:text-6xl">
|
|
// {frontmatter.title}
|
|
// </h2>
|
|
// <p className="border- my-8 text-center text-gray-500">{frontmatter.description}</p>
|
|
// <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">
|
|
// <MdxContent source={serialized} />
|
|
// </div>
|
|
// </div>
|
|
|
|
// <div className="top-24 flex h-max w-full flex-col justify-end self-start px-4 sm:px-6 lg:sticky lg:w-2/5 lg:px-8">
|
|
// <div className="mx-auto flex items-center justify-start gap-4 border-y-0 p-2 md:mx-0 md:border-b md:border-b-gray-200">
|
|
// <Avatar className="h-14 w-14 justify-items-start">
|
|
// <AvatarImage src={author.image?.src} alt={author.name} />
|
|
// </Avatar>
|
|
// <div className="text-sm text-gray-950">
|
|
// <div className="font-semibold">{author.name}</div>
|
|
// </div>
|
|
// </div>
|
|
// {
|
|
// <div className="hidden md:block">
|
|
// <h3 className="mb-4 mt-8 text-lg font-bold uppercase tracking-wide text-gray-600">
|
|
// Table of Contents
|
|
// </h3>
|
|
// <ScrollArea className="flex h-[580px] flex-col">
|
|
// <div className="p-4">
|
|
// {headings.map((heading) => {
|
|
// return (
|
|
// <div key={`#${heading.slug}`} className="my-2">
|
|
// <a
|
|
// data-level={heading.level}
|
|
// className={
|
|
// heading.level === "two" || heading.level === "one"
|
|
// ? "text-md font-semibold"
|
|
// : "ml-4 text-sm"
|
|
// }
|
|
// href={`#${heading.slug}`}
|
|
// >
|
|
// {heading.text}
|
|
// </a>
|
|
// </div>
|
|
// );
|
|
// })}
|
|
// </div>
|
|
// <ScrollBar orientation="vertical" />
|
|
// </ScrollArea>
|
|
// </div>
|
|
// }
|
|
// </div>
|
|
// </div>
|
|
// </Container>
|
|
// <FadeIn>
|
|
// <div className="mx-auto max-w-7xl py-24 sm:px-6 sm:py-32 lg:px-8">
|
|
// <div className="relative isolate overflow-hidden bg-gray-900 px-6 pt-16 shadow-2xl sm:rounded-xl sm:px-16 md:pt-24 lg:flex lg:gap-x-20 lg:px-24 lg:pt-0">
|
|
// <svg
|
|
// viewBox="0 0 1024 1024"
|
|
// className="absolute left-1/2 top-1/2 -z-10 h-[64rem] w-[64rem] -translate-y-1/2 [mask-image:radial-gradient(closest-side,white,transparent)] sm:left-full sm:-ml-80 lg:left-1/2 lg:ml-0 lg:-translate-x-1/2 lg:translate-y-0"
|
|
// aria-hidden="true"
|
|
// >
|
|
// <circle
|
|
// cx={512}
|
|
// cy={512}
|
|
// r={512}
|
|
// fill="url(#759c1415-0410-454c-8f7c-9a820de03641)"
|
|
// fillOpacity="0.7"
|
|
// />
|
|
// <defs>
|
|
// <radialGradient id="759c1415-0410-454c-8f7c-9a820de03641">
|
|
// <stop stopColor="#6030B3" />
|
|
// <stop offset={1} stopColor="#6030B3" />
|
|
// </radialGradient>
|
|
// </defs>
|
|
// </svg>
|
|
// <div className="mx-auto max-w-md text-center lg:mx-0 lg:flex-auto lg:py-32 lg:text-left">
|
|
// <h2 className="text-3xl font-bold tracking-tight text-white sm:text-4xl">
|
|
// Accelerate your API development
|
|
// </h2>
|
|
|
|
// <div className="mt-10 flex items-center justify-center gap-x-6 lg:justify-start">
|
|
// <Link
|
|
// href="/app"
|
|
// className="rounded-md bg-white px-3.5 py-2.5 text-sm font-semibold text-gray-900 shadow-sm hover:bg-gray-100 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-white"
|
|
// >
|
|
// Get started
|
|
// </Link>
|
|
// <a
|
|
// href="https://unkey.dev/docs"
|
|
// target="_blank"
|
|
// rel="noopener noreferrer"
|
|
// className="text-sm font-semibold leading-6 text-white"
|
|
// >
|
|
// Documentation <span aria-hidden="true">→</span>
|
|
// </a>
|
|
// </div>
|
|
// </div>
|
|
// <div className="relative mt-16 h-80 lg:mt-8">
|
|
// <img
|
|
// className="g-white/5 absolute left-0 top-0 w-[57rem] max-w-none rounded-md ring-1 ring-white/10"
|
|
// src="/images/blog-images/admin-dashboard-new.png"
|
|
// alt="App screenshot"
|
|
// width={1824}
|
|
// height={1080}
|
|
// />
|
|
// </div>
|
|
// </div>
|
|
// </div>
|
|
// </FadeIn>
|
|
|
|
// {/* {moreArticles.length > 0 && (
|
|
// <PageLinks
|
|
// className="mt-24 sm:mt-32 lg:mt-40"
|
|
// title="More articles"
|
|
// intro=""
|
|
// contentType="blog"
|
|
// pages={moreArticles}
|
|
// />
|
|
// )} */}
|
|
// </>
|
|
// );
|
|
// };
|