2way-vtrc-api — Persistence & Integrations
บทนี้อธิบายฐานข้อมูลและการเชื่อมต่อภายนอกของ 2way-vtrc-api (branch: prod) — ทุก claim มี file:line จากซอร์ส
Database 1 — MSSQL ผ่าน Sequelize
Connection factory
util/db.config.ts สร้าง Sequelize instance เดียว:
const sequelizeDB = new SequelizeDB(env.database, env.username, env.password, {
host: env.host,
dialect: env.dialect,
port: env.port,
dialectOptions: {
options: {
useUTC: false,
encrypt: false,
}
},
timezone: env.timezone,
pool: { max: env.max, min: env.pool.min, acquire: env.pool.acquire, idle: env.pool.idle }
});แหล่ง: 2way-vtrc-api/util/db.config.ts:1-22
จุดสำคัญ:
| หัวข้อ | ค่า / พฤติกรรม | อ้างอิง |
|---|---|---|
| Dialect | mssql (จาก env.ts) | util/env.ts:23 |
| TLS | encrypt: false | db.config.ts:11 |
| Pool | max 5, acquire 30000, idle 10000 | env.ts:26-31 |
| Timezone | Asia/Bangkok | env.ts:24 |
Credentials — hardcode ในซอร์ส ไม่ได้อ่านจาก process.env
บล็อกที่ active ใน util/env.ts (comment ว่า // Prod):
export const env = {
database: '2way',
username: 'sa',
password: '...', // literal ในไฟล์ — ดู env.ts:21
host: '10.188.128.61',
dialect: 'mssql',
timezone: "Asia/Bangkok",
port: 1433,
pool: { max: 5, min: 0, acquire: 30000, idle: 10000 }
};แหล่ง: util/env.ts:17-32
มีบล็อก Dev ที่ comment ปิด ชี้ host 10.188.128.58 (env.ts:34-49) และบล็อกเก่าที่ comment ปิดด้านบน (env.ts:1-15)
ผลกระทบ: สลับ environment ต้องแก้โค้ดหรือ uncomment บล็อก — ไม่สอดคล้องกับ .env ที่ dotenv โหลดสำหรับ LINE/JWT; credential DB ไม่อยู่ใน .env.prod ในส่วนที่ตรวจ (.env.prod มี API_KEY / LINE / JWT แต่ไม่มี DB host) — SEC-2WAYVTRC-03b
mysql / mysql2 อยู่ใน package.json แต่ไม่มี Sequelize dialect mysql ใน env.ts ที่ active — เป็น dependency ค้าง
Model registry
db1.post = require('../model/post.ts')(sequelizeDB, SequelizeDB);
db1.login = require('../model/login.ts')(...);
db1.incident = require('../model/incident/incident.ts')(...);
db1.feedback = require('../model/feedback/feedback.ts')(...);
db1.survey = require('../model/survey/survey.ts')(...);
db1.userLine = require('../model/userLine/userLine.ts')(...);
db1.userLineProfile = require('../model/userLine/userProfile.ts')(...);
db1.faqs = require('../model/faqs/faqs.ts')(...);
db1.announcement = require('../model/announcement/announcement.ts')(...);
db1.autoReply = require('../model/autoReply/autoReply.ts')(...);
db1.otp = require('../model/otp/otp.ts')(...);
db1.token = require('../model/token/token.ts')(...);แหล่ง: db.config.ts:29-40
ทุก model ใช้ freezeTableName: true และ timestamps: false เป็นแพทเทิร์น (ตัวอย่าง userLine.ts:35-38, token.ts:25-28)
Schema sync — ปิดอยู่
lib/model.ts มี syncModels array ครอบ announcement / autoReply / faqs / feedback / incident / otp / userLine (lib/model.ts:4-49) แต่การเรียกถูก comment:
//syncModelToTable();แหล่ง: app.ts:265
Production จึงพึ่ง schema ที่มีอยู่แล้วใน MSSQL — ไม่ auto-migrate ตอน boot
ตารางที่ business logic แตะบ่อย (จาก model exports)
| Domain | Sequelize models ในไฟล์ | ไฟล์ |
|---|---|---|
| Incident | IncidentType, IncidentRunning, IncidentEdit, Attachment, AttachType, Incident, ProgressStatus, Remark | model/incident/incident.ts:336 |
| UserLine | UserLine | model/userLine/userLine.ts:43 |
| Token | Token | model/token/token.ts:31 |
| OTP | จาก model/otp/otp.ts | registry db1.otp |
Database 2 — MongoDB ผ่าน Mongoose (chat) — connection ไม่ทำงานจาก entrypoint
Connection module
const dbconnect = '10.188.128.64';
const username = 'rootofadmin'
const password = '...' // literal — ดู mongo.config.ts:5
const database = 'chatDB'
mongoose.connect('mongodb://'+username+':'+password+'@'+dbconnect+':'+portmongo+'/'+database+"?authSource=admin", {
useFindAndModify: false,
useNewUrlParser: true,
bufferMaxEntries: 0,
connectTimeoutMS: 20000,
socketTimeoutMS: 20000,
useUnifiedTopology: true
})แหล่ง: util/mongo.config.ts:2-20
Entrypoint ปิดการ require
// require('./util/mongo.config.ts');แหล่ง: app.ts:10
ผล: process นี้ไม่เปิด Mongo connection ตอน start — แต่ lib/Socket.func.ts ยัง require("../model/mongo/chatSchema") และเรียก Chat.findOne / chat.save (Socket.func.ts:15,85-100)
HTTP routes chatCenter ถูก comment ใน app.ts:245-250 — surface ที่เหลือคือ socket.io
Chat document shape
Collection name "conversation" (chatSchema.ts:43):
| Field | Type | Required |
|---|---|---|
channel | String | ใช่ (ใช้เก็บ lineID) |
countNotRead | String | ใช่ |
replyToken | String | ใช่ |
updateDate | Date | ไม่บังคับ |
chat[] | subdocs: message, type, isRead, role, createDate |
แหล่ง: model/mongo/chatSchema.ts:3-41
Integration 1 — cu-central-api (GraphQL)
URL ที่ hardcode ในโค้ด
หลายไฟล์ยิงตรงไป:
https://centralizeapi.redcross.or.th:25563/api/graphqlตัวอย่าง:
| Caller | Query | อ้างอิง |
|---|---|---|
getProfile.ts | fetchListEmployeeForTwoWay | getProfile.ts:96-120 |
loginSSO.ts | fetchListEmployeeForTwoWay (ตรวจว่าเป็นพนักงาน CD หรือไม่) | loginSSO.ts:91+ |
URL ใน env (ไม่ได้ใช้ทุกที่)
.env.prod กำหนด:
CDB_BASE_CONNECT=https://centralizeapi.redcross.or.th:25563/api/graphql
VTRC_BASE_CONNECT=https://vtrcapi.redcross.or.th/api/graphqlแหล่ง: .env.prod:7-8
มี axios interceptor files:
util/axiosinterceptors/cdb.connector.tsutil/axiosinterceptors/vtrc.connector.ts
แต่ getProfile.ts comment ปิด import cdbConnector แล้วใช้ axios.post ตรงกับ URL hardcode (getProfile.ts:4,96) — pattern ไม่สม่ำเสมอทั่ว repo
เปรียบเทียบกับ 2way-api
2way-api อ่าน URL จาก process.env.CENTRALIZE_API_URL (2way-api auth.service — ดูเอกสาร 2way-api) ในขณะที่ repo นี้ hardcode host:port centralizeapi.redcross.or.th:25563 ในหลายจุด — query name เดียวกัน (fetchListEmployeeForTwoWay) ยืนยันว่าปลายทางเป็น GraphQL schema ของ cu-central กลุ่มเดียวกัน แต่ห้ามสมมติว่าเป็น instance/environment เดียวกันทุกครั้งโดยไม่ตรวจ DNS/routing
Integration 2 — LINE Messaging API / LINE Login
Config จาก env
config.ts อ่าน:
LINE_CHANNEL_SECRET = process.env.LINE_CHANNEL_SECRET,
LINE_CHANNEL = process.env.LINE_CHANNEL,
LINE_AUTH = process.env.LINE_AUTH,แหล่ง: config.ts:15-17
ค่า literal เก่าถูก comment ปิดเป็นตัวอย่าง (config.ts:12-14) — ไม่มี hardcoded fallback ในไฟล์นี้
.env.prod มีทั้งชุด RC และ CU:
| Key | บทบาท | อ้างอิง |
|---|---|---|
LINE_CHANNEL_SECRET / LINE_CHANNEL / LINE_AUTH | OA หลัก (RC) | .env.prod:11-13 |
RICH_MENU | rich menu id RC | .env.prod:14 |
LINE_CU_CHANNEL_SECRET / LINE_CU_CHANNEL / LINE_CU_AUTH | OA CU | .env.prod:16-18 |
RICH_MENU_CU | rich menu id CU | .env.prod:19 |
LINE_CU_URL | LIFF URL CU | .env.prod:20 |
Call sites ที่พิสูจน์แล้ว
| การใช้งาน | Endpoint | อ้างอิง |
|---|---|---|
| Push หลังสร้าง incident | https://api.line.me/v2/bot/message/push | createIncident.ts:142-143 |
| Verify access token (LINE middleware) | https://api.line.me/oauth2/v2.1/verify | checkByPassTokenLine.ts:50 |
| Get LINE profile | https://api.line.me/v2/profile | checkByPassTokenLine.ts:55 |
| Webhook inbound | /2way/WebHookApi/Line, /2way/cu/webHookApi | app.ts:114-117 |
| Webhook outbound Authorization | Bearer ${process.env.LINE_AUTH} | WebHookApi/index.ts:18-21 |
fetchAlways ใน triggerMenu.ts เป็น retry wrapper ที่หลาย route ใช้ร่วมกัน
Upload URL hardcode ฝั่ง chat
เมื่อ admin ส่งรูปผ่าน socket:
body.msg = `https://vtrcdev.redcross.or.th/2way/file/${rs.name}`แหล่ง: lib/Socket.func.ts:75 — hostname dev hardcode แม้ process อาจรันบน UAT/prod
Static serve จริงอยู่ที่ /2way/file → storage/2way/ (app.ts:11,80)
Integration 3 — Internal SSO service
routes/TwoWayAPI/sso/loginSSO.ts ยิงไป IP ภายใน:
http://10.188.127.131/SSOService/apiพร้อม SSO_APP_ID, SSO_USERNAME, SSO_PASSWORD, SSO_REQUEST_ID, AES_SECRET_KEY, AES_INIT_VECTOR เป็น string literal ในไฟล์ (loginSSO.ts:21-27)
Flow: AES encrypt credentials → POST SSO → ถ้าสำเร็จ jwt.sign ด้วย secret จาก jwt config → Token stamp ใน MSSQL
Endpoints ที่เกี่ยวข้องในกลุ่มเดียวกัน (ไม่มี Bearer middleware):
| Path | ไฟล์ |
|---|---|
/2way/loginSSO | sso/loginSSO.ts |
/2way/changePasswordSSO | sso/changePassword.ts |
/2way/verifyUserNonEmp | sso/verifyUserNonEmp.ts |
/2way/verifyOTPNonEmp | sso/verifyOTPNonEmp.ts |
/2way/changePasswordNonEmpForget | sso/changePasswordNonEmpForget.ts |
Integration 4 — JWT secrets (อย่างน้อย 2 ระบบ)
| Secret | แหล่ง | ผู้ใช้ |
|---|---|---|
SECRET literal | util/jwt.config.ts:2 | jwtpassport.ts, routes/auth/login.ts (sign/test tokens) |
SECRET_JWT_TWOWAY | process.env (เช่น .env.prod:4) | checkBypassTokenMiddleWare ผ่าน decodeJwt (checkbypasstoken.ts:65) |
SECRET_JWT_VTRC | .env.prod:3 | มีใน env — ต้องไล่ caller แยกถ้าจะ rotate; ไม่ยืนยันทุก call site ในบทนี้ |
jwt.config.ts ยังตั้ง expiration แบบ test:
jwtExpiration: 60, // 1 minute
jwtRefreshExpiration: 120, // 2 minutesแหล่ง: jwt.config.ts:7-8 — SEC-2WAYVTRC-04 / CORR-2WAYVTRC-03
decodeJwt helper:
export const decodeJwt = async(token, secert) => {
return await jwt.verify(token, secert, function(err, decoded) {
if (err) { return false }
return decoded
});
}แหล่ง: util/helper/jwtdecode.ts:3-11 — ใช้ callback กับ await ซึ่งได้ผลลัพธ์จาก verify แบบ callback-style (ค่าที่ return ใน callback คือค่าที่ promise ของบางเวอร์ชัน jwt คืน; ต้องระวังเมื่อ maintain)
Integration 5 — Sentry
Sentry.init({
// dsn: "https://c2d8e329ff9d479b9cb5d5b31ece7660@o1092051.ingest.sentry.io/6141406",
dsn: "https://35187a830cfd21a32ea4c9b6a11c5a7e@o4504546330607616.ingest.us.sentry.io/4507854563246080",
integrations: [
new Sentry.Integrations.Http({ tracing: true }),
new Tracing.Integrations.Express({ app }),
],
tracesSampleRate: 1.0,
});แหล่ง: app.ts:53-68
- DSN hardcode (สลับ project ต้องแก้โค้ด)
tracesSampleRate: 1.0= ส่ง 100% transactions — ต้นทุน/volume สูงใน production- DSN เก่า comment ค้างคู่กัน
Integration 6 — API keys จาก env
จาก .env.prod:
| Key | ค่าตัวอย่างในไฟล์ | ใช้โดย |
|---|---|---|
API_KEY | มีในไฟล์ | checkApiKeyMiddleWare (checkbypasstoken.ts:119) |
API_KEY_OUTBOUND_VTRC | UUID-like | outbound ไป vtrc (ต้องไล่ caller เพิ่มถ้า audit เต็ม) |
BASE_URL | https://vtrcapi.redcross.or.th | .env.prod:5 |
FILE_PATH | /file/ | .env.prod:6 |
NODE_ENV | production | .env.prod:9 |
dotenv โหลดเฉพาะ path .env ตอน boot (app.ts:70-72) — การ deploy ต้องให้ไฟล์ชื่อ .env มีค่าที่ต้องการ (หรือ symlink จาก .env.prod)
File storage
| หัวข้อ | รายละเอียด | อ้างอิง |
|---|---|---|
| Local dir | ./storage/2way/ | app.ts:11 |
| URL prefix | /2way/file | app.ts:80 |
| Compose volume | host /home/hrappladmin/storage/2way → container path | docker-compose.yml:9-10 |
| Uploader | lib/Uploadimage.class.ts | ใช้จาก incident/feedback/chat |
Network topology สรุป (จากโค้ด — ไม่ใช่จาก infra diagram)
LIFF / Browser / LINE Platform
│ HTTPS
▼
2way-vtrc-api :8001
├── MSSQL 10.188.128.61:1433 database=2way (env.ts)
├── Mongo 10.188.128.64:27017 chatDB (mongo.config — ปิด require)
├── cu-central GraphQL centralizeapi:25563
├── LINE api.line.me
├── SSO 10.188.127.131/SSOService/api
└── Sentry ingest (US)IP ในตารางเป็นค่าที่ปรากฏในซอร์ส — ไม่ยืนยัน firewall / NAT / ว่า host เหล่านี้ยังเป็น production จริงในทุก environment
สิ่งที่อยู่นอกขอบเขตบทนี้
- Request traces → 02-core-pipeline
- Severity + remediation → 04-scorecard