Skip to content

1 · Repository map

แผนที่โครงสร้าง ai-recruitment-api บน branch prod — ไม่มีโฟลเดอร์ src/ (ต่างจาก NestJS services ใน volume นี้) โค้ดอยู่ที่ root ของ repo

สารบัญ volume: index


แผนที่โฟลเดอร์

ai-recruitment-api/
├── server.js                         entry — dotenv, CORS, verifyJWT, static, bodyParser, listen
├── logger.js                         JSON file logger → logs/{YYYY-MM-DD}/{level}.log
├── package.json / package-lock.json  Express + openai + mssql + puppeteer + ...
├── Dockerfile                        node:18-alpine + chromium + canvas native deps
├── .env                              tracked despite .gitignore — secrets (ดู 04/06)
├── .gitignore                        ละเว้น node_modules, .env, logs/, storage/
├── text.json                         ไฟล์ว่าง (0 bytes) ณ เวลาสำรวจ
├── middleware/
│   └── verifyJWT.js                  jwt.verify + decode สำหรับ log uid
├── config/
│   ├── db.js                         mssql config object จาก DB_* env
│   ├── openai.js                     callOpenAI() → gpt-4o via axios
│   └── dalle.js                      callDalle() → openai.images.generate
├── routes/
│   ├── index.js                      **mounted** จาก server.js — 9 POST endpoints
│   └── get-post-upload_image.js      **orphan** — ไม่ถูก require จาก server.js
├── controllers/                      12 ไฟล์ handler
│   ├── generateJobDescriptionController.js       389 บรรทัด — MSSQL + OpenAI ×4
│   ├── generateInterviewQuestionsController.js   570 บรรทัด — MSSQL หลายตาราง + OpenAI ×6+
│   ├── summarizeJobDescriptionController.js      88 — OpenAI gpt-3.5-turbo
│   ├── summarizeJobQualificationController.js    91 — OpenAI gpt-3.5-turbo
│   ├── module2_1_get_background.js               101 — สุ่มพื้นหลัง b1–b29 (mounted)
│   ├── module2_1_get_background_v2.js            102 — เวอร์ชัน tagged seriousness (orphan)
│   ├── module2_2_controller.js                   216 — career classify + DALL-E + remove.bg
│   ├── module_2_3_combine_image.js               443 — Puppeteer combine (orphan)
│   ├── combineJobPostingController.js            451 — Puppeteer combine (mounted)
│   ├── removeBgUploadController.js               183 — upload → remove.bg (mounted)
│   ├── get-post-upload_image.js                  176 — เกือบซ้ำ removeBgUpload; export คนละชื่อ
│   └── uploadController.js                       30 — saveFileToAPI → writeFileAsync
├── services/
│   └── removeBgService.js            removeBgAndSave() สำหรับ module2_2
├── utils/
│   └── file-util.js                  makeDir / writeFileAsync ใต้ storage/
├── data/
│   └── allImages.js                  array แผนที่ตำแหน่ง/เพศ → path ภาพใน storage-uploads
└── storage-uploads/
    ├── Background/                   b1.png … b29.png
    └── 2025-02-06/uploads/           คลังตัวละคร (c*.png และอื่น ๆ)

จำนวนไฟล์ .js ที่ไม่ใช่ node_modules: 23 ไฟล์ (นับจาก tree ด้านบน + root)


Boot sequence

1. require('dotenv').config()                    server.js:2
2. express() + mkdir storage-uploads ถ้าไม่มี     server.js:4-15
3. express.static ×2 ภายใต้ /ai-recruitment-api/api
      → ./storage และ ./storage-uploads           server.js:18-19
4. app.disable("x-powered-by")                   server.js:21
5. cors(_cors) whitelist 3 origins               server.js:23-34
6. app.use(verifyJWT)  ← global, ไม่มี bypass     server.js:37
7. require('./routes') → routes/index.js         server.js:40
8. request logging middleware (logger.info)      server.js:44-52
9. bodyParser.json / urlencoded limit 50mb       server.js:56-57
10. app.use('/ai-recruitment-api/api', router)   server.js:60
11. GET /ai-recruitment-api/api welcome string   server.js:62-64
12. listen(PORT || 3000)                         server.js:67-72

ลำดับสำคัญ: JWT ทำงานก่อน bodyParser และก่อน router — request ที่ไม่มี token ถูกตัดที่ middleware ก่อนเข้า controller

Static files ก็อยู่หลัง verifyJWT ใน stack เดียวกัน (server.js:18-19 ก่อน JWT แต่ Express จับคู่ path ตามลำดับ registration: static ลงทะเบียนก่อน JWT) — ถ้า request ตรงกับไฟล์ static จะถูกส่งก่อนถึง JWT; ถ้าไม่ตรงไฟล์ จะไปต่อ JWT แล้ว router


Endpoint catalog (mounted)

Prefix จริง: /ai-recruitment-api/api + path ด้านล่าง
แหล่ง mount: routes/index.js ผ่าน server.js:60

#MethodPathHandlerไฟล์
1POST/generate-job-descriptiongenerateJobDescriptiongenerateJobDescriptionController.js
2POST/get-post-backgroundgetBackgroundmodule2_1_get_background.js
3POST/get-post-charactermodule2_2Controller (หรือ 500 guard)module2_2_controller.js
4OPTIONS/POST/get-post-upload_imageremoveBgUploadController + multer.anyremoveBgUploadController.js
5POST/get-post-summarize-job-descriptionsummarizeJobDescriptionsummarizeJobDescriptionController.js
6POST/get-post-summarize-job-qualificationsummarizeJobQualificationsummarizeJobQualificationController.js
7POST/get-post-combinecombineJobPostingcombineJobPostingController.js
8POST/generate-interview-questionsgenerateInterviewQuestionsgenerateInterviewQuestionsController.js
9POST/save-file-to-apisaveFileToAPIuploadController.js
GET/ai-recruitment-api/apiwelcome textserver.js:62-64

หลักฐานประกาศ route: routes/index.js:53-144

Guard ของ /get-post-character

javascript
if (typeof module2_2Controller === 'function') {
  router.post('/get-post-character', module2_2Controller);
} else {
  router.post('/get-post-character', (req, res) => {
    res.status(500).json({ status: 'error', message: 'module2_2Controller is not loaded. ...' });
  });
}

แหล่ง: routes/index.js:63-78 — ออกแบบมาให้ server ไม่ crash ถ้า require controller ล้ม


Orphan / dead code map

ไฟล์สถานะหลักฐาน
routes/get-post-upload_image.jsไม่ถูก mountserver.js require เฉพาะ ./routes (= routes/index.js)
controllers/module2_1_get_background_v2.jsไม่ถูก requireroutes/index.js:21 ชี้แค่ module2_1_get_background
controllers/module_2_3_combine_image.jsไม่ถูก requiremount ใช้ combineJobPostingController ที่ routes/index.js:36,134
controllers/get-post-upload_image.jsrequire แล้วแต่ export ผิดชื่อไฟล์ export { removeBgUploadController } (get-post-upload_image.js:176) แต่ routes/index.js:33 destructure { uploadImageController } → ค่าเป็น undefined (log ที่บรรทัด 44) — route จริงใช้ removeBgUploadController จากไฟล์อีกตัว (routes/index.js:86-117)

สรุป: upload ที่ทำงานจริงคือ removeBgUploadController.js ไม่ใช่ orphan route file และไม่ใช่ uploadImageController


Config modules

config/db.js

javascript
const dbConfig = {
  user: process.env.DB_USER,
  password: process.env.DB_PASSWORD,
  server: process.env.DB_SERVER,
  database: process.env.DB_DATABASE,
  options: {
    encrypt: false,
    trustServerCertificate: true
  }
};

แหล่ง: config/db.js:5-14 — export { sql, dbConfig }

หมายเหตุ: generateJobDescriptionController เรียก sql.connect แล้ว override encrypt: true ใน options ของ connection นั้น (generateJobDescriptionController.js:128-135) ในขณะที่ generateInterviewQuestionsController สร้าง config เองและตั้ง encrypt: false (generateInterviewQuestionsController.js:29-36) — ค่า encrypt ไม่สม่ำเสมอข้าม controller

config/openai.js

  • URL คงที่: https://api.openai.com/v1/chat/completions (openai.js:5)
  • Model: gpt-4o (openai.js:12)
  • Fail-soft: catch → console.error แล้ว return '' (openai.js:24-27)
  • ไม่ตั้ง timeout / retry ของ axios

config/dalle.js

  • Client: new OpenAI({ apiKey: process.env.OPENAI_API_KEY }) (dalle.js:15-17)
  • Default model gpt-image-1, size 1024x1024 (dalle.js:41)
  • ถ้าได้ b64_json จะเขียนไฟล์ใต้ PUBLIC_DIR/IMAGE_OUT_DIR แล้วประกอบ URL จาก BASE_URL (dalle.js:57-78)
  • Default BASE_URL = http://localhost:3000, PUBLIC_DIR = ../publicโฟลเดอร์ public/ ไม่ปรากฏใน tree ของ repo (อาจสร้างตอน runtime)

Logger

logger.js เขียน JSON line ต่อไฟล์ logs/{date}/{level}.log (logger.js:26-38)
ระดับ: info, error, warn, debug (logger.js:42-47)

server.js ใช้ logger.info สำหรับ incoming request (server.js:46-50)
uploadController ใช้ logger.error (uploadController.js:19-24)
จุดอื่นส่วนใหญ่ใช้ console.log / console.error — ดู OBS ใน scorecard


Data asset: data/allImages.js

Array ของ object { position, gender, image } ส่งออกเป็น module.exports = allImages (allImages.js:342)

ใช้เมื่อ module2_2 อยู่ในโหมด "first time" — สุ่มภาพจากคลังแทนการเรียก DALL-E (module2_2_controller.js:124-157)

ตำแหน่งที่พบในไฟล์ (ตัวอย่าง): Office worker, Doctor, Nurse, Lawyer, Dentist, pharmacist, Nurse Assistant, IT admin, Janitor, Driver, Gardener, Manager, Engineer, Chef — ตรงกับรายการ keyword ใน prompt career (module2_2_controller.js:105-110)

Path ภาพชี้ไป /2025-02-06/uploads/c*.png ซึ่งถูกเสิร์ฟผ่าน static storage-uploads (server.js:19)


Env key inventory (ชื่อเท่านั้น — ไม่ใส่ค่า)

จาก .env บน working tree (branch prod) บรรทัด 1–12:

บรรทัดKeyใช้ในโค้ด?
1DB_USERใช่ — config/db.js, interview controller
2DB_PASSWORDใช่
3DB_SERVERใช่
4DB_DATABASEใช่
5OPENAI_API_KEYใช่ — openai.js, dalle.js, summarize*, interview, removeBg ไม่ใช้
6PORTใช่ — server.js:67
7REMOVE_BG_API_KEYใช่ — removeBgService, removeBgUploadController
8DALLE_ENDPOINTไม่พบ caller
9–10HCTI_USER_ID, HCTI_API_KEYไม่พบ caller
11JWT_SECRETใช่ — verifyJWT.js:4
12GEN_BASE_URLไม่พบ caller (มี BASE_URL ใน dalle/removeBgService แทน)

Dependency notes ที่ควรรู้ตอนอ่านโค้ด

Package ใน package.jsonการใช้จริงใน .js
puppeteercombineJobPostingController.js, module_2_3_combine_image.js (orphan)
openaiconfig/dalle.js เท่านั้น — text path ส่วนใหญ่ใช้ axios ตรง
axiosopenai wrapper, summarize, interview, QR, remove.bg
mssqljob description + interview controllers
multerroutes/index.js
form-dataremoveBgService / removeBgUpload
jsonwebtokenverifyJWT
cors, dotenv, qsserver / dotenv; qs ไม่พบ require ตรง
canvas, sharpไม่พบ require — ยังดึง native build ใน Dockerfile
uuidrequire('uuid') ใน background controllers แต่ ไม่อยู่ใน dependencies ของ package.json — พึ่ง transitive หรือ runtime ล้มถ้าไม่มี

ตัวเลขที่ควรจำ

ตัวเลขค่า
Controllers12 ไฟล์
Endpoints ที่ mount จริง9 POST + 1 GET welcome + OPTIONS upload
Services1 (removeBgService.js)
DB connections ใน config กลาง1 (dbConfig)
Controller ที่ใหญ่สุดgenerateInterviewQuestionsController.js (570 บรรทัด)
พื้นหลังสำเร็จรูป29 (b1b29)
ไฟล์ใต้ storage-uploads/361 (นับ ณ เวลาสำรวจ)
Test files (.test.js / .spec.js)0

เคล็ดลับตอนทำงานจริง

  1. เพิ่ม endpoint ใหม่ — แก้เฉพาะ routes/index.js แล้ว export จาก controller ที่ชัดเจน; อย่าใช้ routes/get-post-upload_image.js
  2. อย่าสับสน combineJobPostingController (live) กับ module_2_3_combine_image (dead)
  3. ก่อนแก้ prompt ของ character — อ่าน PROMPT_RULES + isFirstTime ใน module2_2_controller.js ให้ครบ เพราะมีสอง path (local gallery vs DALL-E)
  4. ตรวจ encrypt ของ MSSQL ให้ตรงกับ environment จริง — โค้ดมีทั้ง true และ false

ไป บท 2 Core pipeline