Skip to content

16.1 · Common incidents

แต่ละ entry มีโครงสร้าง: อาการ (symptom) → สาเหตุที่เป็นไปได้ (likely cause) → ที่ต้องดู (where to look) → วิธีแก้ที่แนะนำ (suggested fix)

0. สงสัยว่ามีคนใช้ backdoor login / bypass token

อาการ — พบ JWT ที่ออกโดยไม่มี SSO trail, หรือ access log มี pattern แปลกจาก IP ภายนอกไป endpoint ที่ควรมี auth

สาเหตุที่เป็นไปได้

  • 2way-api backdoor i3admin (SEC-2WAYAPI-01) — ออก JWT admin 48h โดยไม่ผ่าน SSO
  • 2way-vtrc-api bypass string "testnaja" (SEC-2WAYVTRC-01)
  • cu-central-api /auth/signinbypass (SEC-CU-04)
  • @Public() write endpoints (benefit-api wdHospitals, pms-api salary, recruitment-api blacklist)

ที่ต้องดู — access log ของ service ที่สงสัย; grep source สำหรับ i3admin, testnaja, signinbypass, @Public() บน mutation

วิธีแก้ที่แนะนำ (immediate) — ปิด backdoor ในโค้ด + rotate JWT secret ที่เกี่ยวข้อง + บังคับ re-login ทั้งระบบ; ดู Vol 12.5


1. ข้อมูลโรงพยาบาล (WdHospitals) ไม่ตรงกันระหว่างระบบ welfare เก่ากับใหม่

อาการ — พนักงานแจ้งว่าโรงพยาบาลที่ลงทะเบียนไว้ใน benefit-api (ระบบใหม่) ไม่ตรงกับที่เห็นใน vtrc-api/vtrc-web (ระบบเดิม) หรือสถานะการอนุมัติค้างที่สถานะหนึ่งในระบบเดิมแต่ระบบใหม่แสดงว่าเสร็จแล้ว

สาเหตุที่เป็นไปได้benefit.wdHospitals และ vtrc.WDHospitals เป็น 2 schema คนละตัวที่เก็บข้อมูลเดียวกันแบบไม่ sync อัตโนมัติ sync เกิดผ่าน raw SQL เฉพาะบาง flow เท่านั้น (cancel/update bank detail) — ดู CORR-BIZ-01, benefit-api/04-auth-integrations

ที่ต้องดู — เทียบ record ID เดียวกันใน benefit.wdHospitals (ผ่าน benefit-api DB connection) กับ vtrc.WDHospitals (ผ่าน vtrc-api DB connection db) โดยตรงด้วย SQL query — ไม่มี API หรือ dashboard ที่ทำ diff ให้อัตโนมัติ

วิธีแก้ที่แนะนำ — เขียน SQL แก้ record ที่ drift ด้วยมือหลังยืนยัน state ที่ถูกต้องกับทีม business แก้ระยะยาวต้องตัดสินใจ source of truth เดียว (ดู ROI item 1 ของ Vol 14 §03)


2. ต้อง rotate JWT secret หลังสงสัยว่ารั่ว

อาการ — พบ .env ที่มี SECRET_KEY หลุดออกไปนอกทีม (เช่น commit ผิดที่ไปยัง public repo, หรือแชร์ผ่านช่องทางไม่ปลอดภัย)

สาเหตุที่เป็นไปได้benefit-api, recruitment-api, ai-recruitment-api, pms-api ใช้ค่า SECRET_KEY เดียวกันทุกตัวอักษร ไม่มี JWKS/central auth service — ดู SEC-API-02

ที่ต้องดู.env ของทั้ง 4 repo (บรรทัดที่อ้างใน SEC-BENEFIT-01/SEC-RECRUIT-01/SEC-AIRECRUIT-01/SEC-PMS-02 ของ scorecard ระดับ repo แต่ละตัว) และตรวจว่า vtrc-api token (issuer redcross.or.th:vtrc) verify ผ่านที่ไหนบ้างเพิ่มเติม (ยืนยันแล้วว่า recruitment-api verify ผ่าน)

วิธีแก้ที่แนะนำ — ต้อง rotate ทั้ง 4 repo พร้อมกัน ห้าม rotate ทีละตัวเพราะ user ที่ login ผ่าน vtrc-api แล้วถือ token เดิมจะ verify ไม่ผ่านฝั่งที่ rotate ไปแล้ว — วางแผน dual-verify (accept ทั้ง secret เก่าและใหม่ชั่วคราว) ก่อน deprecate secret เก่า ไม่มี mechanism อัตโนมัติสำหรับเรื่องนี้ ต้องทำเป็น manual coordinated deploy


3. Cron sync dbWorkforcepms-api ไม่ทำงาน แต่ไม่มี alert

อาการ — ข้อมูลตำแหน่ง/salary range/องค์กรใน pms-api (ใช้ประกอบการประเมินผลและปรับเงินเดือน) เก่ากว่าที่ควรจะเป็น พนักงานหรือ HR แจ้งว่าเห็นตำแหน่ง/หน่วยงานผิด

สาเหตุที่เป็นไปได้pms-api มี cron UsagePermissionService.handleCron รันทุก 1 นาทีเพื่อ sync จาก dbWorkforce โดยไม่มี health-check/alert ถ้า cron หยุดทำงานเงียบ ๆ (crash, deploy ผิด, DB connection หลุด) จะไม่มีใครรู้จนกว่าผลประเมิน/เงินเดือนผิดปกติถูกสังเกตเห็น — ดู CORR-BIZ-03, pms-api/02-core-pipeline

ที่ต้องดู — log ของ process pms-api (ไม่มี dedicated cron log แยก ต้อง grep stdout/log file ของทั้ง service) หา timestamp ล่าสุดที่ cron เขียน record ลง dbWorkforce-derived table เทียบกับเวลาปัจจุบัน

วิธีแก้ที่แนะนำ — restart pms-api process ถ้า cron หยุดเพราะ crash ตรวจสอบ connection string ไป dbWorkforce ว่ายัง valid อยู่ แก้ระยะยาวคือเพิ่ม health-check endpoint ที่รายงานเวลา sync ล่าสุด แล้วต่อเข้า monitoring ภายนอก


4. HR แจ้งว่า HRMI ไม่มีข้อมูลพนักงานใหม่ทั้งที่ระบบต้นทางว่า "เสร็จแล้ว"

อาการ — ผู้สมัครที่ recruitment-api mark ว่า hired แล้ว หรือพนักงานที่ pms-api confirm การปรับเงินเดือนแล้ว แต่ HRMI ยังไม่มีข้อมูลตรงกัน

สาเหตุที่เป็นไปได้ — ทั้งสอง flow ส่งข้อมูลไป HRMI ผ่านมนุษย์ ไม่ใช่ API sync — recruitment-api ส่งอีเมลแจ้ง HRMI (ContractService), pms-api export Excel ให้ HR นำเข้าด้วยมือ (SalaryAdjustToHRMIModule) — ถ้าเจ้าหน้าที่พลาดไม่ได้ทำตามอีเมล/ไฟล์ ระบบต้นทางจะไม่รู้ตัว — ดู CORR-BIZ-02

ที่ต้องดู — ตรวจกล่องอีเมลที่ ContractService ส่งไป (recruitment case) หรือไฟล์ Excel export ล่าสุดจาก pms-api (salary case) — ไม่มี record ในระบบใดที่บอกว่า HRMI "รับทราบแล้ว" เพราะไม่มี webhook/callback กลับ

วิธีแก้ที่แนะนำ — ตรวจสอบกับเจ้าหน้าที่ HRMI ด้วยมือว่าดำเนินการตามอีเมล/ไฟล์แล้วหรือยัง แก้ระยะยาวคือเพิ่ม reconciliation job เทียบจำนวน record ที่ mark "ส่งแล้ว" กับจำนวนจริงใน HRMI (ROI item 2 ของ Vol 14 §03)


5. cu-central-api ล่ม ทำให้ทุกฟีเจอร์ที่ต้องใช้ข้อมูล HR ใน vtrc-api ใช้งานไม่ได้

อาการ — Frontend (vtrc-web/vtrc-mobile/backoffice) แสดง generic GraphQL error พร้อมกันในหลายฟีเจอร์: profile, slip, leave, withdraw, fund — ไม่ใช่แค่ฟีเจอร์เดียว

สาเหตุที่เป็นไปได้vtrc-api เรียก cu-central-api แบบไม่มี timeout, retry, หรือ circuit breaker (GraphQLClient/axios ใช้ default ทั้งคู่) — ถ้า cu-central-api ล่มหรือช้า vtrc-api จะ hang หรือ propagate error ทันทีสำหรับทุก field ที่ต้องพึ่ง HR data — ดู vtrc-api/04-centralize-bridge §จุดที่ไม่มี

ที่ต้องดู — สถานะ process cu-central-api (port 8080/8082 ตาม env) โดยตรง — error ที่ frontend เห็นจะเป็น generic GraphQL error เดียวกันไม่ว่า vtrc-api หรือ cu-central-api ล่ม ต้องแยกด้วยการเช็ค process ทั้งสองแยกกัน ไม่สามารถแยกจาก error message ฝั่ง client ได้

วิธีแก้ที่แนะนำ — restart cu-central-api ก่อน (dependency ปลายทาง) ถ้ายังไม่หายให้ตรวจ LDAP/AD และ MSSQL HRMI connection ที่ cu-central-api ต่ออยู่ ระยะยาวควรเพิ่ม timeout + circuit breaker ที่ layer centralize/ ของ vtrc-api เพื่อ fail fast แทน hang


6. พบ endpoint ที่ควร login แต่เข้าได้โดยไม่ต้อง auth

อาการ — Security scan หรือ pentest พบว่า endpoint ของ centralize-api, vtrc-common, หรือ 2way-vtrc-api เข้าได้โดยไม่ส่ง JWT token

สาเหตุที่เป็นไปได้ — ทั้งสาม service มี JwtAuthGuard ประกาศเป็น global guard ในโค้ด แต่ centralize-api/vtrc-common ใส่ @Public() ทุก endpoint ที่มีอยู่ และ 2way-vtrc-api มี master bypass token เป็น static string — ดู SEC-API-01

ที่ต้องดู — ต้องแยกให้ชัดว่านี่คือ "ตั้งใจเปิด public" (เช่น endpoint สำหรับ health check หรือ webhook จากบุคคลที่สาม) หรือ "ลืมปิด" — ตรวจ decorator @Public() ที่ controller แต่ละตัวเทียบกับว่า endpoint นั้นควรเปิด public จริงหรือไม่ตาม business requirement

วิธีแก้ที่แนะนำ — ถ้าไม่ควรเปิด public ให้ลบ @Public()/bypass token ออกทันที ถ้าตั้งใจเปิดจริง (เช่น webhook) ให้เพิ่ม API key แยกเฉพาะ endpoint นั้นแทนการเปิดทั้ง controller — ห้ามปล่อยไว้เพราะ Swagger ที่เปิดคู่กัน (OBS-API-01) ทำให้ผู้โจมตีเห็น endpoint ทั้งหมดที่ bypass ได้ทันที


7. พบข้อมูลส่วนบุคคล (PII) ของพนักงาน/ผู้สมัครหลุดอยู่ใน log ที่ commit เข้า git

อาการ — ตรวจพบไฟล์ log (เช่น .log ที่ export มาทำ analysis) ที่มีเลขบัตรประชาชน วันเกิด หรือเลขประจำตัวพนักงานในรูปแบบ plaintext อยู่ใน git history

สาเหตุที่เป็นไปได้vtrc-api request log (GraphQLLogging plugin) redact เฉพาะ header authorization เป็น _REPLACED_ แต่ query argument เช่น citizenId, birthDate, empCode ใน mutation sendVerifyCode ไม่ถูก mask เลย — พบกรณีจริงที่ไฟล์ log เหล่านี้ถูก copy ไปวิเคราะห์แล้ว commit เข้า repo แยก (report-log) — ดู SEC-REPORTLOG-01, vtrc-api/09-cross-cutting §Request log

ที่ต้องดู — ถ้าพบ log แบบนี้ใน repo ใดก็ตาม (grep หา pattern citizenId:" หรือเลข 13 หลัก) ต้องถือว่าเป็น PII leak ทันที ไม่ใช่แค่ debt ทางเทคนิค

วิธีแก้ที่แนะนำ — ลบไฟล์ log ออกจาก git history ทั้งหมด (ไม่ใช่แค่ลบไฟล์ใน commit ล่าสุด ต้อง rewrite history หรือขอ Bitbucket ลบ) แจ้งทีม compliance/legal ตามนโยบายองค์กรเรื่อง PII breach แก้ต้นเหตุคือเพิ่ม redaction ให้ query argument ที่มี PII ใน GraphQLLogging plugin ของ vtrc-api ไม่ใช่แค่ header


8. Docker Compose หลาย service ชนพอร์ตกันเมื่อรันบนเครื่องเดียวกัน

อาการ — พยายาม docker-compose up มากกว่า 1 service (เช่น 2way-api, 2way-line-service, chat-center-api) บนเครื่อง dev เดียวกัน แล้ว container ตัวที่สองล้มเหลว bind port

สาเหตุที่เป็นไปได้ — ทั้งสามตั้ง PORT=8200 เหมือนกันโดยไม่รู้ตัวว่าเลขซ้ำ — ดู QUAL-API-05

ที่ต้องดู.env/docker-compose.yml ของแต่ละ repo ที่กำหนด PORT

วิธีแก้ที่แนะนำ — override PORT ผ่าน environment variable ตอนรัน local (ไม่แก้ .env.example ที่ commit ไว้เพื่อไม่ทำให้ repo อื่น sync ผิด) หรือ map port ต่างกันใน docker-compose.yml ฝั่ง host ("8201:8200")


9. job-scheduler-api deploy ไม่ได้เมื่อแยกจาก vtrc-api

อาการ — พยายาม docker-compose up job-scheduler-api แยกตัวเดียวบน host ใหม่ แล้ว error ว่าไม่พบ network

สาเหตุที่เป็นไปได้job-scheduler-api/docker-compose.yml join network ชื่อ vtrc-api_default แบบ external: true ซึ่งมีอยู่ก็ต่อเมื่อ vtrc-api compose stack ถูก up มาก่อนบน host เดียวกัน — ดู OBS-INFRA-02

ที่ต้องดู — ตรวจว่า vtrc-api compose stack รันอยู่บน host เดียวกันหรือไม่ก่อน (docker network ls หา vtrc-api_default)

วิธีแก้ที่แนะนำ — deploy vtrc-api compose stack ก่อนเสมอบน host ที่จะรัน job-scheduler-api หรือแก้ compose file ให้สร้าง network ของตัวเองแทนการพึ่ง network ภายนอกแบบ implicit


10. แก้ bug ที่ 2way-api แล้วปัญหาฝั่ง 2way-meeting-backoffice ยังไม่หาย

อาการ — ทีมแก้/deploy 2way-api (NestJS) ไปแล้วแต่ bug ที่รายงานจาก 2way-meeting-backoffice ยังเกิดซ้ำ

สาเหตุที่เป็นไปได้2way-meeting-backoffice เรียก 2way-vtrc-api (Express, legacy) เป็น backend หลักในปัจจุบัน ไม่ใช่ 2way-api (NestJS) แม้ทั้งสองชื่อคล้ายกันและอยู่ในกลุ่ม "2way Platform" เดียวกัน — ดู 2way-meeting-backoffice/03-persistence-integrations §Integration 5

ที่ต้องดู — ยืนยัน base URL ที่ frontend เรียกจริงจาก network request (browser devtools/browser_network_requests) ก่อนแก้โค้ด ไม่ควรเดาจากชื่อ repo

วิธีแก้ที่แนะนำ — แก้ที่ 2way-vtrc-api แทน สื่อสารกับทีมให้ชัดเจนว่าชื่อ repo ไม่ได้บอก role จริงเสมอไป พิจารณา rename repo หรือเพิ่ม README ที่ระบุ consumer จริงของแต่ละ backend เพื่อลดความสับสนซ้ำในอนาคต


ไป 16.2 Debugging cross-service issues