03 · Persistence & integrations — vtrc-common
บทนี้อธิบาย data stores, entity schema, SQL migration history, disk layout ของไฟล์, และ inbound integrations จาก consumer — outbound ไป service อื่น ไม่พบ ในโค้ด src/
อ้างอิง branch: prod
1. Data stores overview
| Store | Connection / path | Database / location | หลักฐาน |
|---|---|---|---|
| MSSQL | TypeORM name main | vtrc_common @ 10.188.128.61:1433 | app.module.ts:18-37, .env:7-12 |
| Local disk | process.cwd()/uploads/{module}/{YYYY-MM-DD}/ | container filesystem (compose mount ./uploads เฉพาะ service dev) | file.service.ts:53-58, docker-compose.yml:11-13 |
TypeORM options ที่สำคัญ (app.module.ts:21-34):
| Option | ค่า | ความหมาย |
|---|---|---|
type | mssql | driver |
synchronize | false | ไม่ auto-alter schema |
logging | ['error', 'query'] | log SQL ทุก query ใน log |
extra.trustServerCertificate | true | ข้าม cert validation |
extra.encrypt | false | ไม่ encrypt TDS |
Config mapping: configuration.ts อ่าน DB_HOST/DB_PORT/DB_USER/DB_PASSWORD/DB_NAME → db.* (config/configuration.ts:3-10)
2. Entity model — address hierarchy
ความสัมพันธ์ 4 ระดับ:
m_province (1)
└── m_aumphur (N) province_id FK
└── m_tumbol (N) aumphur_id FK
└── m_zipcode (1) tumbol_id FKBaseEntity
คอลัมน์ร่วม (vtrc-common/src/entities/base.entity.ts (branch: prod):3-18):
| Property | Column / type | หมายเหตุ |
|---|---|---|
id | PK increment | |
createdDate | datetime, default CURRENT_TIMESTAMP | |
createdBy | varchar(50), nullable | |
updatedDate | datetime | |
updatedBy | varchar(50), nullable |
Drift กับ SQL เดิม — script MariaDB ใช้ชื่อคอลัมน์ create_date / create_by / update_date / update_by (sql/common_20230214_Initial_Structure_Table.sql:13-16) ในขณะที่ TypeORM entity map เป็น createdDate / createdBy โดยไม่มี @Column({ name: 'create_date' }) ชัดเจนใน BaseEntity — ถ้า schema MSSQL หลัง convert ใช้ชื่อตรงกับ entity property หรือมี mapping อื่นใน DB จริง ต้องยืนยันตอน migrate (ดู UNVERIFIED ใน scorecard)
MProvince → table m_province
vtrc-common/src/entities/m_province.entity.ts (branch: prod):5-21
| Property | DB column | Type |
|---|---|---|
provinceCode | province_code | varchar(10) |
provinceNameEN | province_name_en | varchar(255) |
provinceNameTH | province_name_th | varchar(255) |
isActive | is_active | bit |
aumphur | — | OneToMany → MAumphur cascade |
MAumphur → table m_aumphur
m_aumphur.entity.ts:6-26 — province_id JoinColumn, aumphur_code / names / is_active, OneToMany tumbol
MTumbol → table m_tumbol
m_tumbol.entity.ts:6-26 — aumphur_id, tumbol codes/names, OneToOne zipcode cascade
MZipcode → table m_zipcode
m_zipcode.entity.ts:5-16 — property ชื่อ code map ไป column zip_code
3. Entity model — bank & file
BankEntity → table bank
vtrc-common/src/entities/bank.entity.ts (branch: prod):4-20
| Property | Column | หมายเหตุ |
|---|---|---|
bankCode | bankCode varchar(3) | camelCase ใน DB |
bankBranch | bankBranch varchar(4) | '0000' = HQ ใน business logic |
bankNameTh / bankNameEn | ตามชื่อ | |
isActive | isActive bit | service เปรียบเทียบเป็น number 1 |
FileEntity → table file
vtrc-common/src/entities/file.entity.ts (branch: prod):4-26
| Property | บทบาท |
|---|---|
module | namespace โฟลเดอร์ เช่น benefit |
filePath | path สัมพัทธ์เช่น /uploads/benefit/2024-01-15/....png |
fileName | ชื่อเดิมจาก client |
fileType | MIME จาก mime.lookup |
sort | ลำดับในชุดอัปโหลด |
description | optional |
isCancel | soft-delete flag (default 0); delete ตั้งเป็น 1 |
4. SQL scripts — migration history
โฟลเดอร์ vtrc-common/sql/ (branch: prod) commit ไว้เป็นหลักฐาน schema — ไม่ ถูก TypeORM รันอัตโนมัติ
| ไฟล์ | เนื้อหาโดยสรุป |
|---|---|
common_20230214_Initial_Structure_Table.sql | MariaDB CREATE TABLE + seed จังหวัด (m_province รหัส 10, 11, …) — บรรทัดต้นใช้ backtick MySQL syntax |
common_20230313_Convert_MariaDB_To_MsSql.sql | script convert ไป MSSQL (ชื่อไฟล์ยืนยันช่วงมี.ค. 2023) |
common_20230313_Master_Data.sql | master data เพิ่มเติมหลัง convert |
ตัวอย่างหัวตารางจาก initial script:
CREATE TABLE IF NOT EXISTS `m_province` (
`id` INT(11) NOT NULL AUTO_INCREMENT,
`province_code` VARCHAR(10) NOT NULL,
`province_name_en` VARCHAR(255) NOT NULL,
`province_name_th` VARCHAR(255) NOT NULL,
`is_active` TINYINT(1) NOT NULL DEFAULT 1,
...
UNIQUE KEY `unq_province_code` (`province_code`)
) ENGINE = INNODB;แหล่ง: vtrc-common/sql/common_20230214_Initial_Structure_Table.sql (branch: prod):4-23
Seed ใช้ตัวแปร @updateBy := 'INIT_PRD_2023-02-15' (:1, :26-27) — บ่งชี้ว่าเคยเตรียมข้อมูล production ตั้งแต่ ก.พ. 2023
5. File persistence on disk
Layout ที่ FileService สร้าง:
{process.cwd()}/uploads/
└── {module}/ e.g. benefit
└── {YYYY-MM-DD}/ moment().format
└── {random}.{ext}แหล่ง: file.service.ts:52-58
docker-compose.yml service dev mount ./uploads:/usr/src/app/uploads (:11-13) แต่ service prod ใน compose ไม่ได้ mount uploads (:17-28) — ถ้า deploy ด้วย compose prod โดยไม่มี volume ภายนอก ไฟล์จะหายเมื่อ recreate container
MULTER_DEST=./uploads ใน .env:14 ไม่ถูกอ่านใน FileService — dead config
Soft-delete ไม่แตะ disk → orphan files สะสมเมื่อมีการ delete บ่อย (ดู QUAL ใน scorecard)
6. Query patterns ต่อตาราง
| ตาราง | วิธีเข้าถึงหลัก | ปลอดภัย? |
|---|---|---|
m_province | TypeORM find | ใช่ |
m_aumphur (+ relations) | TypeORM find / QueryBuilder | ใช่ (ยกเว้นไม่ใช้ใน raw paths) |
m_tumbol / m_zipcode | join ผ่าน relations หรือ raw SQL | raw SQL 2 จุด = injection |
bank | TypeORM find + Not() | ใช่ |
file | TypeORM save / findOne / find + In() | ใช่ (แต่ auth อ่อน) |
7. Outbound integrations
ไม่พบ ใน src/modules/** และ src/main.ts:
- ไม่มี
HttpService/axioscall ออกไปcentralize-api,vtrc-api, LDAP, หรือ LINE axiosอยู่ในpackage.jsondependencies (package.json:36) แต่ไม่ถูก import ใน module address/bank/file ที่ตรวจแล้วCENTRALIZE_SERVER=http://10.188.128.60:3000/ใน.env:16— vestigial (pattern เดียวกับ chat-center-api)
สรุป: vtrc-common เป็น leaf service — มีแต่ inbound HTTP ไม่เรียก service อื่นต่อ
8. Inbound integrations (สรุป — รายละเอียดเต็มใน 02)
Backend → vtrc-common
| Caller | Env / base | Endpoints | หลักฐาน |
|---|---|---|---|
benefit-api | VTRC_COMMON_SERVER | file/save, file/delete, file/getFileArray | disbursement.service.ts:143+, payments.service.ts:902, wdhospitals.service.ts:67 |
benefit-api ยังมี UploadService เก็บไฟล์ local disk คนละกลไก (benefit-api docs) — module ที่เรียก vtrc-common คือ disbursement / payments / wdhospitals ไม่ใช่ทุก welfare flow
Frontend / Mobile → vtrc-common (ตรงจาก client)
| Caller | Base URL resolution | Endpoints |
|---|---|---|
vtrc-web | urlCommon() / checkLinkURL() ใน auth.js | address dropdowns ใน welfare disaster + recruit contact |
vtrc-rc-backoffice | axios-common.js prod/UAT switch | address ผ่าน axiosCommon; bank hardcode ใน OtherPaymentBank |
vtrc-mobile | ${HOST}/vtrc-common/api/v1/... | address ใน withdraw/recruitment; file upload/preview |
เพราะ frontend เรียกตรง:
- CORS ต้องเปิด (ปัจจุบัน
origin: '*') @Public()บน address/bank ทำให้ไม่ต้องแนบ JWT จาก browser (สะดวก แต่ขยาย attack surface ของ SQLi)- การเปลี่ยน hostname ต้องแก้หลายไฟล์ hardcode — ไม่มี single service discovery
Vestigial config เท่านั้น
| Repo | รายละเอียด |
|---|---|
job-scheduler-api | env VTRC_COMMON_SERVER ไม่มี call ใน src/ |
recruitment-api | commonServer ใน configuration ไม่มี call ใน src/ |
recruitment-web | URL comment ปิดใน axios-common.js |
9. Cron / queue / messaging
| ชนิด | พบใน repo? |
|---|---|
@nestjs/schedule | ไม่มีใน package.json |
| Bull / queue | ไม่มี |
| Kafka / MQTT | ไม่มี |
| WebSocket | ไม่มี |
งานทั้งหมดเป็น request-response HTTP เท่านั้น
10. Observability ที่เกี่ยวกับ persistence
- TypeORM
logging: ['error', 'query']→ SQL ทุกคำสั่ง (รวมค่า parameter ในบางโหมด) อาจรั่วเข้า log ของI3GatewayLogger JwtAuthGuard.handleRequestloguserทั้งก้อนเป็น JSON (jwt-auth.guard.ts:28) — กระทบเฉพาะ route ที่ไม่ใช่@Public()- ไม่มี metrics / health endpoint ในโค้ดที่ตรวจแล้ว
- Compose mount
./logsสำหรับ dev/prod (docker-compose.yml:12,26) แต่ logger เขียน path จริงขึ้นกับ implementation ของI3GatewayLogger
11. Local MariaDB ใน docker-compose vs runtime MSSQL
docker-compose.yml ประกาศ service database เป็น mariadb:10.5 พร้อม password ใน plain text (:30-43) ในขณะที่แอปเชื่อม MSSQL ตาม .env — สองอย่างนี้คนละโลก:
- MariaDB ใน compose น่าจะเป็นซากจากช่วงก่อน convert มี.ค. 2023
- การพัฒนา local ถ้าทำตาม
.envต้องมี network ถึง10.188.128.61หรือแก้ env ชี้ DB อื่น - อย่าสับสนว่า production ยังรัน MariaDB จาก compose นี้
12. สิ่งที่ยัง UNVERIFIED จากใน repo นี้
- ว่า schema MSSQL production ตรงกับ TypeORM entity ทุกคอลัมน์ (โดยเฉพาะชื่อ
createdDatevscreate_date) หลัง script convert - ว่า nginx / ingress จำกัด HTTP method หรือ rate-limit หน้า
/vtrc-common/หรือไม่ - ว่า volume ของ
uploads/ใน production cluster mount แบบใด (compose prod ไม่ mount) - ว่า
SECRET_KEYชุดเดียวถูกใช้ jointly กับ service ใดบ้างใน runtime จริง (ใน git เห็นรูปแบบเดียวกันในหลาย repo — ดู canonical-truths)