# DMS Backend (ESM)

- Node 20+, ESM
- No `.env` — ใช้ `environment:` จาก docker-compose / Container Station
- พอร์ตภายใน: 3001 (สอดคล้องกับ Nginx upstream: `backend:3001`)

## Endpoints
- `GET /health` — DB ping
- `POST /api/v1/auth/login` — {username, password} → {access_token, refresh_token}
- `POST /api/v1/auth/refresh` — {refresh_token}
- `GET /api/v1/users` — (Admin) List
- `GET /api/v1/users/:id` — (Admin) Read
- `POST /api/v1/users` — (Admin) Create
- `PATCH /api/v1/users/:id` — (Admin) Update (+ password)
- `DELETE /api/v1/users/:id` — (Admin) Delete

## ENV example (ตั้งใน compose)
- BACKEND_PORT=3001
- DB_HOST=mariadb
- DB_PORT=3306
- DB_USER=center
- DB_PASSWORD=Center2025
- DB_NAME=dms_db
- JWT_SECRET=CHANGE
- JWT_REFRESH_SECRET=CHANGE
- CORS_ALLOWLIST=https://dcs.mycloudnas.com,https://localhost,https://127.0.0.1


## RFA & Correspondence Endpoints
- `GET /api/v1/rfas` (filters: q, project_id, status, page, page_size)
- `GET /api/v1/rfas/:id`
- `POST /api/v1/rfas` (Admin/Editor)
- `PATCH /api/v1/rfas/:id` (Admin/Editor)
- `DELETE /api/v1/rfas/:id` (Admin)
- `GET /api/v1/rfas/:id/revisions`
- `POST /api/v1/rfas/:id/revisions` (Admin/Editor)
- `GET /api/v1/correspondences` (filters: q, project_id, status, page, page_size)
- `GET /api/v1/correspondences/:id`
- `POST /api/v1/correspondences` (Admin/Editor)
- `PATCH /api/v1/correspondences/:id` (Admin/Editor)
- `DELETE /api/v1/correspondences/:id` (Admin)
- `GET /api/v1/correspondences/:id/versions`
- `POST /api/v1/correspondences/:id/versions` (Admin/Editor)


## Drawings / Documents / Transmittals
- `GET/POST/PATCH/DELETE /api/v1/drawings[/:id]` + `/drawings/:id/revisions`
- `GET/POST/PATCH/DELETE /api/v1/documents[/:id]`
- `GET/POST/PATCH/DELETE /api/v1/transmittals[/:id]` + items under `/transmittals/:id/items`
- Mapping: `GET/POST/DELETE /api/v1/maps/rfa/:rfa_id/drawings/:drawing_id` and `/maps/correspondence/:corr_id/documents/:doc_id`


## Specialized Tables & File Uploads
- Volumes: `/api/v1/volumes`
- Sub Categories: `/api/v1/sub_categories`
- Organizations: `/api/v1/organizations`
- Upload file: `POST /api/v1/uploads/:module/:refId` field `file` (Admin/Editor)
  - Allowed modules: rfa, correspondence, drawing, document, transmittal
  - Default size limit 100MB; set `MAX_UPLOAD_BYTES` if needed
- Download file: `GET /api/v1/files/:file_id?token=...` (Admin bypass token)
- Mount host path `/share/dms-data` into the container at the same path


## Views (read-only)
- `GET /api/v1/views/rfas` (q, project_id, status, page, page_size)
- `GET /api/v1/views/correspondences` (q, project_id, status, page, page_size)
- `GET /api/v1/views/drawings/latest` (q, project_id, discipline, status, page, page_size)
- `GET /api/v1/views/transmittals` (q, project_id, status, page, page_size)
- `GET /api/v1/views/files` (module, ref_id, page, page_size)

> หมายเหตุ: ชื่อตาราง view ต้องตรงกับ SQL ใน `04_*_views.sql`. หากต่าง ให้แก้ชื่อในไฟล์ models/vw_*.js ให้ตรงสคีมา


## MVP Endpoints Added
- Auth: GET /api/v1/auth/me
- Users: PATCH /api/v1/users/:id/password
- Projects: CRUD /api/v1/projects + membership endpoints
- Files alias: GET /api/v1/{rfas|correspondences|drawings|documents|transmittals}/:id/files
- Lookups: statuses, disciplines, categories, organizations
- Ops: GET /ready, /live, /version (from package.json v0.5.0)


## MVP Endpoints Added
- Auth: `GET /api/v1/auth/me`, `POST /api/v1/auth/logout`
- Users: `PATCH /api/v1/users/:id/password`, `GET /api/v1/users/search?q=`
- User projects: `GET /api/v1/users/me/projects`
- Projects: `GET/POST/PATCH/DELETE /api/v1/projects`, members `GET/POST/DELETE /api/v1/projects/:id/members/:user_id`
- Files: `HEAD /api/v1/files/:file_id`, `DELETE /api/v1/files/:file_id`, `POST /api/v1/files/:file_id/rename`, `POST /api/v1/files/:file_id/refresh-url`
- Module file aliases: `GET /api/v1/{rfas|correspondences|drawings|documents|transmittals}/:id/files`
- Lookups: `GET /api/v1/lookups/statuses|disciplines|categories|organizations`
- Ops: `GET /ready`, `GET /live`, `GET /version`


## RBAC / Permission Granularity
- `GET /api/v1/rbac/effective` — roles + permissions ของผู้ใช้ปัจจุบัน
- `GET/POST/DELETE /api/v1/rbac/roles`
- `GET/POST/DELETE /api/v1/rbac/permissions`
- bind/unbind: `POST/DELETE /api/v1/rbac/roles/:role_id/permissions/:permission_id`
- assign/unassign: `POST/DELETE /api/v1/rbac/users/:user_id/roles/:role_id`
- Helper middleware: `requirePerm('rfa:create')` (ไฟล์ `middleware/permGuard.js`)


## ABAC (Project-scoped) on Views
- ทุก endpoint ใน `/api/v1/views/*` ถูกครอบด้วย `requirePerm('<module>:view')` และ `projectScopedView('<module>')` แล้ว
- ถ้าไม่ส่ง `project_id` และผู้ใช้ไม่ใช่ Admin:
  - ถ้ามี permission `<module>:view` → เห็นทุกโปรเจ็กต์
  - ถ้าไม่มี → ระบบจะจำกัดผลลัพธ์ให้เฉพาะโปรเจ็กต์ที่ผู้ใช้เป็นสมาชิก (`user_project_roles`)
- ถ้าส่ง `project_id` → ต้องเป็นสมาชิกของโปรเจ็กต์นั้นหรือมี `<module>:view`


## ABAC for Mutations (Create/Update/Delete)
- ทุกโมดูลที่มี `project_id` ถูกตรวจสอบ **การเป็นสมาชิกโปรเจ็กต์** แล้ว
  - Create → ต้องส่ง `project_id` ใน body และต้องเป็นสมาชิก
  - Update/Delete → ระบบจะดึงเรคคอร์ดเดิมเพื่อหาระบุ `project_id` แล้วตรวจสมาชิก
- Uploads/Files/Mapping ก็ตรวจ project membership ตามเอนทิตีอ้างอิง

## Views: Require project_id (ยกเว้น Admin)
- `/api/v1/views/*` **ต้องส่ง** `?project_id=` เสมอ (ถ้าไม่ใช่ Admin)
- ใช้ร่วมกับ `requirePerm('<module>:view')` และ `projectScopedView('<module>')`

## Seeds เพิ่มเติม
- `seed/09_user_project_roles_seed.sql` — ตัวอย่างการผูกผู้ใช้เข้ากับโปรเจ็กต์
