122 lines
4.8 KiB
TypeScript
122 lines
4.8 KiB
TypeScript
import { NextResponse } from "next/server"
|
|
import { getServerSession } from "next-auth/next"
|
|
import { authOptions } from "@/lib/auth"
|
|
import connectToMongoDB from "@/lib/mongoose"
|
|
import { Chapter } from "@/lib/models/chapter"
|
|
import { prisma } from "@/lib/prisma"
|
|
|
|
export async function POST(req: Request) {
|
|
const session = await getServerSession(authOptions)
|
|
if (!session || (session.user.role !== "MOD" && session.user.role !== "ADMIN")) {
|
|
return NextResponse.json({ error: "Unauthorized" }, { status: 401 })
|
|
}
|
|
|
|
try {
|
|
const body = await req.json()
|
|
const { novelId, action = "replace", findText, replaceText, matchCase = false, trashWords = "", preview = false } = body
|
|
|
|
if (!novelId) {
|
|
return NextResponse.json({ error: "novelId is required" }, { status: 400 })
|
|
}
|
|
|
|
// Verify that the novel belongs to the uploader
|
|
let novelQuery: any = { id: novelId }
|
|
if (session.user.role !== "ADMIN") {
|
|
novelQuery.uploaderId = session.user.id
|
|
}
|
|
|
|
const novel = await prisma.novel.findFirst({
|
|
where: novelQuery,
|
|
})
|
|
|
|
if (!novel) {
|
|
return NextResponse.json({ error: "Truyện không tồn tại hoặc không đủ quyền" }, { status: 403 })
|
|
}
|
|
|
|
await connectToMongoDB()
|
|
|
|
let patterns: { regex: RegExp, replaceWith: string }[] = []
|
|
|
|
if (action === "replace") {
|
|
if (!findText) return NextResponse.json({ error: "findText is required for replace action" }, { status: 400 })
|
|
const flags = matchCase ? "g" : "gi"
|
|
const safeFindText = findText.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')
|
|
patterns.push({ regex: new RegExp(safeFindText, flags), replaceWith: replaceText || "" })
|
|
} else if (action === "trash") {
|
|
let words: string[] = []
|
|
if (Array.isArray(trashWords)) {
|
|
words = trashWords
|
|
} else if (typeof trashWords === "string") {
|
|
words = trashWords.split(',').map((w: string) => w.trim()).filter((w: string) => w.length > 0)
|
|
}
|
|
|
|
if (words.length === 0) return NextResponse.json({ error: "No valid words provided" }, { status: 400 })
|
|
|
|
words.forEach((word: string) => {
|
|
const safeWord = word.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')
|
|
patterns.push({ regex: new RegExp(safeWord, 'gi'), replaceWith: "" })
|
|
})
|
|
} else {
|
|
return NextResponse.json({ error: "Invalid action" }, { status: 400 })
|
|
}
|
|
|
|
// Find all chapters for the novel
|
|
const chapters = await Chapter.find({ novelId }).sort({ number: 1 })
|
|
let updatedCount = 0
|
|
let previewResults: any[] = []
|
|
|
|
for (const chap of chapters) {
|
|
let originalContent = chap.content || ""
|
|
let newContent = originalContent
|
|
let modified = false
|
|
|
|
patterns.forEach(({ regex, replaceWith }) => {
|
|
if (regex.test(newContent)) {
|
|
modified = true
|
|
newContent = newContent.replace(regex, replaceWith)
|
|
}
|
|
})
|
|
|
|
if (modified) {
|
|
if (preview && previewResults.length < 5) { // Limit previews to 5 chapters to save payload size
|
|
// Capture a small text snippet from the first pattern match
|
|
let snippet = ""
|
|
if (patterns.length > 0) {
|
|
const match = patterns[0].regex.exec(originalContent)
|
|
if (match) {
|
|
const matchIndex = match.index
|
|
const start = Math.max(0, matchIndex - 30)
|
|
const end = Math.min(originalContent.length, matchIndex + match[0].length + 30)
|
|
snippet = "..." + originalContent.substring(start, end).replace(/\n/g, ' ') + "..."
|
|
}
|
|
}
|
|
|
|
previewResults.push({
|
|
chapterId: chap._id,
|
|
number: chap.number,
|
|
title: chap.title,
|
|
snippet
|
|
})
|
|
}
|
|
|
|
if (!preview) {
|
|
chap.content = newContent
|
|
await chap.save()
|
|
}
|
|
|
|
updatedCount++
|
|
}
|
|
}
|
|
|
|
return NextResponse.json({
|
|
message: preview ? "Preview generated" : "Success",
|
|
updatedChapters: updatedCount,
|
|
previews: previewResults
|
|
}, { status: 200 })
|
|
|
|
} catch (error) {
|
|
console.error("Global Replace Error:", error)
|
|
return NextResponse.json({ error: "Failed to perform global replacement" }, { status: 500 })
|
|
}
|
|
}
|