Initial reader-api backend extracted from reader
This commit is contained in:
@@ -0,0 +1,200 @@
|
||||
import { NextResponse } from "next/server"
|
||||
import { getServerSession } from "next-auth/next"
|
||||
import { authOptions } from "@/lib/auth"
|
||||
import { prisma } from "@/lib/prisma"
|
||||
import { slugify } from "@/lib/utils"
|
||||
|
||||
function normalizeText(value: any): string {
|
||||
return typeof value === "string" ? value.trim() : ""
|
||||
}
|
||||
|
||||
async function resolveEditableSeries(
|
||||
id: string,
|
||||
session: { user: { role: "USER" | "MOD" | "ADMIN"; id: string } }
|
||||
) {
|
||||
return prisma.series.findFirst({
|
||||
where: session.user.role === "ADMIN"
|
||||
? { id }
|
||||
: {
|
||||
id,
|
||||
OR: [
|
||||
{ novels: { some: { uploaderId: session.user.id } } },
|
||||
{ novels: { some: { uploaderId: null } } },
|
||||
{ novels: { none: {} } },
|
||||
],
|
||||
},
|
||||
select: { id: true },
|
||||
})
|
||||
}
|
||||
|
||||
export async function GET() {
|
||||
const session = await getServerSession(authOptions)
|
||||
if (!session || (session.user.role !== "MOD" && session.user.role !== "ADMIN")) {
|
||||
return NextResponse.json({ error: "Unauthorized" }, { status: 401 })
|
||||
}
|
||||
|
||||
try {
|
||||
const series = await prisma.series.findMany({
|
||||
where: session.user.role === "ADMIN"
|
||||
? undefined
|
||||
: {
|
||||
OR: [
|
||||
{ novels: { some: { uploaderId: session.user.id } } },
|
||||
{ novels: { some: { uploaderId: null } } },
|
||||
{ novels: { none: {} } },
|
||||
],
|
||||
},
|
||||
orderBy: { updatedAt: "desc" },
|
||||
select: {
|
||||
id: true,
|
||||
name: true,
|
||||
slug: true,
|
||||
description: true,
|
||||
_count: { select: { novels: true } },
|
||||
},
|
||||
})
|
||||
|
||||
return NextResponse.json(series)
|
||||
} catch {
|
||||
return NextResponse.json({ error: "Failed to fetch series" }, { status: 500 })
|
||||
}
|
||||
}
|
||||
|
||||
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 name = normalizeText(body?.name)
|
||||
const description = normalizeText(body?.description)
|
||||
|
||||
if (!name) {
|
||||
return NextResponse.json({ error: "Tên series không được để trống" }, { status: 400 })
|
||||
}
|
||||
|
||||
const existing = await prisma.series.findFirst({
|
||||
where: { name: { equals: name, mode: "insensitive" } },
|
||||
select: { id: true, name: true, slug: true, description: true },
|
||||
})
|
||||
|
||||
if (existing) {
|
||||
return NextResponse.json(existing)
|
||||
}
|
||||
|
||||
const baseSlug = slugify(name)
|
||||
let slug = baseSlug
|
||||
let counter = 1
|
||||
|
||||
while (await prisma.series.findUnique({ where: { slug } })) {
|
||||
slug = `${baseSlug}-${counter}`
|
||||
counter += 1
|
||||
}
|
||||
|
||||
const created = await prisma.series.create({
|
||||
data: { name, slug, description: description || null },
|
||||
select: { id: true, name: true, slug: true, description: true },
|
||||
})
|
||||
|
||||
return NextResponse.json(created, { status: 201 })
|
||||
} catch {
|
||||
return NextResponse.json({ error: "Failed to create series" }, { status: 500 })
|
||||
}
|
||||
}
|
||||
|
||||
export async function PUT(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 id = normalizeText(body?.id)
|
||||
const name = normalizeText(body?.name)
|
||||
const description = normalizeText(body?.description)
|
||||
|
||||
if (!id || !name) {
|
||||
return NextResponse.json({ error: "Thiếu thông tin series" }, { status: 400 })
|
||||
}
|
||||
|
||||
const target = await resolveEditableSeries(id, session as any)
|
||||
if (!target) {
|
||||
return NextResponse.json({ error: "Không tìm thấy series hoặc không đủ quyền" }, { status: 404 })
|
||||
}
|
||||
|
||||
const duplicated = await prisma.series.findFirst({
|
||||
where: {
|
||||
id: { not: id },
|
||||
name: { equals: name, mode: "insensitive" },
|
||||
},
|
||||
select: { id: true },
|
||||
})
|
||||
|
||||
if (duplicated) {
|
||||
return NextResponse.json({ error: "Tên series đã tồn tại" }, { status: 409 })
|
||||
}
|
||||
|
||||
const baseSlug = slugify(name)
|
||||
let slug = baseSlug
|
||||
let counter = 1
|
||||
|
||||
while (await prisma.series.findFirst({ where: { slug, id: { not: id } }, select: { id: true } })) {
|
||||
slug = `${baseSlug}-${counter}`
|
||||
counter += 1
|
||||
}
|
||||
|
||||
const updated = await prisma.series.update({
|
||||
where: { id },
|
||||
data: {
|
||||
name,
|
||||
slug,
|
||||
description: description || null,
|
||||
},
|
||||
select: {
|
||||
id: true,
|
||||
name: true,
|
||||
slug: true,
|
||||
description: true,
|
||||
_count: { select: { novels: true } },
|
||||
},
|
||||
})
|
||||
|
||||
return NextResponse.json(updated)
|
||||
} catch {
|
||||
return NextResponse.json({ error: "Failed to update series" }, { status: 500 })
|
||||
}
|
||||
}
|
||||
|
||||
export async function DELETE(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 url = new URL(req.url)
|
||||
const id = normalizeText(url.searchParams.get("id"))
|
||||
|
||||
if (!id) {
|
||||
return NextResponse.json({ error: "Thiếu id series" }, { status: 400 })
|
||||
}
|
||||
|
||||
const target = await resolveEditableSeries(id, session as any)
|
||||
if (!target) {
|
||||
return NextResponse.json({ error: "Không tìm thấy series hoặc không đủ quyền" }, { status: 404 })
|
||||
}
|
||||
|
||||
const usedCount = await prisma.novel.count({ where: { seriesId: id } })
|
||||
if (usedCount > 0) {
|
||||
return NextResponse.json({ error: "Series đang chứa truyện, không thể xóa" }, { status: 409 })
|
||||
}
|
||||
|
||||
await prisma.series.delete({ where: { id } })
|
||||
return NextResponse.json({ success: true })
|
||||
} catch {
|
||||
return NextResponse.json({ error: "Failed to delete series" }, { status: 500 })
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user