# 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(':view')` และ `projectScopedView('')` แล้ว - ถ้าไม่ส่ง `project_id` และผู้ใช้ไม่ใช่ Admin: - ถ้ามี permission `:view` → เห็นทุกโปรเจ็กต์ - ถ้าไม่มี → ระบบจะจำกัดผลลัพธ์ให้เฉพาะโปรเจ็กต์ที่ผู้ใช้เป็นสมาชิก (`user_project_roles`) - ถ้าส่ง `project_id` → ต้องเป็นสมาชิกของโปรเจ็กต์นั้นหรือมี `: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(':view')` และ `projectScopedView('')` ## Seeds เพิ่มเติม - `seed/09_user_project_roles_seed.sql` — ตัวอย่างการผูกผู้ใช้เข้ากับโปรเจ็กต์