18e915bcbb
- 文章管理:计数请求补 page=1 参数,命中分页接口返回正确的 total - 分类管理:编辑模式新增描述输入框,保存时一并提交 description - CSP:img-src 加入 https: 允许加载外部图片 - 关于页:数据存储描述从 JSON 文件更正为 SQLite 数据库 - Footer:添加 ICP 备案号
41 lines
1.2 KiB
TypeScript
41 lines
1.2 KiB
TypeScript
import { NextResponse } from "next/server";
|
|
import { ZodError } from "zod";
|
|
import { checkAuth } from "./auth";
|
|
|
|
/** 统一的未授权响应。 */
|
|
export function unauthorized() {
|
|
return NextResponse.json({ error: "未授权" }, { status: 401 });
|
|
}
|
|
|
|
/** 解析并校验请求体,失败时返回 422 响应(由调用方 return)。 */
|
|
export async function parseBody<T>(
|
|
request: Request,
|
|
schema: { parse: (d: unknown) => T }
|
|
): Promise<{ ok: true; data: T } | { ok: false; response: NextResponse }> {
|
|
try {
|
|
const json = await request.json();
|
|
const data = schema.parse(json);
|
|
return { ok: true, data };
|
|
} catch (err) {
|
|
if (err instanceof ZodError) {
|
|
return {
|
|
ok: false,
|
|
response: NextResponse.json(
|
|
{ error: "输入校验失败", issues: err.issues.map((i) => i.message) },
|
|
{ status: 422 }
|
|
),
|
|
};
|
|
}
|
|
return {
|
|
ok: false,
|
|
response: NextResponse.json({ error: "请求格式错误" }, { status: 400 }),
|
|
};
|
|
}
|
|
}
|
|
|
|
/** 在受保护路由开头做鉴权,未通过则返回 401 响应。 */
|
|
export async function requireAuth(): Promise<NextResponse | null> {
|
|
if (!(await checkAuth())) return unauthorized();
|
|
return null;
|
|
}
|