fix: 定时任务修复+管理后台Task7支持 (Task4外键修复, Task7管理员邮箱动态查询, Task5限流防护, Task7加入SCRIPT_MAP)
This commit is contained in:
@@ -0,0 +1,66 @@
|
||||
"use client";
|
||||
|
||||
import { useState } from "react";
|
||||
|
||||
interface ReplyFormProps {
|
||||
topicId: number;
|
||||
isLocked: boolean;
|
||||
}
|
||||
|
||||
export function ReplyForm({ topicId, isLocked }: ReplyFormProps) {
|
||||
const [submitting, setSubmitting] = useState(false);
|
||||
|
||||
if (isLocked) {
|
||||
return (
|
||||
<div className="mt-8 text-center py-6 rounded-xl border border-border/40 bg-muted/20">
|
||||
<p className="text-sm text-muted-foreground">此话题已锁定,无法回复</p>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="mt-8">
|
||||
<form
|
||||
onSubmit={async (e) => {
|
||||
e.preventDefault();
|
||||
setSubmitting(true);
|
||||
const formData = new FormData(e.currentTarget);
|
||||
const content = formData.get("content") as string;
|
||||
if (!content.trim()) {
|
||||
setSubmitting(false);
|
||||
return;
|
||||
}
|
||||
const res = await fetch(`/api/forum/topics/${topicId}/posts`, {
|
||||
method: "POST",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
body: JSON.stringify({ content }),
|
||||
});
|
||||
setSubmitting(false);
|
||||
if (res.ok) {
|
||||
window.location.reload();
|
||||
}
|
||||
}}
|
||||
className="rounded-xl border border-border/50 bg-white p-5 shadow-[0_1px_2px_rgba(0,0,0,0.04)]"
|
||||
>
|
||||
<h3 className="text-sm font-semibold mb-3">发表回复</h3>
|
||||
<textarea
|
||||
name="content"
|
||||
placeholder="写下你的回复..."
|
||||
rows={4}
|
||||
maxLength={5000}
|
||||
className="w-full resize-none rounded-lg border border-border/60 bg-white px-3 py-2 text-sm focus:outline-none focus:border-primary/50 focus:ring-2 focus:ring-primary/15 transition-colors placeholder:text-muted-foreground/40 mb-3"
|
||||
disabled={submitting}
|
||||
/>
|
||||
<div className="flex justify-end">
|
||||
<button
|
||||
type="submit"
|
||||
disabled={submitting}
|
||||
className="rounded-lg bg-primary text-white text-sm px-5 py-2 hover:bg-primary/90 transition-colors shadow-sm shadow-primary/20 disabled:opacity-50"
|
||||
>
|
||||
{submitting ? "提交中..." : "发表回复"}
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -2,6 +2,7 @@ import { prisma } from "@/lib/prisma";
|
||||
import { notFound } from "next/navigation";
|
||||
import { getServerSession } from "next-auth";
|
||||
import { authOptions } from "@/lib/auth";
|
||||
import { ReplyForm } from "./ReplyForm";
|
||||
|
||||
export const dynamic = "force-dynamic";
|
||||
|
||||
@@ -17,7 +18,7 @@ export default async function TopicPage({ params }: Props) {
|
||||
where: { id: topicId },
|
||||
include: {
|
||||
category: { select: { name: true, slug: true, icon: true } },
|
||||
user: { select: { id: true, name: true, email: true, avatarUrl: true, level: true, points: true } },
|
||||
user: { select: { id: true, name: true, email: true, avatarUrl: true, level: true, points: true, isBot: true } },
|
||||
_count: { select: { posts: true } },
|
||||
},
|
||||
});
|
||||
@@ -32,7 +33,7 @@ export default async function TopicPage({ params }: Props) {
|
||||
const posts = await prisma.forumPost.findMany({
|
||||
where: { topicId },
|
||||
include: {
|
||||
user: { select: { id: true, name: true, email: true, avatarUrl: true, level: true, points: true } },
|
||||
user: { select: { id: true, name: true, email: true, avatarUrl: true, level: true, points: true, isBot: true } },
|
||||
},
|
||||
orderBy: { createdAt: "asc" },
|
||||
take: 50,
|
||||
@@ -79,7 +80,7 @@ export default async function TopicPage({ params }: Props) {
|
||||
</div>
|
||||
)}
|
||||
<div>
|
||||
<p className="text-sm font-medium">{topic.user.name || topic.user.email?.split("@")[0]}</p>
|
||||
<p className="text-sm font-medium">{topic.user.name || topic.user.email?.split("@")[0]}{topic.user.isBot && <span className="ml-1.5 px-1.5 py-0.5 text-[9px] font-medium bg-gradient-to-r from-sky-100 to-purple-100 text-sky-700 rounded-full border border-sky-200/50">🤖 AI</span>}</p>
|
||||
<p className="text-xs text-muted-foreground/50">
|
||||
{topic.createdAt?.toLocaleDateString("zh-CN")} {topic.createdAt?.toLocaleTimeString("zh-CN", { hour: "2-digit", minute: "2-digit" })}
|
||||
</p>
|
||||
@@ -113,7 +114,7 @@ export default async function TopicPage({ params }: Props) {
|
||||
</div>
|
||||
)}
|
||||
<div>
|
||||
<p className="text-sm font-medium">{post.user.name || post.user.email?.split("@")[0]}</p>
|
||||
<p className="text-sm font-medium">{post.user.name || post.user.email?.split("@")[0]}{post.user.isBot && <span className="ml-1.5 px-1.5 py-0.5 text-[9px] font-medium bg-gradient-to-r from-sky-100 to-purple-100 text-sky-700 rounded-full border border-sky-200/50">🤖 AI</span>}</p>
|
||||
<p className="text-xs text-muted-foreground/50">
|
||||
{post.createdAt?.toLocaleDateString("zh-CN")} {post.createdAt?.toLocaleTimeString("zh-CN", { hour: "2-digit", minute: "2-digit" })}
|
||||
</p>
|
||||
@@ -133,52 +134,7 @@ export default async function TopicPage({ params }: Props) {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{!topic.isLocked && (
|
||||
<div className="mt-8">
|
||||
<form
|
||||
onSubmit={async (e) => {
|
||||
e.preventDefault();
|
||||
const formData = new FormData(e.currentTarget);
|
||||
const content = formData.get("content") as string;
|
||||
if (!content.trim()) return;
|
||||
|
||||
const res = await fetch(`/api/forum/topics/${topicId}/posts`, {
|
||||
method: "POST",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
body: JSON.stringify({ content }),
|
||||
});
|
||||
|
||||
if (res.ok) {
|
||||
window.location.reload();
|
||||
}
|
||||
}}
|
||||
className="rounded-xl border border-border/50 bg-white p-5 shadow-[0_1px_2px_rgba(0,0,0,0.04)]"
|
||||
>
|
||||
<h3 className="text-sm font-semibold mb-3">发表回复</h3>
|
||||
<textarea
|
||||
name="content"
|
||||
placeholder="写下你的回复..."
|
||||
rows={4}
|
||||
maxLength={5000}
|
||||
className="w-full resize-none rounded-lg border border-border/60 bg-white px-3 py-2 text-sm focus:outline-none focus:border-primary/50 focus:ring-2 focus:ring-primary/15 transition-colors placeholder:text-muted-foreground/40 mb-3"
|
||||
/>
|
||||
<div className="flex justify-end">
|
||||
<button
|
||||
type="submit"
|
||||
className="rounded-lg bg-primary text-white text-sm px-5 py-2 hover:bg-primary/90 transition-colors shadow-sm shadow-primary/20"
|
||||
>
|
||||
发表回复
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{topic.isLocked && (
|
||||
<div className="mt-8 text-center py-6 rounded-xl border border-border/40 bg-muted/20">
|
||||
<p className="text-sm text-muted-foreground">此话题已锁定,无法回复</p>
|
||||
</div>
|
||||
)}
|
||||
<ReplyForm topicId={topicId} isLocked={topic.isLocked} />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user