Rename the unified application to dashboard

This commit is contained in:
2026-11-04 23:01:32 +08:00
parent 4ec83f2603
commit 61d80f352f
223 changed files with 42 additions and 42 deletions
@@ -0,0 +1,126 @@
import { ZodError } from "zod";
import type { NextApiRequest, NextApiResponse } from "next";
import { currentAccount } from "@/lib/account";
import { trustedOrigin } from "@/lib/account-crypto";
import { documentInput, parseDocumentXml } from "@/lib/guidance-input";
import {
discoverDocuments,
publishDocument,
resolveDocument,
withdrawDocument,
} from "@/lib/guidance-store";
import { guidanceXml } from "@/content/guides/catalog";
export default async function handler(
req: NextApiRequest,
res: NextApiResponse,
) {
res.setHeader("Cache-Control", "no-store");
const path = req.query.path || [];
if (!Array.isArray(path) || path.length > 1) return res.status(404).end();
const id = path[0];
try {
if (req.method === "GET") {
if (!id) {
const author = req.query.author;
if (
author !== undefined &&
(typeof author !== "string" ||
!/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(
author,
))
)
return res.status(400).json({ error: "作者 ID 无效" });
return res.json({ documents: await discoverDocuments(author) });
}
const revision = req.query.revision;
if (
revision !== undefined &&
(typeof revision !== "string" ||
!/^[1-9][0-9]{0,9}$/.test(revision) ||
Number(revision) > 2147483647)
)
return res.status(400).json({ error: "版本无效" });
const doc = await resolveDocument(
id,
revision === undefined ? undefined : Number(revision),
);
if (!doc) return res.status(404).json({ error: "文档或精确版本不存在" });
if (req.query.format === "xml") {
res.setHeader("Content-Type", "application/xml; charset=utf-8");
return res.send(guidanceXml(doc));
}
return res.json({ document: doc });
}
if (!["POST", "DELETE"].includes(req.method || "")) {
res.setHeader("Allow", "GET, POST, DELETE");
return res.status(405).end();
}
const request = new Request(
new URL(
req.url || "/",
process.env.ACCOUNT_PUBLIC_ORIGIN || "http://" + req.headers.host,
),
{
headers: new Headers(
Object.entries(req.headers).filter(
([, v]) => typeof v === "string",
) as [string, string][],
),
},
);
if (!trustedOrigin(request))
return res.status(403).json({ error: "请求来源不受信任" });
const user = await currentAccount(request);
if (!user) return res.status(401).json({ error: "请先登录已验证邮箱账户" });
if (req.method === "DELETE")
return id && (await withdrawDocument(user.id, id))
? res.json({ ok: true })
: res.status(404).json({ error: "无权删除或不存在" });
const input =
typeof req.body === "string"
? parseDocumentXml(req.body)
: documentInput.parse(req.body);
if (id === "preview") {
const links = await Promise.all(
input.links.map(async (l) => ({
...l,
resolved: !!(await resolveDocument(l.target, l.revision)),
})),
);
return res.json({ document: input, xml: guidanceXml(input), links });
}
if (id && id !== input.id)
return res.status(400).json({ error: "文档 ID 不匹配" });
return res
.status(201)
.json({ document: await publishDocument(user.id, input) });
} catch (e) {
if (e instanceof ZodError)
return res
.status(400)
.json({
error: "输入不符合协议,请检查以下字段",
fields: e.issues.slice(0, 12).map((i) => i.path.join(".") || "文档"),
});
const message = e instanceof Error ? e.message : "";
if (message === "INVALID_ID")
return res
.status(400)
.json({
error: "文档 ID 必须以 user-你的账户ID- 开头,请使用编辑器生成的前缀",
});
if (message === "FORBIDDEN")
return res.status(403).json({ error: "仅作者可编辑" });
if (message === "CONFLICT" || (e as { code?: string }).code === "23505")
return res
.status(409)
.json({ error: "版本冲突:请加载最新版本,已发布版本不可修改" });
if (
e instanceof Error &&
(e.name === "ZodError" || /XML|实体|元素|属性|协议|链接/.test(message))
)
return res.status(400).json({ error: "输入不符合协议" });
return res.status(503).json({ error: "文档服务暂不可用" });
}
}
export const config = { api: { bodyParser: { sizeLimit: "128kb" } } };