ai-recruitment-api Deep Dive
ai-recruitment-api (branch: prod — default branch main เป็น branch เปล่า; tracked files บน prod ≈ 390) เป็น Express service แยกต่างหากจาก recruitment-api ทั้ง repo และทั้ง stack — ทำหน้าที่เป็น AI content / graphic generation helper สำหรับกระบวนการสรรหาบุคลากร ไม่ใช่ CRUD API ของข้อมูลผู้สมัคร
ชื่อใน package.json ตรงกับชื่อ repo (ai-recruitment-api/package.json (branch: prod):2) — entry script คือ server.js (package.json:5-8) ไม่ใช่ NestJS main.ts / app.module.ts (ไฟล์เหล่านั้นไม่มีใน repo นี้)
หน้าที่หลัก (จาก endpoint จริง)
- สร้างเนื้อหาประกาศงานด้วย AI — generate / สรุป job description, qualification และชุดคำถามสัมภาษณ์ ผ่าน OpenAI Chat Completions (
gpt-4oหรือgpt-3.5-turboตาม controller) - สร้างภาพกราฟิกประกาศรับสมัคร — เลือกพื้นหลังสำเร็จรูป, สร้าง/เลือกภาพตัวละคร (DALL-E /
gpt-image-1หรือคลัง local), ลบพื้นหลัง (remove.bg), ผสม HTML + Puppeteer เป็นภาพประกาศสำหรับโซเชียล
หลักฐาน route ทั้งหมด: ai-recruitment-api/routes/index.js (branch: prod):53-144
Stack
| ส่วน | ค่า | หลักฐาน |
|---|---|---|
| Framework | Express 4 (express ^4.18.2) — ไม่ใช่ NestJS | ai-recruitment-api/package.json (branch: prod):15 |
| Language | JavaScript ล้วน (ไม่มี TypeScript / tsconfig.json) | โครงสร้างไฟล์ .js ทั้งหมด |
| AI text | openai SDK ^4.104.0 + raw axios → https://api.openai.com/v1/chat/completions | package.json:20, config/openai.js:4-28, summarize/interview controllers |
| AI image | OpenAI Images API ผ่าน openai.images.generate() (gpt-image-1 / dall-e-3) | config/dalle.js (branch: prod):41-52 |
| Background removal | remove.bg REST https://api.remove.bg/v1.0/removebg | services/removeBgService.js:66-78, controllers/removeBgUploadController.js:16,42 |
| HTML → image | Puppeteer ^22.6.5 + system Chromium ใน Docker | package.json:23, combineJobPostingController.js:377-386, Dockerfile:14,20-21 |
| Image libs (declared) | sharp ^0.33.4, canvas ^3.1.2 — ไม่พบ require() ในโค้ด .js ของ repo | package.json:12,22 (ดู scorecard) |
| QR code | api.qrserver.com (ไม่มี env) | combineJobPostingController.js:65-75 |
| Database | MSSQL ตรงผ่าน mssql ^9.0.1 — ไม่มี ORM | package.json:18, config/db.js:1-19 |
| Auth | Custom verifyJWT ด้วย jsonwebtoken — ไม่มี Passport / ไม่มี role guard | middleware/verifyJWT.js:7-40, server.js:37 |
| Upload | multer memory storage | routes/index.js:15, 99-117 |
| Package manager | npm (package-lock.json) | root lockfile |
| Scripts | start → node server.js, dev → nodemon server.js | package.json:6-8 |
Entry point และพอร์ต
- Entry:
ai-recruitment-api/server.js (branch: prod):1-72 - Mount path:
/ai-recruitment-api/api(server.js:18-19,60) - พอร์ตจาก env key
PORT— ค่าใน.envบรรทัด 6 คือ 9430; fallback ในโค้ดเป็น3000(server.js:67) - Dockerfile
EXPOSE 9430(Dockerfile:33) สอดคล้องกับค่า PORT ใน.env - ไม่มี Swagger / OpenAPI ใน repo
CORS whitelist (สำคัญ)
origin: [
"https://uat-vtrcbackoffice.redcross.or.th",
"https://vtrcbackoffice.redcross.or.th",
"http://localhost:3000",
],
methods: ["GET", "POST", "PUT", "DELETE"],
allowedHeaders: ["Content-Type", "authorization", "apiKey"],แหล่ง: server.js:23-31
นี่คือหลักฐานว่า consumer ที่ตั้งใจไว้คือ vtrc-backoffice (UAT/prod) และ local :3000 — ไม่ใช่ candidate-facing recruitment-web
หมายเหตุ: route upload ตั้ง Access-Control-Allow-Origin เป็น req.headers.origin || '*' ซ้ำอีกชั้น (routes/index.js:89-106) — กว้างกว่า CORS global ของ server.js
Auth แบบ one-liner
app.use(verifyJWT) ก่อน mount router (server.js:37,60) — ทุก HTTP request รวม static path ที่ผ่าน middleware stack นี้ต้องมี Bearer JWT (ไม่มี @Public() / allowlist ใน Express นี้) ดูรายละเอียดที่ 04 Auth + integrations
Deploy artifact
Dockerfile (branch: prod):
| รายการ | ค่า | บรรทัด |
|---|---|---|
| Base | node:18-alpine | 1 |
| Native build deps | python3, make, g++, cairo/pango/giflib/jpeg/libpng | 4-12 |
| Browser | chromium + fonts + PUPPETEER_EXECUTABLE_PATH=/usr/bin/chromium-browser | 14-21 |
| Install | npm install --legacy-peer-deps | 28 |
| Start | CMD ["npm", "start"] → node server.js | 35 + package.json:7 |
Node 18 ต่างจาก benefit-api / recruitment-api ที่ใช้ Node 20 — ดู LEG-AIRECRUIT-01 ใน 06 Scorecard
Database coupling
เชื่อม MSSQL database ชื่อจาก DB_DATABASE (.env:4) — ค่า hash ของชื่อ database ตรงกับ DB_AI_NAME ของ recruitment-api (ยืนยันด้วย SHA-256 prefix เดียวกันบนเครื่องเอกสารนี้; ค่า plaintext ไม่ใส่ในคู่มือ)
DB_SERVER ของ ai-recruitment-api ตรง hash กับ DB_AI_HOST ของ recruitment-api เช่นกัน
สรุป: shared-database coupling กับ recruitment-api connection ชื่อ ai — ดู 03 Persistence
JWT secret coupling
JWT_SECRET (.env:11) ที่ verifyJWT อ่าน (middleware/verifyJWT.js:4) มี SHA-256 hash เหมือน SECRET_KEY ของทั้ง benefit-api และ recruitment-api — shared signing key ข้ามอย่างน้อย 3 domain service (ดู 04)
สิ่งที่ควรรู้ก่อนอ่านบทถัดไป
.envอยู่ใน.gitignore:51แต่ ยังถูก track ใน git (git ls-files .envคืน blob) — มี MSSQL password, OpenAI key, remove.bg key, HCTI keys, JWT secret เป็น plaintext ใน history/working tree ของ branch prodstorage-uploads/มีภาพสำเร็จรูปหลายร้อยไฟล์ commit ใน repo (Background 29 ไฟล์ + คลังตัวละคร)routes/index.jsมี try/catch + diagnostic guard สำหรับmodule2_2_controllerเพราะเคยมี require-time crash (routes/index.js:23-78)- มี dead / orphan files:
module2_1_get_background_v2.js,module_2_3_combine_image.js,routes/get-post-upload_image.js— ไม่ถูกrequireจากserver.js - Env
HCTI_*,DALLE_ENDPOINT,GEN_BASE_URLปรากฏใน.envแต่ ไม่พบจุดเรียกใช้ใน.jsระหว่างการสำรวจนี้
สารบัญ
| บท | หัวข้อ |
|---|---|
| 01 | Repository map — โฟลเดอร์, boot, endpoint table, orphan files |
| 02 | Core pipeline — text AI + image AI + combine end-to-end |
| 03 | Persistence — MSSQL tables ที่ query จริง + file storage |
| 04 | Auth (no-role) + OpenAI / remove.bg / QR / Puppeteer map |
| 05 | Cron jobs — ไม่มี scheduler |
| 06 | Scorecard + debt register |
ขอบเขตการยืนยันของเล่มนี้
ยืนยันจากซอร์สบน branch prod แล้ว
- Express boot, CORS whitelist, global JWT, 9 POST routes ใน
routes/index.js - OpenAI call sites ทั้งหมดที่ grep พบ (wrapper + inline axios)
- remove.bg call sites (service + upload controller)
- ตาราง MSSQL ที่ interview / job-description controllers อ้าง
- ไม่มี NestJS modules /
@Public/ role guard
ไม่ยืนยันในเอกสารนี้
- ว่า front-end
vtrc-backofficeเรียก path ใดเป็นลำดับตายตัว (อนุมานจากชื่อ endpoint + I/O) - sync mechanism ระหว่าง
recruitment.dbo.jobReqPositionกับrecruitment_ai.dbo.jobReqPosition - ว่า HCTI เคยถูกใช้ใน commit เก่าหรือไม่ (ปัจจุบันไม่พบ caller)