Skip to content

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

StoreConnection / pathDatabase / locationหลักฐาน
MSSQLTypeORM name mainvtrc_common @ 10.188.128.61:1433app.module.ts:18-37, .env:7-12
Local diskprocess.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ค่าความหมาย
typemssqldriver
synchronizefalseไม่ auto-alter schema
logging['error', 'query']log SQL ทุก query ใน log
extra.trustServerCertificatetrueข้าม cert validation
extra.encryptfalseไม่ encrypt TDS

Config mapping: configuration.ts อ่าน DB_HOST/DB_PORT/DB_USER/DB_PASSWORD/DB_NAMEdb.* (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 FK

BaseEntity

คอลัมน์ร่วม (vtrc-common/src/entities/base.entity.ts (branch: prod):3-18):

PropertyColumn / typeหมายเหตุ
idPK increment
createdDatedatetime, default CURRENT_TIMESTAMP
createdByvarchar(50), nullable
updatedDatedatetime
updatedByvarchar(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

PropertyDB columnType
provinceCodeprovince_codevarchar(10)
provinceNameENprovince_name_envarchar(255)
provinceNameTHprovince_name_thvarchar(255)
isActiveis_activebit
aumphurOneToManyMAumphur cascade

MAumphur → table m_aumphur

m_aumphur.entity.ts:6-26province_id JoinColumn, aumphur_code / names / is_active, OneToMany tumbol

MTumbol → table m_tumbol

m_tumbol.entity.ts:6-26aumphur_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

PropertyColumnหมายเหตุ
bankCodebankCode varchar(3)camelCase ใน DB
bankBranchbankBranch varchar(4)'0000' = HQ ใน business logic
bankNameTh / bankNameEnตามชื่อ
isActiveisActive bitservice เปรียบเทียบเป็น number 1

FileEntity → table file

vtrc-common/src/entities/file.entity.ts (branch: prod):4-26

Propertyบทบาท
modulenamespace โฟลเดอร์ เช่น benefit
filePathpath สัมพัทธ์เช่น /uploads/benefit/2024-01-15/....png
fileNameชื่อเดิมจาก client
fileTypeMIME จาก mime.lookup
sortลำดับในชุดอัปโหลด
descriptionoptional
isCancelsoft-delete flag (default 0); delete ตั้งเป็น 1

4. SQL scripts — migration history

โฟลเดอร์ vtrc-common/sql/ (branch: prod) commit ไว้เป็นหลักฐาน schema — ไม่ ถูก TypeORM รันอัตโนมัติ

ไฟล์เนื้อหาโดยสรุป
common_20230214_Initial_Structure_Table.sqlMariaDB CREATE TABLE + seed จังหวัด (m_province รหัส 10, 11, …) — บรรทัดต้นใช้ backtick MySQL syntax
common_20230313_Convert_MariaDB_To_MsSql.sqlscript convert ไป MSSQL (ชื่อไฟล์ยืนยันช่วงมี.ค. 2023)
common_20230313_Master_Data.sqlmaster data เพิ่มเติมหลัง convert

ตัวอย่างหัวตารางจาก initial script:

sql
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_provinceTypeORM findใช่
m_aumphur (+ relations)TypeORM find / QueryBuilderใช่ (ยกเว้นไม่ใช้ใน raw paths)
m_tumbol / m_zipcodejoin ผ่าน relations หรือ raw SQLraw SQL 2 จุด = injection
bankTypeORM find + Not()ใช่
fileTypeORM save / findOne / find + In()ใช่ (แต่ auth อ่อน)

7. Outbound integrations

ไม่พบ ใน src/modules/** และ src/main.ts:

  • ไม่มี HttpService / axios call ออกไป centralize-api, vtrc-api, LDAP, หรือ LINE
  • axios อยู่ใน package.json dependencies (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

CallerEnv / baseEndpointsหลักฐาน
benefit-apiVTRC_COMMON_SERVERfile/save, file/delete, file/getFileArraydisbursement.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)

CallerBase URL resolutionEndpoints
vtrc-weburlCommon() / checkLinkURL() ใน auth.jsaddress dropdowns ใน welfare disaster + recruit contact
vtrc-rc-backofficeaxios-common.js prod/UAT switchaddress ผ่าน 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-apienv VTRC_COMMON_SERVER ไม่มี call ใน src/
recruitment-apicommonServer ใน configuration ไม่มี call ใน src/
recruitment-webURL 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.handleRequest log user ทั้งก้อนเป็น 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 ทุกคอลัมน์ (โดยเฉพาะชื่อ createdDate vs create_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)