feat : copy youtube studio
CI / Verify (push) Successful in 2m8s
CI / Build immutable images and deploy (push) Successful in 2m39s

This commit is contained in:
2026-10-08 03:26:00 +07:00 Unverified
parent 9bc710c94b
commit b665fe7497
16 changed files with 196 additions and 46 deletions
+1 -1
View File
@@ -11,7 +11,7 @@ export default async function AdminCommentsPage({ searchParams }: { searchParams
const query = await searchParams;
const initialTarget = query.target;
const initialComment = initialTarget && query.comment && /^[0-9a-f-]{36}$/i.test(query.comment) ? query.comment : undefined;
const [initial, targets] = await Promise.all([listComments({ viewer, inbox: true, target: initialTarget }), listCommentTargets()]);
const [initial, targets] = await Promise.all([listComments({ viewer, inbox: true, grouped: true, target: initialTarget }), listCommentTargets()]);
return <><AdminHeader /><main className="mx-auto flex w-full max-w-7xl flex-col gap-6 p-4 sm:p-6 lg:p-8">
<header><h1 className="font-heading text-3xl font-semibold">ความคิดเห็น</h1><p className="mt-2 text-muted-foreground">ตอบกลับและจัดการความคิดเห็นจากทุกไกด์</p></header>
<AdminCommentInbox key={`${initialTarget ?? "all"}:${initialComment ?? ""}`} initialTarget={initialTarget} initialComment={initialComment} initial={initial} targets={targets} />
+1 -1
View File
@@ -6,7 +6,7 @@ export async function GET(request: Request) {
const viewer = await requireCommentViewer();
requireCommentAdmin(viewer);
const query = new URL(request.url).searchParams;
return Response.json(await listComments({ viewer, inbox: true, target: query.get("target") || undefined,
return Response.json(await listComments({ viewer, inbox: true, grouped: query.get("grouped") === "true", target: query.get("target") || undefined,
status: query.get("status") || undefined, unanswered: query.get("unanswered") === "true", cursor: query.get("cursor") }),
{ headers: { "Cache-Control": "private, no-store" } });
} catch (cause) { return errorResponse(cause); }
+5 -1
View File
@@ -1,3 +1,5 @@
import { after } from "next/server";
import { sendCommentPush } from "@/lib/comments/push";
import { authorizeComment, commentId, getCommentViewer, listComments, mutateComment, requireCommentViewer } from "@/lib/comments/repository";
import { publishComment } from "@/lib/comments/publish";
import { errorResponse, requireSameOrigin } from "@/lib/security/http";
@@ -18,7 +20,9 @@ export async function PATCH(request: Request, context: Context) {
requireSameOrigin(request);
const viewer = await requireCommentViewer();
await limitRequest("comment-publish", viewer.id, 20);
return Response.json(await publishComment(request, viewer, { id: commentId((await context.params).id) }));
const result = await publishComment(request, viewer, { id: commentId((await context.params).id) });
after(async () => { await sendCommentPush(result.id, result.version).catch(() => console.error("Comment notification failed")); });
return Response.json(result);
} catch (cause) { return errorResponse(cause); }
}
export async function DELETE(request: Request, context: Context) {
+1 -1
View File
@@ -21,7 +21,7 @@ export async function POST(request: Request) {
const target = new URL(request.url).searchParams.get("target");
if (!target) throw new HttpError(400, "target-required");
const result = await publishComment(request, viewer, { target });
after(async () => { await sendCommentPush(result.id).catch(() => console.error("Comment notification failed")); });
after(async () => { await sendCommentPush(result.id, result.version).catch(() => console.error("Comment notification failed")); });
return Response.json(result, { status: 201 });
} catch (cause) { return errorResponse(cause); }
}