13.2 · REST APIs
16 repository เปิด REST เป็น API หลัก (ไม่รวม vtrc-api/cu-central-api ที่มี REST เสริมคู่กับ GraphQL — ดู 13.1) ส่วนใหญ่เป็น NestJS 9 ที่ใช้ template เดียวกัน (global prefix {BASE_PATH}api/v1, JwtAuthGuard global guard + @Public() override, Swagger ที่ {BASE_PATH}docs) — เป็น pattern ที่ copy ข้าม repo จน "เหมือนกันไบต์ต่อไบต์" ในหลายจุด (ดู Volume 6 index)
13.2.1 · ตารางสรุปทุก REST API
| Repo | Framework | Base path | Port | Auth pattern | Swagger |
|---|---|---|---|---|---|
vtrc-api | Express (REST เสริม GraphQL) | / (routes.js, ~68 KB) | 8081 | apiKey + JWT (บางส่วนไม่มี) | ไม่มี |
cu-central-api | Express (REST เสริม GraphQL) | / (endpoint auth) | 8080 (default code) | apiKey + LDAP/Basic auth | ไม่มี |
centralize-api | NestJS 9 | /api/* | 3000 default | JWT guard ประกาศไว้แต่ @Public() ทุก endpoint (17/17) | มี (/docs) |
2way-api | NestJS 9 | 2way-api/api/v1 | 8200 (.env.example, ไม่ใช่ 3000 default ใน main.ts) | JWT global guard + @Public() | มี ({BASE_PATH}docs) |
2way-batch | NestJS 9 | 2way-batch/api/v1 | UNVERIFIED (ไม่มี .env.example, Dockerfile เขียน EXPOSE 3000) | JWT global guard | มี |
2way-line-service | NestJS 9 | 2way-line-service/api/v1 | 8200 (.env.example) | JWT global guard | มี |
2way-vtrc-api | Express + ts-node | /2way/... | 8001 hardcode (ไม่อ่านจาก env) | Passport JWT + master bypass token static string "testnaja" (SEC-2WAYVTRC-01) | มี (swagger-jsdoc, title ยังเป็น "JSONPlaceholder" ตัวอย่าง) |
benefit-api | NestJS 9 | benefit-api/api/v1 | 9320 | JWT global guard (Passport JWT) | มี, เปิดใน production ไม่มี env flag ปิด |
recruitment-api | NestJS 9 | recruitment-api/api/v1 | 9330 | JWT global guard + self-managed JWT (AuthDataService) | มี, CORS origin: '*' |
ai-recruitment-api | Express (ไม่ใช่ NestJS) | /ai-recruitment-api/api | 9430 | custom verifyJWT middleware (jsonwebtoken ตรง ไม่มี Passport) | ไม่มี |
pms-api | NestJS 9 | pms-api/api/v1 | 9331 | JWT global guard + @Public() (GET /budget/getEmployeeSalary เป็น public — SEC-PMS-01) + ThrottlerGuard (100 req/60s — เฉพาะ repo นี้) | มี |
chat-center-api | NestJS 9 | chat-center-api/api/v1 | 8200 (ชนกับ 2way-api/2way-line-service เลขพอร์ตเดียวกันทั้ง 3 repo — คนละ container/host จึงไม่ conflict จริง แต่บ่งชี้ว่าไม่มี port-allocation convention กลาง) | JWT global guard | มี |
job-scheduler-api | NestJS 9 | ประกาศ benefit-api/ (ผิด — QUAL-JOBSCHED-02) | 9900 (.env, Dockerfile เขียน EXPOSE 3000 ไม่ตรง) | JWT global guard (แต่ AppController ว่างเปล่า — ไม่มี business endpoint) | มี |
workflow-api | NestJS 10 | workflow-api/api/v1 | 8000 default (.env.example) | ไม่มี global guard — อ่าน JWT payload ไม่ verify signature | มี (SWAGGER_PATH, comment ในโค้ดยอมรับตรงว่า "signature is not verified in this service") |
sso-api | NestJS 10 | {BASE_PATH}{API_VERSION_PATH} | UNVERIFIED (ไม่มี .env/.env.example/Dockerfile ใน repo เลย) | JWT global guard (Passport JWT ฝั่งรับ request) | มี |
vtrc-common | NestJS 9 | vtrc-common/api/v1 | 9310 | JWT guard ประกาศไว้แต่ ทุก endpoint ที่มีเป็น @Public() (SEC-COMMON-01) | มี |
meeting-vtrc-api | Express + ts-node | /meeting/... | 8000 hardcode (app.ts) | Passport JWT bridge ผ่าน vtrc-api | ไม่มี |
vtrc-ocr-api | Express | /api | 3000 default, UNVERIFIED ค่าจริง (ไม่มี .env.example) | ไม่มี auth เลย | มี (swagger-jsdoc, /docs + /docs.json) |
13.2.2 · Swagger/OpenAPI — มีเยอะแต่ไม่มี consumer ที่ยืนยันได้
14 จาก 16 repo REST เปิด Swagger/OpenAPI UI จริง — ทุก NestJS service ที่ใช้ @nestjs/swagger default (10 ตัว: centralize-api, 2way-api, 2way-batch, 2way-line-service, benefit-api, recruitment-api, pms-api, chat-center-api, job-scheduler-api, workflow-api, sso-api, vtrc-common — จริงๆนับได้ 12) รวมกับ 2way-vtrc-api และ vtrc-ocr-api ที่ใช้ swagger-jsdoc/swagger-ui-express แทน มีเพียง ai-recruitment-api และ meeting-vtrc-api ที่ไม่มี Swagger เลยในกลุ่ม REST ทั้งหมด แต่ ไม่พบหลักฐานว่ามีทีมใดใช้ Swagger spec สำหรับ codegen client หรือ contract testing — เป็น documentation ที่มีอยู่เฉยๆ ไม่ผูกกับ pipeline อัตโนมัติใดในทุก repo ที่ตรวจสอบ
Swagger เปิดใน production โดยไม่มี env flag ปิดใน benefit-api/recruitment-api (ทั้งสองไม่มี guard ปิด Swagger ตอน NODE_ENV=prod) — ดู 13.4 Scorecard
13.2.3 · Base path collision และ port ที่ไม่ตรง env
พบ 2 pattern ที่ก่อความสับสนซ้ำในหลาย repo:
EXPOSEใน Dockerfile ไม่ตรงกับPORTจริงใน.env—job-scheduler-api(EXPOSE 3000ใน Dockerfile แต่.envตั้งPORT=9900จริง,docker-compose.ymlmap9900:9900ถูกต้อง —EXPOSEเป็น documentation เฉยๆ ไม่ผูกกับ listen port จริง) —benefit-api/recruitment-api/pms-apiใช้ConfigService.get('port')fallback3000ในmain.tsแต่ค่าจริงมาจาก.envเสมอ (9320/9330/9331 ตามลำดับ ตรงกับ DockerfileEXPOSEของทั้งสามตัว) — ต้องเปิด.env/Dockerfile คู่กันเสมอ ห้ามเชื่อ default ในmain.tsอย่างเดียวBASE_PATHcopy ผิดจาก repo อื่น —job-scheduler-apiประกาศBASE_PATH=benefit-api/(ค้างจากการ copy.envต้นแบบของbenefit-api) ทำให้ URL ที่ nginx ต้อง route จริง ({SERVER_URL}{BASE_PATH}api/v1) ไม่ตรงกับชื่อ service — ดูQUAL-JOBSCHED-02ใน job-scheduler-api/04-scorecard- เลขพอร์ต 8200 ถูกใช้ซ้ำ 3 ครั้งในแพลตฟอร์ม —
2way-api,2way-line-service,chat-center-apiทั้งสามตัวตั้งPORT=8200ใน.env/.env.exampleของตัวเอง เป็นไปได้เพราะแต่ละตัว deploy เป็น container/host แยกกันจึงไม่ชนกันจริงในการรัน แต่สะท้อนว่าไม่มี central port-allocation registry ข้ามทีม — ความเสี่ยงคือถ้าวันหนึ่งมีคนพยายามรันสอง service นี้บน host เดียวกัน (เช่น ตอน local dev หรือ docker-compose รวม) จะชนกันทันที
13.2.4 · Auth pattern แบ่งเป็น 5 กลุ่ม
| กลุ่ม | Repo | ลักษณะ |
|---|---|---|
| Legacy apiKey + JWT | vtrc-api, cu-central-api | apiKey header ระบุ APP_TYPE, JWT decode (ไม่ verify ที่ layer แรก, verify ที่ @auth directive) |
| LDAP + JWT | cu-central-api | ผสมกับกลุ่มบน — LDAP/AD bind สำหรับ authentication จริง |
JWT global guard + @Public() (NestJS มาตรฐาน) | 2way-api, 2way-batch, 2way-line-service, benefit-api, recruitment-api, pms-api, chat-center-api, job-scheduler-api, vtrc-common, sso-api | pattern ที่ copy ข้าม repo มากที่สุดในแพลตฟอร์ม — @Public() decorator override guard ต่อ route |
| JWT ไม่ verify signature | workflow-api | อ่าน payload เพื่อดึง role/username เท่านั้น ไม่มี guard บังคับระดับ global — ข้อยกเว้นเดียวในกลุ่ม NestJS ทั้งหมด |
| ไม่มี auth เลย / bypass ทั้งระบบ | vtrc-ocr-api (ไม่มี auth), centralize-api (guard ประกาศแต่ @Public() 17/17), vtrc-common (guard ประกาศแต่ @Public() ทุก endpoint), 2way-vtrc-api (มี master bypass token แบบ static string) | ความเสี่ยงสูงสุดของกลุ่มนี้ |
ความไม่สม่ำเสมอนี้คือหัวใจของ QUAL-API-01 ใน 13.4 Scorecard
13.2.5 · Shared JWT secret ข้าม service (ยืนยันจาก 4+ repo)
SECRET_KEY ที่ benefit-api, recruitment-api, ai-recruitment-api, pms-api ใช้ verify JWT เป็นค่าเดียวกันทุกตัวอักษร — token ที่ service ใดออก (รวม vtrc-api ตาม issuer redcross.or.th:vtrc ที่ recruitment-api เช็ค) ใช้ยืนยันตัวตนข้าม service ในกลุ่มนี้ได้ทันทีโดยไม่ผ่าน central auth หรือ JWKS ใด ๆ — เป็นรูปแบบ "shared-secret SSO" ที่ทำงานได้จริงแต่ไม่มี key rotation mechanism และ secret หลุดอยู่แล้วผ่าน .env ที่ commit เข้า git ทุก repo ที่กล่าวถึง
ดูหลักฐานต่อ repo ที่ benefit-api/04-auth-integrations, recruitment-api/04-auth-integrations, ai-recruitment-api/04-auth-integrations, pms-api/04-auth-integrations