3.2.7 · External integrations
บทนี้ catalog การเชื่อมต่อกับระบบภายนอกของ cu-central-api — 6 integrations ที่ cu-central-api เป็น service เดียวใน VTRC topology ที่เรียก
Integration map
┌─────────────────────────────────────────────────────────────┐
│ cu-central-api │
└─────┬───────────────────────────────────────────────────────┘
│
├──▶ SSO (lib/sso/) — committed certs
├──▶ Conicle SFTP (lib/conicle/) — ssh2-sftp-client
├──▶ HIS heal-card (lib/his/) — HTTP API
├──▶ HRMI HTTP API (lib/hrmi/) — native http/https
├──▶ SMS gateway (lib/notification/) — HTTP
└──▶ SMTP (lib/mailer/) — nodemailer1 · SSO integration (lib/sso/)
SSO ของ Red Cross — cu-central-api เป็น SSO client ที่เรียก SSO service เพื่อ authenticate เพิ่มเติมจาก LDAP
Committed certificates
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 — cert ควร fetch จาก secret manager หรือ truststore แทน
AES encryption
config (config.js:101-102)
AES_SECRET_KEY = '' (env, default empty)
AES_INIT_VECTOR = '' (env, default empty)ใช้สำหรับ encrypt/decrypt payload ระหว่าง cu-central-api กับ SSO service
🔴 keys ทั้งสอง default เป็น empty string — ถ้า env unset การ encrypt ไม่ทำงาน (silent failure ถ้า code path ไม่ตรวจ)
.env.develop-test committed values
AES_SECRET_KEY="EoABvcD7RsFBeKRZgSOHS4LLD08To6Pc"
AES_INIT_VECTOR="AaNZRKzytx9yCAiA"(รายละเอียด cover ใน Volume 12 Security)
ssoLogin integration ใน signin flow
ถูกเรียกหลัง Auth.signIn ใน routes.js:25 (cu-central-api/main-api/src/routes.js:25) — password ถูก forward ไป SSO service ด้วย เพื่อ mint SSO token ควบคู่กับ AD JWT
2 · Conicle SFTP (lib/conicle/)
Conicle เป็น learning management system ที่ Red Cross ใช้ — cu-central-api push ข้อมูลการเรียนผ่าน SFTP
Library
"ssh2-sftp-client": "^6.0.1"Files
cu-central-api/main-api/src/models/hrmi/conicle/
├── conicleFIleLog.js (typo "FIle")
├── longTerm.js ← long-term learning records
├── migrateConicle.js
└── shortTerm.js ← short-term learning recordsCron job
// Every day at 03:45.
jobs.push(schedule.scheduleJob('40 3 * * *', async () => {
await cronMethods.conicleTransferLongTerm()
}))daily 03:40 — SFTP push long-term records ไป Conicle
Manual trigger
app.post(`${API_PREFIX_PATH}/conicle/longterm`, urlencodedParser, async (req, res) => {
const params = req.body
if (typeof params.runLong !== 'undefined' && params.runLong === '20k') {
console.log('Run Long term and send to Conicle.')
await transferFileToConicle('long')
console.log('Run finished.')
}
res.send('OK').end()
})trigger ด้วย magic value runLong=20k — pattern ที่ fragile (ไม่มี auth, ใครก็เรียกได้)
Config
CONICLE_DEST_SHORT_TERM_PATH = '/upload/inbound_files/stg/short_term/'
CONICLE_DEST_LONG_TERM_PATH = '/upload/inbound_files/stg/long_term/'
CONICLE_SFTP_HOST / PORT / USERNAME / PASSWORDpassword อยู่ใน env (CONICLE_SFTP_PASSWORD)
3 · HIS heal-card (lib/his/)
HIS = Hospital Information System — cu-central-api ดึงข้อมูล heal-card discount จาก HIS API
Cron job
jobs.push(schedule.scheduleJob('8 * * * *', async () => {
console.log("In cron funciton.")
await cronMethods.hisTransferLongTerm()
}))hourly — pull heal-card discount จาก HIS API มาเก็บใน dbHRMI_Center_HIS
Models
cu-central-api/main-api/src/models/hrmiHis/healCard/
├── healCardDiscount.js ← HealCardDiscount
└── healCardDiscountLog.js ← HealCardDiscountLogทั้งสองอยู่ใน connection dbHRMI_Center_HIS (DB schema dbHRMI_Center_HIS)
Library
import axios from 'axios'HIS integration ใช้ axios (ของแท้) — ต่างจากไฟล์อื่นที่ใช้ native http/https แต่ axios ไม่ได้ declare ใน package.json (มีแค่ axois typo) ดู 3.2.8 §C5
4 · HRMI HTTP API (lib/hrmi/)
นอกจาก MSSQL direct แล้ว cu-central-api ยังเรียก HRMI ผ่าน HTTP API ด้วย (สำหรับ operations ที่ HTTP เป็น interface หลัก)
Library
cu-central-api/main-api/src/lib/hrmi/token.js (ใช้ native http/https)ไม่ใช้ axios — ใช้ native Node http/https (เหตุผลน่าจะเป็น dependency น้อยกว่า แต่ทำให้ error handling ซับซ้อนกว่า)
Config
HRMI_END_POINT
HRMI_USERNAME
HRMI_PASSWORD
WS_HRMI_END_POINT ← SOAP endpoint (legacy)
STACK_REQUEST_LIMIT = 300
TIME_PER_STACK_REQUEST = 100msSTACK_REQUEST_LIMIT จำกัดการยิง batch 300 request ต่อ stack — pattern สำหรับ rate-limit ฝั่ง client
STACK_REQUEST_LIMIT = 300,
TIME_PER_STACK_REQUEST = 100, // millisecUse case
- ดึง approver chain สำหรับ leave workflow (
hrmi.jsresolver →hrmiLeave.getLeaveApprover) - ดึง medical expenses reference
- อ่าน organization structure
5 · SMS gateway (lib/notification/)
ส่ง OTP + notification ผ่าน SMS gateway
Config
SMS_RESENT_SECONDS = 30
SMS_OTP_EXPIRE_MINUTES = 3
SMS_GATEWAY_URL
SMS_GATEWAY_KEY
SMS_SANDBOX_PHONE_NUMBER ← bypass production SMS ใน UAT SMS_RESENT_SECONDS = 30,
SMS_OTP_EXPIRE_MINUTES = 3,
SMS_GATEWAY_URL = '',
SMS_GATEWAY_KEY = '',
SMS_SANDBOX_PHONE_NUMBER = '',Library
HTTP POST ไป SMS_GATEWAY_URL พร้อม SMS_GATEWAY_KEY
ใช้ lib/httpRequest.js ที่ import axios (ที่ไม่ได้ declare — ดู 3.2.8 §C5)
import { httpPost } from './httpRequest'🔴 httpRequest.js import axois (typo) จาก package.json — ถ้า SMS code path ทำงานจะ crash เพราะ module resolution ใช้ชื่อผิด
6 · SMTP / email (lib/mailer/)
ส่ง email ผ่าน nodemailer
Library
"email-templates": "^7.1.1" ← pug-based templates
"nodemailer": "^6.4.11"Templates
cu-central-api/main-api/src/email-templates/
└── requestResetPassword/
├── html.pug
└── subject.pugtemplate เดียวที่มี — สำหรับ reset password flow
Config
SMTP_HOST
SMTP_USERNAME
SMTP_PASSWORD
SMTP_PORT
SMTP_SECURE
EMAIL_TLS_REJECT_UNAUTHORIZED = null ← 🟡 ถ้า set เป็น false = TLS ปิด
EMAIL_SEND_FROM
EMAIL_BCC_RECEIVE_BACKUP SMTP_HOST = '',
SMTP_USERNAME = '',
SMTP_PASSWORD = '',
SMTP_PORT = '',
SMTP_SECURE = '',
// Using for SMTP host is not required authorize.
// You can set to false
EMAIL_TLS_REJECT_UNAUTHORIZED = null,
EMAIL_SEND_FROM = '',
EMAIL_BCC_RECEIVE_BACKUP,🔴 EMAIL_TLS_REJECT_UNAUTHORIZED เป็น env ที่สามารถปิด TLS verification ได้ — ถ้า set false ในการ deploy ใด email จะถูกส่งผ่าน unencrypted channel (MITM risk)
Dependency overview
| Integration | Library | Version | สถานะ |
|---|---|---|---|
| SSO | native crypto + AES | — | 🟡 committed certs + AES key default empty |
| Conicle SFTP | ssh2-sftp-client | ^6.0.1 | 🟢 OK |
| HIS | axios (ไม่ declare) | — | 🔴 missing dep — crash risk |
| HRMI HTTP | native http/https | — | 🟢 OK |
| SMS | native HTTP + lib/httpRequest.js | — | 🔴 httpRequest.js ใช้ axois typo |
| SMTP | nodemailer + email-templates | ^6.4.11 / ^7.1.1 | 🟡 EMAIL_TLS_REJECT_UNAUTHORIZED อาจปิด TLS |
Cross-cutting concerns
Timeout
ไม่มี centralized timeout config — แต่ละ integration ตั้ง timeout เอง (หรือไม่ตั้งเลย)
| Integration | Timeout | Source |
|---|---|---|
| MSSQL | requestTimeout: 30000 | connector.js:15 |
| LDAP | (ไม่มี — ใช้ default) | activeDirectory.connector.js |
| SSO | (ไม่มี) | — |
| Conicle SFTP | (default ssh2) | — |
| HIS | (ไม่มี) | — |
| HRMI HTTP | STACK_REQUEST_LIMIT (แต่ไม่ใช่ timeout) | — |
| SMS | (ไม่มี) | — |
| SMTP | (default nodemailer) | — |
🔴 ถ้า upstream service down บางตัว (เช่น SSO, HIS) request อาจ hang ไม่มี timeout
Retry
ไม่มี retry logic — ถ้า integration fail จะ throw โดยตรง (หรือในบางกรณี catch + log + return null — silent failure)
Circuit breaker
ไม่มี — ถ้า upstream down ทุก request จะพยายามเชื่อมต่อไปเรื่อย ๆ
Secret management
| Secret | Storage |
|---|---|
| SSO AES key + IV | env (AES_SECRET_KEY, AES_INIT_VECTOR) |
| Conicle SFTP password | env (CONICLE_SFTP_PASSWORD) |
| HRMI password | env (HRMI_PASSWORD) |
| SMS gateway key | env (SMS_GATEWAY_KEY) |
| SMTP password | env (SMTP_PASSWORD) |
| AD service account | env (AD_USERNAME, AD_PASSWORD) |
| SSO username/password | env (SSO_USERNAME, SSO_PASSWORD) |
| MSSQL password | env (DB_PASSWORD) |
| JWT signing key | env (JWT_SECRET) |
| HMAC key | env (HASH_KEY) |
ทุก secret อยู่ใน env — แต่ .env.develop-test (UAT) commit อยู่ใน repo (Volume 12 Security)
สรุป integration health
| Integration | Severity | ปัญหา |
|---|---|---|
| SSO | 🟡 | committed certs + AES key default empty |
| Conicle SFTP | 🟢 | — |
| HIS | 🔴 | import axios ที่ไม่ declare — crash risk |
| HRMI HTTP | 🟢 | — |
| SMS | 🔴 | ผ่าน httpRequest.js ที่ import axois typo — crash risk |
| SMTP | 🟡 | EMAIL_TLS_REJECT_UNAUTHORIZED env ที่อาจปิด TLS |
| Cross-cutting | 🔴 | ไม่มี timeout/retry/circuit breaker |
Manual trigger /conicle/longterm | 🟡 | ไม่มี auth — magic value เป็น secret เพียงตัวเดียว |
อ่านต่อ → 3.2.8 Security + debt