3.2.1 · Repository map
บทนี้เจาะโครงสร้างของ cu-central-api/main-api/src/ เพื่อให้อ่านและค้นหาโค้ดได้ง่ายขึ้น พร้อมทั้งระบุ typos และไฟล์ที่ไม่ได้ใช้แล้ว (dead files) ที่ต้องระมัดระวังเมื่อแก้ไขโค้ด
โครงสร้าง repo ระดับบน
cu-central-api/ (independent git repo บน Bitbucket)
├── .gitignore
├── Dockerfile-nginx nginx proxy image
├── Dockerfile-redis redis image
├── docker-compose-dev.yml dev compose
├── docker-compose.yml 3x cu-cen-api replicas + redis + nginx
├── ecosystem.config.js PM2 config (legacy, ไม่ได้ใช้ใน prod)
├── package-lock.json
├── pm2Cmd.txt
├── database/ legacy DB scripts
├── ldap/ LDAP-related resources
├── nginx/ nginx conf.d
├── redis-db/ redis.conf + data volume
├── web-site/ static nginx html
└── main-api/ ★ service หลัก (Volume นี้ focus ที่นี่)
├── .dockerignore
├── .env.develop-test UAT secrets (committed — ดู Volume 12 Security)
├── .env.example sanitized template
├── Dockerfile FROM node:12.18.3
├── index.js 1-line entrypoint: import './src'
├── package.json
├── test/validator.js test file เดียว (mocha)
└── src/ ★ application coderepo อยู่บน Bitbucket (bitbucket.org/i3dev/cu-central-api) ภายใต้ user i3wp ตามที่ระบุใน cu-central-api/main-api/package.json:13 — ต่างจาก repo อื่นที่อยู่บน GitHub ส่วนใหญ่
main-api/src/ top-level files
| File | LOC | หน้าที่ | บทที่เจาะลึก |
|---|---|---|---|
index.js | 118 | Apollo + Express bootstrap | 3.2.2 |
config.js | 110 | env var destructuring (50+ vars) | 3.2.2 |
connector.js | 196 | 6 Sequelize instances | 3.2.2 |
cron.js | 32 | node-schedule jobs (3 jobs, gated ด้วย APP_NO) | 3.2.2 |
routes.js | 64 | REST routes (signin/signout/token/conicle) | 3.2.3 |
testScript.js | 167 | dev-only scratch runner (gated ด้วย !IS_PRODUCTION) | — |
directives/auth.js | 79 | @auth schema directive | 3.2.4 |
directives/index.js | 6 | directive registry | 3.2.4 |
main-api/src/ subdirectories
| Directory | ไฟล์ | หมายเหตุ |
|---|---|---|
typeDefs/ | 32 .js (29 root + 3 ใน hr/) + index.js | hand-authored GraphQL SDL — รายละเอียดใน 3.2.6 |
resolvers/ | 28 .js (25 root + 3 hr/ + 1 bank/) + index.js | 1,826 LOC total |
models/ | 6 subdirs + index.js | recursive loader — รายละเอียดใน 3.2.2 |
lib/ | 28 root .js + 31 subdirs | bulk ของ business logic |
directives/ | 2 files | มีแค่ @auth |
email-templates/ | requestResetPassword/ | nodemailer pug templates |
lib/ subdirectories (31 โฟลเดอร์ ตาม bounded context คร่าว ๆ)
lib/
├── auth/ LDAP + AD + JWT + apiKey + session
├── bank/ book bank lookup
├── cache/ Redis cache helper
├── childTuition/ child tuition allowance
├── class/ employee class
├── conicle/ Conicle SFTP integration
├── dataConverter/ data shape converter
├── finance/ employee finance (★ typo: finanace.js)
├── his/ HIS heal-card API
├── hr/ HR mutations (taxes/address/phone)
├── hrPerProbation/ probation check
├── hrmi/ HRMI HTTP API + leave/medical
├── mailer/ nodemailer wrapper
├── masterData/ master data CRUD
├── notification/ SMS gateway
├── organization/ org unit lookup
├── otp/ OTP generation + verify
├── personalProfile/ employee personal profile
├── phoneNumber/ phone change log
├── referenceData/ reference data
├── salaryCert/ salary certificate
├── session/ Redis session store
├── sso/ SSO integration (★ committed certs)
├── statLog/ stat logging
├── studyLeaveRequest/ study leave
├── transferFile/ file transfer helper
├── twoWay/ 2-way employee sync
├── userIdGenerator/ userId mapping generator
├── userIdsMapping/ userIdsMapping table ops
├── userVerification/ user verification flow
└── verifyCode/ verify code flowTypos ใน production code
Codebase มี typos หลายจุดที่ "ship แล้วเปลี่ยนไม่ได้" เพราะอยู่ใน GraphQL schema สาธารณะ
finanace (finance สะกดผิด) — 3 จุดที่ consistent กัน
| ไฟล์ | รายละเอียด |
|---|---|
lib/finance/finanace.js | ชื่อไฟล์เอง |
resolvers/finanace.js | resolver file (26 LOC) |
typeDefs/finance.js:5,11 | type EmployeeFinanace + query employeeFinanceType |
อ่านเจอครั้งแรกจะนึกว่ามี 2 files (finance + finanace) แต่แท้จริงแล้ว typeDef ใช้ชื่อโฟลเดอร์ที่สะกดถูก (finance/) แต่ชื่อไฟล์ข้างในสะกดผิด
typos อื่น ๆ ใน GraphQL schema
| Type / Query | ควรเป็น | ไฟล์ |
|---|---|---|
EmployeeFinanace | EmployeeFinance | typeDefs/finance.js:5 |
OrgazationAddress | OrganizationAddress | typeDefs/organization.js |
Subscribtion | Subscription | typeDefs/root.js:17 |
GraphQL schema เป็น contract สาธารณะ — เปลี่ยนชื่อไม่ได้ถ้าไม่ break client ทั้งหมด (web + mobile + backoffice ที่ query ผ่าน vtrc-api)
Dead files และ legacy artifacts
typeDefs/payrollSlip_removed.js (112 LOC)
// ไฟล์นี้ไม่ถูก import โดย typeDefs/index.js — dead SDL- เป็นเวอร์ชันเก่าของ
payrollSlip.jsที่ใช้empCodeเป็น parameter แทนprofileKey(ที่ใช้ในปัจจุบัน) - ไม่ถูก import โดย
typeDefs/index.jsจึงไม่ปรากฏใน live schema - แต่ยังอยู่ใน repo เพราะเป็น "schema archaeology" reference
src/testScript.js (167 LOC)
if (!IS_PRODUCTION) {
testScript()
}เป็น scratch IIFE ที่ใช้สำหรับรัน one-off data-fix scripts (เช่น สร้าง AD user จำนวนมาก, mask ID card) ที่ผ่านมาในแต่ละเดือน/ปี
- gated โดย
NODE_ENV !== 'production' - แต่ top-level IIFE ยัง fire ทุกครั้งที่ boot ใน UAT/dev
- เรียก
checkAdAuthenConnection()ซ้ำกับ boot-time check ในindex.js:93
connector.js debug comment
// const dbHRMI_test = new Sequelize("dbHRMI_test", "root", "1q2w3e4r00", {
// host: "localhost",
// port: 3303,
// dialect: "mariadb",
// ...Comment ทิ้ง local-dev config พร้อม plaintext password 1q2w3e4r00 — pattern เดียวกับ vtrc-api/api/.env.example ที่มี plaintext credential commit อยู่ใน codebase เป็น evidence ว่าทีมเคยใช้ local MariaDB port 3303 + password 1q2w3e4r00 ในการ dev
dbHRMI_test connection (dead weight)
const dbHRMI_test = new Sequelize("dbHRMI_test", DB_USERNAME, DB_PASSWORD, {
host: DB_HOST,
port: DB_PORT,
dialect: DB_SCHEMA,
// ...
})
export { db, dbHr, dbHrTemp, dbMasDb, dbHRMI_test, dbHRMI_Center_HIS }// walk(path.join(__dirname, 'hrmi_test'))
// .filter(...)
// .forEach(file => {
// const model = require(...).default(dbHRMI_test, ...)
// Models[model.name] = model
// })- database name
"dbHRMI_test"เป็น hardcoded literal ไม่ได้มาจาก env (ต่างจาก connection อื่น ๆ ที่อ่านจากprocess.env) - exported ที่
connector.js:195แต่ model loader (models/index.js:95-105) commented out - ผล — connection pool ที่มี
pool.max: 100เปิดขึ้นทุก boot แต่ไม่มี model ผูก ทำให้เปลือง MSSQL connection 100 session ที่ไม่มีใครใช้
Fix: ลบทั้ง connection ใน connector.js และ loader comment ใน models/index.js ออกพร้อมกัน
SSO certificates committed
cu-central-api/main-api/src/lib/sso/
├── DigiCert_Global_Root_CA.cer root CA
├── TPRDWSSOWEB01.crt prod SSO cert
└── TUATWSSOWEB01.crt UAT SSO certcertificate ของ SSO service (ทั้ง prod และ UAT) commit อยู่ใน repo — เป็น public cert ไม่ใช่ private key จึงไม่ใช่ secret leak โดยตรง แต่เป็น anti-pattern ที่ควร fetch จาก secret manager หรือ truststore แทน รายละเอียดการใช้งานอยู่ใน 3.2.7
สรุปขนาด codebase
| ส่วน | ขนาด |
|---|---|
| SDL (typeDefs) | 2,629 LOC (32 ไฟล์) |
| Resolvers | 1,826 LOC (28 ไฟล์) |
index.js (bootstrap) | 118 LOC |
connector.js (DB) | 196 LOC |
lib/ (business logic) | ~10,000+ LOC (31 subdirs) |
| Models | ~50+ files (6 subdirs) |
| รวมประมาณ | ~15,000 LOC |
เล็กกว่า vtrc-api (routes.js อย่างเดียว 77 KB) แต่มี complexity ของตัวเอง — โดยเฉพาะ multi-tenant sharding ใน 3.2.5
ทำไมถึงมี typos และ dead code มาก
อ่านจากประวัติ — codebase นี้ port มาจาก HRMI leave-management system เก่า (hrmi.js เป็น SDL ใหญ่สุดที่ 394 LOC ทุก type มีพารามิเตอร์ sourceDb ซึ่งบ่งบอกว่าออกแบบมาให้ใช้กับหลาย shard) เมื่อ migrate หลายรอบโดยไม่มี lint ที่เข้มงวด (มี dep standard ใน package.json:50 แต่ไม่มี lint script) — typo และ dead file จึงสะสม
ใน Wave 1 modernization (Volume 19 Roadmap) lint ที่ลบ no-dupe-keys, no-unused-vars จะ catch ปัญหาพวกนี้ตอน commit
เคล็ดลับตอนทำงานจริง
ก่อนแก้ resolver ที่ดูแปลก
เช็กว่าชื่อไฟล์สะกดถูกไหม — finanace.js ไม่ใช่ bug เป็นชื่อที่ intentional และ GraphQL schema contract อ้างถึง
ถ้าเห็นไฟล์ที่ไม่ถูก import จาก index.js
อย่าลบทันที — เช็ก *_bk*.js, *_copy*, *_removed* ก่อน อาจเป็น "schema archaeology" ที่ team เก็บไว้
ก่อนเพิ่มไฟล์ typeDefs/resolvers ใหม่
อย่าลืม import ใน typeDefs/index.js หรือ resolvers/index.js — payrollSlip_removed.js คือตัวอย่างที่ลืม import จนกลายเป็น dead file
อ่านต่อ → 3.2.2 Bootstrap + 6 DB connections