This commit is contained in:
1 parent
c1541b80df
commit
b4c7908029
97 files changed
+12403
-154
No files matched your search
@@ -0,0 +1,101 @@
|
||||
import { NextResponse } from "next/server";
|
||||
import {
|
||||
ok,
|
||||
noContent,
|
||||
withErrorHandling,
|
||||
parseJsonBody,
|
||||
ApiError,
|
||||
} from "@/lib/api/response";
|
||||
import { updatePostSchema } from "@/lib/validation/post";
|
||||
import {
|
||||
getPostBySlug,
|
||||
getPostBySlugIncludingDrafts,
|
||||
updatePost,
|
||||
deletePost,
|
||||
assertSlugAvailable,
|
||||
} from "@/lib/posts/repository";
|
||||
|
||||
/**
|
||||
* /api/posts/[slug] —— 单篇文章的读取、更新与删除
|
||||
*
|
||||
* 注意:GET 默认只返回已发布文章;传入 ?includeDrafts=true 可读到草稿,
|
||||
* 供后续接入后台管理时使用(v0.1 尚无鉴权,接入后台前应加上权限校验)。
|
||||
*/
|
||||
|
||||
type RouteContext = { params: Promise<{ slug: string }> };
|
||||
|
||||
/** GET /api/posts/[slug] —— 读取单篇文章(含正文) */
|
||||
export const GET = withErrorHandling(
|
||||
async (request: Request, context: RouteContext) => {
|
||||
const { slug } = await context.params;
|
||||
const { searchParams } = new URL(request.url);
|
||||
const includeDrafts = searchParams.get("includeDrafts") === "true";
|
||||
|
||||
const post = includeDrafts
|
||||
? await getPostBySlugIncludingDrafts(slug)
|
||||
: await getPostBySlug(slug);
|
||||
|
||||
if (!post) {
|
||||
throw ApiError.notFound(`文章 "${slug}" 不存在或尚未发布`);
|
||||
}
|
||||
|
||||
return ok(post);
|
||||
},
|
||||
);
|
||||
|
||||
/**
|
||||
* PATCH /api/posts/[slug] —— 更新文章(部分更新)
|
||||
*
|
||||
* 支持更新 slug 本身;此时会校验新 slug 未被占用。
|
||||
*/
|
||||
export const PATCH = withErrorHandling(
|
||||
async (request: Request, context: RouteContext) => {
|
||||
const { slug } = await context.params;
|
||||
const body = await parseJsonBody(request);
|
||||
const input = updatePostSchema.parse(body);
|
||||
|
||||
// 目标文章必须存在(含草稿,便于把草稿改为已发布)
|
||||
const existing = await getPostBySlugIncludingDrafts(slug);
|
||||
if (!existing) {
|
||||
throw ApiError.notFound(`文章 "${slug}" 不存在`);
|
||||
}
|
||||
|
||||
// 改 slug 时需要判重(排除自身)
|
||||
if (input.slug && input.slug !== slug) {
|
||||
await assertSlugAvailable(input.slug, slug);
|
||||
}
|
||||
|
||||
const updated = await updatePost(slug, input);
|
||||
if (!updated) {
|
||||
throw ApiError.notFound(`文章 "${slug}" 不存在`);
|
||||
}
|
||||
|
||||
return ok(updated);
|
||||
},
|
||||
);
|
||||
|
||||
/** PUT 与 PATCH 同义,便于不支持 PATCH 的客户端使用 */
|
||||
export const PUT = PATCH;
|
||||
|
||||
/** DELETE /api/posts/[slug] —— 删除文章 */
|
||||
export const DELETE = withErrorHandling(
|
||||
async (_request: Request, context: RouteContext) => {
|
||||
const { slug } = await context.params;
|
||||
|
||||
const removed = await deletePost(slug);
|
||||
if (!removed) {
|
||||
throw ApiError.notFound(`文章 "${slug}" 不存在`);
|
||||
}
|
||||
|
||||
// 204 No Content:删除成功且无响应体
|
||||
return noContent();
|
||||
},
|
||||
);
|
||||
|
||||
/** 列出该资源支持的方法 */
|
||||
export function OPTIONS() {
|
||||
return new NextResponse(null, {
|
||||
status: 204,
|
||||
headers: { Allow: "GET, PATCH, PUT, DELETE, OPTIONS" },
|
||||
});
|
||||
}
|
||||
Reference in new issue
Block a user