diff --git a/docker-compose.yml b/docker-compose.yml
index b4e61b26..4cb7f569 100755
--- a/docker-compose.yml
+++ b/docker-compose.yml
@@ -81,7 +81,7 @@ services:
DB_USER: "center"
DB_PASSWORD: "Center#2025"
DB_NAME: "dms"
- JWT_SECRET: "8b0df02e4aee9f9f79a4f2d8ba77b0b82c1ee3446b68cb0bae94ab54d60f8d9e"
+ JWT_SECRET: "9a6d8705a6695ab9bae4ca1cd46c72a6379aa72404b96e2c5b59af881bb55c639dd583afdce5a885c68e188da55ce6dbc1fb4aa9cd4055ceb51507e56204e4ca"
JWT_EXPIRES_IN: "12h"
PASSWORD_SALT_ROUNDS: "10"
FRONTEND_ORIGIN: "https://lcbp3.np-dms.work"
@@ -130,10 +130,11 @@ services:
CHOKIDAR_USEPOLLING: "1"
WATCHPACK_POLLING: "true"
NEXT_PUBLIC_API_BASE: "https://lcbp3.np-dms.work"
+ NEXT_PUBLIC_AUTH_MODE: "cookie"
NEXT_PUBLIC_DEBUG_AUTH: "1"
NEXT_TELEMETRY_DISABLED: "1"
- JWT_ACCESS_SECRET: "change-this-access-secret"
- JWT_REFRESH_SECRET: "change-this-refresh-secret"
+ JWT_ACCESS_SECRET: "9a6d8705a6695ab9bae4ca1cd46c72a6379aa72404b96e2c5b59af881bb55c639dd583afdce5a885c68e188da55ce6dbc1fb4aa9cd4055ceb51507e56204e4ca"
+ JWT_REFRESH_SECRET: "743e798bb10d6aba168bf68fc3cf8eff103c18bd34f1957a3906dc87987c0df139ab72498f2fe20d6c4c580f044ccba7d7bfa4393ee6035b73ba038f28d7480c"
expose:
- "3000"
networks: [dmsnet]
diff --git a/frontend/app/(auth)/login/page.jsx b/frontend/app/(auth)/login/page.jsx
index d8cb953b..178dfe04 100755
--- a/frontend/app/(auth)/login/page.jsx
+++ b/frontend/app/(auth)/login/page.jsx
@@ -1,54 +1,34 @@
// File: frontend/app/(auth)/login/page.jsx
-
"use client";
-// ✅ ปรับให้ตรง backend: ใช้ Bearer token (ไม่ใช้ cookie)
-// - เรียก POST /api/auth/login → รับ { token, refresh_token, user }
-// - เก็บ token/refresh_token ใน localStorage (หรือ sessionStorage ถ้าไม่ติ๊กจำไว้)
-// - ไม่ใช้ credentials: "include" อีกต่อไป
-// - เอา RootLayout/metadata ออก เพราะไฟล์เพจเป็น client component
-// - เพิ่มการอ่าน NEXT_PUBLIC_API_BASE และ error handling ให้ตรงกับ backend
-// - เพิ่มโหมดดีบัก เปิดด้วย NEXT_PUBLIC_DEBUG_AUTH=1
-
import { useState, useMemo, Suspense } from "react";
import { useSearchParams, useRouter } from "next/navigation";
import {
- Card,
- CardHeader,
- CardTitle,
- CardDescription,
- CardContent,
- CardFooter,
+ Card, CardHeader, CardTitle, CardDescription, CardContent, CardFooter,
} from "@/components/ui/card";
import { Label } from "@/components/ui/label";
import { Input } from "@/components/ui/input";
import { Button } from "@/components/ui/button";
import { Alert, AlertDescription } from "@/components/ui/alert";
-const API_BASE = process.env.NEXT_PUBLIC_API_BASE?.replace(/\/$/, "") || "";
+const API_BASE = (process.env.NEXT_PUBLIC_API_BASE || "").replace(/\/$/, "");
const DEBUG =
String(process.env.NEXT_PUBLIC_DEBUG_AUTH || "").trim() !== "" &&
process.env.NEXT_PUBLIC_DEBUG_AUTH !== "0" &&
process.env.NEXT_PUBLIC_DEBUG_AUTH !== "false";
function dlog(...args) {
- if (DEBUG && typeof window !== "undefined") {
- console.debug("[login]", ...args);
- }
+ if (DEBUG && typeof window !== "undefined") console.debug("[login]", ...args);
}
function LoginForm() {
const router = useRouter();
const searchParams = useSearchParams();
- const nextPath = useMemo(
- () => searchParams.get("next") || "/dashboard",
- [searchParams]
- );
+ const nextPath = useMemo(() => searchParams.get("next") || "/dashboard", [searchParams]);
const [username, setUsername] = useState("");
const [password, setPassword] = useState("");
const [showPw, setShowPw] = useState(false);
- const [remember, setRemember] = useState(false);
const [submitting, setSubmitting] = useState(false);
const [err, setErr] = useState("");
@@ -63,69 +43,37 @@ function LoginForm() {
try {
setSubmitting(true);
-
- // ── DEBUG: ค่าเบื้องต้น
- dlog("API_BASE =", API_BASE || "(empty → จะเรียก path relative)");
- dlog("nextPath =", nextPath);
- dlog("remember =", remember);
- dlog("payload =", { username: "[hidden]", password: "[hidden]" });
+ dlog("API_BASE =", API_BASE || "(empty → relative path)"); dlog("nextPath =", nextPath);
const res = await fetch(`${API_BASE}/api/auth/login`, {
method: "POST",
headers: { "Content-Type": "application/json" },
- body: JSON.stringify({ username, password }),
+ credentials: "include", // << ใช้คุกกี้
cache: "no-store",
+ body: JSON.stringify({ username, password }),
});
- dlog("response.status =", res.status);
- dlog("response.headers.content-type =", res.headers.get("content-type"));
-
+ dlog("status =", res.status, "ctype =", res.headers.get("content-type"));
let data = {};
- try {
- data = await res.json();
- } catch (e) {
- dlog("response.json() error =", e);
- }
- dlog("response.body =", data);
+ try { data = await res.json(); } catch {}
if (!res.ok) {
const msg =
data?.error === "INVALID_CREDENTIALS"
? "ชื่อผู้ใช้หรือรหัสผ่านไม่ถูกต้อง"
: data?.error || `เข้าสู่ระบบไม่สำเร็จ (HTTP ${res.status})`;
- dlog("login FAILED →", msg);
setErr(msg);
return;
}
- if (!data?.token) {
- dlog("login FAILED → data.token not found");
- setErr("รูปแบบข้อมูลตอบกลับไม่ถูกต้อง (ไม่มี token)");
- return;
- }
-
- // ✅ เก็บ token ตามโหมดจำไว้/ไม่จำ
- const storage = remember ? window.localStorage : window.sessionStorage;
- storage.setItem("dms.token", data.token);
- storage.setItem("dms.refresh_token", data.refresh_token);
- storage.setItem("dms.user", JSON.stringify(data.user || {}));
- dlog("token stored in", remember ? "localStorage" : "sessionStorage");
-
- // (ออปชัน) เผยแพร่ event ให้แท็บอื่นทราบ
- try {
- window.dispatchEvent(
- new StorageEvent("storage", { key: "dms.auth", newValue: "login" })
- );
- } catch {}
-
- dlog("navigating →", nextPath);
+ // คุกกี้ (HttpOnly) ถูกตั้งด้วย Set-Cookie จาก backend แล้ว
+ dlog("login ok → redirect", nextPath);
router.replace(nextPath);
} catch (e) {
dlog("exception =", e);
setErr("เชื่อมต่อเซิร์ฟเวอร์ไม่ได้ กรุณาลองใหม่");
} finally {
setSubmitting(false);
- dlog("done");
}
}
@@ -133,32 +81,22 @@ function LoginForm() {
-
- เข้าสู่ระบบ
-
-
- Document Management System • LCBP3
-
+ เข้าสู่ระบบ
+ Document Management System • LCBP3
{err ? (
-
- {err}
-
+ {err}
) : null}
-
-
-
-
- ลืมรหัสผ่าน?
-
-
-
-