3.10 · Scorecard + known bugs
บทสุดท้ายของ Volume 3 — รวมสิ่งที่เรียนรู้ทั้งหมดจาก 3.1–3.9 ไว้เป็นรายการ severity และ scorecard ที่ใช้ตัดสินใจได้
ไม่ใช่รายการ bug ครบถ้วน — เป็น "สิ่งที่ต้องรู้ก่อนแตะ vtrc-api" รายละเอียดเชิงลึกจะอยู่ใน Volume 9 (Security), Volume 14 (Tech Debt) และ Volume 15 (Modernization)
Severity ranking — ทั้งหมดที่พบใน Volume 3
รายการนี้ครอบคลุมเฉพาะปัญหาที่อยู่ใน vtrc-api repo เท่านั้น ปัญหา cross-service อยู่ใน Volume 2.8
Severity: Critical
V3-S1 · execSync('qpdf --encrypt ...') — command injection
ที่ — vtrc-api/api/src/routes.js:73, 120, 227, 277, 355 (5 endpoints)
password ที่ interpolate เข้า shell มาจาก user (passwordPDF, identityCard) — ถ้าค่ามี shell metacharacter ก็ execute ได้
รายละเอียดอยู่ใน บท 3.7
V3-S2 · JWT_SECRET default 'secret'
ที่ — vtrc-api/api/src/config.js:12
ถ้า env ไม่ตั้ง → ใครก็ forge JWT ได้เพราะ secret เป็น public knowledge
รายละเอียดใน บท 3.3
V3-S3 · signInByPass ที่ centralize รับ user lookup จาก vtrc-api
ที่ — flow ผ่าน vtrc-api/api/src/centralize/controllers/auth.js → cu-central-api มี bypass path
vtrc-api ส่ง empCode ไปขอ JWT โดยไม่ต้อง verify password ที่ cu-central — ถ้า vtrc-api resolver ไหนเรียก path นี้โดยไม่ได้ intend ก็ mint token ได้
รายละเอียดใน บท 3.4
V3-S4 · signInWithUserAndPassword auth mismatch
ที่ — vtrc-api/api/src/lib/repositories/session/session.js#checkSession + checkMenuAuth
bug ใน RBAC check ที่ใช้ getCurrentRoleUser ที่ดึง role จาก context ผิดตัว ทำให้ user อาจได้สิทธิ์เกินจริง หรือเห็น menu ของคนอื่น
รายละเอียดใน บท 3.3
Severity: High
V3-H1 · transaction: t ผิดตำแหน่ง — transaction ไม่ทำงาน
ที่ — กระจายอยู่ทั่ว lib/controllers/ (~20+ function)
pattern ที่ผิด
await Model.create({ ... }, { transaction: t }) // ✓ ถูก
await Model.create({ ... }, { transaction: t }) // ✗ แท้จริงแล้ว argument ที่ 2 คือ options
// แต่หลายที่ส่ง transaction เป็น arg ที่ 3ผล — หลาย flow ที่คิดว่า "atomic" แท้จริงแล้วไม่ใช่ ถ้า query ที่ 2 fail query ที่ 1 ยัง commit
รายละเอียดใน บท 3.5
V3-H2 · firebasePushNotification hard-coded device token + PII log
ที่ — vtrc-api/api/src/lib/firebase/firebasePushNotification.js:41, 44, 47, 80
- FCM token ของ device ทดสอบติดอยู่ใน source (commit leak)
console.logdevice token / setData ในทุกครั้งที่ส่ง → PII ใน stdout
รายละเอียดใน บท 3.9
V3-H3 · CORS ไม่ทำงานตามที่ตั้งใจ
ที่ — vtrc-api/api/src/index.js:76-91
- key
corss(typo) ส่งเข้าapplyMiddlewareแต่ Apollo รับcors→ config ถูก discard - ต่อมา
app.use(cors())ใช้ default = origin*เปิดหมด
ผล — allowlist 5 origins ที่ตั้งใจไว้ไม่ทำงานจริง
รายละเอียดใน บท 3.9
V3-H4 · clouse.js limit cap bug
ที่ — vtrc-api/api/src/lib/clouse.js:21-36
let limit ไม่มีค่า → if (limit > 50) เป็น undefined > 50 → false เสมอ
ผล — client ขอ limit: 10000 ได้ (potential DoS ที่ GraphQL query)
V3-H5 · statLog cron — SQL injection ผ่าน string concat
ที่ — vtrc-api/api/src/lib/controllers/statLog/statLog.js:22-28, 99-104, 175-180
ค่า param จาก log service ถูก concat เข้า SQL string โดยตรง ใน 3 block query + insert 6 จุด ถ้า log service ถูก compromise → SQL injection ที่ db2 (vtrc-centralize schema)
รายละเอียดใน บท 3.8
Severity: Medium
V3-M1 · /webdeployment ไม่มี auth — รัน shell binary ได้
ที่ — vtrc-api/api/src/routes.js (execSync binary vtrc-update-uat-loadtest)
endpoint ที่ trigger deployment ไม่มี auth ไม่มี parameter check
V3-M2 · updateDelegatesExpire — fire-and-forget ไม่มี await ไม่มี log
ที่ — vtrc-api/api/src/lib/controllers/fund/fund.js:926-928
function ไม่ async ไม่ return promise ถ้า query fail จะกลายเป็น unhandledRejection ไม่มีใครเห็น debug ยาก
รายละเอียดใน บท 3.8
V3-M3 · Redis wrapper มี typo crash ที่ tr
ที่ — vtrc-api/api/src/lib/controllers/redis.controller.js:16
getCacheData มี tr เฉยๆ ใน body — ถ้า revive cache caller จะ crash ทันที (ReferenceError)
ปัจจุบันไม่ trigger เพราะ caller ทุกตัวถูก comment แต่เป็น landmine
V3-M4 · ชื่อ centralize/ ในโค้ด ≠ NestJS centralize-api
ที่ — vtrc-api/api/.env.example:36-37 + centralize/controllers/graphql.js
โฟลเดอร์และ env ชื่อ centralize / END_POINT_CENTRALIZE แต่ URL จริงชี้ GraphQL ของ cu-central-api (CLIENT_TARGET ใน .env.example อาจเป็น 8082; CODE_DEFAULT APP_PORT=8080) ไม่ใช่ NestJS centralize-api (port 3000)
รายละเอียดใน บท 3.4
V3-M5 · formatError มี console.log raw ในทุก error
ที่ — vtrc-api/api/src/index.js:61-64
production จะมี error object raw ปนอยู่ใน stdout ทำให้ grep ที่ structured log ยาก
V3-M6 · ไม่มี health check endpoint
ที่ — ไม่มีทั้งใน routes.js และ index.js
k8s ใช้ TCP probe แทน ทำให้ detect "app ready แต่ serve ไม่ได้" ไม่ได้
V3-M7 · ไม่มี graceful shutdown
ไม่มี SIGTERM handler ปิด HTTP server + DB + cron อย่างสะอาด ตอน rolling deploy request ถูกตัดกลางคัน
Severity: Low
V3-L1 · node-schedule in-process — ไม่ persist
ถ้า process restart ระหว่าง cron tick → job หาย ไม่มี retry ไม่มี dead-letter queue
V3-L2 · APP_NO leader election แบบ manual
ถ้า replica #1 ที่รัน fund cron down → cron หายทั้งระบบ ไม่มี takeover
V3-L3 · wlog synchronous I/O
fs.appendFileSync block event loop ทุก log line ใน workload หนัก response time พุ่ง
V3-L4 · GraphQLLogging plugin ไม่ log duration
debug "request ช้า" ต้องเทียบ timestamp ของ request กับ response เอง
V3-L5 · typo ที่ต้อง maintain
responError/getResposeMessage— function name ผิด แต่ใช้ทั่วระบบCONINCLE_USERNAME—vtrc-api/api/src/config.jsEncryptAll— naming inconsistent (Allsuffix)clouse— ผิดจากclause
vtrc-api Health Scorecard
วัด 10 dimension ที่สำคัญ ของ vtrc-api โดยเฉพาะ
- 🟢 เขียว — ผ่านเกณฑ์ ไม่มีปัญหา
- 🟡 เหลือง — ใช้ได้ แต่มีจุดอ่อน ต้องระวังตอนแก้
- 🔴 แดง — มีปัญหาจริง ควร refactor
สรุประดับสี
| Dimension | สี |
|---|---|
| Layering (typeDefs / resolvers / lib) | 🟡 |
| Auth pipeline | 🔴 |
| Persistence & transaction | 🔴 |
| Centralize bridge resilience | 🔴 |
| REST surface security | 🔴 |
| Background jobs | 🟡 |
| Logging & observability | 🔴 |
| Caching strategy | 🔴 |
| Config & secrets | 🔴 |
| Test coverage | 🔴 |
คะแนนรวม: 1 🟢 / 2 🟡 / 7 🔴
อ่านตารางอย่างไร — มองสีก่อน (🟢🟡🔴) แล้วค่อยอ่านหมายเหตุ
ตารางหลัก
| Dimension | สถานะ | หมายเหตุ |
|---|---|---|
| Layering (typeDefs / resolvers / lib) | 🟡 | มี 3 layer + REST bypass ที่ข้าม controller |
| Auth pipeline | 🔴 | JWT_SECRET default + RBAC bug + bypass path |
| Persistence & transaction | 🔴 | transaction arg ผิดที่ ~20+ แห่ง + syncModelToTable ที่ boot |
| Centralize bridge resilience | 🔴 | ไม่มี retry + timeout ไม่ config + circuit breaker |
| REST surface security | 🔴 | execSync qpdf + /webdeployment + /file/:id/download bypass |
| Background jobs | 🟡 | node-schedule in-process + APP_NO leader election แบบ manual |
| Logging & observability | 🔴 | sync wlog + ไม่มี metric/tracing + ไม่มี duration log |
| Caching strategy | 🔴 | Redis vestigial — caller ทุกตัว comment + typo crash ใน wrapper |
| Config & secrets | 🔴 | GCP SA key + device keys + JWT_SECRET ใน code/env default |
| Test coverage | 🔴 | ไม่มี unit test / integration test ใน repo |
สิ่งที่ต้อง maintain ต่อไป (แม้จะผิด)
รายการต่อไปนี้ดูผิด/แปลกตา แต่แท้จริงแล้วถูกใช้อยู่ทั่ว vtrc-api และ "มีคนพึ่งพาอยู่" — ห้ามแก้ทีเดียวจบ
PEDDING_CANCLEenum — สะกดผิด แต่เก็บใน DB หลายตารางแล้ว ถ้าจะแก้ต้องเขียน migration ครบทุกตารางพร้อมกันAPP_NOvalues1, 2, 3, 5(ไม่มี 4) — ใช้ใน cron leader election เปลี่ยนลำดับไม่ได้KEY_PLASMAUUID — ฝังใน business rule ของclouse.jsเปลี่ยนต้อง sync ทุกที่responError/getResposeMessagetypo — ลามไปทั้ง codebase แก้ชื่อจะกระทบ import หลายร้อยจุดcorsstypo ในapplyMiddleware— ถ้าแก้เป็นcorsต้องลบapp.use(cors())ออกด้วย ไม่งั้นซ้อนกันsignInByPassของ centralize — ถ้าจะลบ ต้องเช็กทุก caller ใน vtrc-api ก่อน เพราะบาง flow อาจจะตั้งใจใช้trในredis.controller.js:16— ถ้าจะ revive Redis cache ต้องแก้ก่อน ไม่งั้น first call crash
กฎเหล็ก — เจอแล้วไม่แน่ใจอย่าเดาเอง ให้ถามทีม HR/i3 ก่อนเสมอ
Modernization — ลำดับ ROI
ถ้าจะลงทุน refactor vtrc-api ลำดับความคุ้มค่า (จากประสบการณ์)
- แทนที่
execSyncqpdf ด้วย library หรือ worker thread — risk:reward สูงสุด ลบ S1 ออกทันที - เพิ่ม health check
/healthz+ readiness probe — investment เล็ก คุณค่ามหาศาล (k8s detect issue ได้) - ตั้ง JWT_SECRET ที่ deploy และ remove default
'secret'— fix S2 - แยก REST API ออกเป็น service ต่างหาก — PDF/Excel generation เป็น workload คนละแบบ ไม่ควรอยู่ใน GraphQL server
- เพิ่ม transaction migration — goose, atlas หรือ Sequelize migration runner แทน
syncModelToTable - แก้
transaction: targ position — sweep codebase และ fix ทุกจุด - แทนที่
node-scheduleด้วย BullMQ + Redis lock — leader election ที่เชื่อถือได้ - เพิ่ม structured logging (pino) + tracing ID — debug ง่ายขึ้นมาก
- แยก secret ออกจาก code — Vault, AWS Secrets Manager, หรือ k8s Secret
- เพิ่ม unit test + integration test — coverage สูงสุดที่ controller + repository layer
ส่วน upgrade ใหญ่ Sequelize 5 → 6, Node 12 → 20 LTS, Apollo v2 → v4 — เป็นเรื่องใหญ่ ไม่คุ้มถ้าไม่มี feature ใหม่ที่ต้องการ
เคล็ดลับตอนทำงานจริง
ก่อนแก้ resolver ไหน — อ่าน 3 อย่างนี้ก่อน
- typeDef ของ operation นั้น — ดู
@authdirective - controller ที่ resolver เรียก — ดูว่ามี
transaction: tpattern ไหม - repository ที่ controller เรียก — ดูว่ามี centralize call ไหม
อย่า refactor พร้อมกันหลายอย่าง
กฎ — หนึ่ง change ต่อ PR เสมอ เพราะทุก change มี risk ถ้า refactor 3 อย่างพร้อมกันแล้วพัง → ไม่รู้ว่าสาเหตุมาจากอะไร
"ใช้งานได้" ≠ "ปลอดภัย"
vtrc-api ใช้งานมาหลายปีแล้ว แต่มี hole หลายจุด (ดู V3-S1 ถึง V3-S4) ก่อนจะ expose endpoint ใหม่สู่ internet → ผ่าน security review เสมอ
ถ้าเจอ "ทำไมไม่ crash ทั้งที่ควร crash"
อาจจะเป็นเพราะ
- caller ของ function นั้นถูก comment หมดแล้ว (เหมือน Redis)
- function นั้น throw ใน try/catch ที่ swallow (เหมือน
setCacheDataที่ return false ตอน error) - transaction ไม่ทำงานจริง ทำให้ race condition ไม่ manifest ชัด
Debugging order ที่แนะนำ
- ดูที่
error_{yyyy}-{mm}-{dd}.logใน container ก่อน — raw สุด - ดูที่
request_*.logและresponse_*.logเพื่อดู operation name + variables (redacted) - ถ้าเป็น GraphQL → เปิด Playground ที่
/api/graphqlใน non-prod env เพื่อ reproduce - ถ้าเป็น centralize call → ดูที่
centralize/controllers/*.jsแล้ว trace ไปที่END_POINT_CENTRALIZE
จบ Volume 3
ถ้าอ่านมาถึงตรงนี้ — คุณน่าจะเข้าใจ vtrc-api ในระดับที่สามารถ maintain, debug, และ plan modernization ได้
สิ่งที่ควรทำต่อ
- อ่าน source จริง — คู่มือเป็นแผนที่ แต่ก่อนแก้ของจริงต้องอ่าน code ใน context ของตัวเอง
- ทำ security audit — Volume 9 จะเจาะ V3-S1 ถึง V3-S4 ในระดับ deeper
- เขียน runbook — Volume 13 จะมี incident response playbook
บทถัดไปขอแนะนำ
- ถ้าอยากเข้าใจ data model ลึก → Volume 9 Data Model
- ถ้าอยากเข้าใจ security เชิงระบบ → Volume 10 Security
- ถ้าอยากเข้าใจ frontend → Volume 7 vtrc-web หรือ Volume 8 backoffice
ขอบคุณที่อ่านมาถึงตรงนี้