7.2.2 · Boot, routing, API calls, auth flow
branch: UAT
Boot sequence
1. next.config.js
basePath '/meetingbo'
withSentryConfig(...) next.config.js:21,38
typescript.ignoreBuildErrors :13-18
│
2. _app.tsx
appWithTranslation(MyApp)
CacheProvider → ThemeProvider → Layout → page
ไม่มี auth redirect _app.tsx:20-37
│
3. Layout.tsx
Navbar + Sidebar ทุกหน้า
│
4. Page
· index / listapprover → getStaticProps (SSG ที่ build time)
· creategroup / editgroup / createapprover → client axios
· [token] → useEffect axios redirectbypassRouting detail
SSG pages — ข้อมูลค้างตามรอบ build
pages/index.tsx:29-48:
export const getStaticProps: GetStaticProps = async ({ locale }) => {
const res = await fetch(
`${process.env.NEXT_PUBLIC_APP_MEETING_API}/secure/group/listgroup`
);
const result = await res.json();
// ...
return {
props: {
data,
...(await serverSideTranslations(locale as string, ['home'])),
},
};
};- ไม่มี
revalidate→ ไม่ใช่ ISR — รายการกลุ่มเปลี่ยนที่ backend จะไม่ขึ้นจนกว่า rebuild/redeploy fetchไม่มี headerAuthorization/X-API-Key- ใช้
serverSideTranslationsสำหรับ i18n namespacehome
listapprover/index.tsx ใช้ pattern เดียวกันกับ listApprover endpoint
Client mutation pages
หน้าสร้าง/แก้ ยิง axios ตอนผู้ใช้กดปุ่ม — ได้ข้อมูลสดกว่า list หน้าแรกที่เป็น SSG
ตัวอย่าง create group (creategroup/index.tsx:349):
POST ${NEXT_PUBLIC_APP_MEETING_API}/secure/group/creategroup
Dropdown org chain:
GET .../secure/finddropdownbyorg/org/GET .../secure/finddropdownbyorg/division/GET .../secure/finddropdownbyorg/department/GET .../secure/finduserlistbyorg
(พบใน creategroup, editgroup, createapprover)
แคตตาล็อก API ทั้งหมดที่พบใน src/pages
Base: process.env.NEXT_PUBLIC_APP_MEETING_API (= UAT /meeting จาก .env:13)
| Method | Path | Call site |
|---|---|---|
| GET | /secure/group/listgroup | index.tsx:30, editgroup:321 |
| POST | /secure/group/creategroup | creategroup:349 |
| POST | /secure/group/editgroup | editgroup:413 |
| GET | /secure/approver/listApprover | listapprover:30, createapprover:169 |
| POST | /secure/approver/addNewApprover | createapprover:377 |
| (delete) | /secure/approver/deleteapprover | createapprover:365 |
| GET | /secure/finduserlistbyorg | create/edit/approver pages |
| GET | /secure/finddropdownbyorg/org/ | 同上 |
| GET | /secure/finddropdownbyorg/division/ | 同上 |
| GET | /secure/finddropdownbyorg/department/ | 同上 |
| GET | /redirectbypass | [token].tsx:14 |
Base 2way: NEXT_PUBLIC_APP_2WAY_API (.env:14)
| Method | Path | Call site |
|---|---|---|
| POST | /getUserByCondition | creategroup:392, createapprover:415 |
หมายเหตุ: ใน meeting-backoffice (UAT) การเรียก getUserByCondition ไม่แนบ Authorization header — ต่างจาก meeting-backoffice-new ที่ hardcode Bearer testnaja
Auth flow — [token].tsx
ทางเข้าเดียวที่มี token ใน repo นี้:
External system
└── redirect → /meetingbo/{token}
└── [token].tsx useEffect
└── GET {MEETING_API}/redirectbypass
headers:
Authorization: Bearer {tokenID}
X-API-Key: "01010101" ← hardcode ใน frontend
│
▼
response.data.token
└── setToken → localStorage.accessToken
└── router.replace("/meetingbo")หลักฐาน: pages/[token].tsx:12-28, utils/auth.tsx:1-11
ข้อเท็จจริงสำคัญเรื่อง auth ที่ตามมา
X-API-Key: "01010101"ฝังใน client bundle ([token].tsx:20) — ใครเปิด sources เห็นได้- หลังได้ token เก็บใน
localStorageแล้ว — หน้าอื่นไม่ได้อ่าน token ไปใส่ header ของ/secure/*(ไม่พบgetToken()usage ใน pages อื่น) - จึงสรุปจากโค้ดฝั่ง UI ได้ว่า: การป้องกันการเข้าถึงหน้า list/CRUD ไม่ได้อยู่ที่ frontend route guard — พึ่ง path secrecy + พฤติกรรม backend
/secure/*(ดู scorecard ของmeeting-vtrc-api) - ต้นทางที่สร้างลิงก์
/meetingbo/{token}ไม่ได้อยู่ใน repo นี้ → UNVERIFIED ว่าใคร generate (อาจเป็น legacy backoffice / meeting API อื่น)
index.layout = "L3" ถูกตั้งใน [token].tsx:56 แต่ _app ของ repo นี้ ไม่ได้ อ่าน Component.layout (ต่างจาก 2way-meeting-backoffice) — ค่านี้ไม่มีผลต่อ Layout
Env resolution (ไม่มี checkLinkURL)
ต่างจาก legacy vtrc-web ที่สลับ endpoint ตาม window.location.href — repo นี้ฝัง NEXT_PUBLIC_* ตอน build จาก .env
| Environment | วิธีได้ค่า |
|---|---|
| Local / Docker ตาม Dockerfile | อ่าน .env ที่ copy เข้า image (UAT URLs) |
build:production | env-cmd -f .env next build |
ถ้าต้องการชี้ production host ต้องเปลี่ยน .env แล้ว rebuild — ไม่มี runtime switch ในโค้ด
i18n
next-i18next+serverSideTranslationsบน SSG pagespublic/locales/enและdeมีไฟล์ — ไม่มีthในโฟลเดอร์ locales ที่ตรวจ ทั้งที่ UI ข้อความใน Sidebar/หน้าเป็นภาษาไทย hardcode ใน JSX (Sidebar.tsx:46-58)- Locale เยอรมันเป็นซาก starter template
Error page และ Sentry capture path
pages/_error.js import @sentry/nextjs และเรียก Sentry.captureException เมื่อมี err — แต่ถ้า Sentry.init ถูก comment ใน config ไฟล์ client/server การส่ง event อาจไม่ไปถึง project (ขึ้นกับว่า SDK no-op อย่างไรเมื่อยังไม่ init) — ดู 03
เปรียบเทียบสั้นกับ meeting-backoffice-new
| หัวข้อ | meeting-backoffice (UAT) | meeting-backoffice-new (UAT) |
|---|---|---|
| Router | Next Pages + basePath /meetingbo | CRA + PUBLIC_URL /meeting-backoffice |
| List data | SSG getStaticProps | useEffect fetch ทุกครั้ง |
| Auth entry | มี [token].tsx | ไม่มี |
| 2way search auth | ไม่แนบ Bearer | hardcode Bearer testnaja |
| Sentry dep | มี (init comment) | ไม่มี |
| Embed ใน vtrc-backoffice | ไม่ใช่ artifact ที่ฝัง | ใช่ (build/ shasum ตรง) |
Approver save flow (delete แล้ว add)
createapprover/index.tsx:363-392 เมื่อบันทึก:
1. สร้าง body จากรายการที่เลือก:
{ empCode, sourceDB: "dbHRMI_RC_C_rep" } ← sourceDB hardcode
2. สร้าง delBody จากรายการที่ลบ (sourceDB เดียวกัน)
3. axios.delete(.../secure/approver/deleteapprover, { data: { deleteApprover: delBody } })
4. ใน .then → axios.post(.../secure/approver/addNewApprover, { addApprover: body })
5. สำเร็จ → router.push('/listapprover')ลำดับนี้หมายความว่า:
- ทุกครั้งที่เซฟจะพยายาม ลบชุดหนึ่งก่อน แล้วค่อยเพิ่ม — ไม่ใช่ PATCH ทีละคน
sourceDBถูก hardcode เป็น"dbHRMI_RC_C_rep"(createapprover/index.tsx:351,358) — ถ้า org มาจาก DB อื่นจะผิดแหล่ง- Headers ที่ส่งมีแค่
Accept/Content-Type— ไม่มี Authorization จากlocalStorage
Create / edit group — พฤติกรรมหลัก
ทั้งสองหน้ามี pattern คล้ายกัน (~700 บรรทัด/ไฟล์):
- โหลด dropdown org → division → department จาก meeting API
- ค้นหาพนักงานด้วย
getUserByConditionผ่าน 2way API (paginationlimit/offset) - ผู้ใช้เลือกสมาชิกเข้าตาราง
- Submit
creategroupหรือeditgroup
Edit รับ groupID จาก query (Listsgroup ส่งมา) แล้วโหลด listgroup อีกรอบเพื่อหาแถวที่จะแก้ (editgroup/index.tsx:321)
Favicon / public asset path
pages/index.tsx:17 อ้าง href="/meetingbo/favicon.ico" — สอดคล้อง basePath (ต้องใส่ prefix เองเมื่อใช้ <link> ดิบ ไม่ผ่าน Next asset helper)