7.1.1 · Repository map (V1 depth)
branch: prod-meeting-vtrc-api (180 tracked files)
บทนี้เป็นแผนที่ไฟล์ของ meeting-vtrc-api — Express 4 + Sequelize 6 + MSSQL สำหรับจองและบริหารการประชุม พร้อม outbound ไป legacy vtrc-api และ employee directory แบบ "TwoWay"
ชื่อ package ใน package.json คือ "express" (package.json:2) — ไม่ตรงกับชื่อ repo; เป็นร่องรอย scaffold เก่า
แผนที่โฟลเดอร์ระดับ repo
meeting-vtrc-api/ (branch: prod-meeting-vtrc-api)
├── app.ts # entry — Express + Sentry + route mount + listen(8000)
├── package.json # name=express, main=app.ts, start=ts-node
├── Dockerfile # node:16.19.0-alpine + Chromium + COPY .env${ENV}
├── docker-compose.yml # meetingservice, 8000:8000, storage volume
├── .envdev / .envuat / .envprod # plaintext secrets committed
├── tsconfig.json / jest.config.js / .eslintrc.json
├── README.md # สั้นมาก — ไม่ใช่ source of truth
├── model/
│ ├── meeting/meeting.ts # 11 tables: Meeting, Guest, OtherGuest, Attached,
│ │ # Summary, StatusResponse, Remark, Delegate,
│ │ # Comment, Token, ScheduleJob
│ ├── approver/approver.ts # Approver
│ ├── usergroup/usergroup.ts # GroupMeeting + UserGroup
│ └── login.ts # user + user_group (domain ไม่เกี่ยวประชุม)
├── util/
│ ├── db.config.ts # Sequelize instance + import 4 model files
│ ├── env.ts # DATABASE/USERNAME/… จาก process.env
│ ├── jwt.config.ts # SECRET hardcode "123"
│ ├── mongo.config.ts # ทั้งไฟล์ comment — ไม่มี Mongo
│ ├── axiosinterceptors/
│ │ ├── vtrc.connector.ts # → VTRC_BASE_CONNECT (vtrc-api GraphQL)
│ │ └── cdb.connector.ts # → CDB_BASE_CONNECT (employee GraphQL)
│ ├── helper/ # corn.email, sendNoti, emailsender, mapuser, …
│ ├── emailtemplate/emailtemplate.ts
│ └── pdftemplate/ # sign_join_meeting.ejs, layout.html.ejs
├── middleware/
│ ├── checkbypass.ts # Bearer + TokenMeeting (ใช้จริงน้อยมาก)
│ └── checkbypasswhenredirect.ts # redirect flow: verify vtrc + stamp TokenMeeting
├── lib/
│ ├── pdfgenbyhtml.func.ts # EJS → PDF (html-pdf-node / puppeteer)
│ ├── Emailsender.func.ts # nodemailer + iCal
│ ├── Uploadimage.class.ts # Tinify key hardcode (call commented)
│ ├── uploadfile.func.ts # เขียนไฟล์แนบลง disk
│ ├── Usermanage.class.ts / Dateformat.class.ts / model.ts
├── corn-job/
│ ├── config.ts # JOB_SCHEDULE = "*/15 * * * *"
│ └── email.corn.ts # scheduleEmail() — ดูหมายเหตุ boot ด้านล่าง
├── routes/
│ ├── auth/
│ │ ├── redirectbypass.ts # mount ที่ /meeting/redirectbypass
│ │ └── refreshtoken.ts # ไม่ถูก mount — dead code
│ ├── secure/meeting/ # ~30 route files
│ ├── secure/approver/approver.ts
│ ├── secure/usergroup/usergroup.ts
│ └── mockdelatlast/mockuser.ts # PII mock — ไม่มี auth
├── fonts/ # Kanit ฯลฯ สำหรับ PDF ใน Docker
├── storage/ # static ผ่าน /meeting/file
├── interfacedef/ # TypeScript interfaces (approver, user)
├── __test__/ / coverage/ / dist/ # test + build output — ห้ามแก้เป็น source
└── dataMeeting/ # sample data folderหลักการจัดกลุ่มไฟล์
model/= ที่เดียวที่นิยาม schema — ไม่มี migration folder; boot เรียกแค่authenticate()(util/db.config.ts:27-33) ไม่มี.sync()routes/secure/*ไม่ได้แปลว่ามี auth — คำว่า secure เป็นชื่อโฟลเดอร์; global guard ถูก comment ที่app.ts:104-120util/axiosinterceptors/คือขอบเขต outbound 2 ทาง —vtrc.connector.tsและcdb.connector.tscorn-job/สะกดผิดจาก "cron" ทั้ง repo รวม public path/meeting/secure/corn-email— คงสะกดตามโค้ดจริง
Boot sequence
1. require("./corn-job/email.corn") app.ts:4
│ ← โหลด module แต่ไม่เรียก scheduleEmail() — cron auto-start ไม่เกิด
▼
2. require("./util/mongo.config.ts") app.ts:8
│ ← side-effect เปล่า (ไฟล์ comment ทั้งก้อน)
▼
3. dotenv.config({ path: "./.env" }) app.ts:21-23
│ ← ชื่อไฟล์ต้องเป็น .env (Docker COPY .env${ENV} → .env)
▼
4. swagger-jsdoc + swagger-ui @ /docs app.ts:50-78
▼
5. Sentry.init + request/tracing/error app.ts:79-89
▼
6. bodyParser json limit 500mb app.ts:123-124
▼
7. CORS Allow-Origin: * (มือเขียน) app.ts:126-144
▼
8. static /meeting/file → storage/ app.ts:147
▼
9. mount ~30 route prefixes app.ts:148-231
▼
10. app.listen(8000) app.ts:232-234Cron ไม่ถูก start ตอน boot — ข้อเท็จจริงสำคัญ
corn-job/email.corn.ts:6-18 export scheduleEmail แต่ app.ts แค่ require module เป็น myCorn โดยไม่เรียก myCorn.scheduleEmail()
ผลที่ตรวจได้จากซอร์ส:
| ช่องทาง | สถานะ | หลักฐาน |
|---|---|---|
| Auto cron ทุก 15 นาที | ไม่ถูกเรียกตอน boot | app.ts:4 require อย่างเดียว |
| Manual HTTP trigger | ทำงาน | GET /meeting/secure/corn-email → executeTask("email") (routes/secure/meeting/corn-email.ts:10-14) |
ถ้า production พึ่ง cron ใน process นี้โดยไม่มี external scheduler ยิง /corn-email — อีเมลจะไม่ถูกส่งอัตโนมัติ (UNVERIFIED ว่ามี cron ภายนอกหรือไม่)
Cluster fork code ที่ app.ts:25-36 ถูก comment ทั้งก้อน — รัน single process
dotenv path mismatch
| จุดโหลด | Path | หลักฐาน |
|---|---|---|
app.ts / util/env.ts | ./.env | app.ts:21-23, util/env.ts:1-3 |
lib/Emailsender.func.ts | ./.env ถ้า NODE_ENV==production มิฉะนั้น ./.envdev | lib/Emailsender.func.ts:6-8 |
middleware/checkbypasswhenredirect.ts | เหมือน Emailsender | middleware/checkbypasswhenredirect.ts:11-13 |
| Docker build | COPY ".env${ENV}" .env | Dockerfile:24 |
ไฟล์ที่ commit ชื่อ .envdev / .envuat / .envprod — ไม่มี .env ใน working tree จนกว่า Docker/CI จะ copy
Inventory — ทุก REST route ที่ mount ใน app.ts
Prefix มาจาก app.use(...) ใน app.ts:147-231 รวมกับ path ใน route.(get|post|delete) ของแต่ละไฟล์
Auth / redirect / mock
| Method | Full path | File | Auth |
|---|---|---|---|
| GET | /meeting/redirectbypass/ | routes/auth/redirectbypass.ts:22 | checkBypassWhenRedirect + x-api-key |
| GET | /meeting/mockuser/one | routes/mockdelatlast/mockuser.ts:29 | ไม่มี — คืน PII hardcode |
| — | (ไม่ mount) refreshtoken.ts | routes/auth/refreshtoken.ts | dead code |
Approver
| Method | Full path | File:line |
|---|---|---|
| POST | /meeting/secure/approver/addNewApprover | approver.ts:22-24 |
| GET | /meeting/secure/approver/listApprover | approver.ts:50 |
| DELETE | /meeting/secure/approver/deleteApprover | approver.ts:82-84 |
Mount: app.ts:148-149
User group (/meeting/secure/group)
| Method | Full path | File:line |
|---|---|---|
| POST | /meeting/secure/group/creategroup | usergroup.ts:23-25 |
| POST | /meeting/secure/group/editgroup | usergroup.ts:55-57 |
| GET | /meeting/secure/group/listgroup | usergroup.ts:86 |
| GET | /meeting/secure/group/ | usergroup.ts:119 |
| GET | /meeting/secure/group/meetinggroup | usergroup.ts:153 |
| DELETE | /meeting/secure/group/deletegroup | usergroup.ts:185-187 |
| GET | /meeting/secure/group/listgroupmeetingall | usergroup.ts:207 |
| GET | /meeting/secure/group/finduserlistbygroup/:id | usergroup.ts:224-226 |
Mount: app.ts:218-219
Meeting CRUD + lists
| Method | Full path | File | หมายเหตุ |
|---|---|---|---|
| POST | /meeting/secure/meeting/addNewMeeting | meeting.ts:29-31 | Flow 1 — สร้างประชุม |
| DELETE | /meeting/secure/meeting/deletemeeting | meeting.ts:51-53 | soft-delete isActive: 0 |
| GET | /meeting/secure/meeting/send | meeting.ts:73 | test hook — sendMail(4403) hardcode |
| POST | /meeting/secure/updateMeeting/ | updatemeeting.ts:23-25 | |
| POST | /meeting/secure/searchmeeting/searchMeeting | searchmeeting.ts:22-24 | |
| POST | /meeting/secure/searchmeetingsummary/ | searchmeetingsummary.ts:22 | |
| POST | /meeting/secure/listmeetingdraft/ | listmeetingdraft.ts:22 | |
| POST | /meeting/secure/listmeetingevercreated/ | listmeetingevercreated.ts:22 | |
| POST | /meeting/secure/listmeetingpendingapproval/ | listmeetingpendingapproval.ts:22 | |
| POST | /meeting/secure/listmeetingrecordafter/ | listmeetingrecordafter.ts:22 | |
| POST | /meeting/secure/listmeetingrequestapproval/ | listmeetingrequestapproval.ts:22 | |
| POST | /meeting/secure/listmeetinginvited/ | listmeetinginvited.ts:29 | |
| POST | /meeting/secure/listmeetingagree/ | listmeetingagree.ts:29-31 | |
| POST | /meeting/secure/meetingdetail/ | meetingdetail.ts:35 | |
| POST | /meeting/secure/approvemeeting/ | approvemeeting.ts:24-26 | Flow 3 |
| POST | /meeting/secure/acceptancemeeting/ | acceptancemeeting.ts:31-33 | Flow 4 — join/reject/delegate |
| POST | /meeting/secure/guestlistmeeting/ | guestlistmeeting.ts:22 | |
| POST | /meeting/secure/updateSummaryMeeting/ | updatesummarymeeting.ts:25-27 | |
| POST | /meeting/secure/commentSummaryMeeting/ | commentsummarymeeting.ts:22-24 | |
| POST | /meeting/secure/listCommentSummaryMeeting/ | listcommentsummarymeeting.ts:22-24 | |
| GET | /meeting/secure/listCommentbyMultiMeeting/ | listcommentbymultimeeting.ts:22-24 | |
| POST | /meeting/secure/listMeetinginMonth/ | listmeetinginmonth.ts:36-38 | |
| POST | /meeting/secure/acceptsummarymeeting/ | acceptsummarymeeting.ts:22-24 |
Mount lines: app.ts:150-207, 222-223
Group meeting (legacy duplicate path)
| Method | Full path | File |
|---|---|---|
| GET | /meeting/secure/groupmeeting/ | groupmeeting.ts:22 |
| GET | /meeting/secure/groupmeeting/meetinggroup | groupmeeting.ts:42 |
| DELETE | /meeting/secure/groupmeeting/deletegroup | groupmeeting.ts:74-76 |
Mount: app.ts:152-153 — ฟังก์ชันคล้าย /meeting/secure/group แต่คนละ router
Lookup / org / PDF / attachment / ops
| Method | Full path | File | หมายเหตุ |
|---|---|---|---|
| GET | /meeting/secure/finddropdownbyorg/:level | finddropdownbyorg.ts:22 | |
| GET | /meeting/secure/finduserlistbyorg/ | finduserlistbyorg.ts:23 | |
| POST | /meeting/secure/finduserlist/ | finduserlist.ts:19-21 | |
| POST | /meeting/secure/createpdfinvitemeeting/ | createpdfinvitemeeting.ts:44 | import auth แต่ไม่ apply |
| POST | /meeting/secure/createpdfsignaturemeeting/ | createpdfsignaturemeeting.ts:15 | |
| GET | /meeting/secure/attacment/:id | attacment.ts:29 | typo "attacment"; import auth ไม่ apply |
| GET | /meeting/secure/corn-email/ | corn-email.ts:10 | manual email task — ไม่มี auth |
| GET | /meeting/secure/health-check/email | health-check.ts:9 | ส่งอีเมลจริงไป hardcode address |
Static:
| Method | Full path | หลักฐาน |
|---|---|---|
| GET | /meeting/file/* | app.ts:147 → ./storage/meeting/ |
Auth surface — สิ่งที่โค้ด enforce จริง
app.ts:104-120 ← global /meeting/secure/* guard ถูก comment ทั้งก้อน
ผล:
เกือบทุก mutation/list รับ request ได้โดยไม่ต้องมี Bearer หรือ x-api-key
ยกเว้น redirectbypass ที่ใส่ middleware บน route เองmiddleware/checkbypass.ts — พฤติกรรมจริง
- ถ้ามี
Authorization: Bearer …→ เรียกrequestCheckInVtrcApi(token)(checkbypass.ts:17-26) requestCheckInVtrcApiสร้าง GraphQL query string แต่ ไม่ยิงvtrcConnector— มีแค่decodeJwt(token)แบบส่ง arg เดียว (checkbypass.ts:59-68) ในขณะที่decodeJwt(token, secert)ต้องการ secret (util/helper/jwtdecode.ts:12-19) →jwt.verifyด้วยundefinedมัก fail- ถ้าไม่มี Bearer →
next()ด้วยconfirmBypass = false(checkbypass.ts:28-30) — ไม่บล็อก
checkApiKeyMiddleWare เทียบ x-api-key กับ process.env.API_KEY (checkbypass.ts:91-105)
middleware/checkbypasswhenredirect.ts — flow ที่ใช้จริง
- Decode JWT ด้วย
SECRET_JWT_VTRC(checkbypasswhenredirect.ts:67) - POST GraphQL
fetchEmployeeProfileForOtherServiceผ่านvtrcConnector(:69-71) - ออก JWT ใหม่ด้วย
SECRET_JWT_MEETINGหมดอายุ 48h (signNewJwt,:99-106) - Stamp / update ตาราง
Token(TokenMeeting) (stampDBToken,:109-127) checkApiKeyMiddleWareบังคับx-api-key(:130-145)
นี่คือ auth bridge หลักระหว่าง meeting-backoffice กับ service นี้
Helper / lib map (ไฟล์ที่ flow หลักพึ่งพา)
| ไฟล์ | บทบาท |
|---|---|
util/helper/corn.email.helper.ts | createScheduleJob, executeTask, emailTask |
util/helper/emailsender.ts | orchestration ส่งเมล (เรียก lib/Emailsender.func.ts) |
util/helper/sendNoti.ts | push bridge → sendNotificationFrom3Party |
util/helper/mapuser.ts | map empCode → profile ผ่าน cdb |
util/helper/createSignnature.helper.ts | PDF ลายเซ็น / เชิญ |
util/helper/filenamegen.ts | ชื่อไฟล์ gen |
util/helper/utils.ts | checkFileType |
lib/pdfgenbyhtml.func.ts | EJS → PDF buffer |
lib/Emailsender.func.ts | SMTP + iCal |
lib/uploadfile.func.ts | เขียนไฟล์แนบ |
ชื่อไฟล์ emailsender.ts vs Emailsender.func.ts ต่างกันแค่ตัวพิมพ์ — เสี่ยงบน Linux case-sensitive FS (ดู scorecard QUAL-MTVTRC-03)
Test / quality tooling
| Tool | Config | หมายเหตุ |
|---|---|---|
| Jest | jest.config.js, script test | มี __test__/test.spec.ts + coverage output |
| ESLint | .eslintrc.json, script lint | |
| Swagger | /docs | apis glob ./routes//.ts น่าจะผิดพลาด — อาจไม่ gen path จริง (app.ts:75) |
ไฟล์ที่ห้ามแก้ / ไม่ใช่ source of truth
dist/,coverage/,ut-report/— compiled / reportyarn-error.log,*.pdfตัวอย่างใน root_bk*ไม่พบใน repo นี้ แต่ถ้ามีให้ข้ามตามกฎ workspace
สรุปสำหรับผู้อ่านที่เพิ่งเข้า repo
- ยืนยัน branch
prod-meeting-vtrc-apiก่อนอ่านโค้ด - Persistence = Sequelize + MSSQL — ไม่ใช่ Mongoose
- อย่าเชื่อ path
/secure/ว่ามี auth — เปิดไฟล์ route +app.tsทุกครั้ง - Push ไม่ได้อยู่ใน service นี้ — อยู่ที่
sendNoti→vtrc-api - Mock
/meeting/mockuser/oneเป็นพื้นผิว leak ที่มีเอกสารใน 7.1.4