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-apibackdoori3admin(SEC-2WAYAPI-01) — ออก JWT admin 48h โดยไม่ผ่าน SSO2way-vtrc-apibypass string"testnaja"(SEC-2WAYVTRC-01)cu-central-api/auth/signinbypass(SEC-CU-04)@Public()write endpoints (benefit-apiwdHospitals,pms-apisalary,recruitment-apiblacklist)
ที่ต้องดู — 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 dbWorkforce → pms-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 เพื่อลดความสับสนซ้ำในอนาคต