20 KiB
Executable File
DMS Architecture Deep Dive (Backend + Frontend)
Project: Document Management System (DMS) — LCBP3 Platform: QNAP TS‑473A (Container Station) Last updated: 2025‑10‑07 (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/dms‑data พร้อมมาตรฐานการตั้งชื่อ+โฟลเดอร์
- Dev/Prod แยกชัดเจนผ่าน Docker multi‑stage + docker‑compose + โฟลเดอร์ 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 multi‑subdomain) │
│ └─ 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; ใช้ client‑guard + server action อย่างระมัดระวัง
2.4 Auth Flow (Cookie-based)
- ผู้ใช้ submit form
/login→POST /api/auth/login(Backend) - Backend set HttpOnly cookie (JWT) +
SameSite=Lax/Strict,Secure - หน้า protected เรียก
GET /api/auth/meเพื่อตรวจสอบสถานะ - หาก 401 → redirect →
/login
CORS/Fetch: เปิด
credentials: 'include'ทุกครั้ง, ตั้งNEXT_PUBLIC_API_BASEเป็น origin ของ backend ผ่าน proxy (เช่นhttps://lcbp3.np-dms.work)
2.5 UI/UX
- Sea‑blue palette, sidebar พับได้, card‑based KPI
- ตารางข้อมูลเตรียมรองรับ server‑side 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): web‑vitals, 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
/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
helmet+cors(allow specific origin; credentials true)cookie-parser,json limit(e.g., 2MB)requestLogger→ trace + response time- Route handler →
authGuard(protected) →requirePermission(per‑route) → Controller - 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)
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/dms‑data
- โครงโฟลเดอร์:
{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 (dms‑data)
3.7 Config & ENV (BE)
DB_HOST, DB_PORT, DB_USER, DB_PASS, DB_NAMEJWT_SECRET, COOKIE_NAME, COOKIE_SAMESITE, COOKIE_SECURECORS_ORIGIN, LOG_LEVEL, APP_BASE_URLFILE_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/container‑side)
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
01_*_deploy_table_rbac.sql— สร้างตารางหลักทั้งหมด + RBAC02_*_triggers.sql— บังคับ data rules, auto‑audit fields03_*_procedures_handlers.sql— upsert/bulk handlers (เช่นsp_bulk_import_contract_dwg)04_*_views.sql— รายงาน/เมทริกซ์สิทธิ์ (v_role_permission_matrix, etc.)05_*_seed_data.sql— ค่าพื้นฐาน domain (project, categories, statuses)06_*_seed_users.sql— บัญชีเริ่มต้น (superadmin, editors, viewers)07_*_seed_contract_dwg.sql— ข้อมูลตัวอย่างแบบสัญญา
4.3 Indexing & Performance
- Composite indexes ตามคอลัมน์ filter/sort (เช่น
(project_id, updated_at DESC)) - Full‑text 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
- เก็บสำเนา off‑NAS (encrypted)
5) Reverse Proxy & TLS
5.1 Nginx (Alpine) — ตัวอย่าง server block
สำคัญ: บนสภาพแวดล้อมนี้ ให้ใช้คนละบรรทัด:
listen 443 ssl;http2 on;หลีกเลี่ยงlisten 443 ssl http2;
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 หลาย sub‑domain ใน UI แต่ mainten ดีเรื่อง symlink/renew
5.3 TLS & Certificates
- Let’s Encrypt (HTTP‑01 webroot/standalone) + HSTS
- QNAP mgmt เปลี่ยนเป็น 8443 → พอร์ต 443 public ว่างสำหรับ Nginx/NPM
6) Docker Compose Topology
6.1 Services (สรุป)
frontend(Next.js) :3000backend(Express) :3001mariadb(10.11) :3306 (internal)phpmyadmin:80 (internal)nginxornpm: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 pingwith credentials - nginx:
nginx -tat startup
6.4 Security Hardening
- รัน container ด้วย user non‑root (
user: nodeสำหรับ FE/BE) - จำกัด capabilities; read‑only 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 → CORScredentials: true - EACCES Next.js: สิทธิ์
.next/*+ run asnode, โวลุ่ม 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: keep‑alive, gzip/deflate at proxy, pool 10–25, 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 off‑NAS)
9) Backlog → Architecture Mapping
- RBAC Enforcement ครบ → เติม
requirePermissionทุก route + test matrix ผ่าน view - AuditLog ครบ CRUD/Mapping → trigger + table
audit_logs+ BE hook - Upload/Download จริงของ Drawing Revisions → BE endpoints + virus scan (optional)
- Dashboard KPI → BE summary endpoints + FE cards/charts
- Server‑side DataTables → paging/sort/filter + indexesรองรับ
- รายงาน Export CSV/Excel/PDF → BE export endpoints + FE buttons
- Soft delete (
deleted_at) → BE filter default scope + restore endpoint - Validation เข้ม → celebrate/zod schema + consistent error shape
- Indexing/Perf → slow query log + EXPLAIN review
- 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)
app.use(cors({
origin: ['https://lcbp3.np-dms.work'],
credentials: true,
}));
11.2 Secure Download (guarded)
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
router.get('/health', (req, res) => res.json({ ok: true }));
12) Deployment Workflow (Suggested)
- Git (Gitea) branch strategy
feature/*→ PR → main - Build images (dev/prod) via Dockerfile multi‑stage; pin Node/MariaDB versions
docker compose up -d --buildจาก/share/Container/dms- Validate:
/health,/api/health, login roundtrip - 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: UTF‑8 (
utf8mb4_unicode_ci) - Large file policy: size limit (e.g., 50–200MB), allowlist extensions
- Retention: archive strategy for old revisions (optional)
หากต้องการ เวอร์ชัน README.md พร้อมโค้ดตัวอย่าง compose/nginx จัดรูปแบบให้นำไปวางใน repo ได้ทันที แจ้งได้เลยว่าจะให้แตกไฟล์เป็น
/docs/Architecture.md+/nginx/dms.conf+/docker-compose.ymltemplate หรือรูปแบบอื่นที่สะดวกต่อการใช้งานของทีม