Add admin pinning and user favorites with role management
This commit is contained in:
60
app/api/posts/[slug]/favorite/route.ts
Normal file
60
app/api/posts/[slug]/favorite/route.ts
Normal file
@@ -0,0 +1,60 @@
|
||||
import { NextRequest, NextResponse } from "next/server";
|
||||
import { cookieName, verifySession } from "@/lib/auth";
|
||||
import { getDb } from "@/lib/mongo";
|
||||
|
||||
async function getSessionFromRequest(req: NextRequest) {
|
||||
const token = req.cookies.get(cookieName)?.value;
|
||||
return verifySession(token);
|
||||
}
|
||||
|
||||
async function countFavorites(postSlug: string) {
|
||||
const db = await getDb();
|
||||
return db.collection("favorites").countDocuments({ postSlug });
|
||||
}
|
||||
|
||||
export async function POST(req: NextRequest, { params }: { params: { slug: string } }) {
|
||||
const session = await getSessionFromRequest(req);
|
||||
if (!session?.uid) {
|
||||
return NextResponse.json({ error: "请先登录后再收藏" }, { status: 401 });
|
||||
}
|
||||
|
||||
const db = await getDb();
|
||||
const post = await db.collection("posts").findOne({ slug: params.slug }, { projection: { _id: 1 } });
|
||||
if (!post) {
|
||||
return NextResponse.json({ error: "内容不存在" }, { status: 404 });
|
||||
}
|
||||
|
||||
await db.collection("favorites").updateOne(
|
||||
{ ownerId: session.uid, postSlug: params.slug },
|
||||
{
|
||||
$setOnInsert: {
|
||||
ownerId: session.uid,
|
||||
postSlug: params.slug,
|
||||
createdAt: new Date().toISOString()
|
||||
}
|
||||
},
|
||||
{ upsert: true }
|
||||
);
|
||||
|
||||
return NextResponse.json({
|
||||
ok: true,
|
||||
isFavorited: true,
|
||||
favoriteCount: await countFavorites(params.slug)
|
||||
});
|
||||
}
|
||||
|
||||
export async function DELETE(req: NextRequest, { params }: { params: { slug: string } }) {
|
||||
const session = await getSessionFromRequest(req);
|
||||
if (!session?.uid) {
|
||||
return NextResponse.json({ error: "请先登录后再取消收藏" }, { status: 401 });
|
||||
}
|
||||
|
||||
const db = await getDb();
|
||||
await db.collection("favorites").deleteOne({ ownerId: session.uid, postSlug: params.slug });
|
||||
|
||||
return NextResponse.json({
|
||||
ok: true,
|
||||
isFavorited: false,
|
||||
favoriteCount: await countFavorites(params.slug)
|
||||
});
|
||||
}
|
||||
55
app/api/posts/[slug]/pin/route.ts
Normal file
55
app/api/posts/[slug]/pin/route.ts
Normal file
@@ -0,0 +1,55 @@
|
||||
import { NextRequest, NextResponse } from "next/server";
|
||||
import { cookieName, verifySession } from "@/lib/auth";
|
||||
import { getDb } from "@/lib/mongo";
|
||||
import { canPinPost } from "@/lib/posts";
|
||||
|
||||
async function getSessionFromRequest(req: NextRequest) {
|
||||
const token = req.cookies.get(cookieName)?.value;
|
||||
return verifySession(token);
|
||||
}
|
||||
|
||||
async function getPost(slug: string) {
|
||||
const db = await getDb();
|
||||
const post = await db.collection("posts").findOne({ slug });
|
||||
return { db, post };
|
||||
}
|
||||
|
||||
export async function POST(req: NextRequest, { params }: { params: { slug: string } }) {
|
||||
const session = await getSessionFromRequest(req);
|
||||
const { db, post } = await getPost(params.slug);
|
||||
|
||||
if (!post) {
|
||||
return NextResponse.json({ error: "内容不存在" }, { status: 404 });
|
||||
}
|
||||
if (!canPinPost(post, session)) {
|
||||
return NextResponse.json({ error: "只有管理员可以置顶内容" }, { status: 403 });
|
||||
}
|
||||
|
||||
const now = new Date().toISOString();
|
||||
await db.collection("posts").updateOne(
|
||||
{ _id: post._id },
|
||||
{ $set: { isPinned: true, pinnedAt: now, updatedAt: now } }
|
||||
);
|
||||
|
||||
return NextResponse.json({ ok: true, isPinned: true, pinnedAt: now });
|
||||
}
|
||||
|
||||
export async function DELETE(req: NextRequest, { params }: { params: { slug: string } }) {
|
||||
const session = await getSessionFromRequest(req);
|
||||
const { db, post } = await getPost(params.slug);
|
||||
|
||||
if (!post) {
|
||||
return NextResponse.json({ error: "内容不存在" }, { status: 404 });
|
||||
}
|
||||
if (!canPinPost(post, session)) {
|
||||
return NextResponse.json({ error: "只有管理员可以取消置顶" }, { status: 403 });
|
||||
}
|
||||
|
||||
const now = new Date().toISOString();
|
||||
await db.collection("posts").updateOne(
|
||||
{ _id: post._id },
|
||||
{ $set: { isPinned: false, updatedAt: now }, $unset: { pinnedAt: "" } }
|
||||
);
|
||||
|
||||
return NextResponse.json({ ok: true, isPinned: false });
|
||||
}
|
||||
@@ -84,5 +84,6 @@ export async function DELETE(req: NextRequest, { params }: { params: { slug: str
|
||||
}
|
||||
|
||||
await db.collection("posts").deleteOne({ _id: existingPost._id });
|
||||
await db.collection("favorites").deleteMany({ postSlug: params.slug });
|
||||
return NextResponse.json({ ok: true });
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ import { z } from "zod";
|
||||
import { cookieName, verifySession } from "@/lib/auth";
|
||||
import { getDb } from "@/lib/mongo";
|
||||
import { DEFAULT_OPC_SIGNAL, OPC_SIGNAL_VALUES } from "@/lib/opc";
|
||||
import { buildPinnedSort, serializePost } from "@/lib/posts";
|
||||
import { generateSlug } from "@/lib/slug";
|
||||
import { findUserById, getEffectiveDailyPostLimit, getShanghaiDayRange } from "@/lib/users";
|
||||
|
||||
@@ -19,17 +20,11 @@ export async function GET() {
|
||||
const posts = await db
|
||||
.collection("posts")
|
||||
.find({}, { projection: { markdown: 0 } })
|
||||
.sort({ createdAt: -1 })
|
||||
.sort(buildPinnedSort())
|
||||
.limit(50)
|
||||
.toArray();
|
||||
|
||||
return NextResponse.json(
|
||||
posts.map((post) => ({
|
||||
...post,
|
||||
author: post.author ?? "匿名",
|
||||
_id: post._id?.toString()
|
||||
}))
|
||||
);
|
||||
return NextResponse.json(posts.map((post) => serializePost(post)));
|
||||
}
|
||||
|
||||
export async function POST(req: NextRequest) {
|
||||
@@ -85,7 +80,8 @@ export async function POST(req: NextRequest) {
|
||||
slug,
|
||||
createdAt: now,
|
||||
updatedAt: now,
|
||||
views: 0
|
||||
views: 0,
|
||||
isPinned: false
|
||||
});
|
||||
|
||||
return NextResponse.json({ ok: true, slug, todayCount: todayCount + 1, limit });
|
||||
|
||||
Reference in New Issue
Block a user