2way-api — Persistence & Integrations
บทนี้ map ชั้นข้อมูล MSSQL/TypeORM และทุก outbound integration ของ 2way-api (branch: prod) ที่พิสูจน์จากซอร์ส — รวม dual-centralize, SSO, LINE, vtrc-api, และ cross-schema PMS
แผนที่โมดูล: 01-repository-map
Request traces: 02-core-pipeline
Database connection (TypeORM → MSSQL)
TypeOrmModule.forRootAsync({
name: 'main',
useFactory: (configService: ConfigService) => ({
type: 'mssql',
host: configService.get('db.host'),
port: +configService.get<number>('db.port'),
username: configService.get('db.user'),
password: configService.get('db.password'),
database: configService.get('db.databaseName'),
synchronize: false,
logging: [`error`, `query`],
entities: [__dirname + '/**/*.entity{.ts,.js}'],
extra: {
trustServerCertificate: true,
encrypt: false,
},
}),
inject: [ConfigService],
})แหล่ง: 2way-api/src/app.module.ts:26-45
| Setting | ค่า | ความหมาย |
|---|---|---|
| connection name | 'main' | ใช้กับ @InjectEntityManager('main') |
synchronize | false | ไม่ auto-alter schema จาก entity |
encrypt | false | TDS ไม่บังคับ TLS encrypt |
trustServerCertificate | true | ถ้ามี TLS จะไม่ verify cert |
| logging | error, query | log SQL ทุก query ในระดับ Nest logger |
Mapping จาก env ผ่าน config/configuration.ts:3-9:
| Config path | Env var | ตัวอย่างใน .env.example |
|---|---|---|
db.host | DB_HOST | 10.188.128.58 |
db.port | DB_PORT | 1433 |
db.user | DB_USER | sa |
db.password | DB_PASSWORD | (มีใน example — ห้าม commit secret จริงเพิ่ม) |
db.databaseName | DB_NAME | 2way |
Driver: mssql@9.1.1 (package.json:47) + typeorm@^0.3.11 (package.json:54)
SQL bootstrap อ้างอิง: 2way-api/sql/20230412_create_structure_table_prod.sql — ใช้เทียบ schema กับ entity เมื่อสงสัย drift; runtime ไม่ได้รันไฟล์นี้เอง
BaseEntity — คอลัมน์ร่วม
export class BaseEntity {
@PrimaryGeneratedColumn('increment', { name: 'id' })
id?: number;
@Column({ name: 'create_date', default: () => 'CURRENT_TIMESTAMP' })
createDate?: Date;
@Column({ name: 'create_by', type: 'varchar', length: 50, nullable: true })
createBy?: string;
@Column({ name: 'update_date', default: () => 'CURRENT_TIMESTAMP' })
updateDate?: Date;
@Column({ name: 'update_by', type: 'varchar', length: 50, nullable: true })
updateBy?: string;
}แหล่ง: 2way-api/src/entities/base.ts:3-24
เกือบทุก m_* / t_* extends BaseEntity — ยกเว้น UserLine ที่นิยาม PK เอง (userLine.entity.ts:4-6)
Domain model — Content กลาง (Announces)
Announces ใช้ตาราง t_content เป็นเอกสารหลัก แยกประเภทผ่าน m_content_type (content_type_id)
t_content คอลัมน์สำคัญ
| Column (DB) | Property | ประเภท | หมายเหตุ |
|---|---|---|---|
topic_name | topicName | varchar(1500) | หัวข้อประกาศ |
topic_etc | topicEtc | varchar(1500) | รายละเอียดเสริม |
details | details | varchar(1500) | เนื้อหา |
offer_date | offerDate | date | วันที่มีผล / แจ้ง |
dir_attach / attach_files | — | path ไฟล์ | |
link_url / link_result | — | URL | |
type_assign | typeAssign | default ATTENDEE | หรือ ALL |
type_group | typeGroup | default RC | RC vs CU |
visible_btn / btn_text | — | UI ปุ่มรับทราบ | |
show_when_register | — | int |
แหล่ง: 2way-api/src/entities/t_content.entity.ts:12-102
Relations:
OneToOne→TContentStatus,TAttachmentAnnounceOneToMany→TContentFlowHistory,TSendNotify,TContentAssign,TContentManager,TContentAssignAll
Assign tables
t_content_assign — มอบหมายรายบุคคล: emp_code, org/divi/dept, know_date, read_date, identitycard
แหล่ง: t_content_assign.entity.ts
t_content_assign_all — เมื่อ type_assign = 'ALL': emp_code, line_id, know_date, read_date
แหล่ง: t_content_assign_all.entity.ts
Flow history
t_content_flow_history เก็บ seq_no, emp/org/divi, his_dttm, remark — list query ของ announce เลือก MAX(seq_no) เพื่อได้สถานะล่าสุด (announces.service.ts:216-219)
Domain model — Surveys
ตารางหลัก t_content_survey แยกจาก t_content:
| Column | หมายเหตุ |
|---|---|
type_id → m_type_survey | ประเภท |
reuse_type_id → m_reuse_survey | รูปแบบ reuse |
subject, detail, story_detail | เนื้อหา |
link_url, link_result | ลิงก์แบบสำรวจ/ผล |
org_code_assign, divi_code_assign, source_db | ขอบเขต |
type_assign | default ATTENDEE |
แหล่ง: t_content_survey.entity.ts:12-72
ตารางคู่ขนาน:
| Table | Entity | บทบาท |
|---|---|---|
t_assign_survey | TAssignSurvey | มอบหมายรายคน |
t_content_survey_all | TContentSurveyAll (ไฟล์ t_content_assign_survey.entity.ts) | assign-all |
t_content_survey_ignore | TContentSurveyIgnore | คนที่ถูกตัดออก |
t_history_survey | THistorySurvey | ประวัติสถานะ |
t_reuse_survey | TReuseSurvey | รอบวันที่ reuse |
m_status_survey | MStatusSurvey | master status |
temp_survey | TempSurvey | temp |
Cron survey อ่าน t_content_survey + t_history_survey + t_assign_survey + t_reuse_survey ด้วย raw SQL (survey.task.service.ts:31-40)
Domain model — Complaints
ตารางหลัก t_content_complaint:
| Column | หมายเหตุ |
|---|---|
type_id → m_type_complaint | ประเภท |
subject_id → m_subject_complaint | หัวข้อ (+ subject_other) |
mention, detail | เนื้อหา |
org_code_assign, divi_code_assign, emp_code_assign | มอบหมาย |
contact_no, source_db | ติดต่อ / แหล่ง HR DB |
is_call_back, is_know, is_know_date, is_draft | flags |
แหล่ง: t_content_complaint.entity.ts:9-68
ตารางคู่ขนาน:
| Table | บทบาท |
|---|---|
t_assign_complaint | ผู้รับผิดชอบ |
t_attachment_complaint | ไฟล์แนบ |
t_history_complaint | ประวัติ workflow |
m_status_complaint | status + next_status_code + stage |
t_change_divicode_complaints | audit เปลี่ยนฝ่าย |
temp_complaint_role | temp role สำหรับ cron |
m_subject_complaint มี org_code_admin / divi_code_admin / emp_code_admin สำหรับ routing เรื่องไปแอดมินที่ถูกฝ่าย (m_subject_complaint.entity.ts:17-23)
Domain model — LINE identity
userLine
| Column | บทบาท |
|---|---|
userID | รหัสพนักงาน / identity ที่ join กับระบบอื่น |
identityCard | บัตรประชาชน |
birthDate | string |
lineID | LINE user ID สำหรับ push |
richMenuID | rich menu ปัจจุบัน |
isActive | bit |
แหล่ง: userLine.entity.ts:3-25
PMS cron join: pms.dbo.tbnotify.empcode ↔ [2way].dbo.userline.userid ด้วย collation Thai_100_CI_AS (notification-pms.task.service.ts:34-36)
userLineProfile
Entity ใหญ่ (~6 KB ไฟล์) — ใช้ใน announce CU path กรองด้วย identityCard → EmpCode (announces.service.ts ช่วง findListOfAnnounceCu)
m_user_manager
ใช้ใน EmployeesService.getManagers / getEmployees เพื่อ กรองออก empCode ที่เป็น manager ออกจากผลลัพธ์ centralize (employees.service.ts:90-101,128-139)
คอลัมน์: emp_code, org/divi/dept, is_active, is_manager, is_deputy (m_user_manager.entity.ts:5-52)
Domain model — Config / audit / notify
| Table | บทบาท |
|---|---|
m_config | module, name, value, is_active |
m_error_message | รหัส error ไทย/อังกฤษ + severity |
m_role | role_name, action_view |
m_content_flow | stage machine ของ announce content |
t_send_notify | คิวแจ้งเตือนผูก t_content |
t_logs_notification | audit หลัง push (เช่น knowContent) |
t_thumbnail_announce / t_attachment_announce | สื่อประกาศ |
Raw SQL vs TypeORM — แผนที่การเข้าถึง
| Module | TypeORM find/save/QB | entityManager.query |
|---|---|---|
| announces | create/update/know/read | list SQL หลัก (injection surface) |
| surveys | ส่วนใหญ่ workflow | มีจุด ${item.id} และ util queries |
| complaints | workflow หลัก | summary/count queries |
| auth | — | teamMember level lookup (parameterized @0) |
| employees | MUserManager.find | VM_PERMISSION_FOR_SEARCH_EMPLOYEE (static SQL) |
| crontab/* | Line + occasional save | หลักเป็น raw SQL |
หลักการอ่านโค้ด: ถ้าเห็น template string กับ ${dto.*} ใน query(...) ให้ถือว่าเป็น candidate SQL injection จนกว่าจะพิสูจน์ว่าค่ามาจาก DB ภายในเท่านั้น
Integration map — ภาพรวม
┌─────────────────────┐
│ 2way-api (NestJS) │
└──────────┬──────────┘
┌───────────────────┼───────────────────┐
│ │ │
▼ ▼ ▼
MSSQL DB_NAME=2way CENTRALIZE_SERVER CENTRALIZE_API_URL
(+ schema pms) NestJS REST GraphQL (cu-central)
│ centralize-api │
│ │ │
▼ ▼ ▼
userLine / t_* /api/employee/* fetchListEmployeeForTwoWay
│
├──── SSO_END_POINT (HTTPS Basic + AES body)
├──── api.line.me (Bearer channel token)
├──── http://20.212.116.206:8200 (2way-line-service)
└──── VTRC_BASE_CONNECT GraphQL (caller commented)Integration 1 — Dual centralize (ยืนยันแล้ว)
2way-api เรียก สองบริการคนละตัว ที่ชื่อคล้ายกัน:
1A — NestJS centralize-api (REST)
| รายการ | ค่า |
|---|---|
| Env | CENTRALIZE_SERVER |
| ตัวอย่าง UAT | http://10.188.128.160:3000/ (.env.example:25) |
| Client | EmployeesService ผ่าน this.config.get('CENTRALIZE_SERVER') |
| Protocol | HTTP GET + query params |
| Auth ไปปลายทาง | ไม่เห็น header apiKey/JWT ใน service นี้ — ยิง axios ตรง |
Endpoints ที่เรียกจริง (employees.service.ts):
| Local method | Upstream path |
|---|---|
getOrganization | api/employee/organize |
getDivisions | api/employee/division |
getDepartments | api/employee/department |
getManagers | api/employee/manager |
getEmployees | api/employee |
getEmployeesCu | api/employee/cu |
getEmployeesByEmpCode | api/employee/getByEmpCode |
getLevels | api/employee/level |
getPositions | api/employee/position |
getManagerPositions | api/employee/posManage |
getTechnicalPositions | api/employee/posTechnical |
getEmployeesGroups | api/employee/group |
หมายเหตุ config: CENTRALIZE_SERVER ไม่อยู่ ใน object ที่ configuration.ts return — Nest ConfigService ยังอ่านจาก process env ได้เมื่อ ConfigModule โหลด .env
Filter ฝั่ง 2way หลังได้ข้อมูล:
getOrganization: ถ้าquery.type === 'CU'กรองsourceDBที่มี'CU'(employees.service.ts:49-51)getManagers/getEmployees/getEmployeesCu: ตัดempCodeที่อยู่ในm_user_managerที่isManager=true
Stub ที่ไม่เรียก upstream: getAdmin, getContent, createManager, updateEmployee, updateManager, deleteManager — คืน input กลับอย่างเดียว (employees.service.ts:85-214)
1B — cu-central-api (GraphQL)
| รายการ | ค่า |
|---|---|
| Env | CENTRALIZE_API_URL |
| ตัวอย่าง UAT | https://uat-centralizeapi.redcross.or.th/api-uat/graphql (.env.example:32) |
| Client | AuthService.checkEmployeeCD |
| Protocol | HTTP POST GraphQL |
| Operation | fetchListEmployeeForTwoWay |
await this.axios.post(`${process.env.CENTRALIZE_API_URL}`, {
query: `query fetchListEmployeeForTwoWay($user: [childEmployeeForTwoWay]) { ... }`,
variables: { user: [{ userID: username }] },
});แหล่ง: auth.service.ts:76-104
ชื่อ env มีคำว่า CENTRALIZE แต่ URL path เป็น /api-uat/graphql และ operation name เป็นของ cu-central GraphQL — ไม่ใช่ NestJS centralize-api ที่เป็น REST /api/employee
สรุป dual-centralize (canonical)
| Env var | ระบบจริง | ใช้โดย | Transport |
|---|---|---|---|
CENTRALIZE_SERVER | NestJS centralize-api | EmployeesService | REST |
CENTRALIZE_API_URL | cu-central-api | AuthService.checkEmployeeCD | GraphQL |
นี่คือหลักฐานจากมุม 2way-api ว่ามี caller ไปทั้งสองระบบ — เกี่ยวข้องกับ debt การตั้งชื่อ "centralize" ที่สับสนข้าม repo
Integration 2 — SSO login
Env + hardcoded fallback
| Field | Env | Fallback ในโค้ด |
|---|---|---|
| AES key | SSO_AES_SECRET_KEY | 'EoABvcD7RsFBeKRZgSOHS4LLD08To6Pc' |
| AES IV | SSO_AES_INIT_VECTOR | 'AaNZRKzytx9yCAiA' |
| Endpoint | SSO_END_POINT | 'https://uatssoapi.redcross.or.th/SSOService_dev/api' |
| App ID | SSO_APP_ID | 'VTRCBO' |
| Request ID | SSO_REQUEST_ID | 'SSO220607000000031' |
| Basic user | SSO_USERNAME | 'user' |
| Basic pass | SSO_PASSWORD | 'P@ssw0rd' |
แหล่ง: auth.service.ts:20-26 และ .env.example:38-45
Flow
aesEncryptFn(username)/aesEncryptFn(password)—aes-256-cbcผ่าน Nodecrypto.createCipheriv(auth.service.ts:122-131)- POST
${SSO_END_POINT}/ssoLoginพร้อม body{ requestAppId, requestId, username, password }และ HTTP Basic Auth (auth.service.ts:151-172) - ถ้า
responseCode === '0000'→isSuccess: true(ssoAxiosInstance,auth.service.ts:138-141) - ออก JWT two-way เก็บแค่
{ loginToken: sso.loginToken }อายุ 48h
ความเสี่ยง: fallback secrets ฝังในซอร์ส — ถ้า deploy ลืมตั้ง env จะใช้ค่า UAT/dev ที่ commit ไว้ใน git
Integration 3 — vtrc-api GraphQL (ถูก comment ใน production path)
private async fetchEmployeeProfileForOtherService(token: string) {
return await this.axios.post(process.env.VTRC_BASE_CONNECT, fetchEmployee, {
headers: {
apiKey: process.env.API_KEY_OUTBOUND_VTRC,
Authorization: `Bearer ${token}`,
},
});
}แหล่ง: auth.service.ts:44-67
| Env | ตัวอย่าง .env.example |
|---|---|
VTRC_BASE_CONNECT | https://uat-vtrcapi.redcross.or.th/api-uat/graphql |
API_KEY_OUTBOUND_VTRC | UUID string |
Operation: fetchEmployeeProfileForOtherService — ตรงกับ pattern ของ legacy vtrc-api GraphQL + apiKey header
สถานะ runtime: ถูก comment ที่ signInRedirectByPass (auth.service.ts:295-296) และ profile fields ที่เคยเติมจากผลลัพธ์ถูก hardcode เป็น null (auth.service.ts:326-329)
สรุป: โค้ดเชื่อม vtrc-api มีอยู่ แต่เส้นทาง production ปัจจุบันไม่เรียก
Integration 4 — LINE Messaging API
Tokens จาก config
lineToken: process.env.LINE_AUTH_TOKEN, // RC
lineCUToken: process.env.LINE_AUTH_CU_TOKEN, // CUแหล่ง: configuration.ts:11-12
เลือก token ใน service:
const token = lineToken === 'CU' ? this.config.get('lineCUToken') : this.config.get('lineToken');แหล่ง: line.service.ts:22,50
พารามิเตอร์ที่สองของ pushMassage ชื่อ lineToken แต่ค่าที่ส่งจาก announce คือ content.typeGroup ('RC'/'CU') — announces.service.ts:1382
Endpoints
| Method | URL | ใช้โดย |
|---|---|---|
| POST | https://api.line.me/v2/bot/message/push | pushMassage |
| POST | https://api.line.me/v2/bot/message/multicast | multiCast |
แหล่ง: line.service.ts:30,57
Payload รูปแบบ text อย่างเดียว: { to, messages: [{ type: 'text', text }] }
Logging: pushMassage log token เต็มใน logger (line.service.ts:23) — ความเสี่ยงเลอะ secret ใน log aggregate
Integration 5 — 2way-line-service (hardcoded IP)
await this.axios.post('http://20.212.116.206:8200/api/v1/line/nextPage', data);
await this.axios.delete('http://20.212.116.206:8200/api/v1/line/previousPage', data);แหล่ง: line.service.ts:76,86
| ข้อเท็จจริง | รายละเอียด |
|---|---|
| Host | IP คงที่ ไม่ผ่าน env |
| Port | 8200 — ตรงพอร์ต default ของ 2way services |
| Path | /api/v1/line/nextPage, /previousPage |
| Scheme | http ไม่ใช่ https |
ไม่มีการ authenticate ไปยัง line-service ในโค้ดส่วนนี้ — เชื่อถือ network path
Integration 6 — Cross-schema PMS
Database instance เดียวเข้าถึงได้ทั้ง:
[2way].dbo.*— แอปหลักpms.dbo.tbnotify— แจ้งเตือนจากระบบ PMS
Query หลัก (notification-pms.task.service.ts:27-46):
SELECT tn.*, ul.lineID, ul.richMenuID
FROM pms.dbo.tbnotify AS tn
INNER JOIN [2way].dbo.userline AS ul
ON tn.empcode COLLATE Thai_100_CI_AS = ul.userid COLLATE Thai_100_CI_AS
AND ul.isActive = 1
WHERE tn.notifyDate <= cast(getdate() AS date)
AND tn.lineStatus = 1
AND tn.isSendLine = 0
AND tn.notifyGroup = 'PMS'
AND tn.fiscalYear = CASE
WHEN DATEPART(MONTH, getdate()) >= 10 THEN YEAR(getdate()) + 543 + 1
ELSE YEAR(getdate()) + 543
ENDหลัง multicast สำเร็จ → UPDATE pms.dbo.tbNotify ตั้ง isSendLine = 1 โดยต่อ notifyCode / empCode เป็น string (notification-pms.task.service.ts:77-84)
สถานะ: cron handler ถูก comment — ดูบท 02; schema coupling ยังอยู่ในซอร์ส
Integration 7 — Consumer frontend (route prefix)
| ฝั่ง | ค่า | แหล่ง |
|---|---|---|
API BASE_PATH | 2way-api/ | .env.example:3 |
| Global prefix | 2way-api/api/v1 | main.ts:24-26 |
| Swagger | 2way-api/docs | main.ts:42 |
| Static files | URL prefix /2way-api/ | main.ts:27-29 |
Frontend 2way-meeting-backoffice production ตั้ง APP_CONNECT_API ชี้มาที่ host เดียวกันด้วย path /2way-api — ตรงกับ prefix นี้ (ยืนยันใน index ของ volume / env ของ backoffice)
Secrets และ JWT — แผนที่ตัวแปร
| ตัวแปร | ใช้ทำอะไร | ปรากฏใน |
|---|---|---|
SECRET_JWT_TWOWAY | ออก/ตรวจ JWT ของ 2way | jwt.strategy, auth.service, profiles.util |
SECRET_KEY | ตรวจ token จาก VTRC ใน redirectbypass; map ใน config เป็น jwtSecretKey | auth.service, configuration.ts |
LINE_AUTH_TOKEN | LINE OA RC | configuration → LineService |
LINE_AUTH_CU_TOKEN | LINE OA CU | configuration → LineService |
SSO_* | SSO encrypt + Basic | AuthService fields |
API_KEY_OUTBOUND_VTRC | header ไป vtrc-api | AuthService (commented caller) |
NEXT_PUBLIC_ENCRYPT_SECRET | crypto-js helper | encrypt.util.ts default 'CHANGE_ME_PLS' |
Backdoor ไม่ใช้ env — ใช้ string ตรงใน signInNonEmp (auth.service.ts:188)
.env.example มีค่าคล้าย production/UAT จำนวนมาก (DB password, LINE tokens, secrets) — ถือเป็น secret hygiene issue ระดับ repo; เอกสารนี้ไม่วางค่าเต็มซ้ำนอกที่จำเป็นต่อการอ้างอิง path
Status code tables ใน configuration
configuration.ts ฝัง status mapping สำหรับ announcement และ survey (ใช้เป็น business constant ในโค้ด ไม่ได้อ่านจาก DB อย่างเดียว):
Announcement (contentStatus.announcement)
| shortName | statusCode | ความหมาย |
|---|---|---|
| CAS | 00 | Create Announcement Send Admin |
| CAU | 00 | Create Announcement Not send Admin |
| MRN | 10 | Manager received |
| ERA | 11 | Employee received |
แหล่ง: configuration.ts:14-18
Survey (contentStatus.survey)
| shortName | statusCode | ความหมาย |
|---|---|---|
| ACS | 00 | Create in-scope |
| ACD | 00 | Create out-scope |
| ERN | 40 | Employee received |
| HRN | 41 | HR Admin received |
| HAP | 42 | HR Admin approved |
| HUA | 43 | HR Admin unapproved |
Action enum: approve 10, unapprove 20, adminReceive 30, assign 40, read 50 (configuration.ts:27-33)
Error codes SV100–SV105 ฝังข้อความไทย (configuration.ts:34-76)
Complaint ใน configuration
บล็อก contentStatus.complaint ส่วนใหญ่ถูก comment; เหลือ secretary.employeeCodes: ['', '', ''] (configuration.ts:79-111) — ค่าว่าง แสดงว่า secretary list ย้ายไปใช้ DB/m_config หรือ constant อื่นแทน
Complaints constants ใน complaints.constant.ts ยังอ้าง list.empCode.secretary และ action names SECRETARY_*
File storage persistence
ไม่ใช่ DB blob — ไฟล์อยู่บน filesystem:
| รายการ | ค่า |
|---|---|
| Write path | /uploads/{prefixPath}/{YYYY-MM-DD}/... |
| Env hint | MULTER_DEST=./uploads (.env.example:23) — upload service ใช้ path ตรงมากกว่า multer |
| HTTP read | static prefix /2way-api/ |
| MIME allowlist | jpeg, jpg, png, pdf, mp4 |
แหล่ง: upload.service.ts:35-55, main.ts:27-29
dir_attach / attach_files ใน entity เก็บ path/ชื่อไฟล์อ้างอิงกลับมาที่ disk
Views / tables นอก entity glob
โค้ดอ้างชื่อที่ไม่มีไฟล์ .entity.ts ตรงๆ:
| ชื่อ | ที่ใช้ | หมายเหตุ |
|---|---|---|
VM_PERMISSION_FOR_SEARCH_EMPLOYEE | employees.service.ts:267 | VIEW คืน JSON empList |
teamMember / teamAdmin | auth.service.ts:299-304 | ใช้ใน redirectbypass หา level |
pms.dbo.tbnotify | notification-pms | คนละ schema |
t_content_survey_all | surveys.service raw SQL | มี entity ชื่อ class TContentSurveyAll map ตารางนี้ |
สิ่งเหล่านี้ยืนยันว่า schema จริงกว้างกว่าชุด entity files — เมื่อ migrate ต้องดูทั้ง SQL folder และ raw queries
Docker / runtime กับ persistence
Dockerfile.prod:
- Build stage Node 16 +
yarn build - Production stage Node 18
COPY .env.prodแล้วmv .env.prod .envCMD yarn start:prod→node dist/src/main
แหล่ง: Dockerfile.prod (อ่านจาก repo)
ผล: connection string และ secrets ใน image มาจากไฟล์ .env.prod ที่ถูก bake ตอน build — หมุน secret ต้อง rebuild/redeploy
Checklist ตรวจ integration ตอน debug
- พนักงาน list ผิด → ดู
CENTRALIZE_SERVERถึง NestJS centralize-api หรือยัง + filterm_user_manager - Login SSO ล้ม → ดู
SSO_END_POINT/ AES key-IV ตรงกับฝั่ง SSO หรือไม่; อย่าลืมว่า employee ที่พบใน CD จะถูกปฏิเสธก่อนถึง SSO - Login ผ่านแต่เป็น admin ปลอม → ตรวจว่าใช้ backdoor
i3adminหรือไม่ - LINE ไม่ถึง → แยก
typeGroupRC/CU กับ token ที่ถูก; ดู log ว่า leak token หรือไม่ - Rich menu เพจไม่เปลี่ยน → ตรวจว่า IP
20.212.116.206:8200ยังชี้ line-service จริง - PMS notify เงียบ → ยืนยันว่า
@CronของNotificationPmsTaskServiceยังถูก comment - SQL error แปลกๆ จาก announce list → สงสัย injection/input พิเศษใน query string ก่อน
สิ่งที่บทนี้ยืนยัน vs ไม่ยืนยัน
ยืนยันจากซอร์สแล้ว
- Dual call: REST
CENTRALIZE_SERVER+ GraphQLCENTRALIZE_API_URL - SSO AES + Basic + fallback secrets
- LINE push/multicast URLs และ dual token
- Hardcoded IP ไป line-service
- PMS cross-schema SQL (แม้ cron ปิด)
- vtrc-api client มีแต่ caller comment
synchronize: false,encrypt: false
ไม่ยืนยันในเอกสารนี้
- ว่า production DNS/IP ปัจจุบันของแต่ละ env ตรงกับ
.env.exampleหรือไม่ (ต้องดู secret store ตอน deploy) - ว่า
centralize-apiปลายทาง enforce auth อย่างไร (อยู่นอก repo นี้ — ดู volume ของ centralize-api) - schema drift ระหว่าง
sql/*.sqlกับ DB จริงในแต่ละ environment