82 lines
2.9 KiB
TypeScript
82 lines
2.9 KiB
TypeScript
|
|
// GET /api/note/files/[id]/bootstrap — ข้อมูลเริ่มต้นของ viewer (file + annotations
|
||
|
|
// + bookmarks + replies เป็น DTO) ให้ client gate ของหน้า /note/annotate/[id]
|
||
|
|
// ดึงหลังมี access_token (ฉบับ token 2026-10-30 — เดิมหน้านี้เป็น server component
|
||
|
|
// อ่าน cookie ผ่าน getSessionUser) · Bearer บังคับ (401) · ไม่ publish SSE ·
|
||
|
|
// query ชุดเดียวกับหน้าเดิมตอนเป็น server component
|
||
|
|
import { asc, eq, inArray } from "drizzle-orm";
|
||
|
|
import { NextResponse } from "next/server";
|
||
|
|
|
||
|
|
import { db } from "@/db";
|
||
|
|
import {
|
||
|
|
annotationReplies,
|
||
|
|
annotations,
|
||
|
|
bookmarks,
|
||
|
|
files,
|
||
|
|
toAnnotationDTO,
|
||
|
|
toBookmarkDTO,
|
||
|
|
toReplyDTO,
|
||
|
|
} from "@/db/schema";
|
||
|
|
import { noteUserFromRequest } from "@/lib/note-token";
|
||
|
|
|
||
|
|
export const runtime = "nodejs";
|
||
|
|
export const dynamic = "force-dynamic";
|
||
|
|
|
||
|
|
const UUID_RE =
|
||
|
|
/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
||
|
|
|
||
|
|
export async function GET(
|
||
|
|
request: Request,
|
||
|
|
{ params }: { params: { id: string } },
|
||
|
|
): Promise<Response> {
|
||
|
|
const user = noteUserFromRequest(request);
|
||
|
|
if (user === null) {
|
||
|
|
return NextResponse.json(
|
||
|
|
{ error: "session หมดอายุ — กรุณากลับ e-Court กดลิงก์เปิดเอกสารใหม่" },
|
||
|
|
{ status: 401, headers: { "cache-control": "no-store" } },
|
||
|
|
);
|
||
|
|
}
|
||
|
|
const { id } = params;
|
||
|
|
if (!UUID_RE.test(id)) {
|
||
|
|
return NextResponse.json({ error: "ไม่พบไฟล์นี้" }, { status: 404 });
|
||
|
|
}
|
||
|
|
|
||
|
|
const [file] = await db.select().from(files).where(eq(files.id, id));
|
||
|
|
if (!file) {
|
||
|
|
return NextResponse.json({ error: "ไม่พบไฟล์นี้" }, { status: 404 });
|
||
|
|
}
|
||
|
|
|
||
|
|
const [annotationRows, bookmarkRows] = await Promise.all([
|
||
|
|
db
|
||
|
|
.select()
|
||
|
|
.from(annotations)
|
||
|
|
.where(eq(annotations.fileId, id))
|
||
|
|
.orderBy(asc(annotations.createdAt)),
|
||
|
|
db
|
||
|
|
.select()
|
||
|
|
.from(bookmarks)
|
||
|
|
.where(eq(bookmarks.fileId, id))
|
||
|
|
.orderBy(asc(bookmarks.createdAt)),
|
||
|
|
]);
|
||
|
|
|
||
|
|
// replies ของทุก comment ในไฟล์นี้ (F9) — แม่ถูก CASCADE ไปแล้ว row ก็หายตาม
|
||
|
|
const annotationIds = annotationRows.map((r) => r.id);
|
||
|
|
const replyRows = annotationIds.length
|
||
|
|
? await db
|
||
|
|
.select()
|
||
|
|
.from(annotationReplies)
|
||
|
|
.where(inArray(annotationReplies.annotationId, annotationIds))
|
||
|
|
.orderBy(asc(annotationReplies.createdAt))
|
||
|
|
: [];
|
||
|
|
|
||
|
|
return NextResponse.json(
|
||
|
|
{
|
||
|
|
file: { id: file.id, title: file.title, pdfUrl: file.pdfUrl },
|
||
|
|
annotations: annotationRows.map(toAnnotationDTO),
|
||
|
|
bookmarks: bookmarkRows.map(toBookmarkDTO),
|
||
|
|
replies: replyRows.map(toReplyDTO),
|
||
|
|
user: { userId: user.userid, userName: user.username },
|
||
|
|
},
|
||
|
|
{ headers: { "cache-control": "no-store" } },
|
||
|
|
);
|
||
|
|
}
|