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

20 KiB
Executable File
Raw Blame History

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 อย่างระมัดระวัง
  1. ผู้ใช้ submit form /loginPOST /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
/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)

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;

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)

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)

  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 หรือรูปแบบอื่นที่สะดวกต่อการใช้งานของทีม