From 83a2cc3e5ef9194b36fa50be9a5bf7059c0523c6 Mon Sep 17 00:00:00 2001 From: Your Name Date: Mon, 2 Nov 2026 11:12:11 +0700 Subject: [PATCH] fix(note): use relative redirect Location behind proxy MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit หลัง reverse proxy ที่เขียนทับ Host เป็น upstream (nginx default \$proxy_host เช่น 0.0.0.0:3000) การ 302 ด้วย NextResponse.redirect( new URL(path, request.url)) ประกอบ absolute URL จาก origin ฝั่ง server ทำให้บน prod กดลิงก์เปิดเอกสารตัวอย่างแล้ว browser ถูกพาไป https://0.0.0.0:3000/docedit/note/annotate/... แทนโฮสต์จริง เปลี่ยนเป็น Location แบบ relative (NextResponse + header เอง) ให้ browser resolve เทียบ origin ที่ตัวเองยิงมา — ถูกเสมอไม่ว่า proxy จะตั้ง header แบบไหน: - GET /api/note/files/open: 302 เข้า viewer และ 302 หน้าแรก กรณี fileError - GET /api/note/auth/dev-link: 302 เข้า /note?fileUrl=…&t=… ไม่แตะ dev-link POST ที่คืน {url} เต็ม — dev-only และลิงก์ต้อง absolute เพื่อคัดลอกไปเปิดที่อื่น Co-Authored-By: Claude Code --- app/api/note/auth/dev-link/route.ts | 9 ++++++--- app/api/note/files/open/route.ts | 11 ++++++++--- 2 files changed, 14 insertions(+), 6 deletions(-) diff --git a/app/api/note/auth/dev-link/route.ts b/app/api/note/auth/dev-link/route.ts index ae21397..e8fa16a 100644 --- a/app/api/note/auth/dev-link/route.ts +++ b/app/api/note/auth/dev-link/route.ts @@ -62,9 +62,12 @@ export async function GET(request: Request): Promise { // ปลายทาง = หน้าแรก /note พร้อม credential + fileUrl เหมือนลิงก์จริง — ให้ bootstrap // ของ NoteHome แลกเป็น session token แล้วพาเข้า viewer (เห็น URL ทุกขั้นตอน) - const res = NextResponse.redirect(new URL(`${withBasePath("")}?${params.toString()}`, url), 302); - res.headers.set("cache-control", "no-store"); - return res; + // Location relative เหมือน files/open — origin ของ request หลัง reverse proxy + // (Host ถูกเขียนทับเป็น upstream) ไม่น่าเชื่อพอจะประกอบ absolute URL + return new NextResponse(null, { + status: 302, + headers: { location: `${withBasePath("")}?${params.toString()}`, "cache-control": "no-store" }, + }); } export async function POST(request: Request): Promise { diff --git a/app/api/note/files/open/route.ts b/app/api/note/files/open/route.ts index 3b555f3..263329d 100644 --- a/app/api/note/files/open/route.ts +++ b/app/api/note/files/open/route.ts @@ -16,10 +16,15 @@ export async function GET(request: Request): Promise { const url = new URL(request.url); // withBasePath("") = หน้าแรก /docedit/note · withBasePath("/annotate/x") = viewer — // อย่าส่ง "/note" เข้า withBasePath (มันเติม app path ให้อีกชั้นเป็น /note/note — D11) + // Location เป็น relative โดยเจตนา: browser resolve เทียบ origin ที่ตัวเองยิงมาเอง + // ส่วน NextResponse.redirect(new URL(path, request.url)) จะประกอบ absolute URL จาก + // Host ที่ reverse proxy ส่งมา (nginx default เขียนทับเป็น upstream เช่น 0.0.0.0:3000) + // แล้วพา browser หลุดโฮสต์จริง (prod 2026-11-02) const redirectTo = (path: string, query = ""): NextResponse => { - const res = NextResponse.redirect(new URL(`${withBasePath(path)}${query}`, url), 302); - res.headers.set("cache-control", "no-store"); - return res; + return new NextResponse(null, { + status: 302, + headers: { location: `${withBasePath(path)}${query}`, "cache-control": "no-store" }, + }); }; const fileUrl = url.searchParams.get("fileUrl") ?? "";