import { promises as fs } from "fs"; import path from "path"; import GithubSlugger from "github-slugger"; import { type MDXRemoteSerializeResult } from "next-mdx-remote"; import { serialize } from "next-mdx-remote/serialize"; import { notFound } from "next/navigation"; import rehypeAutolinkHeadings from "rehype-autolink-headings"; import rehypeCodeTitles from "rehype-code-titles"; import rehypePrettyCode from "rehype-pretty-code"; import rehypeSlug from "rehype-slug"; import remarkGfm from "remark-gfm"; import { BUNDLED_LANGUAGES, type HighlighterOptions, getHighlighter } from "shiki"; import gitHubLight from "shiki/themes/github-light.json"; import service from "./http/service"; import { ArticleData, QAData } from "@/components/article/article"; export const BLOG_PATH = path.join(process.cwd(), "content", "blog"); export const CHANGELOG_PATH = path.join(process.cwd(), "content", "changelog"); export const POLICY_PATH = path.join(process.cwd(), "content", "policies"); export const JOBS_PATH = path.join(process.cwd(), "content", "jobs"); export const changelogFilePaths = fs.readdir(CHANGELOG_PATH); // console.log(BLOG_PATH) export const getFilePaths = async (contentPath: string) => { return await fs.readdir(contentPath); }; export const raw = async ({ contentPath, filepath, }: { contentPath: string; filepath: string; }) => { console.log(contentPath, filepath) try { const fileContent = await fs.readFile(`${contentPath}/${filepath}.mdx`, "utf-8"); return fileContent; } catch { return notFound(); } }; type Headings = { slug: string | undefined; level: string; text: string | undefined; }; type Post = { serialized: MDXRemoteSerializeResult; frontmatter: TFrontmatter; headings: Headings[]; }; type AIPost = { serialized: MDXRemoteSerializeResult; frontmatter: TFrontmatter; headings: Headings; }; type Changelog = { serialized: MDXRemoteSerializeResult; frontmatter: TFrontmatter; }; type ChangeLogFrontmatter = { title: string; date: string; description: string; }; type Policy = { serialized: MDXRemoteSerializeResult; frontmatter: TFrontmatter; }; type PolicyFrontmatter = { title: string; }; type Job = { serialized: MDXRemoteSerializeResult; frontmatter: TFrontmatter; }; type JobFrontmatter = { title: string; description: string; visible: boolean; salary: string; }; type Frontmatter = { title: string; date: string; description: string; author: string; visible: boolean | undefined; salary: string | undefined; level: string | undefined; }; type AIFrontmatter = { id:string; p_name:string; title: string; date: string; description: string; author: string; image_url: string; logo_url: string; model_parameter: number; visible: boolean | undefined; salary: string | undefined; level: string | undefined; tags?: string[]; }; const options = { theme: gitHubLight, getHighlighter: (options: HighlighterOptions) => getHighlighter({ ...options, langs: [...BUNDLED_LANGUAGES], }), defaultLang: { block: "typescript", }, }; // Serialize the MDX content and parse the frontmatter export const mdxSerialized = async ({ rawMdx }: { rawMdx: string }) => { return await serialize(rawMdx, { parseFrontmatter: true, mdxOptions: { remarkPlugins: [remarkGfm], rehypePlugins: [ [rehypePrettyCode, options], rehypeAutolinkHeadings, rehypeSlug, rehypeCodeTitles, ], }, }); }; const getHeadings = async ({ rawMdx }: { rawMdx: string }) => { const slugger = new GithubSlugger(); const regXHeader = /\n(?#{1,6})\s+(?.+)/g; const headings = Array.from(rawMdx.matchAll(regXHeader)).map(({ groups }) => { const flag = groups?.flag; const content = groups?.content; return { level: flag?.length === 1 ? "one" : flag?.length === 2 ? "two" : "three", text: content, slug: content ? slugger.slug(content) : undefined, }; }); return headings; }; const getMoreContent = async ({ contentPath, filepath, }: { contentPath: string; filepath: string; }) => { const moreContent = await fs.readdir(contentPath); const moreContentFiltered = moreContent .filter((path) => /\.mdx?$/.test(path)) .filter((post) => post !== filepath) .slice(0, 2); console.log("-------", moreContentFiltered) return moreContentFiltered; }; export const getAllMDXData = async ({ contentPath, }: { contentPath: string; }) => { const allPosts = await fs.readdir(contentPath); const allPostsFiltered = allPosts.filter((path) => /\.mdx?$/.test(path)); const allPostsData = await Promise.all( allPostsFiltered.map(async (post) => { const rawMdx = await raw({ contentPath, filepath: post.replace(/\.mdx?$/, ""), }); const serializedMdx = await mdxSerialized({ rawMdx }); const frontmatter = serializedMdx.frontmatter as Frontmatter; return { frontmatter, slug: post.replace(/\.mdx$/, ""), }; }), ); return allPostsData; }; export const getContentData = async ({ contentPath, filepath, }: { contentPath: string; filepath: string; }) => { const moreContent = await getMoreContent({ contentPath, filepath }); const moreContentData = await Promise.all( moreContent.map(async (content) => { const rawMdx = await raw({ contentPath, filepath: content.replace(/\.mdx?$/, ""), }); const serializedMdx = await mdxSerialized({ rawMdx }); const frontmatter = serializedMdx.frontmatter as Frontmatter; return { frontmatter, slug: content.replace(/\.mdx$/, ""), }; }), ); return moreContentData; }; export const getPost = async (filepath: string): Promise> => { const rawMdx = await raw({ contentPath: BLOG_PATH, filepath: filepath }); console.log("rawMdx", rawMdx) // Serialize the MDX content and parse the frontmatter const serialized = await mdxSerialized({ rawMdx }); const frontmatter = serialized.frontmatter as Frontmatter; const headings = await getHeadings({ rawMdx }); return { frontmatter, serialized, headings, }; }; export const getPostContent = async (language: string, slug: string): Promise> => { // const rawMdx = await raw({ contentPath: BLOG_PATH, filepath: filepath }); const PageSize = 1 let data: ArticleData = await service.post('/api/v1/news/list', { "id": Number(slug), // "tag": "", // #Blockchain language, "page_no": 0, "page_size": PageSize }, { headers: { // 'Authorization': token } }).then((result: any) => { console.log("result:", result) if (result && result.header.code != 0) { return } return result.data.list[0] }).catch((err) => { console.log(err); }); let rawMdx = data.content // Serialize the MDX content and parse the frontmatter const serialized = await mdxSerialized({ rawMdx }); const frontmatter = serialized.frontmatter as AIFrontmatter; frontmatter.id = String(data.id) frontmatter.p_name = data.p_name frontmatter.title = data.main_title frontmatter.description = data.sub_title || data.main_title frontmatter.date = data.updated_time frontmatter.tags = data.tags frontmatter.image_url = data.image_url frontmatter.logo_url = data.logo_url frontmatter.model_parameter = data.model_parameter // const headings = data.main_title; // 描述:来自sub_title字段,如果没有,则跟main_title一致 // 关键词:来自seo_keywords字段,如果seo_keywords为空,而取keywords字段,如果都为空,则跟首页的关键字一致 // 特殊处理 let text = "" if (data.seo_keywords.length > 0 && typeof data.seo_keywords == "string") { try { const parsedArray = JSON.parse(data.seo_keywords); // 尝试解析字符串为数组 console.log("---parsedArray---", parsedArray) text = Array.isArray(parsedArray) ? parsedArray.join(', ') : "" } catch (error) { console.error('解析失败:seo_keywords', error); // 输出解析失败的错误信息 } } else if (Array.isArray(data.seo_keywords)) { text = data.seo_keywords.join(', ') } if (text.length == 0 && typeof data.keywords == "string") { try { const parsedArray = JSON.parse(data.keywords); text = Array.isArray(parsedArray) ? parsedArray.join(', ') : "" } catch (error) { console.error('解析失败:seo_keywords', data.seo_keywords, error); } } else if (Array.isArray(data.keywords)) { text = data.keywords.join(', ') } console.log('----------rawMdx:', data) return { frontmatter, serialized, headings: { slug: data.main_title, level: "", text: text, }, }; }; export const getQAContent = async (language: string, slug: string): Promise> => { // const rawMdx = await raw({ contentPath: BLOG_PATH, filepath: filepath }); const PageSize = 1 let data: QAData = await service.post('/api/v1/qa/list', { "id": Number(slug), // "tag": "", // #Blockchain language, "page_no": 0, "page_size": PageSize }, { headers: { // 'Authorization': token } }).then((result: any) => { console.log("result:", result) if (result && result.header.code != 0) { return } return result.data.list[0] }).catch((err) => { console.log(err); }); let rawMdx = data.answer // Serialize the MDX content and parse the frontmatter const serialized = await mdxSerialized({ rawMdx }); const frontmatter = serialized.frontmatter as AIFrontmatter; frontmatter.title = data.question frontmatter.description = data.question frontmatter.date = data.updated_time // frontmatter.tags = data.tags // const headings = data.main_title; console.log('----------rawMdx:', data) return { frontmatter, serialized, headings: { slug: data.question, level: "", text: "", }, }; }; export const getChangelog = async (filepath: string): Promise> => { const rawMdx = await raw({ contentPath: CHANGELOG_PATH, filepath: filepath }); // Serialize the MDX content and parse the frontmatter const serialized = await mdxSerialized({ rawMdx }); const frontmatter = serialized.frontmatter as ChangeLogFrontmatter; return { frontmatter, serialized, }; }; export const getPolicy = async (filepath: string): Promise> => { const rawMdx = await raw({ contentPath: POLICY_PATH, filepath: filepath }); // Serialize the MDX content and parse the frontmatter const serialized = await mdxSerialized({ rawMdx }); const frontmatter = serialized.frontmatter as PolicyFrontmatter; return { frontmatter, serialized, }; }; export const getJob = async (filepath: string): Promise> => { const rawMdx = await raw({ contentPath: JOBS_PATH, filepath: filepath }); // Serialize the MDX content and parse the frontmatter const serialized = await mdxSerialized({ rawMdx }); const frontmatter = serialized.frontmatter as JobFrontmatter; return { frontmatter, serialized, }; }; export const getAllJobsData = async ({ contentPath, }: { contentPath: string; }) => { const allJobs = await fs.readdir(contentPath); const allJosFiltered = allJobs.filter((path) => /\.mdx?$/.test(path)); const allPostsData = await Promise.all( allJosFiltered.map(async (post) => { const rawMdx = await raw({ contentPath, filepath: post.replace(/\.mdx?$/, ""), }); const serializedMdx = await mdxSerialized({ rawMdx }); const frontmatter = serializedMdx.frontmatter as JobFrontmatter; if (frontmatter.visible === false) { return; } return { frontmatter, slug: post.replace(/\.mdx$/, ""), }; }), ); return allPostsData; };