Files
lcbp3.np-dms.work/Architech.md

507 lines
20 KiB
Markdown
Executable File
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# DMS Architecture Deep Dive (Backend + Frontend)
**Project:** Document Management System (DMS) — LCBP3
**Platform:** QNAP TS473A (Container Station)
**Last updated:** 20251007 (UTC+7)
---
## 0) TL;DR (Executive Summary)
* Reverse proxy (Nginx/NPM) เผยแพร่ Frontend (Next.js) และ Backend (Node.js/Express) ผ่าน HTTPS (HSTS)
* Backend เชื่อม MariaDB 10.11 (ข้อมูลหลัก DMS) และแยก n8n + Postgres 16 สำหรับ workflow
* RBAC/ABAC ถูกบังคับใช้งานใน middleware + มีชุด SQL (tables → triggers → procedures → views → seed)
* ไฟล์จริง (PDF/DWG) เก็บนอก webroot ที่ **/share/dmsdata** พร้อมมาตรฐานการตั้งชื่อ+โฟลเดอร์
* Dev/Prod แยกชัดเจนผ่าน Docker multistage + dockercompose + โฟลเดอร์ persist logs/config/certs
---
## 1) Runtime Topology & Trust Boundaries
```
Internet Clients (Browser)
│ HTTPS 443 (HSTS) [QNAP mgmt = 8443]
┌─────────────────────────────────────────────────────┐
│ Reverse Proxy Layer │
│ ├─ Nginx (Alpine) or Nginx Proxy Manager (NPM) │
│ ├─ TLS (LE cert; SAN multisubdomain) │
│ └─ Routes: │
│ • /, /_next/* → Frontend (Next.js :3000) │
│ • /api/* → Backend (Express :3001) │
│ • /pma/* → phpMyAdmin │
│ • /n8n/* → n8n (Workflows) │
└─────────────────────────────────────────────────────┘
│ │
│ └──────────┐
▼ │
Frontend (Next.js) │
│ Cookie-based Auth (HttpOnly) │
▼ ▼
Backend (Node/Express ESM) ─────────► MariaDB 10.11
│ │
└────────────────────────────────────┘
Project data (.pdf/.dwg) @ /share/dms-data
n8n (workflows) ──► Postgres 16 (separate DB for automations)
```
**Trust Boundaries**
* Public zone: Internet ↔ Reverse proxy
* App zone: Reverse proxy ↔ FE/BE containers (internal Docker network)
* Data zone: Backend ↔ Databases (MariaDB, Postgres) + `/share/dms-data`
---
## 2) Frontend Architecture (Next.js / React)
### 2.1 Stack & Key libs
* **Next.js (App Router)**, **React**, ESM
* **Tailwind CSS**, **PostCSS**, **shadcn/ui** (components.json)
* Fetch API (credentials include) → Cookie Auth (HttpOnly)
### 2.2 Directory Layout
```
/frontend/
├─ app/
│ ├─ login/
│ ├─ dashboard/
│ ├─ users/
│ ├─ correspondences/
│ ├─ health/
│ └─ layout.tsx / page.tsx (ตาม App Router)
├─ public/
├─ Dockerfile (multi-stage: dev/prod)
├─ package.json
├─ next.config.js
└─ ...
```
### 2.3 Routing & Layouts
* **Public**: `/login`, `/health`
* **Protected**: `/dashboard`, `/users`, `/correspondences`, ... (client-side guard)
* เก็บ **middleware.ts (ของเดิม)** เพื่อหลีกเลี่ยง regression; ใช้ clientguard + server action อย่างระมัดระวัง
### 2.4 Auth Flow (Cookie-based)
1. ผู้ใช้ submit form `/login``POST /api/auth/login` (Backend)
2. Backend set **HttpOnly** cookie (JWT) + `SameSite=Lax/Strict`, `Secure`
3. หน้า protected เรียก `GET /api/auth/me` เพื่อตรวจสอบสถานะ
4. หาก 401 → redirect → `/login`
> **CORS/Fetch**: เปิด `credentials: 'include'` ทุกครั้ง, ตั้ง `NEXT_PUBLIC_API_BASE` เป็น origin ของ backend ผ่าน proxy (เช่น `https://lcbp3.np-dms.work`)
### 2.5 UI/UX
* Seablue palette, sidebar พับได้, cardbased KPI
* ตารางข้อมูลเตรียมรองรับ **serverside DataTables**
* shadcn/ui: Button, Card, Badge, Tabs, Dropdown, Tooltip, Switch, etc.
### 2.6 Config & ENV
* `NEXT_PUBLIC_API_BASE` (ex: `https://lcbp3.np-dms.work`)
* Build output แยก dev/prod; ระวัง EACCES บน QNAP → ใช้ user `node` + ปรับสิทธิ์โวลุ่ม `.next/*`
### 2.7 Error Handling & Observability (FE)
* Global error boundary (app router) + toast/alert patterns
* Network layer: แยก handler สำหรับ 401/403/500 + retry/backoff ที่จำเป็น
* Metrics (optional): webvitals, UX timing (เก็บฝั่ง n8n หรือ simple logging)
---
## 3) Backend Architecture (Node.js ESM / Express)
### 3.1 Stack & Structure
* Node 20.x, **ESM** modules, **Express**
* `mysql2/promise`, `jsonwebtoken`, `cookie-parser`, `cors`, `helmet`, `winston/morgan`
```tree
/backend/
├─ src/
│ ├─ index.js # bootstrap server, CORS, cookies, health
│ ├─ routes/
│ │ ├─ auth.js # /api/auth/* (login, me, logout)
│ │ ├─ users.js # /api/users/*
│ │ ├─ correspondences.js # /api/correspondences/*
│ │ ├─ drawings.js # /api/drawings/*
│ │ ├─ rfas.js # /api/rfas/*
│ │ └─ transmittals.js # /api/transmittals/*
│ ├─ middleware/
│ │ ├─ authGuard.js # verify JWT from cookie
│ │ ├─ requirePermission.js# RBAC/ABAC enforcement
│ │ ├─ errorHandler.js
│ │ └─ requestLogger.js
│ ├─ db/
│ │ ├─ pool.js # createPool, sane defaults
│ │ └─ models/ # query builders (User, Drawing, ...)
│ ├─ utils/
│ │ ├─ hash.js (bcrypt/argon2)
│ │ ├─ jwt.js
│ │ ├─ pagination.js
│ │ └─ responses.js
│ └─ config/
│ └─ index.js # env, constants
├─ Dockerfile
└─ package.json
```
### 3.2 Request Lifecycle
1. `helmet` + `cors` (allow specific origin; credentials true)
2. `cookie-parser`, `json limit` (e.g., 2MB)
3. `requestLogger` → trace + response time
4. Route handler → `authGuard` (protected) → `requirePermission` (perroute) → Controller
5. Error bubbles → `errorHandler` (JSON shape, status map)
### 3.3 Auth & RBAC/ABAC
* **JWT** ใน HttpOnly cookie; Claims: `sub` (user_id), `roles`, `exp`
* **authGuard**: ตรวจ token → แนบ `req.user`
* **requirePermission**: เช็ค permission ตามเส้นทาง/วิธี; แผนขยาย ABAC (เช่น project scope, owner, doc state)
* Roles/Permissions ถูก seed ใน SQL; มี **view เมทริกซ์** เพื่อ debug (เช่น `v_role_permission_matrix`)
**ตัวอย่าง pseudo** `requirePermission(permission)`
```js
export const requirePermission = (perm) => async (req, res, next) => {
if (!req.user) return res.status(401).json({ error: 'Unauthenticated' });
const ok = await checkPermission(req.user.user_id, perm, req.context);
if (!ok) return res.status(403).json({ error: 'Forbidden' });
return next();
};
```
### 3.4 Database Access & Pooling
* `createPool({ connectionLimit: 10~25, queueLimit: 0, waitForConnections: true })`
* ใช้ parameterized queries เสมอ; ปรับ `sql_mode` ที่จำเป็นใน `my.cnf`
### 3.5 File Storage & Secure Download
* Root: **/share/dmsdata**
* โครงโฟลเดอร์: `{module}/{yyyy}/{mm}/{entityId}/` + ชื่อไฟล์ตามมาตรฐาน (เช่น `DRW-<code>-REV-<rev>.pdf`)
* Endpoint download: ตรวจสิทธิ์ (RBAC/ABAC) → `res.sendFile()`/stream; ป้องกัน path traversal
* MIME allowlist + size limit + virus scan (optional; ภายหลัง)
### 3.6 Health & Readiness
* `GET /api/health``{ ok: true }`
* (optional) `/api/ready` ตรวจ DB ping + disk space (dmsdata)
### 3.7 Config & ENV (BE)
* `DB_HOST, DB_PORT, DB_USER, DB_PASS, DB_NAME`
* `JWT_SECRET, COOKIE_NAME, COOKIE_SAMESITE, COOKIE_SECURE`
* `CORS_ORIGIN, LOG_LEVEL, APP_BASE_URL`
* `FILE_ROOT=/share/dms-data`
### 3.8 Logging
* Access log (morgan) + App log (winston) → `/share/Container/dms/logs/backend/`
* รูปแบบ JSON (timestamp, level, msg, reqId) + daily rotation (logrotate/containerside)
---
## 4) Database (MariaDB 10.11)
### 4.1 Schema Overview (ย่อ)
* **RBAC core**: `users`, `roles`, `permissions`, `user_roles`, `role_permissions`
* **Domain**: `drawings`, `contracts`, `correspondences`, `rfas`, `transmittals`, `organizations`, `projects`, ...
* **Audit**: `audit_logs` (แผนขยาย), `deleted_at` (soft delete, แผนงาน)
```
[users]──<user_roles>──[roles]──<role_permissions>──[permissions]
└── activities/audit_logs (future expansion)
[drawings]──<mapping>──[contracts]
[rfas]──<links>──[drawings]
[correspondences] (internal/external flag)
```
### 4.2 Init SQL Pipeline
1. `01_*_deploy_table_rbac.sql` — สร้างตารางหลักทั้งหมด + RBAC
2. `02_*_triggers.sql` — บังคับ data rules, autoaudit fields
3. `03_*_procedures_handlers.sql` — upsert/bulk handlers (เช่น `sp_bulk_import_contract_dwg`)
4. `04_*_views.sql` — รายงาน/เมทริกซ์สิทธิ์ (`v_role_permission_matrix`, etc.)
5. `05_*_seed_data.sql` — ค่าพื้นฐาน domain (project, categories, statuses)
6. `06_*_seed_users.sql` — บัญชีเริ่มต้น (superadmin, editors, viewers)
7. `07_*_seed_contract_dwg.sql` — ข้อมูลตัวอย่างแบบสัญญา
### 4.3 Indexing & Performance
* Composite indexes ตามคอลัมน์ filter/sort (เช่น `(project_id, updated_at DESC)`)
* Fulltext index (optional) สำหรับ advanced search
* Query plan review (EXPLAIN) + เพิ่ม covering index ตามรายงาน
### 4.4 MySQL/MariaDB Config (my.cnf — แนวทาง)
```
[mysqld]
innodb_buffer_pool_size = 4G # ปรับตาม RAM/QNAP
innodb_log_file_size = 512M
innodb_flush_log_at_trx_commit = 1
max_connections = 200
sql_mode = STRICT_TRANS_TABLES,NO_ENGINE_SUBSTITUTION
character-set-server = utf8mb4
collation-server = utf8mb4_unicode_ci
```
> ปรับค่าให้เหมาะกับ workload จริง + เฝ้าดู IO/CPU ของ QNAP
### 4.5 Backup/Restore
* Logical backup: `mysqldump --routines --triggers --single-transaction`
* Physical (snapshot QNAP) + schedule ผ่าน n8n/cron
* เก็บสำเนา offNAS (encrypted)
---
## 5) Reverse Proxy & TLS
### 5.1 Nginx (Alpine) — ตัวอย่าง server block
> **สำคัญ:** บนสภาพแวดล้อมนี้ ให้ใช้คนละบรรทัด:
> `listen 443 ssl;`
> `http2 on;`
> หลีกเลี่ยง `listen 443 ssl http2;`
```nginx
server {
listen 80;
server_name lcbp3.np-dms.work;
return 301 https://$host$request_uri;
}
server {
listen 443 ssl;
http2 on;
server_name lcbp3.np-dms.work;
ssl_certificate /etc/nginx/certs/fullchain.pem;
ssl_certificate_key /etc/nginx/certs/privkey.pem;
add_header Strict-Transport-Security "max-age=63072000; preload" always;
# Frontend
location / {
proxy_pass http://frontend:3000;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
# Next.js static
location /_next/ {
proxy_pass http://frontend:3000;
}
# Backend API
location /api/ {
proxy_http_version 1.1;
proxy_set_header Connection "";
proxy_pass http://backend:3001;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-Proto $scheme;
}
# phpMyAdmin (sub-path)
location /pma/ {
proxy_pass http://phpmyadmin:80/;
}
# n8n
location /n8n/ {
proxy_pass http://n8n:5678/;
}
}
```
### 5.2 Nginx Proxy Manager (NPM) — Tips
* ระวังอย่าใส่ `proxy_http_version` ซ้ำซ้อน (duplicate directive) ใน Advanced
* ถ้าต้องแก้ไฟล์ด้านใน NPM → ระวังไฟล์ใน `/data/nginx/proxy_host/*.conf`
* จัดการ certificate / SAN หลาย subdomain ใน UI แต่ mainten ดีเรื่อง symlink/renew
### 5.3 TLS & Certificates
* Lets Encrypt (HTTP01 webroot/standalone) + HSTS
* QNAP mgmt เปลี่ยนเป็น 8443 → พอร์ต 443 public ว่างสำหรับ Nginx/NPM
---
## 6) Docker Compose Topology
### 6.1 Services (สรุป)
* `frontend` (Next.js) :3000
* `backend` (Express) :3001
* `mariadb` (10.11) :3306 (internal)
* `phpmyadmin` :80 (internal)
* `nginx` or `npm` :80/443 (published)
* `n8n` :5678 (internal)
* `postgres_n8n` (16-alpine)
* `pgadmin4`
### 6.2 Volumes & Paths
```
/share/Container/dms/
├─ mariadb/data
├─ mariadb/init/*.sql
├─ backend/ (code)
├─ frontend/ (code)
├─ phpmyadmin/{sessions,tmp,config.user.inc.php}
├─ nginx/{nginx.conf,dms.conf,certs/}
├─ n8n, n8n-postgres, n8n-cache
└─ logs/{backend,frontend,nginx,pgadmin,phpmyadmin,postgres_n8n}
/share/dms-data (pdf/dwg storage)
```
### 6.3 Healthchecks (suggested)
* **backend**: curl `http://localhost:3001/api/health`
* **frontend**: curl `/health` (simple JSON)
* **mariadb**: `mysqladmin ping` with credentials
* **nginx**: `nginx -t` at startup
### 6.4 Security Hardening
* รัน container ด้วย user nonroot (`user: node` สำหรับ FE/BE)
* จำกัด capabilities; readonly FS (ยกเว้นโวลุ่มจำเป็น)
* เฉพาะ backend เมานต์ `/share/dms-data`
---
## 7) Observability, Ops, and Troubleshooting
### 7.1 Logs
* Frontend → `/logs/frontend/*`
* Backend → `/logs/backend/*` (app/access/error)
* Nginx/NPM → `/logs/nginx/*`
* MariaDB → default datadir log + slow query (เปิดใน my.cnf หากต้องการ)
### 7.2 Common Issues & Playbooks
* **401 Unauthenticated**: ตรวจ `authGuard` → JWT cookie มี/หมดอายุ → เวลา server/FE sync → CORS `credentials: true`
* **EACCES Next.js**: สิทธิ์ `.next/*` + run as `node`, โวลุ่ม map ถูก user:group
* **NPM duplicate directive**: ลบซ้ำ `proxy_http_version` ใน Advanced / ตรวจ `proxy_host/*.conf`
* **LE cert path/symlink**: ตรวจ `/etc/letsencrypt/live/npm-*` symlink ชี้ถูก
* **DB field not found**: ตรวจ schema vs code (migration/init SQL) → sync ให้ตรง
### 7.3 Performance Guides
* **Backend**: keepalive, gzip/deflate at proxy, pool 1025, paginate, avoid N+1
* **Frontend**: prefetch critical routes, cache static, image optimization
* **DB**: เพิ่ม index จุด filter, analyze query (EXPLAIN), ปรับ buffer pool
---
## 8) Security & Compliance
* **HTTPS only** + HSTS (preload)
* **CORS**: allow list เฉพาะ FE origin; `Access-Control-Allow-Credentials: true`
* **Cookie**: HttpOnly, Secure, SameSite=Lax/Strict
* **Input Validation**: celebrate/zod (optional) + sanitize
* **Rate limiting**: per IP/route (optional)
* **AuditLog**: วางแผนเพิ่ม ครอบคลุม CRUD + mapping (actor, action, entity, before/after)
* **Backups**: DB + `/share/dms-data` + config (encrypted offNAS)
---
## 9) Backlog → Architecture Mapping
1. **RBAC Enforcement ครบ** → เติม `requirePermission` ทุก route + test matrix ผ่าน view
2. **AuditLog ครบ CRUD/Mapping** → trigger + table `audit_logs` + BE hook
3. **Upload/Download จริงของ Drawing Revisions** → BE endpoints + virus scan (optional)
4. **Dashboard KPI** → BE summary endpoints + FE cards/charts
5. **Serverside DataTables** → paging/sort/filter + indexesรองรับ
6. **รายงาน Export CSV/Excel/PDF** → BE export endpoints + FE buttons
7. **Soft delete** (`deleted_at`) → BE filter default scope + restore endpoint
8. **Validation เข้ม** → celebrate/zod schema + consistent error shape
9. **Indexing/Perf** → slow query log + EXPLAIN review
10. **Job/Cron Deadline Alerts** → n8n schedule + SMTP
---
## 10) Port & ENV Matrix (Quick Ref)
| Component | Ports | Key ENV |
| --------- | --------------- | ------------------------------------------------ |
| Nginx/NPM | 80/443 (public) | SSL paths, HSTS |
| Frontend | 3000 (internal) | `NEXT_PUBLIC_API_BASE` |
| Backend | 3001 (internal) | `DB_*`, `JWT_SECRET`, `CORS_ORIGIN`, `FILE_ROOT` |
| MariaDB | 3306 (internal) | `MY_CNF`, credentials |
| n8n | 5678 (internal) | `N8N_*`, webhook URL under `/n8n/` |
| Postgres | 5432 (internal) | n8n DB |
**QNAP mgmt**: 8443 (already moved)
---
## 11) Sample Snippets
### 11.1 Backend CORS (credentials)
```js
app.use(cors({
origin: ['https://lcbp3.np-dms.work'],
credentials: true,
}));
```
### 11.2 Secure Download (guarded)
```js
router.get('/files/:module/:id/:filename', authGuard, requirePermission('file.read'), async (req, res) => {
const { module, id, filename } = req.params;
// 1) ABAC: verify user can access this module/entity
const ok = await canReadFile(req.user.user_id, module, id);
if (!ok) return res.status(403).json({ error: 'Forbidden' });
const abs = path.join(FILE_ROOT, module, id, filename);
if (!abs.startsWith(FILE_ROOT)) return res.status(400).json({ error: 'Bad path' });
return res.sendFile(abs);
});
```
### 11.3 Healthcheck
```js
router.get('/health', (req, res) => res.json({ ok: true }));
```
---
## 12) Deployment Workflow (Suggested)
1. Git (Gitea) branch strategy `feature/*` → PR → main
2. Build images (dev/prod) via Dockerfile multistage; pin Node/MariaDB versions
3. `docker compose up -d --build` จาก `/share/Container/dms`
4. Validate: `/health`, `/api/health`, login roundtrip
5. Monitor logs + baseline perf; run SQL smoke tests (views/triggers/procs)
---
## 13) Appendix
* **Naming conventions**: snake_case DB, camelCase JS
* **Timezones**: store UTC in DB; display in app TZ (+07:00)
* **Character set**: UTF8 (`utf8mb4_unicode_ci`)
* **Large file policy**: size limit (e.g., 50200MB), allowlist extensions
* **Retention**: archive strategy for old revisions (optional)
---
> หากต้องการ เวอร์ชัน **README.md พร้อมโค้ดตัวอย่าง compose/nginx** จัดรูปแบบให้นำไปวางใน repo ได้ทันที แจ้งได้เลยว่าจะให้แตกไฟล์เป็น `/docs/Architecture.md` + `/nginx/dms.conf` + `/docker-compose.yml` template หรือรูปแบบอื่นที่สะดวกต่อการใช้งานของทีม