api route 구성 1
This commit is contained in:
14
src/app/api/posts/[id]/approve/route.ts
Normal file
14
src/app/api/posts/[id]/approve/route.ts
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
import { NextResponse } from "next/server";
|
||||||
|
import prisma from "@/lib/prisma";
|
||||||
|
|
||||||
|
export async function POST(_: Request, context: { params: Promise<{ id: string }> }) {
|
||||||
|
const { id } = await context.params;
|
||||||
|
const post = await prisma.post.update({
|
||||||
|
where: { id },
|
||||||
|
data: { status: "published" },
|
||||||
|
select: { id: true, status: true },
|
||||||
|
});
|
||||||
|
return NextResponse.json({ post });
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
20
src/app/api/posts/[id]/comments/route.ts
Normal file
20
src/app/api/posts/[id]/comments/route.ts
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
import { NextResponse } from "next/server";
|
||||||
|
import prisma from "@/lib/prisma";
|
||||||
|
|
||||||
|
export async function GET(_: Request, context: { params: Promise<{ id: string }> }) {
|
||||||
|
const { id } = await context.params;
|
||||||
|
const comments = await prisma.comment.findMany({
|
||||||
|
where: { postId: id },
|
||||||
|
orderBy: { createdAt: "asc" },
|
||||||
|
select: {
|
||||||
|
id: true,
|
||||||
|
content: true,
|
||||||
|
isAnonymous: true,
|
||||||
|
isSecret: true,
|
||||||
|
createdAt: true,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
return NextResponse.json({ comments });
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
21
src/app/api/posts/[id]/pin/route.ts
Normal file
21
src/app/api/posts/[id]/pin/route.ts
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
import { NextResponse } from "next/server";
|
||||||
|
import prisma from "@/lib/prisma";
|
||||||
|
import { z } from "zod";
|
||||||
|
|
||||||
|
const schema = z.object({ pinned: z.boolean(), order: z.number().int().nullable().optional() });
|
||||||
|
|
||||||
|
export async function POST(req: Request, context: { params: Promise<{ id: string }> }) {
|
||||||
|
const { id } = await context.params;
|
||||||
|
const body = await req.json();
|
||||||
|
const parsed = schema.safeParse(body);
|
||||||
|
if (!parsed.success) return NextResponse.json({ error: parsed.error.flatten() }, { status: 400 });
|
||||||
|
const { pinned, order } = parsed.data;
|
||||||
|
const post = await prisma.post.update({
|
||||||
|
where: { id },
|
||||||
|
data: { isPinned: pinned, pinnedOrder: pinned ? order ?? 0 : null },
|
||||||
|
select: { id: true, isPinned: true, pinnedOrder: true },
|
||||||
|
});
|
||||||
|
return NextResponse.json({ post });
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
33
src/app/api/posts/[id]/recommend/route.ts
Normal file
33
src/app/api/posts/[id]/recommend/route.ts
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
import { NextResponse } from "next/server";
|
||||||
|
import prisma from "@/lib/prisma";
|
||||||
|
import { z } from "zod";
|
||||||
|
|
||||||
|
const schema = z.object({ userId: z.string().optional(), clientHash: z.string().optional() }).refine(
|
||||||
|
(d) => !!d.userId || !!d.clientHash,
|
||||||
|
{ message: "Provide userId or clientHash" }
|
||||||
|
);
|
||||||
|
|
||||||
|
export async function POST(req: Request, context: { params: Promise<{ id: string }> }) {
|
||||||
|
const { id } = await context.params;
|
||||||
|
const body = await req.json().catch(() => ({}));
|
||||||
|
const parsed = schema.safeParse(body);
|
||||||
|
if (!parsed.success) return NextResponse.json({ error: parsed.error.flatten() }, { status: 400 });
|
||||||
|
const { userId, clientHash } = parsed.data;
|
||||||
|
|
||||||
|
const existing = await prisma.reaction.findFirst({
|
||||||
|
where: { postId: id, type: "RECOMMEND", userId: userId ?? null, clientHash: clientHash ?? null },
|
||||||
|
select: { id: true },
|
||||||
|
});
|
||||||
|
if (!existing) {
|
||||||
|
await prisma.reaction.create({ data: { postId: id, type: "RECOMMEND", userId: userId ?? null, clientHash: clientHash ?? null } });
|
||||||
|
await prisma.postStat.upsert({
|
||||||
|
where: { postId: id },
|
||||||
|
update: { recommendCount: { increment: 1 } },
|
||||||
|
create: { postId: id, recommendCount: 1 },
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return NextResponse.json({ ok: true });
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
30
src/app/api/posts/[id]/report/route.ts
Normal file
30
src/app/api/posts/[id]/report/route.ts
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
import { NextResponse } from "next/server";
|
||||||
|
import prisma from "@/lib/prisma";
|
||||||
|
import { z } from "zod";
|
||||||
|
|
||||||
|
const schema = z.object({ reason: z.string().min(1) });
|
||||||
|
|
||||||
|
export async function POST(req: Request, context: { params: Promise<{ id: string }> }) {
|
||||||
|
const { id } = await context.params;
|
||||||
|
const body = await req.json().catch(() => ({}));
|
||||||
|
const parsed = schema.safeParse(body);
|
||||||
|
if (!parsed.success) return NextResponse.json({ error: parsed.error.flatten() }, { status: 400 });
|
||||||
|
|
||||||
|
const report = await prisma.report.create({
|
||||||
|
data: { targetType: "POST", postId: id, reason: parsed.data.reason },
|
||||||
|
});
|
||||||
|
|
||||||
|
await prisma.postStat.upsert({
|
||||||
|
where: { postId: id },
|
||||||
|
update: { reportCount: { increment: 1 } },
|
||||||
|
create: { postId: id, reportCount: 1 },
|
||||||
|
});
|
||||||
|
|
||||||
|
await prisma.adminNotification.create({
|
||||||
|
data: { type: "REPORT", message: `신고 발생: post ${id}`, targetType: "POST", targetId: id },
|
||||||
|
});
|
||||||
|
|
||||||
|
return NextResponse.json({ ok: true, reportId: report.id });
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
16
src/app/api/posts/[id]/route.ts
Normal file
16
src/app/api/posts/[id]/route.ts
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
import { NextResponse } from "next/server";
|
||||||
|
import prisma from "@/lib/prisma";
|
||||||
|
|
||||||
|
export async function GET(_: Request, context: { params: Promise<{ id: string }> }) {
|
||||||
|
const { id } = await context.params;
|
||||||
|
const post = await prisma.post.findUnique({
|
||||||
|
where: { id },
|
||||||
|
include: {
|
||||||
|
board: { select: { id: true, name: true, slug: true } },
|
||||||
|
},
|
||||||
|
});
|
||||||
|
if (!post) return NextResponse.json({ error: "Not found" }, { status: 404 });
|
||||||
|
return NextResponse.json({ post });
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -17,10 +17,66 @@ export async function POST(req: Request) {
|
|||||||
return NextResponse.json({ error: parsed.error.flatten() }, { status: 400 });
|
return NextResponse.json({ error: parsed.error.flatten() }, { status: 400 });
|
||||||
}
|
}
|
||||||
const { boardId, authorId, title, content, isAnonymous } = parsed.data;
|
const { boardId, authorId, title, content, isAnonymous } = parsed.data;
|
||||||
|
const board = await prisma.board.findUnique({ where: { id: boardId } });
|
||||||
|
const requiresApproval = board?.requiresApproval ?? false;
|
||||||
const post = await prisma.post.create({
|
const post = await prisma.post.create({
|
||||||
data: { boardId, authorId: authorId ?? null, title, content, isAnonymous: !!isAnonymous },
|
data: {
|
||||||
|
boardId,
|
||||||
|
authorId: authorId ?? null,
|
||||||
|
title,
|
||||||
|
content,
|
||||||
|
isAnonymous: !!isAnonymous,
|
||||||
|
status: requiresApproval ? "hidden" : "published",
|
||||||
|
},
|
||||||
});
|
});
|
||||||
return NextResponse.json({ post }, { status: 201 });
|
return NextResponse.json({ post }, { status: 201 });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const listQuerySchema = z.object({
|
||||||
|
page: z.coerce.number().min(1).default(1),
|
||||||
|
pageSize: z.coerce.number().min(1).max(100).default(10),
|
||||||
|
boardId: z.string().optional(),
|
||||||
|
q: z.string().optional(),
|
||||||
|
});
|
||||||
|
|
||||||
|
export async function GET(req: Request) {
|
||||||
|
const { searchParams } = new URL(req.url);
|
||||||
|
const parsed = listQuerySchema.safeParse(Object.fromEntries(searchParams));
|
||||||
|
if (!parsed.success) {
|
||||||
|
return NextResponse.json({ error: parsed.error.flatten() }, { status: 400 });
|
||||||
|
}
|
||||||
|
const { page, pageSize, boardId, q } = parsed.data;
|
||||||
|
const where = {
|
||||||
|
...(boardId ? { boardId } : {}),
|
||||||
|
...(q
|
||||||
|
? {
|
||||||
|
OR: [
|
||||||
|
{ title: { contains: q } },
|
||||||
|
{ content: { contains: q } },
|
||||||
|
],
|
||||||
|
}
|
||||||
|
: {}),
|
||||||
|
} as const;
|
||||||
|
|
||||||
|
const [total, items] = await Promise.all([
|
||||||
|
prisma.post.count({ where }),
|
||||||
|
prisma.post.findMany({
|
||||||
|
where,
|
||||||
|
orderBy: [{ isPinned: "desc" }, { createdAt: "desc" }],
|
||||||
|
skip: (page - 1) * pageSize,
|
||||||
|
take: pageSize,
|
||||||
|
select: {
|
||||||
|
id: true,
|
||||||
|
title: true,
|
||||||
|
createdAt: true,
|
||||||
|
boardId: true,
|
||||||
|
isPinned: true,
|
||||||
|
status: true,
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
]);
|
||||||
|
|
||||||
|
return NextResponse.json({ total, page, pageSize, items });
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user