2.8 · ข้อจำกัดทางสถาปัตยกรรม + debt
บทสุดท้ายของ Volume 2 — เล่า "สิ่งที่ผิดธรรมดา" และ "จุดที่เปราะบาง" ที่ต้องรู้ก่อนจะแตะโค้ด
ไม่ใช่รายการ bug ครบถ้วน — รายละเอียดเชิงลึกอยู่ใน Volume 10 Security และ Volume 15 Tech Debt — บทนี้เป็นแค่ "things you should know ตอนเริ่มงาน"
ข้อจำกัดเชิงโครงสร้าง
1. ไม่มี API gateway จริง
Nginx ทำแค่ TLS + static — ไม่มี rate limit, ไม่มี auth, ไม่มี routing logic ทุกอย่างผ่าน vtrc-api ที่เป็น Node.js ตัวเดียว
ผล — ถ้า vtrc-api ล่ม ทั้งระบบล่ม ไม่มี fallback
2. ไม่มี bounded context
vtrc-api มี GraphQL schema เดียวที่รวม 43 typeDef (vtrc-api/api/src/typeDefs/) — ไม่ได้แบ่งตาม domain (Identity, Payroll, Leave, Welfare, Fund)
ผล — แก้อะไรครั้งหนึ่งกระทบได้หลายส่วน, code review ยาก
3. 2 transport surfaces ที่ทับซ้อน
GraphQL (vtrc-api/api/src/typeDefs) + REST (vtrc-api/api/src/routes.js 1302 บรรทัด) คู่กัน บาง logic เข้าถึงได้ 2 ทาง
ผล — auth policy ต้องรักษา 2 ที่ให้ sync กัน ซึ่งทำไม่ครบ (REST บาง route ไม่มี auth)
4. ไม่มี migration runner
centralize-api ใช้ TypeORM synchronize: false แต่ไม่มี migration runner vtrc-api ใช้ syncModelToTable() (vtrc-api/api/src/lib/model.js) ที่ auto-sync Sequelize model → DB schema
ผล — schema drift เกิดอย่างเงียบ เวลาแก้ model แล้ว deploy ไม่ครบทุก env
จุดที่เปราะบางที่สุด (severity ranking)
Severity: Critical
S1 · execSync('qpdf --encrypt ${password}...') — command injection
ที่ — vtrc-api/api/src/routes.js:73, 120, 227, 277, 355
password ที่ interpolate เข้า shell มาจาก user (passwordPDF, identityCard) — ถ้าค่ามี shell metacharacter ก็ execute ได้
วิธีแกะ — ถ้าเจอ PDF endpoint ที่ generate ด้วย qpdf → assume vulnerable จนกว่าจะพิสูจน์เป็นอย่างอื่น
S2 · /webdeployment รัน shell binary โดยไม่มี auth
ที่ — vtrc-api/api/src/routes.js:975-984
execSync("vtrc-update-uat-loadtest")ไม่มี auth ไม่มี parameter check — ใครยิงปุ๊บรันปุ๊บ
S3 · /file/:id/download ไม่ตรวจ apiKey
ที่ — vtrc-api/api/src/routes.js:464-495
sibling endpoint /file/:id มี apiKey gate (:389-462) แต่ตัว download ไม่มี → ใครมี file id ก็ download ได้
S4 · phpMyAdmin เปิดสู่สาธารณะ
ที่ — vtrc-api/docker-compose.yml:35-46
PMA_ABSOLUTE_URI: http://vtrcapi.redcross.or.th/db-management/ — DB admin เปิดที่ public URL
S5 · JWT_SECRET default เป็น 'secret'
ที่ — vtrc-api/api/src/config.js:12
ถ้า env ไม่ตั้ง → ใครก็ forge JWT ได้
Severity: High
H1 · signInByPass — mint JWT โดยไม่ตรวจ password
ที่ — cu-central-api/main-api/src/lib/auth/auth.js:173-231 + expose ที่ routes.js:31-37
POST /api/auth/signinbypass
body: { username, org, apiKey }
→ mint JWT ทันที ไม่ต้อง passwordH2 · Secret ใน git
centralize-api/.env:10,16— DB password + JWT secret (plaintext)cu-central-api/docker-compose-dev.yml:56,70— MSSQL SA password + LDAP admin passwordvtrc-api/vtrc-gcp-sa-key.json— GCP service account keyvtrc-api/api/src/config.js:41-149— prod device keys
H3 · LDAP TLS verification ปิดอยู่
ที่ — cu-central-api/main-api/src/lib/auth/activeDirectory.connector.js:21
rejectUnauthorized: false → MITM ได้
H4 · Node 12 EOL
ที่ — cu-central-api/main-api/Dockerfile:1
Node 12 EOL เมื่อ April 2022 — ไม่มี security patch แล้ว
H5 · Redis cache ใช้ไม่ได้จริง
ดู บท 2.5 — call site ส่วนใหญ่ comment ไว้ → ระบบช้ากว่าที่ควร
Severity: Medium
M1 · cron leader election ผ่าน env var
ที่ — vtrc-api/api/src/crons.js:14
ถ้า replica #1 down → cron หายทั้งระบบ ไม่มี fallback
M2 · ไม่มี health check endpoint
ไม่มี /health หรือ /readiness → Docker หรือ load balancer ไม่รู้สภาพจริง
M3 · Nginx network_mode: host
ที่ — vtrc-api/docker-compose.yml:35
container share host network → ไม่มี port isolation
M4 · Frontend ปิด console.log ทั้งหมด
ที่ — vtrc-web/src/containers/App/index.js:15
debug ต้องเปิดเองทุกครั้ง แล้วอย่าลืมปิดกลับ
M5 · _bk files จำนวนมาก
vtrc-web/src/graphql/queries_bk*.js, routes_bk*.js ฯลฯ — ประมาณ 50% ของ LOC เป็น dead code
ผล — code search ช้า, reviewer สับสน
Severity: Low (nuisance)
L1 · Typo ที่ต้อง maintain
PEDDING_CANCLE(ผิดจากPENDING_CANCEL) — เป็น enum ที่ใช้ในหลายที่แล้ว ต้อง maintain ต่อไปCONINCLE_USERNAME(ผิดจากCONICLE) —vtrc-api/api/src/config.js:34nameENvsnameEn(case ไม่เหมือนกัน) —vtrc-api/api/src/config.js:323, 335querries_prd.js(ผิดจากqueries) —vtrc-web/src/graphql/finanace.jsresolver (ผิดจากfinance) —cu-central-api/main-api/src/resolvers/
L2 · "carpark-api" log filename ผิด project
ที่ — cu-central-api/ecosystem.config.js:7-8
copy-paste จากอีก project ไม่ได้แก้
L3 · StimulSoft ติดตั้ง 2 ทาง
vtrc-rc-backoffice/public/index.html:29-33 โหลดเป็น script tag + package.json มีเป็น npm dep — ติดตั้งซ้อนทำกัน
ตาราง Health Scorecard
จาก Audit Doc 02 (docs/audit/02-architecture-map.md)
- 🟢 เขียว — ผ่านเกณฑ์ ไม่มีปัญหา
- 🟡 เหลือง — ใช้ได้ แต่มีจุดอ่อน ต้องระวังตอนแก้
- 🔴 แดง — มีปัญหาจริง ควร refactor
| Repo | 🟢 | 🟡 | 🔴 | n/a |
|---|---|---|---|---|
vtrc-api | 0 | 5 | 3 | 0 |
vtrc-web | 2 | 1 | 3 | 2 |
backoffice | 1 | 1 | 4 | 2 |
centralize-api | 5 | 0 | 3 | 0 |
cu-central-api | 1 | 5 | 2 | 0 |
| Dimension | vtrc-api | vtrc-web | backoffice | centralize-api | cu-central-api |
|---|---|---|---|---|---|
| Layering clarity | 🟡3-layer + REST bypass | 🔴folder-by-type | 🔴folder-by-type + 16 axios | 🟢NestJS modules | 🟡mirror of vtrc-api |
| Bounded-context | 🔴1 schema, 43 modules | 🔴1 queries.js | 🔴1 queries.js | 🟢per-module entity | 🔴1 schema, 30 modules |
| Dependency direction | 🟡 | 🟡 | 🟡 | 🟢DI | 🟡 |
| Transport coupling | 🔴GraphQL + REST + execSync | 🟢 | 🔴GraphQL + 16 axios | 🟢REST + Swagger | 🟢 |
| Auth boundary | 🟡directive + bypass | 🟢 | 🟢 | 🟢JwtAuthGuard + @Public | 🟡directive |
| Reproducible deploy | 🟡 | — | — | 🔴no lockfile | 🟡 |
| HA / scaling | 🟡manual replicas | — | — | 🔴single instance | 🔴single instance |
| Testability | 🔴no tests, execSync inline | 🔴 | 🔴 | 🟢jest + supertest | 🟡mocha/chai |
อ่านตารางอย่างไร — มองสีก่อน (🟢🟡🔴) แล้วค่อยอ่านหมายเหตุใต้สี ตารางสรุปด้านบนช่วยเห็นภาพรวมว่า repo ไหนแดงมากที่สุด
สิ่งที่ต้อง maintain ต่อไป (แม้จะผิด)
รายการต่อไปนี้ดูผิด/แปลกตา แต่แท้จริงแล้วถูกใช้อยู่ทั่วระบบและ "มีคนพึ่งพาอยู่" — ห้ามมาแก้ทีเดียวจบ เพราะกระทบ production หลายจุดพร้อมกัน การแก้แต่ละอันต้องประสานงานและทำควบคู่กันหลายที่ (DB, config, code) มิฉะนั้นระบบจะพังแบบเงียบ
PEDDING_CANCLEenum — สะกดผิดมาตั้งแต่ต้น (ที่ถูกคือPENDING_CANCEL) แต่ค่านี้เก็บอยู่ใน หลายตาราง ของ DB แล้ว ถ้าจะแก้ชื่อ ต้องเขียน migration ครบทุกตารางพร้อมกัน ไม่งั้น query เทียบ enum จะ match ไม่ติดKEY_PLASMA,KEY_CU_H,KEY_HR(UUID) — UUID คงที่ที่ฝังตรงใน business rule ของแต่ละระบบ (PLASMA / CU-H / HR) ถ้าจะเปลี่ยน ต้องเปลี่ยนให้ตรงกันทุกที่ที่อ้างถึง ไม่งั้นกฎทางธุรกิจจะตีความผิดEND_POINT_CENTRALIZEURL pattern — env var ที่ใช้ในทุก environment config (local / UAT / prod) เปลี่ยน pattern ที่เดียวไม่ได้ ต้อง sync ครบทุก env มิฉะนั้น service เรียกข้ามไม่ถึงกันAPP_NOvalues1, 2, 3, 5(ไม่มี 4) — ลำดับนี้ข้ามเลข 4 โดยเจตนา/มรดกตกทอด และถูกใช้ใน cron leader election เปลี่ยนลำดับหรือเติม 4 เข้าไปแล้วลำดับการเลือก leader จะสับสน → cron รันซ้อนหรือไม่รันเลย- CORS allowlist (5 origins) — รายการ origin ที่อนุญาตฝังอยู่ใน config ถ้าลบหรือเพิ่มโดยไม่ครบ → frontend บางตัวจะถูก browser block ที่หน้าโพสต์
กฎเหล็ก — เจอแล้วไม่แน่ใจอย่าเดาเอง ให้ถามทีม HR/i3 ก่อนเสมอ
เคล็ดลับตอนทำงานจริง
ถ้าทำ modernization — เริ่มที่ไหน
จากประสบการณ์ — ลำดับความคุ้มค่า (ROI)
- เพิ่ม health check + observability — investment เล็ก คุณค่ามหาศาล (เห็นปัญหาก่อน user แจ้ง)
- แทนที่
execSyncqpdf — เปลี่ยนเป็น library pure JS หรือ worker thread - แยก REST ออกเป็น service ต่างหาก — PDF/Excel generation เป็น workload คนละแบบ ไม่ควรอยู่ใน GraphQL server
- เพิ่ม migration runner — goose, atlas, หรือ TypeORM migration
- แทนที่ cron ด้วย BullMQ + Redis lock — leader election ที่เชื่อถือได้
ส่วน React 16 → 18, Apollo v2 → v3+, Sequelize → TypeORM — เป็นเรื่องใหญ่ ไม่คุ้มถ้าไม่มี feature ใหม่ที่ต้องการ
อย่า refactor พร้อมกันหลายอย่าง
เพราะทุก change มี risk — ถ้า refactor 3 อย่างพร้อมกันแล้วพัง → ไม่รู้ว่าสาเหตุมาจากอะไร
กฎ — หนึ่ง change ต่อ PR เสมอ
"มันใช้งานได้" ≠ "มันปลอดภัย"
VTRC ใช้งานมาหลายปีแล้ว แต่มี hole หลายจุด (ดู S1-S5 ข้างบน) — ถ้าทำ penetration testing จะพบจำนวนมาก
ก่อนจะ expose endpoint ใหม่สู่ internet → ผ่าน security review เสมอ
จบ Volume 2
ถ้าอ่านมาถึงตรงนี้ — คุณน่าจะเข้าใจภาพใหญ่ของ VTRC พอที่จะเริ่มแตะโค้ดได้
บทถัดไปขอแนะนำ
- ถ้าเป็น engineer ที่จะแก้ไข backend → Volume 3 vtrc-api Deep Dive
- ถ้าเป็น engineer ที่จะแก้ไข frontend → Volume 7 vtrc-web หรือ Volume 8 backoffice
- ถ้าเป็น architect ที่วางแผน modernization → Volume 15 Tech Debt + Volume 16 Modernization
- ถ้าเป็น DevOps → Volume 13 Infrastructure + Volume 14 Runbooks
ขอบคุณที่อ่านมาถึงตรงนี้