feat: make the site database authoritative for documents and catalog
Verify database-backed documents / verify (pull_request) Failing after 6m6s
Verify database-backed documents / verify (pull_request) Failing after 6m6s
This commit is contained in:
@@ -0,0 +1,22 @@
|
||||
import type { NextApiRequest, NextApiResponse } from "next";
|
||||
import { readDocument } from "@/lib/docs";
|
||||
|
||||
// The proxy protects Markdown just like the rendered document and search index.
|
||||
export default function handler(request: NextApiRequest, response: NextApiResponse) {
|
||||
response.setHeader("Cache-Control", "private, no-store");
|
||||
if (request.method !== "GET") {
|
||||
response.setHeader("Allow", "GET");
|
||||
return response.status(405).end();
|
||||
}
|
||||
const slug = request.query.slug;
|
||||
if (typeof slug !== "string") return response.status(400).json({ error: "缺少文档地址" });
|
||||
try {
|
||||
const doc = readDocument(slug);
|
||||
if (!doc) return response.status(404).end();
|
||||
response.setHeader("Content-Type", "text/plain; charset=utf-8");
|
||||
response.setHeader("X-Content-Type-Options", "nosniff");
|
||||
return response.status(200).send(doc.content);
|
||||
} catch {
|
||||
return response.status(503).json({ error: "文档数据库暂时不可用" });
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
import type { NextApiRequest, NextApiResponse } from "next";
|
||||
import { listDocs, readDoc } from "@/lib/docs";
|
||||
import { allDocuments } from "@/lib/docs";
|
||||
|
||||
// The proxy authenticates this endpoint, including index content.
|
||||
export default async function handler(request: NextApiRequest, response: NextApiResponse) {
|
||||
@@ -8,9 +8,10 @@ export default async function handler(request: NextApiRequest, response: NextApi
|
||||
response.setHeader("Allow", "GET");
|
||||
return response.status(405).end();
|
||||
}
|
||||
const docs = await listDocs();
|
||||
const index = await Promise.all(docs.map(async (doc) => ({
|
||||
...doc, searchText: (await readDoc(doc.slug)).content,
|
||||
})));
|
||||
response.status(200).json(index);
|
||||
try {
|
||||
const index = allDocuments().map(({ content, ...doc }) => ({ ...doc, searchText: content }));
|
||||
return response.status(200).json(index);
|
||||
} catch {
|
||||
return response.status(503).json({ error: "文档数据库暂时不可用" });
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import type {
|
||||
GetStaticPaths,
|
||||
GetStaticProps,
|
||||
InferGetStaticPropsType,
|
||||
GetServerSideProps,
|
||||
InferGetServerSidePropsType,
|
||||
} from "next";
|
||||
import Head from "next/head";
|
||||
import dynamic from "next/dynamic";
|
||||
@@ -15,7 +14,7 @@ import {
|
||||
} from "react";
|
||||
import ReactMarkdown from "react-markdown";
|
||||
import remarkGfm from "remark-gfm";
|
||||
import { listDocs, readDoc, type Doc, type DocLink } from "@/lib/docs";
|
||||
import { listDocs, readDocument, type Doc, type DocLink } from "@/lib/docs";
|
||||
import { Shell, Navigation } from "@/components/Shell";
|
||||
import { ArchitectureFigure } from "@/components/ArchitectureFigure";
|
||||
import { FigureGallery } from "@/components/FigureGallery";
|
||||
@@ -26,16 +25,17 @@ import { headingsFor } from "@/lib/navigation";
|
||||
|
||||
type Props = { doc: Doc; docs: DocLink[] };
|
||||
const AICompanyResearch = dynamic(() => import("@/components/AICompanyResearch"));
|
||||
export const getStaticPaths: GetStaticPaths = async () => ({
|
||||
paths: (await listDocs()).map(({ slug }) => ({ params: { slug: slug.split("/") } })),
|
||||
fallback: false,
|
||||
});
|
||||
export const getStaticProps: GetStaticProps<Props> = async ({ params }) => {
|
||||
export const getServerSideProps: GetServerSideProps<Props> = async ({ params, res, resolvedUrl }) => {
|
||||
res.setHeader("Cache-Control", "private, no-store");
|
||||
const slug = Array.isArray(params?.slug) ? params.slug.join("/") : params?.slug;
|
||||
if (typeof slug !== "string") return { notFound: true };
|
||||
const docs = await listDocs();
|
||||
if (!docs.some((d) => d.slug === slug)) return { notFound: true };
|
||||
return { props: { doc: await readDoc(slug), docs } };
|
||||
const doc = readDocument(slug);
|
||||
if (!doc) return { notFound: true };
|
||||
if (slug !== doc.slug) {
|
||||
const query = resolvedUrl.includes("?") ? resolvedUrl.slice(resolvedUrl.indexOf("?")) : "";
|
||||
return { redirect: { destination: `/docs/${doc.slug}${query}`, permanent: true } };
|
||||
}
|
||||
return { props: { doc, docs: await listDocs() } };
|
||||
};
|
||||
function textOf(value: ReactNode): string {
|
||||
return Children.toArray(value)
|
||||
@@ -49,7 +49,7 @@ function textOf(value: ReactNode): string {
|
||||
export default function DocPage({
|
||||
doc,
|
||||
docs,
|
||||
}: InferGetStaticPropsType<typeof getStaticProps>) {
|
||||
}: InferGetServerSidePropsType<typeof getServerSideProps>) {
|
||||
const headings = headingsFor(doc.content);
|
||||
const [active, setActive] = useState("");
|
||||
|
||||
@@ -231,9 +231,9 @@ export default function DocPage({
|
||||
<div className="article-end">
|
||||
|
||||
<a
|
||||
href={`https://gitea.entroia.com/Zayden-Jung/endorphin-tabularium-docs/src/branch/main/content/docs/${doc.sourceSlug}.md`}
|
||||
href={`/api/documents/source?slug=${encodeURIComponent(doc.slug)}`}
|
||||
>
|
||||
查看文档源码 ↗
|
||||
查看 Markdown 原文 ↗
|
||||
</a>
|
||||
</div>
|
||||
<nav className="pagination" aria-label="相邻文档">
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import type { GetStaticProps, InferGetStaticPropsType } from "next";
|
||||
import type { GetServerSideProps, InferGetServerSidePropsType } from "next";
|
||||
import Head from "next/head";
|
||||
import Link from "next/link";
|
||||
import { useState } from "react";
|
||||
@@ -7,11 +7,12 @@ import { Shell, Navigation } from "@/components/Shell";
|
||||
import { Icon } from "@/components/Icon";
|
||||
import { collectionEntries, collectionIcon, collectionLabel, collectionTone } from "@/lib/collections";
|
||||
|
||||
export const getStaticProps: GetStaticProps<{ docs: DocLink[] }> = async () => ({
|
||||
props: { docs: await listDocs() },
|
||||
});
|
||||
export const getServerSideProps: GetServerSideProps<{ docs: DocLink[] }> = async ({ res }) => {
|
||||
res.setHeader("Cache-Control", "private, no-store");
|
||||
return { props: { docs: await listDocs() } };
|
||||
};
|
||||
const PAGE_SIZE = 8;
|
||||
export default function Home({ docs }: InferGetStaticPropsType<typeof getStaticProps>) {
|
||||
export default function Home({ docs }: InferGetServerSidePropsType<typeof getServerSideProps>) {
|
||||
const [collection, setCollection] = useState("");
|
||||
const [query, setQuery] = useState("");
|
||||
const [page, setPage] = useState(0);
|
||||
|
||||
Reference in New Issue
Block a user