Skip to content

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 เดียว:

typescript
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

จุดสำคัญ:

หัวข้อค่า / พฤติกรรมอ้างอิง
Dialectmssql (จาก env.ts)util/env.ts:23
TLSencrypt: falsedb.config.ts:11
Poolmax 5, acquire 30000, idle 10000env.ts:26-31
TimezoneAsia/Bangkokenv.ts:24

Credentials — hardcode ในซอร์ส ไม่ได้อ่านจาก process.env

บล็อกที่ active ใน util/env.ts (comment ว่า // Prod):

typescript
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

typescript
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:

typescript
//syncModelToTable();

แหล่ง: app.ts:265

Production จึงพึ่ง schema ที่มีอยู่แล้วใน MSSQL — ไม่ auto-migrate ตอน boot

ตารางที่ business logic แตะบ่อย (จาก model exports)

DomainSequelize models ในไฟล์ไฟล์
IncidentIncidentType, IncidentRunning, IncidentEdit, Attachment, AttachType, Incident, ProgressStatus, Remarkmodel/incident/incident.ts:336
UserLineUserLinemodel/userLine/userLine.ts:43
TokenTokenmodel/token/token.ts:31
OTPจาก model/otp/otp.tsregistry db1.otp

Database 2 — MongoDB ผ่าน Mongoose (chat) — connection ไม่ทำงานจาก entrypoint

Connection module

typescript
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

typescript
// 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):

FieldTypeRequired
channelStringใช่ (ใช้เก็บ lineID)
countNotReadStringใช่
replyTokenStringใช่
updateDateDateไม่บังคับ
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

ตัวอย่าง:

CallerQueryอ้างอิง
getProfile.tsfetchListEmployeeForTwoWaygetProfile.ts:96-120
loginSSO.tsfetchListEmployeeForTwoWay (ตรวจว่าเป็นพนักงาน 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.ts
  • util/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 อ่าน:

typescript
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_AUTHOA หลัก (RC).env.prod:11-13
RICH_MENUrich menu id RC.env.prod:14
LINE_CU_CHANNEL_SECRET / LINE_CU_CHANNEL / LINE_CU_AUTHOA CU.env.prod:16-18
RICH_MENU_CUrich menu id CU.env.prod:19
LINE_CU_URLLIFF URL CU.env.prod:20

Call sites ที่พิสูจน์แล้ว

การใช้งานEndpointอ้างอิง
Push หลังสร้าง incidenthttps://api.line.me/v2/bot/message/pushcreateIncident.ts:142-143
Verify access token (LINE middleware)https://api.line.me/oauth2/v2.1/verifycheckByPassTokenLine.ts:50
Get LINE profilehttps://api.line.me/v2/profilecheckByPassTokenLine.ts:55
Webhook inbound/2way/WebHookApi/Line, /2way/cu/webHookApiapp.ts:114-117
Webhook outbound AuthorizationBearer ${process.env.LINE_AUTH}WebHookApi/index.ts:18-21

fetchAlways ใน triggerMenu.ts เป็น retry wrapper ที่หลาย route ใช้ร่วมกัน

Upload URL hardcode ฝั่ง chat

เมื่อ admin ส่งรูปผ่าน socket:

typescript
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/filestorage/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/loginSSOsso/loginSSO.ts
/2way/changePasswordSSOsso/changePassword.ts
/2way/verifyUserNonEmpsso/verifyUserNonEmp.ts
/2way/verifyOTPNonEmpsso/verifyOTPNonEmp.ts
/2way/changePasswordNonEmpForgetsso/changePasswordNonEmpForget.ts

Integration 4 — JWT secrets (อย่างน้อย 2 ระบบ)

Secretแหล่งผู้ใช้
SECRET literalutil/jwt.config.ts:2jwtpassport.ts, routes/auth/login.ts (sign/test tokens)
SECRET_JWT_TWOWAYprocess.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:

typescript
jwtExpiration: 60,          // 1 minute
jwtRefreshExpiration: 120,  // 2 minutes

แหล่ง: jwt.config.ts:7-8SEC-2WAYVTRC-04 / CORR-2WAYVTRC-03

decodeJwt helper:

typescript
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

typescript
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_VTRCUUID-likeoutbound ไป vtrc (ต้องไล่ caller เพิ่มถ้า audit เต็ม)
BASE_URLhttps://vtrcapi.redcross.or.th.env.prod:5
FILE_PATH/file/.env.prod:6
NODE_ENVproduction.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/fileapp.ts:80
Compose volumehost /home/hrappladmin/storage/2way → container pathdocker-compose.yml:9-10
Uploaderlib/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


สิ่งที่อยู่นอกขอบเขตบทนี้