This commit is contained in:
1 parent
c1541b80df
commit
b4c7908029
97 files changed
+12403
-154
No files matched your search
@@ -0,0 +1,77 @@
|
||||
import { NextResponse } from "next/server";
|
||||
import { ok, created, withErrorHandling, parseJsonBody } from "@/lib/api/response";
|
||||
import { createPostSchema, listPostsQuerySchema } from "@/lib/validation/post";
|
||||
import { createPost, listPosts } from "@/lib/posts/repository";
|
||||
|
||||
/**
|
||||
* GET /api/posts —— 文章列表
|
||||
*
|
||||
* 查询参数:
|
||||
* - limit 1–100,默认 20
|
||||
* - offset 默认 0
|
||||
* - tag 按标签过滤
|
||||
* - status draft | published(不传则只返回已发布)
|
||||
* - includeDrafts true 时包含草稿(内部/管理用途)
|
||||
*
|
||||
* 响应:{ data: PostMeta[], meta: { total, limit, offset, hasMore } }
|
||||
*/
|
||||
export const GET = withErrorHandling(async (request: Request) => {
|
||||
const { searchParams } = new URL(request.url);
|
||||
|
||||
// 校验查询参数:非法值直接返回 400 + 字段级错误
|
||||
const query = listPostsQuerySchema.parse(
|
||||
Object.fromEntries(searchParams.entries()),
|
||||
);
|
||||
|
||||
const result = await listPosts({
|
||||
limit: query.limit,
|
||||
offset: query.offset,
|
||||
tag: query.tag,
|
||||
includeDrafts: query.includeDrafts,
|
||||
status: query.status,
|
||||
});
|
||||
|
||||
return ok(result.items, {
|
||||
total: result.total,
|
||||
limit: result.limit,
|
||||
offset: result.offset,
|
||||
hasMore: result.hasMore,
|
||||
});
|
||||
});
|
||||
|
||||
/**
|
||||
* POST /api/posts —— 新建文章
|
||||
*
|
||||
* 请求体(JSON):
|
||||
* - slug 必填,^[a-z0-9]+(-[a-z0-9]+)*$
|
||||
* - title 必填
|
||||
* - summary 必填
|
||||
* - content 必填(Markdown / MDX 源码)
|
||||
* - tags 可选,字符串数组
|
||||
* - status draft | published,默认 draft
|
||||
* - publishedAt 可选,ISO 日期;status=published 时缺省为当前时间
|
||||
* - cover 可选
|
||||
* - description 可选,覆盖 SEO 描述
|
||||
*
|
||||
* 响应:201 { data: Post }
|
||||
*/
|
||||
export const POST = withErrorHandling(async (request: Request) => {
|
||||
const body = await parseJsonBody(request);
|
||||
const input = createPostSchema.parse(body);
|
||||
|
||||
// slug 唯一性在应用层先查一次,给出友好的字段级报错;
|
||||
// 数据库唯一索引仍作为最终防线(并发下可能拦截,返回 409)
|
||||
const post = await createPost(input);
|
||||
|
||||
return created(post);
|
||||
});
|
||||
|
||||
/** 其他方法统一返回 405 */
|
||||
export function OPTIONS() {
|
||||
return new NextResponse(null, {
|
||||
status: 204,
|
||||
headers: {
|
||||
Allow: "GET, POST, OPTIONS",
|
||||
},
|
||||
});
|
||||
}
|
||||
Reference in new issue
Block a user