fix: 修复后台文章计数为0 + 分类编辑补全描述字段 + CSP放行外部图片 + 更新关于页描述

- 文章管理:计数请求补 page=1 参数,命中分页接口返回正确的 total
- 分类管理:编辑模式新增描述输入框,保存时一并提交 description
- CSP:img-src 加入 https: 允许加载外部图片
- 关于页:数据存储描述从 JSON 文件更正为 SQLite 数据库
- Footer:添加 ICP 备案号
This commit is contained in:
胡旭
2026-06-24 13:51:48 +08:00
parent 3707eddfd4
commit 18e915bcbb
69 changed files with 6818 additions and 1422 deletions
+10 -15
View File
@@ -1,21 +1,16 @@
import { NextRequest, NextResponse } from "next/server";
import { cookies } from "next/headers";
import { getPost, updatePost } from "@/lib/store";
async function checkAuth(): Promise<boolean> {
const cookieStore = await cookies();
return cookieStore.get("admin_session")?.value === "authenticated";
}
import { requireAuth, parseBody } from "@/lib/http";
import { updatePostSchema } from "@/lib/validation";
export async function GET(
_request: NextRequest,
{ params }: { params: Promise<{ id: string }> }
) {
if (!(await checkAuth())) {
return NextResponse.json({ error: "未授权" }, { status: 401 });
}
const deny = await requireAuth();
if (deny) return deny;
const { id } = await params;
const post = getPost(id);
const post = await getPost(id);
if (!post) return NextResponse.json({ error: "未找到" }, { status: 404 });
return NextResponse.json(post);
}
@@ -24,12 +19,12 @@ export async function PUT(
request: NextRequest,
{ params }: { params: Promise<{ id: string }> }
) {
if (!(await checkAuth())) {
return NextResponse.json({ error: "未授权" }, { status: 401 });
}
const deny = await requireAuth();
if (deny) return deny;
const { id } = await params;
const body = await request.json();
const post = updatePost(id, body);
const parsed = await parseBody(request, updatePostSchema);
if (!parsed.ok) return parsed.response;
const post = await updatePost(id, parsed.data);
if (!post) return NextResponse.json({ error: "未找到" }, { status: 404 });
return NextResponse.json(post);
}
+30 -21
View File
@@ -1,36 +1,45 @@
import { NextRequest, NextResponse } from "next/server";
import { cookies } from "next/headers";
import { getPosts, createPost, deletePost } from "@/lib/store";
import { getPosts, getPostsPaginated, createPost, deletePost } from "@/lib/store";
import { requireAuth, parseBody } from "@/lib/http";
import { createPostSchema } from "@/lib/validation";
async function checkAuth(): Promise<boolean> {
const cookieStore = await cookies();
return cookieStore.get("admin_session")?.value === "authenticated";
}
export async function GET(request: NextRequest) {
const deny = await requireAuth();
if (deny) return deny;
export async function GET() {
if (!(await checkAuth())) {
return NextResponse.json({ error: "未授权" }, { status: 401 });
const { searchParams } = new URL(request.url);
// 如果有分页参数,使用分页查询
if (searchParams.has("page") || searchParams.has("search") || searchParams.has("status") || searchParams.has("sortBy")) {
const result = await getPostsPaginated({
page: Number(searchParams.get("page")) || 1,
pageSize: Number(searchParams.get("pageSize")) || 20,
status: searchParams.get("status") as "draft" | "published" | undefined,
search: searchParams.get("search") || undefined,
sortBy: (searchParams.get("sortBy") as "date" | "createdAt" | "title" | "readingTime") || "createdAt",
sortDir: (searchParams.get("sortDir") as "asc" | "desc") || "desc",
});
return NextResponse.json(result);
}
const posts = getPosts();
return NextResponse.json(posts);
// 兼容旧接口:无参数时返回全量
return NextResponse.json(await getPosts());
}
export async function POST(request: NextRequest) {
if (!(await checkAuth())) {
return NextResponse.json({ error: "未授权" }, { status: 401 });
}
const body = await request.json();
const post = createPost(body);
const deny = await requireAuth();
if (deny) return deny;
const parsed = await parseBody(request, createPostSchema);
if (!parsed.ok) return parsed.response;
const post = await createPost(parsed.data);
return NextResponse.json(post, { status: 201 });
}
export async function DELETE(request: NextRequest) {
if (!(await checkAuth())) {
return NextResponse.json({ error: "未授权" }, { status: 401 });
}
const deny = await requireAuth();
if (deny) return deny;
const { searchParams } = new URL(request.url);
const id = searchParams.get("id");
if (!id) return NextResponse.json({ error: "缺少 id" }, { status: 400 });
const ok = deletePost(id);
return NextResponse.json({ ok });
return NextResponse.json({ ok: await deletePost(id) });
}