260223:1415 20260223 nextJS & nestJS Best pratices
All checks were successful
Build and Deploy / deploy (push) Successful in 4m44s
All checks were successful
Build and Deploy / deploy (push) Successful in 4m44s
This commit is contained in:
@@ -1,505 +0,0 @@
|
||||
# 🛠️ Section 2: System Architecture (สถาปัตยกรรมและเทคโนโลยี)
|
||||
|
||||
---
|
||||
|
||||
title: 'System Architecture'
|
||||
version: 1.8.0
|
||||
status: first-draft
|
||||
owner: Nattanin Peancharoen
|
||||
last_updated: 2026-01-26
|
||||
related: -
|
||||
specs/01-objectives.md
|
||||
|
||||
---
|
||||
|
||||
ชื่อกำหนด สถาปัตยกรรมแบบ Headless/API-First ที่ทันสมัย ทำงานทั้งหมดบน QNAP Server ผ่าน Container Station เพื่อความสะดวกในการจัดการและบำรุงรักษา
|
||||
|
||||
## **2.1 Infrastructure & Environment:**
|
||||
|
||||
- Domain: `np-dms.work`, `www.np-dms.work`
|
||||
- IP: 159.192.126.103
|
||||
- Server: QNAP TS-473A, RAM: 32GB, CPU: AMD Ryzen V1500B, HDD: 4TBx4nos. RAID 5, SSD: 1TB ใช้เป็น caching, มี port 2.5Gbps 2 port
|
||||
- Server: AS5304T, RAM: 16GB, CPU: Intel Celeron CPU @ 2.00GH, HDD: 6TBx3nos. RAID 5, SSD: 1TB ใช้เป็น caching, มี port 2.5Gbps 2 port
|
||||
- Rotuter: TP-LINK ER7206, WAN/LAN port 1 SFP, WAN port 2, WAN/LAN 10/100/1000 port 3-6
|
||||
- Core Switch: TP-LINK TL-SG2428P, LAN port 1-24 10/100/1000, SFP port 25-28 1Gbps
|
||||
- Server Switch: AMPCOM, LAN port 1-8 10/100/1000/2500, SFP+ port 9 10Gbps
|
||||
- Admin Switch: TP-LINK ES205G, LAN port 1-5 10/100/1000
|
||||
- CCTV Switch: TP-LINK TL-SL1226P port 1-24 PoE+ 100Mbps, SFP port 24-25 1Gbps
|
||||
- IP Phone Switch: TP-LINK TL-SG1210P port 1-8 PoE+ 100Mbps , Uplink1 10/100/1000, Uplink2 SFP 1Gbps
|
||||
- Controller: TP-LINK OC200
|
||||
- Wireless Access point: TP-LINK EAP610 16 ตัว
|
||||
- CCTV: HikVision (DS-7732NXI-K4) + กล้อง 6 ตัว
|
||||
- IP Phone: YeaLink 8 ตัว
|
||||
- Admin Desktop: Windows 11, LAN port 10/100/1000/2500
|
||||
- Printer: Kyocera CS 3554ci, LAN port 10/100/1000
|
||||
- Containerization: Container Station (Docker & Docker Compose) ใช้ UI ของ Container Station เป็นหลัก ในการ configuration และการรัน docker command
|
||||
- Development Environment: VS Code/Cursor on Windows 11
|
||||
- Data Storage: /share/dms-data บน QNAP
|
||||
- ข้อจำกัด: ไม่สามารถใช้ .env ในการกำหนดตัวแปรภายนอกได้ ต้องกำหนดใน docker-compose.yml เท่านั้น
|
||||
|
||||
## **2.2 Netwrok Configuration**
|
||||
|
||||
**VLAN Networks**
|
||||
| VLAN ID | Name | Purpose | Gateway/Subnet | DHCP | IP Range | DNS | Lease Time | ARP Detection | IGMP Snooping | MLD Snooping | Notes |
|
||||
| ------- | ------ | --------- | --------------- | ---- | ------------------ | ------- | ---------- | ------------- | ------------- | ------------ | --------------- |
|
||||
| 10 | SERVER | Interface | 192.168.10.1/24 | No | - | Custom | - | - | - | - | Static servers |
|
||||
| 20 | MGMT | Interface | 192.168.20.1/24 | No | - | Custom | - | Enable | Enable | - | Management only |
|
||||
| 30 | USER | Interface | 192.168.30.1/24 | Yes | 192.168.30.10-254 | Auto | 7 Days | - | Enable | - | User devices |
|
||||
| 40 | CCTV | Interface | 192.168.40.1/24 | Yes | 192.168.40.100-150 | Auto | 7 Days | - | Enable | - | CCTV & NVR |
|
||||
| 50 | VOICE | Interface | 192.168.50.1/24 | Yes | 192.168.50.201-250 | Auto | 7 Days | - | - | - | IP Phones |
|
||||
| 60 | DMZ | Interface | 192.168.60.1/24 | No | - | 1.1.1.1 | - | - | - | - | Public services |
|
||||
| 70 | GUEST | Interface | 192.168.70.1/24 | Yes | 192.168.70.200-250 | Auto | 1 Day | - | - | - | Guest |
|
||||
|
||||
|
||||
**Switch Profiles**
|
||||
| Profile Name | Native Network | Tagged Networks | Untagged Networks | Voice Network | Loopback Control | Usage |
|
||||
| ---------------- | -------------- | --------------------- | ----------------- | ------------- | ---------------- | ----------------------- |
|
||||
| 01_CORE_TRUNK | MGMT (20) | 10,30,40,50,60,70 | MGMT (20) | - | Spanning Tree | Router & switch uplinks |
|
||||
| 02_MGMT_ONLY | MGMT (20) | MGMT (20) | - | - | Spanning Tree | Management only |
|
||||
| 03_SERVER_ACCESS | SERVER (10) | MGMT (20) | SERVER (10) | - | Spanning Tree | QNAP / ASUSTOR |
|
||||
| 04_CCTV_ACCESS | CCTV (40) | - | CCTV (40) | - | Spanning Tree | CCTV cameras |
|
||||
| 05_USER_ACCESS | USER (30) | - | USER (30) | - | Spanning Tree | PC / Printer |
|
||||
| 06_AP_TRUNK | MGMT (20) | USER (30), GUEST (70) | MGMT (20) | - | Spanning Tree | EAP610 Access Points |
|
||||
| 07_VOICE_ACCESS | USER (30) | VOICE (50) | USER (30) | VOICE (50) | Spanning Tree | IP Phones |
|
||||
|
||||
|
||||
**ER7206 Port Mapping**
|
||||
| Port | Connected Device | Port | Description |
|
||||
| ---- | ---------------- | ------------- | ----------- |
|
||||
| 1 | - | - | - |
|
||||
| 2 | WAN | - | Internet |
|
||||
| 3 | SG2428P | PVID MGMT(20) | Core Switch |
|
||||
| 4 | - | - | - |
|
||||
| 5 | - | - | - |
|
||||
| 6 | - | - | - |
|
||||
|
||||
**AMPCOM Port Aggregate Setting**
|
||||
| Aggregate Group ID | Type | Member port | Aggregated Port |
|
||||
| ------------------ | ---- | ----------- | --------------- |
|
||||
| Trunk1 | LACP | 3,4 | 3,4 |
|
||||
| Trunk2 | LACP | 5,6 | 5,6 |
|
||||
|
||||
|
||||
**AMPCOM Port VLAN Mapping**
|
||||
| Port | Connected Device | Port vlan type | Access VLAN | Native VLAN | Trunk vlan |
|
||||
| ------ | ---------------- | -------------- | ----------- | ----------- | -------------------- |
|
||||
| 1 | SG2428P | Trunk | - | 20 | 10,20,30,40,50,60,70 |
|
||||
| 2 | - | Trunk | - | 20 | 10,20,30,40,50,60,70 |
|
||||
| 7 | - | Access | 20 | - | - |
|
||||
| 8 | Admin Desktop | Access | 20 | - | - |
|
||||
| Trunk1 | QNAP | Trunk | - | 10 | 10,20,30,40,50,60,70 |
|
||||
| Trunk2 | ASUSTOR | Trunk | - | 10 | 10,20,30,40,50,60,70 |
|
||||
|
||||
|
||||
**NAS NIC Bonding Configuration**
|
||||
| Device | Bonding Mode | Member Ports | VLAN Mode | Tagged VLAN | IP Address | Gateway | Notes |
|
||||
| ------- | ------------------- | ------------ | --------- | ----------- | --------------- | ------------ | ---------------------- |
|
||||
| QNAP | IEEE 802.3ad (LACP) | Adapter 1, 2 | Untagged | 10 (SERVER) | 192.168.10.8/24 | 192.168.10.1 | Primary NAS for DMS |
|
||||
| ASUSTOR | IEEE 802.3ad (LACP) | Port 1, 2 | Untagged | 10 (SERVER) | 192.168.10.9/24 | 192.168.10.1 | Backup / Secondary NAS |
|
||||
|
||||
> **หมายเหตุ**: NAS ทั้งสองตัวใช้ LACP bonding เพื่อเพิ่ม bandwidth และ redundancy โดยต้อง config ให้ตรงกับ AMPCOM Switch (Trunk1)
|
||||
|
||||
|
||||
**SG2428P Port Mapping**
|
||||
| Port | Connected Device | Switch Profile | Description |
|
||||
| ---- | ------------------------- | -------------------- | ------------- |
|
||||
| 1 | ER7206 | 01_CORE_TRUNK | Internet |
|
||||
| 2 | OC200 | 01_CORE_TRUNK | Controller |
|
||||
| 3 | Ampcom 2.5G Switch Port 1 | LAG1 (01_CORE_TRUNK) | Uplink |
|
||||
| 4 | - | LAG1 (01_CORE_TRUNK) | Reserved |
|
||||
| 5 | EAP610-01 | 06_AP_TRUNK | Access Point |
|
||||
| 6 | EAP610-02 | 06_AP_TRUNK | Access Point |
|
||||
| 7 | EAP610-03 | 06_AP_TRUNK | Access Point |
|
||||
| 8 | EAP610-04 | 06_AP_TRUNK | Access Point |
|
||||
| 9 | EAP610-05 | 06_AP_TRUNK | Access Point |
|
||||
| 10 | EAP610-06 | 06_AP_TRUNK | Access Point |
|
||||
| 11 | EAP610-07 | 06_AP_TRUNK | Access Point |
|
||||
| 12 | EAP610-08 | 06_AP_TRUNK | Access Point |
|
||||
| 13 | EAP610-09 | 06_AP_TRUNK | Access Point |
|
||||
| 14 | EAP610-10 | 06_AP_TRUNK | Access Point |
|
||||
| 15 | EAP610-11 | 06_AP_TRUNK | Access Point |
|
||||
| 16 | EAP610-12 | 06_AP_TRUNK | Access Point |
|
||||
| 17 | EAP610-13 | 06_AP_TRUNK | Access Point |
|
||||
| 18 | EAP610-14 | 06_AP_TRUNK | Access Point |
|
||||
| 19 | EAP610-15 | 06_AP_TRUNK | Access Point |
|
||||
| 20 | EAP610-16 | 06_AP_TRUNK | Access Point |
|
||||
| 21 | Reserved | 01_CORE_TRUNK | |
|
||||
| 22 | Reserved | 01_CORE_TRUNK | |
|
||||
| 23 | Printer | 05_USER_ACCESS | Printer |
|
||||
| 24 | ES205G | 01_CORE_TRUNK | Management PC |
|
||||
| 25 | TL-SL1226P | 01_CORE_TRUNK | Uplink |
|
||||
| 26 | SG1210P | 01_CORE_TRUNK | Uplink |
|
||||
| 27 | Reserved | 01_CORE_TRUNK | |
|
||||
| 28 | Reserved | 01_CORE_TRUNK | |
|
||||
|
||||
|
||||
**ES205G Port Mapping (Admin Switch)**
|
||||
| Port | Connected Device | VLAN | Description |
|
||||
| ---- | ---------------- | ----------- | ----------- |
|
||||
| 1 | SG2428P Port 24 | Trunk (All) | Uplink |
|
||||
| 2 | Admin Desktop | MGMT (20) | Admin PC |
|
||||
| 3 | Reserved | MGMT (20) | |
|
||||
| 4 | Reserved | MGMT (20) | |
|
||||
| 5 | Reserved | MGMT (20) | |
|
||||
|
||||
> **หมายเหตุ**: ES205G เป็น Unmanaged Switch ไม่รองรับ VLAN tagging ดังนั้นทุก port จะอยู่ใน Native VLAN (20) ของ uplink
|
||||
|
||||
|
||||
**TL-SL1226P Port Mapping (CCTV Switch)**
|
||||
| Port | Connected Device | PoE | VLAN | Description |
|
||||
| ---- | ---------------- | ---- | --------- | ----------- |
|
||||
| 1 | Camera-01 | PoE+ | CCTV (40) | CCTV Camera |
|
||||
| 2 | Camera-02 | PoE+ | CCTV (40) | CCTV Camera |
|
||||
| 3 | Camera-03 | PoE+ | CCTV (40) | CCTV Camera |
|
||||
| 4 | Camera-04 | PoE+ | CCTV (40) | CCTV Camera |
|
||||
| 5 | Camera-05 | PoE+ | CCTV (40) | CCTV Camera |
|
||||
| 6 | Camera-06 | PoE+ | CCTV (40) | CCTV Camera |
|
||||
| 7-23 | Reserved | PoE+ | CCTV (40) | |
|
||||
| 24 | HikVision NVR | - | CCTV (40) | NVR |
|
||||
| 25 | SG2428P Port 25 | - | Trunk | SFP Uplink |
|
||||
| 26 | Reserved | - | Trunk | SFP |
|
||||
|
||||
|
||||
**SG1210P Port Mapping (IP Phone Switch)**
|
||||
| Port | Connected Device | PoE | Data VLAN | Voice VLAN | Description |
|
||||
| ------- | ---------------- | ---- | --------- | ---------- | ----------- |
|
||||
| 1 | IP Phone-01 | PoE+ | USER (30) | VOICE (50) | IP Phone |
|
||||
| 2 | IP Phone-02 | PoE+ | USER (30) | VOICE (50) | IP Phone |
|
||||
| 3 | IP Phone-03 | PoE+ | USER (30) | VOICE (50) | IP Phone |
|
||||
| 4 | IP Phone-04 | PoE+ | USER (30) | VOICE (50) | IP Phone |
|
||||
| 5 | IP Phone-05 | PoE+ | USER (30) | VOICE (50) | IP Phone |
|
||||
| 6 | IP Phone-06 | PoE+ | USER (30) | VOICE (50) | IP Phone |
|
||||
| 7 | IP Phone-07 | PoE+ | USER (30) | VOICE (50) | IP Phone |
|
||||
| 8 | IP Phone-08 | PoE+ | USER (30) | VOICE (50) | IP Phone |
|
||||
| Uplink1 | Reserved | - | Trunk | - | RJ45 Uplink |
|
||||
| Uplink2 | SG2428P Port 26 | - | Trunk | - | SFP Uplink |
|
||||
|
||||
> **หมายเหตุ**: SG1210P รองรับ Voice VLAN ทำให้ IP Phone ใช้ VLAN 50 สำหรับ voice traffic และ passthrough VLAN 30 สำหรับ PC ที่ต่อผ่าน phone
|
||||
|
||||
|
||||
**Static IP Allocation**
|
||||
| VLAN | Device | IP Address | MAC Address | Notes |
|
||||
| ---------- | --------------- | ------------------ | ----------- | ---------------- |
|
||||
| SERVER(10) | QNAP | 192.168.10.8 | - | Primary NAS |
|
||||
| SERVER(10) | ASUSTOR | 192.168.10.9 | - | Backup NAS |
|
||||
| SERVER(10) | Docker Host | 192.168.10.10 | - | Containers |
|
||||
| MGMT(20) | ER7206 | 192.168.20.1 | - | Gateway/Router |
|
||||
| MGMT(20) | SG2428P | 192.168.20.2 | - | Core Switch |
|
||||
| MGMT(20) | AMPCOM | 192.168.20.3 | - | Server Switch |
|
||||
| MGMT(20) | TL-SL1226P | 192.168.20.4 | - | CCTV Switch |
|
||||
| MGMT(20) | SG1210P | 192.168.20.5 | - | Phone Switch |
|
||||
| MGMT(20) | OC200 | 192.168.20.250 | - | Omada Controller |
|
||||
| MGMT(20) | Admin Desktop | 192.168.20.100 | - | Admin PC |
|
||||
| USER(30) | Printer | 192.168.30.222 | - | Kyocera CS3554ci |
|
||||
| CCTV(40) | NVR | 192.168.40.100 | - | HikVision NVR |
|
||||
| CCTV(40) | Camera-01 to 06 | 192.168.40.101-106 | - | CCTV Cameras |
|
||||
| USER(30) | Admin Desktop | 192.168.30.100 | - | Admin PC (USER) |
|
||||
|
||||
**2.8 DHCP Reservation (MAC Mapping)**
|
||||
|
||||
**CCTV MAC Address Mapping (VLAN 40)**
|
||||
| Device Name | IP Address | MAC Address | Port (Switch) | Notes |
|
||||
| ------------- | -------------- | ----------- | ------------- | ---------- |
|
||||
| HikVision NVR | 192.168.40.100 | | Port 24 | Master NVR |
|
||||
| Camera-01 | 192.168.40.101 | | Port 1 | |
|
||||
| Camera-02 | 192.168.40.102 | | Port 2 | |
|
||||
| Camera-03 | 192.168.40.103 | | Port 3 | |
|
||||
| Camera-04 | 192.168.40.104 | | Port 4 | |
|
||||
| Camera-05 | 192.168.40.105 | | Port 5 | |
|
||||
| Camera-06 | 192.168.40.106 | | Port 6 | |
|
||||
|
||||
**IP Phone MAC Address Mapping (VLAN 50)**
|
||||
| Device Name | IP Address | MAC Address | Port (Switch) | Notes |
|
||||
| ----------- | -------------- | ----------- | ------------- | ------- |
|
||||
| IP Phone-01 | 192.168.50.201 | | Port 1 | Yealink |
|
||||
| IP Phone-02 | 192.168.50.202 | | Port 2 | Yealink |
|
||||
| IP Phone-03 | 192.168.50.203 | | Port 3 | Yealink |
|
||||
| IP Phone-04 | 192.168.50.204 | | Port 4 | Yealink |
|
||||
| IP Phone-05 | 192.168.50.205 | | Port 5 | Yealink |
|
||||
| IP Phone-06 | 192.168.50.206 | | Port 6 | Yealink |
|
||||
| IP Phone-07 | 192.168.50.207 | | Port 7 | Yealink |
|
||||
| IP Phone-08 | 192.168.50.208 | | Port 8 | Yealink |
|
||||
|
||||
|
||||
**Wireless SSID Mapping (OC200 Controller)**
|
||||
| SSID Name | Band | VLAN | Security | Portal Auth | Notes |
|
||||
| --------- | ------- | ---------- | --------- | ----------- | ----------------------- |
|
||||
| PSLCBP3 | 2.4G/5G | USER (30) | WPA2/WPA3 | No | Staff WiFi |
|
||||
| GUEST | 2.4G/5G | GUEST (70) | WPA2 | Yes | Guest WiFi with Captive |
|
||||
|
||||
> **หมายเหตุ**: ทุก SSID broadcast ผ่าน EAP610 ทั้ง 16 ตัว โดยใช้ 06_AP_TRUNK profile ที่ tag VLAN 30 และ 70
|
||||
|
||||
|
||||
**Gateway ACL (ER7206 Firewall Rules)**
|
||||
|
||||
*Inter-VLAN Routing Policy*
|
||||
| # | Name | Source | Destination | Service | Action | Log | Notes |
|
||||
| --- | ----------------- | --------------- | ---------------- | -------------- | ------ | --- | --------------------------- |
|
||||
| 1 | MGMT-to-ALL | VLAN20 (MGMT) | Any | Any | Allow | No | Admin full access |
|
||||
| 2 | SERVER-to-ALL | VLAN10 (SERVER) | Any | Any | Allow | No | Servers outbound access |
|
||||
| 3 | USER-to-SERVER | VLAN30 (USER) | VLAN10 (SERVER) | HTTP/HTTPS/SSH | Allow | No | Users access web apps |
|
||||
| 4 | USER-to-DMZ | VLAN30 (USER) | VLAN60 (DMZ) | HTTP/HTTPS | Allow | No | Users access DMZ services |
|
||||
| 5 | USER-to-MGMT | VLAN30 (USER) | VLAN20 (MGMT) | Any | Deny | Yes | Block users from management |
|
||||
| 6 | USER-to-CCTV | VLAN30 (USER) | VLAN40 (CCTV) | Any | Deny | Yes | Isolate CCTV |
|
||||
| 7 | USER-to-VOICE | VLAN30 (USER) | VLAN50 (VOICE) | Any | Deny | No | Isolate Voice |
|
||||
| 8 | USER-to-GUEST | VLAN30 (USER) | VLAN70 (GUEST) | Any | Deny | No | Isolate Guest |
|
||||
| 9 | CCTV-to-INTERNET | VLAN40 (CCTV) | WAN | HTTPS (443) | Allow | No | NVR cloud backup (optional) |
|
||||
| 10 | CCTV-to-ALL | VLAN40 (CCTV) | Any (except WAN) | Any | Deny | Yes | CCTV isolated |
|
||||
| 11 | VOICE-to-SIP | VLAN50 (VOICE) | SIP Server IP | SIP/RTP | Allow | No | Voice to SIP trunk |
|
||||
| 12 | VOICE-to-ALL | VLAN50 (VOICE) | Any | Any | Deny | No | Voice isolated |
|
||||
| 13 | DMZ-to-ALL | VLAN60 (DMZ) | Any (internal) | Any | Deny | Yes | DMZ cannot reach internal |
|
||||
| 14 | GUEST-to-INTERNET | VLAN70 (GUEST) | WAN | HTTP/HTTPS/DNS | Allow | No | Guest internet only |
|
||||
| 15 | GUEST-to-ALL | VLAN70 (GUEST) | Any (internal) | Any | Deny | Yes | Guest isolated |
|
||||
| 99 | DEFAULT-DENY | Any | Any | Any | Deny | Yes | Catch-all deny |
|
||||
|
||||
*WAN Inbound Rules (Port Forwarding)*
|
||||
| # | Name | WAN Port | Internal IP | Internal Port | Protocol | Notes |
|
||||
| --- | --------- | -------- | ------------ | ------------- | -------- | ------------------- |
|
||||
| 1 | HTTPS-NPM | 443 | 192.168.10.8 | 443 | TCP | Nginx Proxy Manager |
|
||||
| 2 | HTTP-NPM | 80 | 192.168.10.8 | 80 | TCP | HTTP redirect |
|
||||
|
||||
> **หมายเหตุ**: ER7206 ใช้หลักการ Default Deny - Rules ประมวลผลจากบนลงล่าง
|
||||
|
||||
|
||||
**Switch ACL (SG2428P Layer 2 Rules)**
|
||||
|
||||
*Port-Based Access Control*
|
||||
| # | Name | Source Port | Source MAC/VLAN | Destination | Action | Notes |
|
||||
| --- | --------------- | --------------- | --------------- | ------------------- | ------ | ------------------------ |
|
||||
| 1 | CCTV-Isolation | Port 25 (CCTV) | VLAN 40 | VLAN 10,20,30 | Deny | CCTV cannot reach others |
|
||||
| 2 | Guest-Isolation | Port 5-20 (APs) | VLAN 70 | VLAN 10,20,30,40,50 | Deny | Guest isolation |
|
||||
| 3 | Voice-QoS | Port 26 (Phone) | VLAN 50 | Any | Allow | QoS priority DSCP EF |
|
||||
|
||||
*Storm Control (per port)*
|
||||
| Port Range | Broadcast | Multicast | Unknown Unicast | Notes |
|
||||
| ---------- | --------- | --------- | --------------- | ----------------------- |
|
||||
| 1-28 | 10% | 10% | 10% | Prevent broadcast storm |
|
||||
|
||||
*Spanning Tree Configuration*
|
||||
| Setting | Value | Notes |
|
||||
| -------------------- | --------- | ------------------------------ |
|
||||
| STP Mode | RSTP | Rapid Spanning Tree |
|
||||
| Root Bridge Priority | 4096 | SG2428P as root |
|
||||
| Port Fast | Port 5-24 | Edge ports (APs, endpoints) |
|
||||
| BPDU Guard | Port 5-24 | Protect against rogue switches |
|
||||
|
||||
> **หมายเหตุ**: SG2428P เป็น L2+ switch, ACL ทำได้จำกัด ให้ใช้ ER7206 เป็น primary firewall
|
||||
|
||||
|
||||
**EAP ACL (Omada Controller - Wireless Rules)**
|
||||
|
||||
*SSID: PSLCBP3 (Staff WiFi)*
|
||||
| # | Name | Source | Destination | Service | Action | Schedule | Notes |
|
||||
| --- | ------------------- | ---------- | ---------------- | -------- | ------ | -------- | ----------------- |
|
||||
| 1 | Allow-DNS | Any Client | 8.8.8.8, 1.1.1.1 | DNS (53) | Allow | Always | DNS resolution |
|
||||
| 2 | Allow-Server | Any Client | 192.168.10.0/24 | Any | Allow | Always | Access to servers |
|
||||
| 3 | Allow-Printer | Any Client | 192.168.30.222 | 9100,631 | Allow | Always | Print services |
|
||||
| 4 | Allow-Internet | Any Client | WAN | Any | Allow | Always | Internet access |
|
||||
| 5 | Block-MGMT | Any Client | 192.168.20.0/24 | Any | Deny | Always | No management |
|
||||
| 6 | Block-CCTV | Any Client | 192.168.40.0/24 | Any | Deny | Always | No CCTV access |
|
||||
| 7 | Block-Voice | Any Client | 192.168.50.0/24 | Any | Deny | Always | No Voice access |
|
||||
| 8 | Block-Client2Client | Any Client | Any Client | Any | Deny | Always | Client isolation |
|
||||
|
||||
*SSID: GUEST (Guest WiFi)*
|
||||
| # | Name | Source | Destination | Service | Action | Schedule | Notes |
|
||||
| --- | ------------------- | ---------- | ---------------- | ---------- | ------ | -------- | ------------------ |
|
||||
| 1 | Allow-DNS | Any Client | 8.8.8.8, 1.1.1.1 | DNS (53) | Allow | Always | DNS resolution |
|
||||
| 2 | Allow-HTTP | Any Client | WAN | HTTP/HTTPS | Allow | Always | Web browsing |
|
||||
| 3 | Block-RFC1918 | Any Client | 10.0.0.0/8 | Any | Deny | Always | No private IPs |
|
||||
| 4 | Block-RFC1918-2 | Any Client | 172.16.0.0/12 | Any | Deny | Always | No private IPs |
|
||||
| 5 | Block-RFC1918-3 | Any Client | 192.168.0.0/16 | Any | Deny | Always | No internal access |
|
||||
| 6 | Block-Client2Client | Any Client | Any Client | Any | Deny | Always | Client isolation |
|
||||
|
||||
*Rate Limiting*
|
||||
| SSID | Download Limit | Upload Limit | Notes |
|
||||
| ------- | -------------- | ------------ | ----------------------- |
|
||||
| PSLCBP3 | Unlimited | Unlimited | Staff full speed |
|
||||
| GUEST | 10 Mbps | 5 Mbps | Guest bandwidth control |
|
||||
|
||||
*Captive Portal (GUEST SSID)*
|
||||
| Setting | Value | Notes |
|
||||
| ---------------- | --------------- | ---------------------- |
|
||||
| Portal Type | Simple Password | Single shared password |
|
||||
| Session Timeout | 8 Hours | Re-auth after 8 hours |
|
||||
| Idle Timeout | 30 Minutes | Disconnect if idle |
|
||||
| Terms of Service | Enabled | User must accept ToS |
|
||||
|
||||
> **หมายเหตุ**: EAP ACL ทำงานที่ Layer 3 บน Omada Controller ช่วยลด load บน ER7206
|
||||
|
||||
|
||||
**Network Topology Diagram**
|
||||
```mermaid
|
||||
graph TB
|
||||
subgraph Internet
|
||||
WAN[("🌐 Internet<br/>WAN")]
|
||||
end
|
||||
|
||||
subgraph Router["ER7206 Router"]
|
||||
R[("🔲 ER7206<br/>192.168.20.1")]
|
||||
end
|
||||
|
||||
subgraph CoreSwitch["SG2428P Core Switch"]
|
||||
CS[("🔲 SG2428P<br/>192.168.20.2")]
|
||||
end
|
||||
|
||||
subgraph ServerSwitch["AMPCOM 2.5G Switch"]
|
||||
SS[("🔲 AMPCOM<br/>192.168.20.3")]
|
||||
end
|
||||
|
||||
subgraph Servers["VLAN 10 - Servers"]
|
||||
QNAP[("💾 QNAP<br/>192.168.10.10")]
|
||||
ASUSTOR[("💾 ASUSTOR<br/>192.168.10.11")]
|
||||
end
|
||||
|
||||
subgraph AccessPoints["EAP610 x16"]
|
||||
AP[("📶 WiFi APs")]
|
||||
end
|
||||
|
||||
subgraph OtherSwitches["Distribution"]
|
||||
CCTV_SW[("🔲 TL-SL1226P<br/>CCTV")]
|
||||
PHONE_SW[("🔲 SG1210P<br/>IP Phone")]
|
||||
ADMIN_SW[("🔲 ES205G<br/>Admin")]
|
||||
end
|
||||
|
||||
WAN --> R
|
||||
R -->|Port 3| CS
|
||||
CS -->|LAG Port 3-4| SS
|
||||
SS -->|Port 3-4 LACP| QNAP
|
||||
SS -->|Port 5-6 LACP| ASUSTOR
|
||||
SS -->|Port 7| ADMIN_SW
|
||||
CS -->|Port 5-20| AP
|
||||
CS -->|SFP 25| CCTV_SW
|
||||
CS -->|SFP 26| PHONE_SW
|
||||
CS -->|Port 24| ADMIN_SW
|
||||
```
|
||||
|
||||
|
||||
**OC200 Omada Controller Configuration**
|
||||
| Setting | Value | Notes |
|
||||
| --------------- | -------------------------- | ------------------------------ |
|
||||
| Controller IP | 192.168.20.10 | Static IP in MGMT VLAN |
|
||||
| Controller Port | 8043 (HTTPS) | Management Web UI |
|
||||
| Adoption URL | https://192.168.20.10:8043 | URL for AP adoption |
|
||||
| Site Name | LCBP3 | Single site configuration |
|
||||
| Managed Devices | 16x EAP610 | All APs managed centrally |
|
||||
| Firmware Update | Manual | Test before production rollout |
|
||||
| Backup Schedule | Weekly (Sunday 2AM) | Auto backup to QNAP |
|
||||
|
||||
|
||||
## **2.3 การจัดการ Configuration (ปรับปรุง):**
|
||||
|
||||
- ใช้ docker-compose.yml สำหรับ environment variables ตามข้อจำกัดของ QNAP
|
||||
- Secrets Management:
|
||||
- ห้ามระบุ Sensitive Secrets (Password, Keys) ใน docker-compose.yml หลัก
|
||||
- ต้องใช้ไฟล์ docker-compose.override.yml (ที่ถูก gitignore) สำหรับ Inject Environment Variables ที่เป็นความลับในแต่ละ Environment (Dev/Prod)
|
||||
- ไฟล์ docker-compose.yml หลักให้ใส่ค่า Dummy หรือว่างไว้
|
||||
- แต่ต้องมี mechanism สำหรับจัดการ sensitive secrets อย่างปลอดภัย โดยใช้:
|
||||
- Docker secrets (ถ้ารองรับ)
|
||||
- External secret management (Hashicorp Vault) หรือ
|
||||
- Encrypted environment variables
|
||||
- Development environment ยังใช้ .env ได้ แต่ต้องไม่ commit เข้า version control
|
||||
- ต้องมี configuration validation during application startup
|
||||
- ต้องแยก configuration ตาม environment (development, staging, production)
|
||||
- Docker Network: ทุก Service จะเชื่อมต่อผ่านเครือข่ายกลางชื่อ lcbp3 เพื่อให้สามารถสื่อสารกันได้
|
||||
|
||||
## **2.4 Core Services:**
|
||||
|
||||
- Code Hosting: Gitea (Self-hosted on QNAP)
|
||||
|
||||
- Application name: git
|
||||
- Service name: gitea
|
||||
- Domain: git.np-dms.work
|
||||
- หน้าที่: เป็นศูนย์กลางในการเก็บและจัดการเวอร์ชันของโค้ด (Source Code) สำหรับทุกส่วน
|
||||
|
||||
- Backend / Data Platform: NestJS
|
||||
|
||||
- Application name: lcbp3-backend
|
||||
- Service name: backend
|
||||
- Domain: backend.np-dms.work
|
||||
- Framework: NestJS (Node.js, TypeScript, ESM)
|
||||
- หน้าที่: จัดการโครงสร้างข้อมูล (Data Models), สร้าง API, จัดการสิทธิ์ผู้ใช้ (Roles & Permissions), และสร้าง Workflow ทั้งหมดของระบบ
|
||||
|
||||
- Database: MariaDB 11.8
|
||||
|
||||
- Application name: lcbp3-db
|
||||
- Service name: mariadb
|
||||
- Domain: db.np-dms.work
|
||||
- หน้าที่: ฐานข้อมูลหลักสำหรับเก็บข้อมูลทั้งหมด
|
||||
- Tooling: DBeaver (Community Edition), phpmyadmin สำหรับการออกแบบและจัดการฐานข้อมูล
|
||||
|
||||
- Database Management: phpMyAdmin
|
||||
|
||||
- Application name: lcbp3-db
|
||||
- Service: phpmyadmin:5-apache
|
||||
- Service name: pma
|
||||
- Domain: pma.np-dms.work
|
||||
- หน้าที่: จัดการฐานข้อมูล mariadb ผ่าน Web UI
|
||||
|
||||
- Frontend: Next.js
|
||||
|
||||
- Application name: lcbp3-frontend
|
||||
- Service name: frontend
|
||||
- Domain: lcbp3.np-dms.work
|
||||
- Framework: Next.js (App Router, React, TypeScript, ESM)
|
||||
- Styling: Tailwind CSS + PostCSS
|
||||
- Component Library: shadcn/ui
|
||||
- หน้าที่: สร้างหน้าตาเว็บแอปพลิเคชันสำหรับให้ผู้ใช้งานเข้ามาดู Dashboard, จัดการเอกสาร, และติดตามงาน โดยจะสื่อสารกับ Backend ผ่าน API
|
||||
|
||||
- Workflow Automation: n8n
|
||||
|
||||
- Application name: lcbp3-n8n
|
||||
- Service: n8nio/n8n:latest
|
||||
- Service name: n8n
|
||||
- Domain: n8n.np-dms.work
|
||||
- หน้าที่: จัดการ workflow ระหว่าง Backend และ Line
|
||||
|
||||
- Reverse Proxy: Nginx Proxy Manager
|
||||
|
||||
- Application name: lcbp3-npm
|
||||
- Service: Nginx Proxy Manager (nginx-proxy-manage: latest)
|
||||
- Service name: npm
|
||||
- Domain: npm.np-dms.work
|
||||
- หน้าที่: เป็นด่านหน้าในการรับ-ส่งข้อมูล จัดการโดเมนทั้งหมด, ทำหน้าที่เป็น Proxy ชี้ไปยัง Service ที่ถูกต้อง, และจัดการ SSL Certificate (HTTPS) ให้อัตโนมัติ
|
||||
|
||||
- Search Engine: Elasticsearch
|
||||
- Cache: Redis
|
||||
|
||||
## **2.5 Business Logic & Consistency (ปรับปรุง):**
|
||||
|
||||
- 2.5.1 Unified Workflow Engine (หลัก):
|
||||
|
||||
- ระบบการเดินเอกสารทั้งหมด (Correspondence, RFA, Circulation) ต้อง ใช้ Engine กลางเดียวกัน โดยกำหนด Logic ผ่าน Workflow DSL (JSON Configuration) แทนการเขียน Hard-coded ลงในตาราง
|
||||
- Workflow Versioning (เพิ่ม): ระบบต้องรองรับการกำหนด Version ของ Workflow Definition โดยเอกสารที่เริ่มกระบวนการไปแล้ว (In-progress instances) จะต้องใช้ Workflow Version เดิม จนกว่าจะสิ้นสุดกระบวนการ หรือได้รับคำสั่ง Migrate จาก Admin เพื่อป้องกันความขัดแย้งของ State
|
||||
|
||||
- 2.5.2 Separation of Concerns:
|
||||
|
||||
- Module ต่างๆ (Correspondence, RFA, Circulation) จะเก็บเฉพาะข้อมูลของเอกสาร (Data) ส่วนสถานะและการเปลี่ยนสถานะ (State Transition) จะถูกจัดการโดย Workflow Engine
|
||||
|
||||
- 2.5.3 Idempotency & Locking:
|
||||
|
||||
- ใช้กลไกเดิมในการป้องกันการทำรายการซ้ำ
|
||||
|
||||
- 2.5.4 Optimistic Locking:
|
||||
|
||||
- ใช้ Version Column ใน Database ควบคู่กับ Redis Lock สำหรับการสร้างเลขที่เอกสาร เพื่อเป็น Safety Net ชั้นสุดท้าย
|
||||
|
||||
- 2.5.5 จะไม่มีการใช้ SQL Triggers
|
||||
- เพื่อป้องกันตรรกะซ่อนเร้น (Hidden Logic) และความซับซ้อนในการดีบัก
|
||||
|
||||
## **2.6 Data Migration และ Schema Versioning:**
|
||||
|
||||
- ต้องมี database migration scripts สำหรับทุก schema change โดยใช้ TypeORM migrations
|
||||
- ต้องรองรับ rollback ของ migration ได้
|
||||
- ต้องมี data seeding strategy สำหรับ environment ต่างๆ (development, staging, production)
|
||||
- ต้องมี version compatibility between schema versions
|
||||
- Migration scripts ต้องผ่านการทดสอบใน staging environment ก่อน production
|
||||
- ต้องมี database backup ก่อนทำ migration ใน production
|
||||
|
||||
## **2.7 กลยุทธ์ความทนทานและการจัดการข้อผิดพลาด (Resilience & Error Handling Strategy)**
|
||||
|
||||
- 2.7.1 Circuit Breaker Pattern: ใช้สำหรับ external service calls (Email, LINE, Elasticsearch)
|
||||
- 2.7.2 Retry Mechanism: ด้วย exponential backoff สำหรับ transient failures
|
||||
- 2.7.3 Fallback Strategies: Graceful degradation เมื่อบริการภายนอกล้มเหลว
|
||||
- 2.7.4 Error Handling: Error messages ต้องไม่เปิดเผยข้อมูล sensitive
|
||||
- 2.6.5 Monitoring: Centralized error monitoring และ alerting system
|
||||
|
||||
@@ -1,994 +0,0 @@
|
||||
# 🏗️ System Architecture Specification
|
||||
|
||||
---
|
||||
|
||||
**title:** 'System Architecture'
|
||||
**version:** 1.7.0
|
||||
**status:** first-draft
|
||||
**owner:** Nattanin Peancharoen
|
||||
**last_updated:** 2025-12-18
|
||||
**related:**
|
||||
|
||||
- specs/01-requirements/01-02-architecture.md
|
||||
- specs/01-requirements/01-06-non-functional.md
|
||||
- specs/03-implementation/03-01-fullftack-js-v1.7.0.md
|
||||
|
||||
---
|
||||
|
||||
## 📋 ภาพรวม (Overview)
|
||||
|
||||
เอกสารนี้อธิบายสถาปัตยกรรมระบบ LCBP3-DMS (Laem Chabang Port Phase 3 - Document Management System) ที่ใช้แนวทาง **Headless/API-First Architecture** พร้อมการ Deploy บน QNAP Server ผ่าน Container Station
|
||||
|
||||
## 1. 🎯 Architecture Principles
|
||||
|
||||
### 1.1 Component Overview
|
||||
```
|
||||
┌──────────────────────────────────────────────────────┐
|
||||
│ Load Balancer │
|
||||
│ (Nginx Proxy Manager) │
|
||||
└────────────┬─────────────────────────────────────────┘
|
||||
│
|
||||
┌────────┴────────┬──────────────┬──────────────┐
|
||||
│ │ │ │
|
||||
┌───▼────┐ ┌──────▼──────┐ ┌──▼───┐ ┌─────▼─────┐
|
||||
│Backend │ │Backend │ │Backend│ │ Backend │
|
||||
│Node 1 │ │Node 2 │ │Node 3 │ │ Node 4 │
|
||||
└───┬────┘ └──────┬──────┘ └──┬────┘ └─────┬─────┘
|
||||
│ │ │ │
|
||||
└────────────────┴──────────────┴───────────────┘
|
||||
│
|
||||
┌───────────┼───────────┬──────────────┐
|
||||
│ │ │ │
|
||||
┌────▼────┐ ┌──▼───┐ ┌───▼────┐ ┌────▼─────┐
|
||||
│ MariaDB │ │Redis │ │ Redis │ │ Redis │
|
||||
│ Primary │ │Node 1│ │ Node 2 │ │ Node 3 │
|
||||
└────┬────┘ └──────┘ └────────┘ └──────────┘
|
||||
│
|
||||
┌────▼────┐
|
||||
│ MariaDB │
|
||||
│Replicas │
|
||||
└─────────┘
|
||||
```
|
||||
### 1.2 Component Responsibilities
|
||||
|
||||
| Component | Purpose | Critical? |
|
||||
| --------------- | --------------------------------- | --------- |
|
||||
| Backend Nodes | API processing, number generation | YES |
|
||||
| MariaDB Primary | Persistent sequence storage | YES |
|
||||
| Redis Cluster | Distributed locking, reservations | YES |
|
||||
| Load Balancer | Traffic distribution | YES |
|
||||
| Prometheus | Metrics collection | NO |
|
||||
| Grafana | Monitoring dashboard | NO |
|
||||
|
||||
---
|
||||
|
||||
### 1.3 Core Principles
|
||||
|
||||
1. **Data Integrity First:** ความถูกต้องของข้อมูลต้องมาก่อนทุกอย่าง
|
||||
2. **Security by Design:** รักษาความปลอดภัยที่ทุกชั้น
|
||||
3. **Scalability:** รองรับการเติบโตในอนาคต
|
||||
4. **Resilience:** ทนทานต่อ Failure และ Recovery ได้รวดเร็ว
|
||||
5. **Observability:** ติดตามและวิเคราะห์สถานะระบบได้ง่าย
|
||||
|
||||
### 1.4 Architecture Style
|
||||
|
||||
- **Headless CMS Architecture:** แยก Frontend และ Backend เป็นอิสระ
|
||||
- **API-First:** Backend เป็น API Server ที่ Frontend หรือ Third-party สามารถเรียกใช้ได้
|
||||
- **Microservices-Ready:** ออกแบบเป็น Modular Architecture พร้อมแยกเป็น Microservices ในอนาคต
|
||||
|
||||
## 2. 🏢 Infrastructure & Deployment
|
||||
|
||||
### 2.1 Server Infrastructure
|
||||
|
||||
- **Server:** QNAP TS-473A
|
||||
- CPU: AMD Ryzen V1500B
|
||||
- RAM: 32GB
|
||||
- Storage: /share/dms-data
|
||||
- **IP Address:** 159.192.126.103
|
||||
- **Domain:** np-dms.work, <www.np-dms.work>
|
||||
- **Containerization:** Docker & Docker Compose via Container Station
|
||||
- **Development Environment:** VS Code/Cursor on Windows 11
|
||||
|
||||
### 2.2 Network Architecture
|
||||
|
||||
```mermaid
|
||||
graph TB
|
||||
Internet[Internet] --> NPM[Nginx Proxy Manager<br/>npm.np-dms.work]
|
||||
NPM --> Frontend[Next.js Frontend<br/>lcbp3.np-dms.work]
|
||||
NPM --> Backend[NestJS Backend<br/>backend.np-dms.work]
|
||||
NPM --> PMA[phpMyAdmin<br/>pma.np-dms.work]
|
||||
NPM --> N8N[n8n Workflow<br/>n8n.np-dms.work]
|
||||
NPM --> Gitea[Gitea Git<br/>git.np-dms.work]
|
||||
|
||||
Backend --> MariaDB[(MariaDB 11.8<br/>db.np-dms.work)]
|
||||
Backend --> Redis[(Redis Cache)]
|
||||
Backend --> Elastic[Elasticsearch]
|
||||
Backend --> Storage[File Storage<br/>/share/dms-data]
|
||||
|
||||
N8N --> Line[LINE Notify]
|
||||
Backend --> Email[Email Server]
|
||||
```
|
||||
|
||||
**Docker Network:**
|
||||
|
||||
- Network Name: `lcbp3`
|
||||
- ทุก Service เชื่อมต่อผ่าน Internal Docker Network เพื่อความปลอดภัย
|
||||
|
||||
### 2.3 Configuration Management
|
||||
|
||||
> [!WARNING] > **ข้อจำกัดสำคัญ:** QNAP Container Station ไม่รองรับการใช้ `.env` files ในการกำหนด Environment Variables
|
||||
|
||||
**Configuration Strategy:**
|
||||
|
||||
1. **Production/Staging:**
|
||||
|
||||
- ใช้ `docker-compose.yml` สำหรับกำหนด Environment Variables
|
||||
- ห้ามระบุ Sensitive Secrets (Password, Keys) ใน `docker-compose.yml` หลัก
|
||||
- ใช้ `docker-compose.override.yml` (gitignored) สำหรับ Secrets
|
||||
- พิจารณาใช้ Docker Secrets หรือ Hashicorp Vault
|
||||
|
||||
2. **Development:**
|
||||
|
||||
- ใช้ `docker-compose.override.yml` สำหรับ Local Secrets
|
||||
- ไฟล์หลักใส่ค่า Dummy/Placeholder
|
||||
|
||||
3. **Validation:**
|
||||
- ใช้ Joi/Zod validate Environment Variables ตอน App Start
|
||||
- Throw Error ทันทีหากขาด Variable สำคัญ
|
||||
|
||||
## 3. 🔧 Core Services
|
||||
|
||||
### 3.1 Service Overview
|
||||
|
||||
| Service | Application Name | Domain | Technology | Purpose |
|
||||
| :---------------- | :--------------- | :------------------ | :----------------------- | :-------------------------- |
|
||||
| **Frontend** | lcbp3-frontend | lcbp3.np-dms.work | Next.js 14+ (App Router) | Web Application UI |
|
||||
| **Backend** | lcbp3-backend | backend.np-dms.work | NestJS (TypeScript) | API Server & Business Logic |
|
||||
| **Database** | lcbp3-db | db.np-dms.work | MariaDB 11.8 | Primary Database |
|
||||
| **DB Management** | lcbp3-db | pma.np-dms.work | phpMyAdmin | Database Admin UI |
|
||||
| **Reverse Proxy** | lcbp3-npm | npm.np-dms.work | Nginx Proxy Manager | Reverse Proxy & SSL |
|
||||
| **Workflow** | lcbp3-n8n | n8n.np-dms.work | n8n | Workflow Automation |
|
||||
| **Git** | git | git.np-dms.work | Gitea | Self-hosted Git |
|
||||
| **Cache** | - | - | Redis | Caching & Locking |
|
||||
| **Search** | - | - | Elasticsearch | Full-text Search |
|
||||
|
||||
### 3.2 Frontend (Next.js)
|
||||
|
||||
**Stack:**
|
||||
|
||||
- **Framework:** Next.js 14+ with App Router
|
||||
- **Language:** TypeScript (ESM)
|
||||
- **Styling:** Tailwind CSS + PostCSS
|
||||
- **Components:** shadcn/ui
|
||||
- **State Management:**
|
||||
- Server State: TanStack Query (React Query)
|
||||
- Form State: React Hook Form + Zod
|
||||
- UI State: useState/useReducer
|
||||
|
||||
**Responsibilities:**
|
||||
|
||||
- Render Web UI สำหรับผู้ใช้
|
||||
- จัดการ User Interactions
|
||||
- เรียก Backend API
|
||||
- Client-side Validation
|
||||
- Responsive Design (Desktop + Mobile)
|
||||
|
||||
### 3.3 Backend (NestJS)
|
||||
|
||||
**Stack:**
|
||||
|
||||
- **Framework:** NestJS (Node.js + TypeScript)
|
||||
- **ORM:** TypeORM
|
||||
- **Authentication:** JWT + Passport
|
||||
- **Authorization:** CASL (RBAC)
|
||||
- **Validation:** class-validator + class-transformer
|
||||
- **Documentation:** Swagger/OpenAPI
|
||||
|
||||
**Responsibilities:**
|
||||
|
||||
- ให้บริการ RESTful API
|
||||
- Business Logic Processing
|
||||
- Authentication & Authorization
|
||||
- Data Validation
|
||||
- Database Operations
|
||||
- File Upload Handling (Two-Phase Storage)
|
||||
- Workflow Engine
|
||||
- Background Jobs (Notifications, Cleanup)
|
||||
|
||||
### 3.4 Database (MariaDB 11.8)
|
||||
|
||||
**Features:**
|
||||
|
||||
- **JSON Support:** จัดเก็บ `details` fields (Dynamic Schema)
|
||||
- **Virtual Columns:** Index JSON fields สำหรับ Performance
|
||||
- **Partitioning:** สำหรับ `audit_logs` และ `notifications`
|
||||
- **Optimistic Locking:** ใช้ `@VersionColumn()` ป้องกัน Race Condition
|
||||
|
||||
**Key Tables:**
|
||||
|
||||
- Users & Permissions: `users`, `roles`, `permissions`, `user_roles`
|
||||
- Projects: `projects`, `organizations`, `contracts`, `project_parties`
|
||||
- Documents: `correspondences`, `rfas`, `shop_drawings`, `contract_drawings`
|
||||
- Workflow: `workflow_definitions`, `workflow_instances`, `workflow_history`
|
||||
- Files: `attachments`, `correspondence_attachments`, etc.
|
||||
- Audit: `audit_logs`
|
||||
|
||||
### 3.5 Redis
|
||||
|
||||
**Use Cases:**
|
||||
|
||||
1. **Distributed Locking:** Document Numbering, Critical Operations
|
||||
2. **Session Caching:** User Permissions, Profile Data
|
||||
3. **Master Data Cache:** Roles, Permissions, Organizations (TTL: 1 hour)
|
||||
4. **Queue Management:** BullMQ for Background Jobs
|
||||
5. **Rate Limiting:** Track API Request Counts
|
||||
|
||||
### 3.6 Elasticsearch
|
||||
|
||||
**Use Cases:**
|
||||
|
||||
- **Full-text Search:** Search across Correspondence, RFA, Drawings
|
||||
- **Advanced Filtering:** Multi-criteria Search
|
||||
- **Aggregations:** Statistics และ Dashboard Data
|
||||
|
||||
**Indexing Strategy:**
|
||||
|
||||
- Index อัตโนมัติเมื่อ Create/Update เอกสาร
|
||||
- Async Indexing ผ่าน Queue (ไม่ Block Main Request)
|
||||
|
||||
## 4. 🧱 Backend Module Architecture
|
||||
|
||||
### 4.1 Modular Design
|
||||
|
||||
```mermaid
|
||||
graph TB
|
||||
subgraph "Core Modules"
|
||||
Common[CommonModule<br/>Shared Services]
|
||||
Auth[AuthModule<br/>JWT & Guards]
|
||||
User[UserModule<br/>User Management]
|
||||
end
|
||||
|
||||
subgraph "Business Modules"
|
||||
Project[ProjectModule<br/>Projects & Contracts]
|
||||
Corr[CorrespondenceModule<br/>Correspondences]
|
||||
RFA[RfaModule<br/>RFA Management]
|
||||
Drawing[DrawingModule<br/>Shop & Contract Drawings]
|
||||
Circ[CirculationModule<br/>Circulation Sheets]
|
||||
Trans[TransmittalModule<br/>Transmittals]
|
||||
end
|
||||
|
||||
subgraph "Supporting Modules"
|
||||
Workflow[WorkflowEngineModule<br/>Unified Workflow]
|
||||
Numbering[DocumentNumberingModule<br/>Auto Numbering]
|
||||
Search[SearchModule<br/>Elasticsearch]
|
||||
Master[MasterModule<br/>Master Data]
|
||||
JSON[JsonSchemaModule<br/>JSON Validation]
|
||||
end
|
||||
|
||||
Corr --> Workflow
|
||||
RFA --> Workflow
|
||||
Circ --> Workflow
|
||||
|
||||
Corr --> Numbering
|
||||
RFA --> Numbering
|
||||
|
||||
Search --> Corr
|
||||
Search --> RFA
|
||||
Search --> Drawing
|
||||
```
|
||||
|
||||
### 4.2 Module Descriptions
|
||||
|
||||
#### 4.2.1 CommonModule
|
||||
|
||||
**Responsibilities:**
|
||||
|
||||
- Database Configuration
|
||||
- FileStorageService (Two-Phase Upload)
|
||||
- AuditLogService
|
||||
- NotificationService
|
||||
- Shared DTOs, Guards, Interceptors
|
||||
|
||||
#### 4.2.2 AuthModule
|
||||
|
||||
**Responsibilities:**
|
||||
|
||||
- JWT Token Management
|
||||
- Authentication Guards
|
||||
- 4-Level Permission Checking:
|
||||
- Global (Superadmin)
|
||||
- Organization
|
||||
- Project
|
||||
- Contract
|
||||
- Token Refresh & Revocation
|
||||
|
||||
#### 4.2.3 UserModule
|
||||
|
||||
**Responsibilities:**
|
||||
|
||||
- User CRUD Operations
|
||||
- Role Assignment
|
||||
- Permission Management
|
||||
- User Profile Management
|
||||
|
||||
#### 4.2.4 ProjectModule
|
||||
|
||||
**Responsibilities:**
|
||||
|
||||
- Project Management
|
||||
- Contract Management
|
||||
- Organization Management
|
||||
- Project Parties & Contract Parties
|
||||
|
||||
#### 4.2.5 CorrespondenceModule
|
||||
|
||||
**Responsibilities:**
|
||||
|
||||
- Correspondence CRUD
|
||||
- Revision Management
|
||||
- Attachment Handling
|
||||
- Workflow Integration (Routing)
|
||||
|
||||
#### 4.2.6 RfaModule
|
||||
|
||||
**Responsibilities:**
|
||||
|
||||
- RFA CRUD
|
||||
- RFA Item Management
|
||||
- Workflow Integration (Approval Process)
|
||||
- Respond/Approve Actions
|
||||
|
||||
#### 4.2.7 DrawingModule
|
||||
|
||||
**Responsibilities:**
|
||||
|
||||
- Shop Drawing Management
|
||||
- Contract Drawing Management
|
||||
- Drawing Categories
|
||||
- Revision Tracking
|
||||
- Drawing References
|
||||
|
||||
#### 4.2.8 CirculationModule
|
||||
|
||||
**Responsibilities:**
|
||||
|
||||
- Circulation Sheet Management
|
||||
- Circulation Templates
|
||||
- Assignees Management
|
||||
- Workflow Integration (Internal Circulation)
|
||||
|
||||
#### 4.2.9 WorkflowEngineModule (Core)
|
||||
|
||||
> [!IMPORTANT] > **Unified Workflow Engine** - ระบบกลางสำหรับจัดการ Workflow ทั้งหมด
|
||||
|
||||
**Features:**
|
||||
|
||||
- DSL-Based Workflow Definitions (JSON Configuration)
|
||||
- State Machine Management
|
||||
- Workflow Instance Tracking
|
||||
- History & Audit Trail
|
||||
- Workflow Versioning
|
||||
|
||||
**Entities:**
|
||||
|
||||
- `WorkflowDefinition`: กำหนด Workflow Template
|
||||
- `WorkflowInstance`: Instance ที่กำลังรัน
|
||||
- `WorkflowHistory`: ประวัติการเปลี่ยน State
|
||||
|
||||
**Integration:**
|
||||
|
||||
- CorrespondenceModule → Routing Workflow
|
||||
- RfaModule → Approval Workflow
|
||||
- CirculationModule → Internal Circulation Workflow
|
||||
|
||||
#### 4.2.10 DocumentNumberingModule (Internal)
|
||||
|
||||
**Responsibilities:**
|
||||
|
||||
- Auto-generate Document Numbers
|
||||
- Token-Based Generator: `{CONTRACT}-{TYPE}-{DISCIPLINE}-{SEQ:4}`
|
||||
- **Double-Lock Mechanism:**
|
||||
- Layer 1: Redis Distributed Lock
|
||||
- Layer 2: Optimistic Database Lock (`@VersionColumn()`)
|
||||
|
||||
**Algorithm:**
|
||||
|
||||
1. Parse Template → Identify Required Tokens
|
||||
2. Acquire Redis Lock (Key: `project_id:type_id:discipline_id:year`)
|
||||
3. Query `document_number_counters` Table
|
||||
4. Increment Counter (Check Version)
|
||||
5. Generate Final Number
|
||||
6. Release Lock
|
||||
7. Retry on Conflict (Exponential Backoff)
|
||||
|
||||
#### 4.2.11 SearchModule
|
||||
|
||||
**Responsibilities:**
|
||||
|
||||
- Elasticsearch Integration
|
||||
- Full-text Search across Documents
|
||||
- Advanced Filtering
|
||||
- Search Result Aggregation
|
||||
|
||||
#### 4.2.12 JsonSchemaModule (Internal)
|
||||
|
||||
**Responsibilities:**
|
||||
|
||||
- JSON Schema Validation (AJV)
|
||||
- Schema Versioning & Migration
|
||||
- Dynamic Schema Generation
|
||||
- Data Transformation
|
||||
|
||||
## 5. 📊 Data Flow Architecture
|
||||
|
||||
### 5.1 Main Request Flow
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
participant Client as Client (Browser)
|
||||
participant NPM as Nginx Proxy
|
||||
participant BE as Backend (NestJS)
|
||||
participant Redis as Redis Cache
|
||||
participant DB as MariaDB
|
||||
participant ES as Elasticsearch
|
||||
|
||||
Client->>NPM: HTTP Request + JWT
|
||||
NPM->>BE: Forward Request
|
||||
|
||||
BE->>BE: Rate Limit Check
|
||||
BE->>BE: Validate Input (DTO)
|
||||
BE->>BE: JWT Auth Guard
|
||||
BE->>Redis: Get User Permissions
|
||||
Redis-->>BE: Permission Data
|
||||
BE->>BE: RBAC Guard (Check Permission)
|
||||
|
||||
BE->>DB: Query Data
|
||||
DB-->>BE: Return Data
|
||||
|
||||
BE->>BE: Business Logic Processing
|
||||
BE->>DB: Save Changes (Transaction)
|
||||
BE->>ES: Index for Search
|
||||
BE->>Redis: Invalidate Cache
|
||||
|
||||
BE->>DB: Audit Log
|
||||
BE-->>Client: JSON Response
|
||||
```
|
||||
|
||||
### 5.2 File Upload Flow (Two-Phase Storage)
|
||||
|
||||
> [!IMPORTANT] > **Two-Phase Storage** ป้องกัน Orphan Files และรักษา Data Integrity
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
participant Client
|
||||
participant Backend
|
||||
participant ClamAV as Virus Scanner
|
||||
participant TempStorage as Temp Storage
|
||||
participant PermStorage as Permanent Storage
|
||||
participant DB as Database
|
||||
|
||||
Client->>Backend: Upload File
|
||||
Backend->>ClamAV: Scan Virus
|
||||
ClamAV-->>Backend: Scan Result (CLEAN/INFECTED)
|
||||
|
||||
alt File is CLEAN
|
||||
Backend->>TempStorage: Save to temp/
|
||||
Backend-->>Client: Return temp_id
|
||||
|
||||
Client->>Backend: POST Create Document (include temp_id)
|
||||
Backend->>DB: BEGIN Transaction
|
||||
Backend->>DB: Create Document Record
|
||||
Backend->>PermStorage: Move temp/ → permanent/{YYYY}/{MM}/
|
||||
Backend->>DB: Create Attachment Record
|
||||
Backend->>DB: COMMIT Transaction
|
||||
Backend-->>Client: Success Response
|
||||
else File is INFECTED
|
||||
Backend-->>Client: Error: Virus Detected
|
||||
end
|
||||
|
||||
Note over Backend,TempStorage: Cron Job: Delete temp files > 24h
|
||||
```
|
||||
|
||||
### 5.3 Document Numbering Flow
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
participant Service as Correspondence Service
|
||||
participant Numbering as Numbering Service
|
||||
participant Redis
|
||||
participant DB as MariaDB
|
||||
|
||||
Service->>Numbering: generateNextNumber(context)
|
||||
Numbering->>Numbering: Parse Template
|
||||
Numbering->>Redis: ACQUIRE Lock (project:type:year)
|
||||
|
||||
alt Lock Acquired
|
||||
Redis-->>Numbering: Lock Success
|
||||
Numbering->>DB: SELECT counter (with version)
|
||||
DB-->>Numbering: current_number, version
|
||||
Numbering->>DB: UPDATE counter SET last_number = X, version = version + 1 WHERE version = old_version
|
||||
|
||||
alt Update Success
|
||||
DB-->>Numbering: Success
|
||||
Numbering->>Numbering: Generate Final Number
|
||||
Numbering->>Redis: RELEASE Lock
|
||||
Numbering-->>Service: Document Number
|
||||
else Version Conflict (Race Condition)
|
||||
DB-->>Numbering: Update Failed
|
||||
Numbering->>Redis: RELEASE Lock
|
||||
Numbering->>Numbering: Retry (Exponential Backoff)
|
||||
end
|
||||
else Lock Failed
|
||||
Redis-->>Numbering: Lock Timeout
|
||||
Numbering->>Numbering: Retry or Fail
|
||||
end
|
||||
```
|
||||
|
||||
### 5.4 Workflow Execution Flow
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
participant User
|
||||
participant Module as Correspondence Module
|
||||
participant Engine as Workflow Engine
|
||||
participant DB
|
||||
participant Notify as Notification Service
|
||||
|
||||
User->>Module: Create Correspondence
|
||||
Module->>Engine: createWorkflowInstance(definition_id, entity_id)
|
||||
Engine->>DB: Create workflow_instance
|
||||
Engine->>DB: Set initial state
|
||||
Engine-->>Module: Instance Created
|
||||
Module-->>User: Success
|
||||
|
||||
User->>Module: Execute Action (e.g., "Send")
|
||||
Module->>Engine: executeTransition(instance_id, action)
|
||||
Engine->>DB: Check current state
|
||||
Engine->>Engine: Validate transition (DSL)
|
||||
|
||||
alt Transition Valid
|
||||
Engine->>DB: Update state
|
||||
Engine->>DB: Create workflow_history
|
||||
Engine->>Notify: Trigger Notification
|
||||
Notify->>Notify: Queue Email/Line
|
||||
Engine-->>Module: Transition Success
|
||||
Module-->>User: Action Completed
|
||||
else Invalid Transition
|
||||
Engine-->>Module: Error: Invalid State Transition
|
||||
Module-->>User: Error Response
|
||||
end
|
||||
```
|
||||
|
||||
## 6. 🛡️ Security Architecture
|
||||
|
||||
### 6.1 Security Layers
|
||||
|
||||
```mermaid
|
||||
graph TB
|
||||
subgraph "Layer 1: Network Security"
|
||||
SSL[SSL/TLS<br/>Nginx Proxy Manager]
|
||||
Firewall[Firewall Rules<br/>QNAP]
|
||||
end
|
||||
|
||||
subgraph "Layer 2: Application Security"
|
||||
RateLimit[Rate Limiting]
|
||||
CSRF[CSRF Protection]
|
||||
XSS[XSS Prevention]
|
||||
Input[Input Validation]
|
||||
end
|
||||
|
||||
subgraph "Layer 3: Authentication"
|
||||
JWT[JWT Tokens]
|
||||
Refresh[Token Refresh]
|
||||
Revoke[Token Revocation]
|
||||
end
|
||||
|
||||
subgraph "Layer 4: Authorization"
|
||||
RBAC[4-Level RBAC]
|
||||
Guards[Permission Guards]
|
||||
CASL[CASL Rules]
|
||||
end
|
||||
|
||||
subgraph "Layer 5: Data Security"
|
||||
Encrypt[Data Encryption]
|
||||
Audit[Audit Logs]
|
||||
Backup[Backups]
|
||||
end
|
||||
|
||||
subgraph "Layer 6: File Security"
|
||||
Virus[Virus Scanning]
|
||||
FileType[Type Validation]
|
||||
FileAccess[Access Control]
|
||||
end
|
||||
```
|
||||
|
||||
### 6.2 Authentication & Authorization Details
|
||||
|
||||
**JWT Token Structure:**
|
||||
|
||||
```json
|
||||
{
|
||||
"sub": "user_id",
|
||||
"scope": "organization_id|project_id|contract_id",
|
||||
"iat": 1638360000,
|
||||
"exp": 1638388800
|
||||
}
|
||||
```
|
||||
|
||||
**Permission Checking Logic:**
|
||||
|
||||
1. Extract JWT from `Authorization: Bearer <token>`
|
||||
2. Validate Token (Signature, Expiration)
|
||||
3. Get User Permissions from Redis Cache (Key: `user:{user_id}:permissions`)
|
||||
4. Check Permission based on Context:
|
||||
- Global Permission (Superadmin)
|
||||
- Organization Permission
|
||||
- Project Permission (if in project context)
|
||||
- Contract Permission (if in contract context)
|
||||
5. Allow if **any level** grants permission (Most Permissive)
|
||||
|
||||
### 6.3 Rate Limiting
|
||||
|
||||
| Endpoint Category | Limit | Tracking |
|
||||
| :---------------- | :------------ | :--------- |
|
||||
| Anonymous | 100 req/hour | IP Address |
|
||||
| Authentication | 10 req/min | IP Address |
|
||||
| File Upload | 50 req/hour | User ID |
|
||||
| Search | 500 req/hour | User ID |
|
||||
| Viewer | 500 req/hour | User ID |
|
||||
| Editor | 1000 req/hour | User ID |
|
||||
| Document Control | 2000 req/hour | User ID |
|
||||
| Admin | 5000 req/hour | User ID |
|
||||
|
||||
**Implementation:** `rate-limiter-flexible` library with Redis backend
|
||||
|
||||
### 6.4 Input Validation
|
||||
|
||||
**Frontend (Client-Side):**
|
||||
|
||||
- React Hook Form + Zod Schema Validation
|
||||
- Sanitize User Inputs before Display
|
||||
|
||||
**Backend (Server-Side):**
|
||||
|
||||
- class-validator DTOs
|
||||
- Whitelist Validation (`@ValidateIf`, `@IsEnum`, etc.)
|
||||
- Transform Pipes
|
||||
|
||||
**File Upload Validation:**
|
||||
|
||||
1. **File Type Validation:**
|
||||
|
||||
- White-list: PDF, DWG, DOCX, XLSX, ZIP
|
||||
- Magic Number Verification (ไม่ใช่แค่ extension)
|
||||
|
||||
2. **File Size Validation:**
|
||||
|
||||
- Maximum: 50MB per file
|
||||
|
||||
3. **Virus Scanning:**
|
||||
- ClamAV Integration
|
||||
- Scan before saving to temp storage
|
||||
|
||||
### 6.5 OWASP Top 10 Protection
|
||||
|
||||
| Vulnerability | Protection Measure |
|
||||
| :-------------------------------- | :----------------------------------- |
|
||||
| SQL Injection | Parameterized Queries (TypeORM) |
|
||||
| XSS | Input Sanitization + Output Encoding |
|
||||
| CSRF | CSRF Tokens (State-changing ops) |
|
||||
| Broken Auth | JWT + Secure Token Management |
|
||||
| Security Misconfiguration | Security Headers (Helmet.js) |
|
||||
| Sensitive Data Exposure | Encryption + Secure Storage |
|
||||
| Insufficient Logging | Comprehensive Audit Logs |
|
||||
| Insecure Deserialization | Input Validation |
|
||||
| Using Known Vulnerable Components | Regular Dependency Updates |
|
||||
|
||||
## 7. 📈 Performance & Scalability
|
||||
|
||||
### 7.1 Caching Strategy
|
||||
|
||||
| Data Type | Cache Location | TTL | Invalidation |
|
||||
| :--------------- | :------------- | :------ | :------------------------ |
|
||||
| User Permissions | Redis | 30 min | On role/permission change |
|
||||
| Master Data | Redis | 1 hour | On update |
|
||||
| Search Results | Redis | 15 min | Time-based |
|
||||
| File Metadata | Redis | 1 hour | On file update |
|
||||
| Session Data | Redis | 8 hours | On logout |
|
||||
|
||||
### 7.2 Database Optimization
|
||||
|
||||
**Indexes:**
|
||||
|
||||
- Foreign Keys (Auto-indexed)
|
||||
- Search Columns (`idx_cor_project`, `idx_rfa_status`, etc.)
|
||||
- JSON Virtual Columns (for frequently queried JSON fields)
|
||||
|
||||
**Partitioning:**
|
||||
|
||||
- `audit_logs`: Partitioned by Year
|
||||
- `notifications`: Partitioned by Month
|
||||
- Automated Partition Creation (Cron Job)
|
||||
|
||||
**Query Optimization:**
|
||||
|
||||
- Use Views for Complex Queries (`v_current_correspondences`, `v_user_tasks`)
|
||||
- Pagination for Large Datasets
|
||||
- Eager/Lazy Loading Strategy
|
||||
|
||||
### 7.3 Performance Targets
|
||||
|
||||
| Metric | Target | Measurement |
|
||||
| :---------------------------------- | :------ | :------------- |
|
||||
| API Response Time (90th percentile) | < 200ms | Simple CRUD |
|
||||
| Search Query Performance | < 500ms | Complex Search |
|
||||
| File Upload Processing | < 30s | 50MB file |
|
||||
| Concurrent Users | 100+ | Simultaneous |
|
||||
| Cache Hit Ratio | > 80% | Master Data |
|
||||
| Application Startup | < 30s | Cold Start |
|
||||
|
||||
## 8. 🔄 Resilience & Error Handling
|
||||
|
||||
### 8.1 Resilience Patterns
|
||||
|
||||
**Circuit Breaker:**
|
||||
|
||||
- Applied to: Elasticsearch, Email Service, LINE Notify
|
||||
- Threshold: 5 failures in 1 minute
|
||||
- Timeout: 30 seconds
|
||||
- Recovery: Half-open after 1 minute
|
||||
|
||||
**Retry Mechanism:**
|
||||
|
||||
- Strategy: Exponential Backoff
|
||||
- Max Retries: 3
|
||||
- Applied to: External API Calls, Document Numbering
|
||||
|
||||
**Graceful Degradation:**
|
||||
|
||||
- Search Service Down → Return cached results or basic search
|
||||
- Email Service Down → Queue for later retry
|
||||
- LINE Notify Down → Log error, continue operation
|
||||
|
||||
### 8.2 Error Handling
|
||||
|
||||
**Backend:**
|
||||
|
||||
- Global Exception Filter
|
||||
- Structured Error Response Format
|
||||
- Error Logging with Context (Winston)
|
||||
- Don't Expose Internal Details in Error Messages
|
||||
|
||||
**Frontend:**
|
||||
|
||||
- Error Boundaries (React)
|
||||
- Toast Notifications
|
||||
- Fallback UI Components
|
||||
- Retry Mechanisms for Failed Requests
|
||||
|
||||
## 9. 📊 Monitoring & Observability
|
||||
|
||||
### 9.1 Health Checks
|
||||
|
||||
**Endpoints:**
|
||||
|
||||
```
|
||||
GET /health # Overall health
|
||||
GET /health/ready # Readiness probe
|
||||
GET /health/live # Liveness probe
|
||||
```
|
||||
|
||||
**Checks:**
|
||||
|
||||
- Database Connection
|
||||
- Redis Connection
|
||||
- Elasticsearch Connection
|
||||
- Disk Space
|
||||
- Memory Usage
|
||||
|
||||
### 9.2 Metrics Collection
|
||||
|
||||
**Application Metrics:**
|
||||
|
||||
- Request Rate (req/sec)
|
||||
- Response Time (p50, p90, p99)
|
||||
- Error Rate
|
||||
- Active Connections
|
||||
|
||||
**Business Metrics:**
|
||||
|
||||
- Documents Created per Day
|
||||
- Workflow Completion Rate
|
||||
- User Activity
|
||||
- Search Query Performance
|
||||
|
||||
**Infrastructure Metrics:**
|
||||
|
||||
- CPU Usage
|
||||
- Memory Usage
|
||||
- Disk I/O
|
||||
- Network Throughput
|
||||
|
||||
### 9.3 Logging Strategy
|
||||
|
||||
> [!WARNING] > **QNAP Storage Constraints:** ต้องจำกัดปริมาณ Logs
|
||||
|
||||
**Log Levels:**
|
||||
|
||||
- **Production:** WARN and ERROR only
|
||||
- **Staging:** INFO for critical business flows
|
||||
- **Development:** DEBUG allowed
|
||||
|
||||
**Structured Logging:**
|
||||
|
||||
```json
|
||||
{
|
||||
"timestamp": "2025-11-30T13:48:20Z",
|
||||
"level": "INFO",
|
||||
"service": "backend",
|
||||
"module": "CorrespondenceModule",
|
||||
"action": "create",
|
||||
"user_id": 1,
|
||||
"ip_address": "192.168.1.100",
|
||||
"duration_ms": 45,
|
||||
"message": "Correspondence created successfully"
|
||||
}
|
||||
```
|
||||
|
||||
**Log Rotation:**
|
||||
|
||||
- Rotate Daily
|
||||
- Keep 7 days
|
||||
- Compress Old Logs
|
||||
|
||||
### 9.4 Audit Logging
|
||||
|
||||
**Scope:**
|
||||
|
||||
- All CRUD Operations on Critical Data
|
||||
- Permission Changes
|
||||
- Login Attempts (Success/Failure)
|
||||
- File Downloads
|
||||
- Workflow State Changes
|
||||
|
||||
**Audit Log Fields:**
|
||||
|
||||
- `user_id`
|
||||
- `action` (e.g., `correspondence.create`)
|
||||
- `entity_type`, `entity_id`
|
||||
- `old_values`, `new_values` (for updates)
|
||||
- `ip_address`, `user_agent`
|
||||
- `timestamp`
|
||||
|
||||
## 10. 💾 Backup & Disaster Recovery
|
||||
|
||||
### 10.1 Backup Strategy
|
||||
|
||||
**Database Backup:**
|
||||
|
||||
- **Frequency:** Daily (Automated)
|
||||
- **Method:** Full Backup + Transaction Logs
|
||||
- **Retention:** 30 days
|
||||
- **Tool:** QNAP HBS 3 or mysqldump
|
||||
|
||||
**File Storage Backup:**
|
||||
|
||||
- **Frequency:** Daily
|
||||
- **Path:** `/share/dms-data`
|
||||
- **Method:** Incremental Backup
|
||||
- **Retention:** 30 days
|
||||
- **Tool:** QNAP Snapshot or rsync
|
||||
|
||||
### 10.2 Disaster Recovery
|
||||
|
||||
**Recovery Objectives:**
|
||||
|
||||
- **RTO (Recovery Time Objective):** < 4 hours
|
||||
- **RPO (Recovery Point Objective):** < 1 hour
|
||||
|
||||
**Recovery Procedures:**
|
||||
|
||||
1. **Database Restoration:**
|
||||
|
||||
- Restore latest full backup
|
||||
- Apply transaction logs to point-in-time
|
||||
- Verify data integrity
|
||||
|
||||
2. **File Storage Restoration:**
|
||||
|
||||
- Restore from QNAP snapshot
|
||||
- Verify file permissions
|
||||
|
||||
3. **Application Redeployment:**
|
||||
|
||||
- Deploy from known-good Docker images
|
||||
- Verify health checks
|
||||
|
||||
4. **Data Integrity Verification:**
|
||||
- Run consistency checks
|
||||
- Verify critical business data
|
||||
|
||||
## 11. 🏗️ Deployment Architecture
|
||||
|
||||
### 11.1 Container Deployment
|
||||
|
||||
**Docker Compose Services:**
|
||||
|
||||
```yaml
|
||||
services:
|
||||
frontend:
|
||||
image: lcbp3-frontend:latest
|
||||
networks: [lcbp3]
|
||||
depends_on: [backend]
|
||||
|
||||
backend:
|
||||
image: lcbp3-backend:latest
|
||||
networks: [lcbp3]
|
||||
depends_on: [mariadb, redis, elasticsearch]
|
||||
|
||||
mariadb:
|
||||
image: mariadb:10.11
|
||||
networks: [lcbp3]
|
||||
volumes: [/share/dms-data/mysql:/var/lib/mysql]
|
||||
|
||||
redis:
|
||||
image: redis:7-alpine
|
||||
networks: [lcbp3]
|
||||
|
||||
elasticsearch:
|
||||
image: elasticsearch:8.x
|
||||
networks: [lcbp3]
|
||||
|
||||
nginx-proxy-manager:
|
||||
image: jc21/nginx-proxy-manager:latest
|
||||
networks: [lcbp3]
|
||||
ports: [80:80, 443:443]
|
||||
```
|
||||
|
||||
### 11.2 CI/CD Pipeline (Future)
|
||||
|
||||
```mermaid
|
||||
graph LR
|
||||
Git[Gitea Repository] --> Build[Build & Test]
|
||||
Build --> StagingDeploy[Deploy to Staging]
|
||||
StagingDeploy --> Test[Run E2E Tests]
|
||||
Test --> Manual[Manual Approval]
|
||||
Manual --> ProdDeploy[Deploy to Production]
|
||||
ProdDeploy --> Monitor[Monitor & Alert]
|
||||
```
|
||||
|
||||
## 12.🎯 Future Enhancements
|
||||
|
||||
### 12.1 Scalability Improvements
|
||||
|
||||
- [ ] Separate into Microservices (when needed)
|
||||
- [ ] Add Load Balancer (HAProxy/Nginx)
|
||||
- [ ] Database Replication (Master-Slave)
|
||||
- [ ] Message Queue (RabbitMQ/Kafka) for async processing
|
||||
|
||||
### 12.2 Advanced Features
|
||||
|
||||
- [ ] AI-Powered Document Classification
|
||||
- [ ] Advanced Analytics & Reporting
|
||||
- [ ] Mobile Native Apps
|
||||
- [ ] Blockchain Integration for Document Integrity
|
||||
|
||||
### 12.3 Infrastructure Enhancements
|
||||
|
||||
- [ ] Multi-Region Deployment
|
||||
- [ ] CDN for Static Assets
|
||||
- [ ] Automated Failover
|
||||
- [ ] Blue-Green Deployment
|
||||
|
||||
---
|
||||
|
||||
**Document Control:**
|
||||
|
||||
- **Version:** 1.6.2
|
||||
- **Status:** Active
|
||||
- **Last Updated:** 2025-12-17
|
||||
- **Owner:** Nattanin Peancharoen
|
||||
|
||||
```
|
||||
|
||||
```
|
||||
95
specs/02-architecture/02-01-system-context.md
Normal file
95
specs/02-architecture/02-01-system-context.md
Normal file
@@ -0,0 +1,95 @@
|
||||
# 00.1 System Context & Architecture (สถาปัตยกรรมและบริบทของระบบ)
|
||||
|
||||
---
|
||||
|
||||
title: 'System Context & Architecture'
|
||||
version: 1.8.0
|
||||
status: first-draft
|
||||
owner: Nattanin Peancharoen
|
||||
last_updated: 2026-02-23
|
||||
related:
|
||||
- specs/01-requirements/01-objectives.md
|
||||
- specs/03-implementation/03-01-fullftack-js-v1.7.0.md
|
||||
|
||||
---
|
||||
|
||||
## 1. 📋 ภาพรวมระบบ (System Overview)
|
||||
ระบบ LCBP3-DMS (Laem Chabang Port Phase 3 - Document Management System) ถูกออกแบบด้วยสถาปัตยกรรมแบบ **Headless/API-First Architecture** โดยทำงานแบบ **On-Premise 100%** บนเครื่องเซอร์ฟเวอร์ QNAP และ ASUSTOR
|
||||
ระบบทั้งหมดทำงานอยู่ภายใต้สภาวะแวดล้อมแบบ **Container Isolation** (ผ่าน Container Station) เพื่อความปลอดภัย, ง่ายต่อการจัดการ, และไม่ยึดติดกับ Hardware (Hardware Agnostic)
|
||||
|
||||
### 1.1 Architecture Principles
|
||||
1. **Data Integrity First:** ความถูกต้องของข้อมูลต้องมาก่อนทุกอย่าง
|
||||
2. **Security by Design & Container Isolation:** รักษาความปลอดภัยที่ทุกชั้น และแยกส่วนการทำงานของแต่ละระบบอย่างชัดเจน (Network Segmentation & Containerization)
|
||||
3. **On-Premise First:** ข้อมูลและระบบงานทั้งหมดต้องอยู่ภายในเครือข่ายของโครงการเท่านั้น
|
||||
4. **Resilience:** ทนทานต่อ Failure และ Recovery ได้รวดเร็ว โดยมี Backup NAS
|
||||
5. **Observability:** ติดตามและวิเคราะห์สถานะระบบได้ง่าย
|
||||
|
||||
## 2. 🏢 มาตรฐานการติดตั้ง On-Premise (QNAP/ASUSTOR Installation Standards)
|
||||
|
||||
### 2.1 Hardware Infrastructure
|
||||
- **Primary Server (QNAP TS-473A):**
|
||||
- IP: 192.168.10.8 (VLAN 10)
|
||||
- Role: Primary NAS for DMS, Container Host
|
||||
- Storage: `/share/dms-data` (RAID 5 HDD + SSD Caching)
|
||||
- **Backup Server (ASUSTOR AS5304T):**
|
||||
- IP: 192.168.10.9 (VLAN 10)
|
||||
- Role: Backup / Secondary NAS
|
||||
- **Network Interface:** NAS ทั้งสองตัวใช้ LACP bonding แบบ IEEE 802.3ad เพื่อเพิ่ม bandwidth และ redundancy
|
||||
|
||||
### 2.2 Container Isolation & Environment
|
||||
อ้างอิงข้อจำกัดและมาตรฐานของระบบ Container Station บน QNAP:
|
||||
- **Containerization Engine:** Docker & Docker Compose
|
||||
- **Network Isolation:** ทุกคอนเทนเนอร์ (Frontend, Backend, Database, Redis, Search) จะต้องเชื่อมต่อกันผ่าน Internal Docker Network ชื่อ `lcbp3` เท่านั้น ห้าม Expose Port ออกสู่ภายนอกโดยไม่จำเป็น
|
||||
- **Reverse Proxy:** ใช้ Nginx Proxy Manager (`npm.np-dms.work`) เป็น Gateway (Load Balancer & SSL Termination) รับ Traffic เพียงจุดเดียวบน Port 80/443 และ Map เข้าสู่ Internal Docker Network
|
||||
- **Configuration Defaults:**
|
||||
- **ห้ามใช้ `.env` แบบ bind mount สำหรับ secret บน Production** ตามข้อจำกัดของ Container Station
|
||||
- การกำหนดตัวแปรให้ใช้ผ่าน `docker-compose.yml` สำหรับค่าทั่วไปใน Container
|
||||
- Sensitive Secrets ต้องใช้ `docker-compose.override.yml` ที่ไม่ถูกนำขึ้น Git หรือผ่าน Docker Secrets
|
||||
|
||||
## 3. 🌐 Network Segmentation & Security
|
||||
|
||||
ระบบจัดแบ่งเครือข่ายออกเป็น VLANs ต่างๆ เพื่อการควบคุมการเข้าถึงตามหลักการ Zero Trust ย่อยๆ โดยใช้อุปกรณ์เครือข่าย (ER7206 Router & SG2428P Core Switch) ในการบังคับใช้ ACL:
|
||||
|
||||
### 3.1 VLAN Configuration
|
||||
| VLAN ID | Name | Purpose | Subnet | Gateway | Notes |
|
||||
| ------- | ------ | ------------------ | --------------- | ------------ | ----------------------------------------- |
|
||||
| 10 | SERVER | Server & Storage | 192.168.10.0/24 | 192.168.10.1 | Servers (QNAP, ASUSTOR). Static IPs ONLY. |
|
||||
| 20 | MGMT | Management & Admin | 192.168.20.0/24 | 192.168.20.1 | Network devices, Admin PC. |
|
||||
| 30 | USER | User Devices | 192.168.30.0/24 | 192.168.30.1 | Staff PC, Printers. |
|
||||
| 40 | CCTV | Surveillance | 192.168.40.0/24 | 192.168.40.1 | Cameras, NVR. Isolated. |
|
||||
| 50 | VOICE | IP Phones | 192.168.50.0/24 | 192.168.50.1 | SIP traffic. Isolated. |
|
||||
| 60 | DMZ | Public Services | 192.168.60.0/24 | 192.168.60.1 | DMZ. Isolated from Internal. |
|
||||
| 70 | GUEST | Guest Wi-Fi | 192.168.70.0/24 | 192.168.70.1 | Isolated Internet Access only. |
|
||||
|
||||
### 3.2 Network ACL & Isolation Rules
|
||||
- **SERVER Isolation:** `VLAN 30 (USER)` สามารถเข้าถึง `VLAN 10 (SERVER)` ได้เฉพาะพอร์ตที่จำเป็น (HTTP/HTTPS/SSH)
|
||||
- **MGMT Restriction:** ไม่อนุญาตให้ `VLAN 30 (USER)` เข้าถึง `VLAN 20 (MGMT)` โดยเด็ดขาด
|
||||
- **Device Isolation:** `CCTV`, `VOICE`, และ `GUEST` แยกขาดออกจากวงอื่นๆ (Deny-All to Internal)
|
||||
- **Strict Default:** ใช้หลักการ Default Deny บน Gateway (ER7206)
|
||||
- **Container Level ACL:** ภายในวง `SERVER` Docker Network จะแยกออกไปอีกชั้น ทำให้ Host ในเครือข่าย `VLAN 10` ไม่สามารถเข้าถึง Database หรือ Redis ได้โดยตรง หากไม่ผ่าน Nginx Proxy Manager
|
||||
|
||||
## 4. 🧩 Core Services Architecture
|
||||
|
||||
| Service | Application Name | Domain | Technology | Purpose |
|
||||
| ------------ | ---------------- | ------------------- | --------------- | ------------------ |
|
||||
| **Frontend** | lcbp3-frontend | lcbp3.np-dms.work | Next.js 14+ | Web UI |
|
||||
| **Backend** | lcbp3-backend | backend.np-dms.work | NestJS | API Server & Logic |
|
||||
| **Database** | lcbp3-db | db.np-dms.work | MariaDB 11.8 | Primary Data |
|
||||
| **DB Admin** | lcbp3-db | pma.np-dms.work | phpMyAdmin | DB Administration |
|
||||
| **Proxy** | lcbp3-npm | npm.np-dms.work | Nginx Proxy Mgr | Gateway & SSL |
|
||||
| **Workflow** | lcbp3-n8n | n8n.np-dms.work | n8n | Process Automation |
|
||||
| **Git** | git | git.np-dms.work | Gitea | Code Repository |
|
||||
| **Cache** | - | - | Redis | Caching, Locking |
|
||||
| **Search** | - | - | Elasticsearch | Full-text Indexing |
|
||||
|
||||
## 5. 📊 Data Flow & Interactions
|
||||
1. **User Request:** ผู้ใช้งานส่ง Request ไปที่โดเมนผ่าน HTTP/HTTPS
|
||||
2. **Reverse Proxy:** Nginx Proxy Manager รับ Request, ตรวจสอบ SSL, และ Forward ไปให้ Frontend หรือ Backend ในวง Docker Network
|
||||
3. **API Processing:** Backend รัน Business Logic, ประมวลผล Authentication (JWT) และ Permissions (RBAC via Redis Cache)
|
||||
4. **Data Persistence:** Backend ติดต่อ MariaDB (Database) และ Elasticsearch (Search) ที่ถูกซ่อนไว้ในระบบปิด (Isolations)
|
||||
5. **Storage Process:** ไฟล์ถูกคัดกรองผ่าน ClamAV (ถ้ามี) และเก็บลง Storage `/share/dms-data` บน QNAP แบบ Two-Phase Storage (Temp -> Permanent) เพื่อป้องกัน Orphan Files
|
||||
|
||||
## 6. 💾 Backup & Disaster Recovery (DR)
|
||||
- **Database Backup:** ทำ Automated Backup รายวันด้วย QNAP HBS 3 หรือ mysqldump
|
||||
- **File Backup:** ทำ Snapshot หรือ rsync จาก `/share/dms-data` บนเครื่องหลัก (QNAP) ไปยังเครื่องสำรอง (ASUSTOR) อย่างสม่ำเสมอ
|
||||
- **Recovery Standard:** หาก NAS พัง สามารถ Restore Config ย้ายข้อมูล และรัน `docker-compose up` ขึ้นใหม่บนเครื่อง Backup ได้ทันที เนื่องจาก Architecture ออกแบบแบบ Stateless สำหรับตัวแอพพลิเคชั่น และ Data แยกลง Volume Storage ชัดเจน
|
||||
@@ -1,552 +0,0 @@
|
||||
# 🌐 API Design Specification
|
||||
|
||||
---
|
||||
|
||||
**title:** 'API Design'
|
||||
**version:** 1.7.0
|
||||
**status:** active
|
||||
**owner:** Nattanin Peancharoen
|
||||
**last_updated:** 2025-12-18
|
||||
**related:**
|
||||
|
||||
- specs/01-requirements/01-02-architecture.md
|
||||
- specs/02-architecture/02-01-system-architecture.md
|
||||
- specs/03-implementation/03-01-fullftack-js-v1.7.0.md
|
||||
|
||||
---
|
||||
|
||||
## 📋ภาพรวม (Overview)
|
||||
|
||||
เอกสารนี้กำหนดมาตรฐานการออกแบบ API สำหรับระบบ LCBP3-DMS โดยใช้แนวทาง **API-First Design** ที่เน้นความชัดเจน ความสอดคล้อง และความปลอดภัย
|
||||
|
||||
## 🎯 หลักการออกแบบ API (API Design Principles)
|
||||
|
||||
### 1.1 API-First Approach
|
||||
|
||||
- **ออกแบบ API ก่อนการ Implement:** ทำการออกแบบ API Endpoint และ Data Contract ให้ชัดเจนก่อนเริ่มเขียนโค้ด
|
||||
- **Documentation-Driven:** ใช้ OpenAPI/Swagger เป็นเอกสารอ้างอิงหลัก
|
||||
- **Contract Testing:** ทดสอบ API ตาม Contract ที่กำหนดไว้
|
||||
|
||||
### 1.2 RESTful Principles
|
||||
|
||||
- ใช้ HTTP Methods อย่างถูกต้อง: `GET`, `POST`, `PUT`, `PATCH`, `DELETE`
|
||||
- ใช้ HTTP Status Codes ที่เหมาะสม
|
||||
- Resource-Based URL Design
|
||||
- Stateless Communication
|
||||
|
||||
### 1.3 Consistency & Predictability
|
||||
|
||||
- **Naming Conventions:** ใช้ `kebab-case` สำหรับ URL paths
|
||||
- **Property Naming:** ใช้ `camelCase` สำหรับ JSON properties และ query parameters (สอดคล้องกับ TypeScript/JavaScript conventions)
|
||||
- **Database Columns:** Database ใช้ `snake_case` (mapped via TypeORM decorators)
|
||||
- **Versioning:** รองรับการ Version API ผ่าน URL path (`/api/v1/...`)
|
||||
|
||||
## 🔐 Authentication & Authorization
|
||||
|
||||
### 2.1 Authentication
|
||||
|
||||
- **JWT-Based Authentication:** ใช้ JSON Web Token สำหรับการยืนยันตัวตน
|
||||
- **Token Management:**
|
||||
- Access Token Expiration: 8 ชั่วโมง
|
||||
- Refresh Token Expiration: 7 วัน
|
||||
- Token Rotation: รองรับการหมุนเวียน Refresh Token
|
||||
- Token Revocation: บันทึก Revoked Tokens จนกว่าจะหมดอายุ
|
||||
|
||||
**Endpoints:**
|
||||
|
||||
```typescript
|
||||
POST / api / v1 / auth / login;
|
||||
POST / api / v1 / auth / logout;
|
||||
POST / api / v1 / auth / refresh;
|
||||
POST / api / v1 / auth / change - password;
|
||||
```
|
||||
|
||||
### 2.2 Authorization (RBAC)
|
||||
|
||||
- **4-Level Permission Hierarchy:**
|
||||
|
||||
1. **Global Level:** System-wide permissions (Superadmin)
|
||||
2. **Organization Level:** Organization-specific permissions
|
||||
3. **Project Level:** Project-specific permissions
|
||||
4. **Contract Level:** Contract-specific permissions
|
||||
|
||||
- **Permission Checking:** ใช้ Decorator `@RequirePermission('resource.action')`
|
||||
|
||||
**Example:**
|
||||
|
||||
```typescript
|
||||
@RequirePermission('correspondence.create')
|
||||
@Post('correspondences')
|
||||
async createCorrespondence(@Body() dto: CreateCorrespondenceDto) {
|
||||
// Implementation
|
||||
}
|
||||
```
|
||||
|
||||
### 2.3 Token Payload Optimization
|
||||
|
||||
- JWT Payload เก็บเฉพาะ `userId` และ `scope` ปัจจุบัน
|
||||
- **Permissions Caching:** เก็บ Permission List ใน Redis และดึงมาตรวจสอบเมื่อมี Request
|
||||
|
||||
## 📡 API Conventions
|
||||
|
||||
### 3.1 Base URL Structure
|
||||
|
||||
```
|
||||
https://backend.np-dms.work/api/v1/{resource}
|
||||
```
|
||||
|
||||
### 3.2 HTTP Methods & Usage
|
||||
|
||||
| Method | Usage | Idempotent | Example |
|
||||
| :------- | :--------------------------- | :--------- | :----------------------------------- |
|
||||
| `GET` | ดึงข้อมูล (Read) | ✅ Yes | `GET /api/v1/correspondences` |
|
||||
| `POST` | สร้างข้อมูลใหม่ (Create) | ❌ No\* | `POST /api/v1/correspondences` |
|
||||
| `PUT` | อัปเดตทั้งหมด (Full Update) | ✅ Yes | `PUT /api/v1/correspondences/:id` |
|
||||
| `PATCH` | อัปเดตบางส่วน (Partial Update) | ✅ Yes | `PATCH /api/v1/correspondences/:id` |
|
||||
| `DELETE` | ลบข้อมูล (Soft Delete) | ✅ Yes | `DELETE /api/v1/correspondences/:id` |
|
||||
|
||||
**Note:** `POST` เป็น Idempotent ได้เมื่อใช้ `Idempotency-Key` Header
|
||||
|
||||
### 3.3 HTTP Status Codes
|
||||
|
||||
| Status Code | Usage |
|
||||
| :-------------------------- | :----------------------------- |
|
||||
| `200 OK` | Request สำเร็จ (GET, PUT, PATCH) |
|
||||
| `201 Created` | สร้างข้อมูลสำเร็จ (POST) |
|
||||
| `204 No Content` | ลบสำเร็จ (DELETE) |
|
||||
| `400 Bad Request` | ข้อมูล Request ไม่ถูกต้อง |
|
||||
| `401 Unauthorized` | ไม่มี Token หรือ Token หมดอายุ |
|
||||
| `403 Forbidden` | ไม่มีสิทธิ์เข้าถึง |
|
||||
| `404 Not Found` | ไม่พบข้อมูล |
|
||||
| `409 Conflict` | ข้อมูลซ้ำ หรือ State Conflict |
|
||||
| `422 Unprocessable Entity` | Validation Error |
|
||||
| `429 Too Many Requests` | Rate Limit Exceeded |
|
||||
| `500 Internal Server Error` | Server Error |
|
||||
| `503 Service Unavailable` | Maintenance Mode |
|
||||
|
||||
### 3.4 Request & Response Format
|
||||
|
||||
**Request Headers:**
|
||||
|
||||
```http
|
||||
Content-Type: application/json
|
||||
Authorization: Bearer <access_token>
|
||||
Idempotency-Key: <uuid> # สำหรับ POST/PUT/DELETE
|
||||
```
|
||||
|
||||
**Success Response Format:**
|
||||
|
||||
```typescript
|
||||
{
|
||||
"success": true,
|
||||
"data": {
|
||||
// Resource data
|
||||
},
|
||||
"message": "Operation completed successfully"
|
||||
}
|
||||
```
|
||||
|
||||
**Error Response Format:**
|
||||
|
||||
```typescript
|
||||
{
|
||||
"success": false,
|
||||
"error": {
|
||||
"code": "VALIDATION_ERROR",
|
||||
"message": "Validation failed",
|
||||
"details": [
|
||||
{
|
||||
"field": "email",
|
||||
"message": "Invalid email format"
|
||||
}
|
||||
]
|
||||
},
|
||||
"timestamp": "2025-11-30T13:48:20Z",
|
||||
"path": "/api/v1/users"
|
||||
}
|
||||
```
|
||||
|
||||
## 🔄 Idempotency
|
||||
|
||||
### 4.1 Implementation
|
||||
|
||||
- **ทุก Critical Operation** (Create, Update, Delete) ต้องรองรับ Idempotency
|
||||
- Client ส่ง Header: `Idempotency-Key: <uuid>`
|
||||
- Server เช็คว่า Key นี้เคยประมวลผลสำเร็จแล้วหรือไม่
|
||||
- ถ้าเคยทำแล้ว: ส่งผลลัพธ์เดิมกลับไป (ไม่ทำซ้ำ)
|
||||
|
||||
**Example:**
|
||||
|
||||
```http
|
||||
POST /api/v1/correspondences
|
||||
Idempotency-Key: 550e8400-e29b-41d4-a716-446655440000
|
||||
Content-Type: application/json
|
||||
|
||||
{
|
||||
"title": "New Correspondence",
|
||||
"type_id": 1
|
||||
}
|
||||
```
|
||||
|
||||
## 📊 Pagination, Filtering & Sorting
|
||||
|
||||
### 5.1 Pagination (Server-Side)
|
||||
|
||||
**Query Parameters:**
|
||||
|
||||
```
|
||||
GET /api/v1/correspondences?page=1&page_size=20
|
||||
```
|
||||
|
||||
**Response:**
|
||||
|
||||
```typescript
|
||||
{
|
||||
"success": true,
|
||||
"data": [...],
|
||||
"meta": {
|
||||
"current_page": 1,
|
||||
"page_size": 20,
|
||||
"total_items": 150,
|
||||
"total_pages": 8,
|
||||
"has_next_page": true,
|
||||
"has_previous_page": false
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 5.2 Filtering
|
||||
|
||||
```
|
||||
GET /api/v1/correspondences?project_id=1&status=PENDING
|
||||
```
|
||||
|
||||
### 5.3 Sorting
|
||||
|
||||
```
|
||||
GET /api/v1/correspondences?sort=createdAt&order=desc
|
||||
```
|
||||
|
||||
### 5.4 Combined Example
|
||||
|
||||
```
|
||||
GET /api/v1/correspondences?project_id=1&status=PENDING&page=1&page_size=20&sort=createdAt&order=desc
|
||||
```
|
||||
|
||||
## 🛡️ Security Features
|
||||
|
||||
### 6.1 Rate Limiting
|
||||
|
||||
| Endpoint Type | Limit | Scope |
|
||||
| :------------------ | :----------------- | :---- |
|
||||
| Anonymous Endpoints | 100 requests/hour | IP |
|
||||
| Viewer | 500 requests/hour | User |
|
||||
| Editor | 1000 requests/hour | User |
|
||||
| Document Control | 2000 requests/hour | User |
|
||||
| Admin/Superadmin | 5000 requests/hour | User |
|
||||
| File Upload | 50 requests/hour | User |
|
||||
| Search | 500 requests/hour | User |
|
||||
| Authentication | 10 requests/minute | IP |
|
||||
|
||||
**Rate Limit Headers:**
|
||||
|
||||
```http
|
||||
X-RateLimit-Limit: 1000
|
||||
X-RateLimit-Remaining: 999
|
||||
X-RateLimit-Reset: 1638360000
|
||||
```
|
||||
|
||||
### 6.2 Input Validation
|
||||
|
||||
- **DTOs with Class Validator:** ทุก Request ต้องผ่าน Validation
|
||||
- **XSS Protection:** Input Sanitization
|
||||
- **SQL Injection Prevention:** ใช้ ORM (TypeORM) Parameterized Queries
|
||||
- **CSRF Protection:** CSRF Tokens สำหรับ State-Changing Operations
|
||||
|
||||
### 6.3 File Upload Security
|
||||
|
||||
**Endpoint:**
|
||||
|
||||
```
|
||||
POST /api/v1/files/upload
|
||||
```
|
||||
|
||||
**Security Measures:**
|
||||
|
||||
- **Virus Scanning:** ใช้ ClamAV scan ทุกไฟล์
|
||||
- **File Type Validation:** White-list (PDF, DWG, DOCX, XLSX, ZIP)
|
||||
- **File Size Limit:** 50MB per file
|
||||
- **Two-Phase Storage:**
|
||||
1. Upload to `temp/` folder
|
||||
2. Commit to `permanent/{YYYY}/{MM}/` when operation succeeds
|
||||
|
||||
**Response:**
|
||||
|
||||
```typescript
|
||||
{
|
||||
"success": true,
|
||||
"data": {
|
||||
"temp_id": "uuid",
|
||||
"filename": "document.pdf",
|
||||
"size": 1024000,
|
||||
"mime_type": "application/pdf",
|
||||
"scan_status": "CLEAN"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## 📦 Core Module APIs
|
||||
|
||||
### 7.1 Correspondence Module
|
||||
|
||||
**Base Path:** `/api/v1/correspondences`
|
||||
|
||||
| Method | Endpoint | Permission | Description |
|
||||
| :----- | :--------------------------------- | :---------------------- | :-------------------- |
|
||||
| GET | `/correspondences` | `correspondence.view` | รายการ Correspondence |
|
||||
| GET | `/correspondences/:id` | `correspondence.view` | รายละเอียด |
|
||||
| POST | `/correspondences` | `correspondence.create` | สร้างใหม่ |
|
||||
| PUT | `/correspondences/:id` | `correspondence.update` | อัปเดตทั้งหมด |
|
||||
| PATCH | `/correspondences/:id` | `correspondence.update` | อัปเดตบางส่วน |
|
||||
| DELETE | `/correspondences/:id` | `correspondence.delete` | ลบ (Soft Delete) |
|
||||
| POST | `/correspondences/:id/revisions` | `correspondence.update` | สร้าง Revision ใหม่ |
|
||||
| GET | `/correspondences/:id/revisions` | `correspondence.view` | ดู Revisions ทั้งหมด |
|
||||
| POST | `/correspondences/:id/attachments` | `correspondence.update` | เพิ่มไฟล์แนบ |
|
||||
|
||||
### 7.2 RFA Module
|
||||
|
||||
**Base Path:** `/api/v1/rfas`
|
||||
|
||||
| Method | Endpoint | Permission | Description |
|
||||
| :----- | :-------------------- | :------------- | :---------------- |
|
||||
| GET | `/rfas` | `rfas.view` | รายการ RFA |
|
||||
| GET | `/rfas/:id` | `rfas.view` | รายละเอียด |
|
||||
| POST | `/rfas` | `rfas.create` | สร้างใหม่ |
|
||||
| PUT | `/rfas/:id` | `rfas.update` | อัปเดต |
|
||||
| DELETE | `/rfas/:id` | `rfas.delete` | ลบ |
|
||||
| POST | `/rfas/:id/respond` | `rfas.respond` | ตอบกลับ RFA |
|
||||
| POST | `/rfas/:id/approve` | `rfas.approve` | อนุมัติ RFA |
|
||||
| POST | `/rfas/:id/revisions` | `rfas.update` | สร้าง Revision |
|
||||
| GET | `/rfas/:id/workflow` | `rfas.view` | ดู Workflow Status |
|
||||
|
||||
### 7.3 Drawing Module
|
||||
|
||||
**Base Path:** `/api/v1/drawings`
|
||||
|
||||
**Shop Drawings:**
|
||||
|
||||
| Method | Endpoint | Permission | Description |
|
||||
| :----- | :----------------------------- | :---------------- | :------------------ |
|
||||
| GET | `/shop-drawings` | `drawings.view` | รายการ Shop Drawing |
|
||||
| POST | `/shop-drawings` | `drawings.upload` | อัปโหลดใหม่ |
|
||||
| GET | `/shop-drawings/:id/revisions` | `drawings.view` | ดู Revisions |
|
||||
|
||||
**Contract Drawings:**
|
||||
|
||||
| Method | Endpoint | Permission | Description |
|
||||
| :----- | :------------------- | :---------------- | :---------------------- |
|
||||
| GET | `/contract-drawings` | `drawings.view` | รายการ Contract Drawing |
|
||||
| POST | `/contract-drawings` | `drawings.upload` | อัปโหลดใหม่ |
|
||||
|
||||
### 7.4 Project Module
|
||||
|
||||
**Base Path:** `/api/v1/projects`
|
||||
|
||||
| Method | Endpoint | Permission | Description |
|
||||
| :----- | :------------------------ | :----------------------- | :---------------- |
|
||||
| GET | `/projects` | `projects.view` | รายการโครงการ |
|
||||
| GET | `/projects/:id` | `projects.view` | รายละเอียด |
|
||||
| POST | `/projects` | `projects.create` | สร้างโครงการใหม่ |
|
||||
| PUT | `/projects/:id` | `projects.update` | อัปเดต |
|
||||
| POST | `/projects/:id/contracts` | `contracts.create` | สร้าง Contract |
|
||||
| GET | `/projects/:id/parties` | `projects.view` | ดู Project Parties |
|
||||
| POST | `/projects/:id/parties` | `project_parties.manage` | เพิ่ม Party |
|
||||
|
||||
### 7.5 User & Auth Module
|
||||
|
||||
**Base Path:** `/api/v1/users`, `/api/v1/auth`
|
||||
|
||||
**Authentication:**
|
||||
|
||||
```typescript
|
||||
POST / api / v1 / auth / login;
|
||||
POST / api / v1 / auth / logout;
|
||||
POST / api / v1 / auth / refresh;
|
||||
POST / api / v1 / auth / change - password;
|
||||
POST / api / v1 / auth / reset - password;
|
||||
```
|
||||
|
||||
**User Management:**
|
||||
|
||||
```typescript
|
||||
GET /api/v1/users # List users
|
||||
GET /api/v1/users/:id # User details
|
||||
POST /api/v1/users # Create user
|
||||
PUT /api/v1/users/:id # Update user
|
||||
DELETE /api/v1/users/:id # Delete user
|
||||
POST /api/v1/users/:id/roles # Assign roles
|
||||
GET /api/v1/users/me # Current user info
|
||||
GET /api/v1/users/me/permissions # Current user permissions
|
||||
```
|
||||
|
||||
### 7.6 Search Module
|
||||
|
||||
**Base Path:** `/api/v1/search`
|
||||
|
||||
```typescript
|
||||
GET /api/v1/search?q=<query>&type=<correspondence|rfa|drawing>&project_id=<id>
|
||||
```
|
||||
|
||||
**Response:**
|
||||
|
||||
```typescript
|
||||
{
|
||||
"success": true,
|
||||
"data": {
|
||||
"results": [...],
|
||||
"aggregations": {
|
||||
"by_type": { "correspondence": 10, "rfa": 5 },
|
||||
"by_status": { "PENDING": 8, "APPROVED": 7 }
|
||||
}
|
||||
},
|
||||
"meta": {
|
||||
"total": 15,
|
||||
"took_ms": 45
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## 🔔 Notification API
|
||||
|
||||
**Base Path:** `/api/v1/notifications`
|
||||
|
||||
```typescript
|
||||
GET /api/v1/notifications # List notifications
|
||||
GET /api/v1/notifications/:id # Notification details
|
||||
PATCH /api/v1/notifications/:id/read # Mark as read
|
||||
DELETE /api/v1/notifications/:id # Delete notification
|
||||
```
|
||||
|
||||
## 📈 Reporting & Export APIs
|
||||
|
||||
**Base Path:** `/api/v1/reports`
|
||||
|
||||
```typescript
|
||||
GET /api/v1/reports/correspondences?format=csv&project_id=1&from=2025-01-01&to=2025-12-31
|
||||
GET /api/v1/reports/rfas?format=excel&project_id=1
|
||||
GET /api/v1/reports/dashboard # Dashboard KPIs
|
||||
```
|
||||
|
||||
**Supported Formats:**
|
||||
|
||||
- `csv` - CSV file
|
||||
- `excel` - Excel file (.xlsx)
|
||||
- `pdf` - PDF file
|
||||
|
||||
## 🔧 Workflow Engine API
|
||||
|
||||
**Base Path:** `/api/v1/workflows`
|
||||
|
||||
```typescript
|
||||
GET /api/v1/workflows/definitions # List workflow definitions
|
||||
GET /api/v1/workflows/definitions/:id # Definition details
|
||||
POST /api/v1/workflows/instances # Create workflow instance
|
||||
GET /api/v1/workflows/instances/:id # Instance details
|
||||
POST /api/v1/workflows/instances/:id/transition # Execute transition
|
||||
GET /api/v1/workflows/instances/:id/history # View history
|
||||
```
|
||||
|
||||
## ⚡ Performance Optimization
|
||||
|
||||
### 11.1 Caching Strategy
|
||||
|
||||
**Cache Headers:**
|
||||
|
||||
```http
|
||||
Cache-Control: max-age=3600, private
|
||||
ETag: "33a64df551425fcc55e4d42a148795d9f25f89d4"
|
||||
```
|
||||
|
||||
**Cache TTL:**
|
||||
|
||||
- Master Data: 1 hour
|
||||
- User Sessions: 30 minutes
|
||||
- Search Results: 15 minutes
|
||||
- File Metadata: 1 hour
|
||||
|
||||
### 11.2 Response Compression
|
||||
|
||||
```http
|
||||
Accept-Encoding: gzip, deflate, br
|
||||
Content-Encoding: gzip
|
||||
```
|
||||
|
||||
## 🧪 Testing & Documentation
|
||||
|
||||
### 12.1 API Documentation
|
||||
|
||||
- **Swagger/OpenAPI:** Auto-generated จาก NestJS Decorators
|
||||
- **URL:** `https://backend.np-dms.work/api/docs`
|
||||
|
||||
### 12.2 Testing Strategy
|
||||
|
||||
- **Unit Tests:** Test individual controllers & services
|
||||
- **Integration Tests:** Test API endpoints with database
|
||||
- **E2E Tests:** Test complete user flows
|
||||
- **Contract Tests:** Verify API contracts
|
||||
|
||||
## 🚦 Health Check & Monitoring
|
||||
|
||||
```typescript
|
||||
GET /health # Health check endpoint
|
||||
GET /health/ready # Readiness probe
|
||||
GET /health/live # Liveness probe
|
||||
GET /metrics # Prometheus metrics
|
||||
```
|
||||
|
||||
**Response:**
|
||||
|
||||
```typescript
|
||||
{
|
||||
"status": "ok",
|
||||
"uptime": 86400,
|
||||
"checks": {
|
||||
"database": "ok",
|
||||
"redis": "ok",
|
||||
"elasticsearch": "ok"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## 📝 API Versioning Strategy
|
||||
|
||||
### 14.1 Versioning Approach
|
||||
|
||||
- **URL-Based Versioning:** `/api/v1/...`, `/api/v2/...`
|
||||
- **Backward Compatibility:** รองรับ API เวอร์ชันเก่าอย่างน้อย 1 เวอร์ชัน
|
||||
- **Deprecation Headers:**
|
||||
|
||||
```http
|
||||
X-API-Deprecation-Warning: This endpoint will be deprecated on 2026-01-01
|
||||
X-API-Deprecation-Info: https://docs.np-dms.work/migration/v2
|
||||
```
|
||||
|
||||
## 🎯 Best Practices Summary
|
||||
|
||||
1. **ใช้ DTOs สำหรับ Validation ทุก Request**
|
||||
2. **ส่ง Idempotency-Key สำหรับ Critical Operations**
|
||||
3. **ใช้ Proper HTTP Status Codes**
|
||||
4. **Implement Rate Limiting บน Client Side**
|
||||
5. **Handle Errors Gracefully**
|
||||
6. **Cache Frequently-Accessed Data**
|
||||
7. **Use Pagination สำหรับ Large Datasets**
|
||||
8. **Document ทุก Endpoint ด้วย Swagger**
|
||||
|
||||
---
|
||||
|
||||
**Document Control:**
|
||||
|
||||
- **Version:** 1.7.0
|
||||
- **Status:** Active
|
||||
- **Last Updated:** 2025-12-18
|
||||
- **Owner:** Nattanin Peancharoen
|
||||
125
specs/02-architecture/02-02-software-architecture.md
Normal file
125
specs/02-architecture/02-02-software-architecture.md
Normal file
@@ -0,0 +1,125 @@
|
||||
# 02.3 Software Architecture & Design (สถาปัตยกรรมซอฟต์แวร์และการออกแบบ)
|
||||
|
||||
---
|
||||
|
||||
title: 'Software Architecture'
|
||||
version: 1.8.0
|
||||
status: first-draft
|
||||
owner: Nattanin Peancharoen
|
||||
last_updated: 2026-02-23
|
||||
related:
|
||||
- specs/02-Architecture/00-01-system-context.md
|
||||
|
||||
---
|
||||
|
||||
## 1. 🧱 Backend Module Architecture (NestJS)
|
||||
|
||||
### 1.1 Modular Design
|
||||
```mermaid
|
||||
graph TB
|
||||
subgraph "Core Modules"
|
||||
Common[CommonModule<br/>Shared Services]
|
||||
Auth[AuthModule<br/>JWT & Guards]
|
||||
User[UserModule<br/>User Management]
|
||||
end
|
||||
|
||||
subgraph "Business Modules"
|
||||
Project[ProjectModule<br/>Projects & Contracts]
|
||||
Corr[CorrespondenceModule<br/>Correspondences]
|
||||
RFA[RfaModule<br/>RFA Management]
|
||||
Drawing[DrawingModule<br/>Shop & Contract Drawings]
|
||||
Circ[CirculationModule<br/>Circulation Sheets]
|
||||
end
|
||||
|
||||
subgraph "Supporting Modules"
|
||||
Workflow[WorkflowEngineModule<br/>Unified Workflow]
|
||||
Numbering[DocumentNumberingModule<br/>Auto Numbering]
|
||||
Search[SearchModule<br/>Elasticsearch]
|
||||
end
|
||||
|
||||
Corr --> Workflow
|
||||
RFA --> Workflow
|
||||
Circ --> Workflow
|
||||
|
||||
Corr --> Numbering
|
||||
RFA --> Numbering
|
||||
|
||||
Search --> Corr
|
||||
Search --> RFA
|
||||
Search --> Drawing
|
||||
```
|
||||
|
||||
### 1.2 Key Architectural Patterns
|
||||
|
||||
#### Unified Workflow Engine (DSL-Based)
|
||||
ระบบการเดินเอกสาร (Correspondence, RFA, Circulation) ใช้ Engine กลางเดียวกัน ผ่าน **Workflow DSL (JSON Configuration)**
|
||||
- **Separation of Concerns:** Modules เก็บเฉพาะข้อมูล (Data) ส่วน Flow/State ถูกจัดการโดย Engine
|
||||
- **Versioning:** อาศัย Workflow Definition Version ป้องกันความขัดแย้งของ State เมื่อมีการแก้ไข Flow
|
||||
|
||||
#### Double-Locking Mechanism (Auto Numbering)
|
||||
เพื่อป้องกัน Race Condition ในการขอเลขเอกสารพร้อมกัน:
|
||||
- **Layer 1:** Redis Distributed Lock (ล็อคการเข้าถึงในระดับ Server/Network)
|
||||
- **Layer 2:** Optimistic Database Lock ผ่าน `@VersionColumn()` (ป้องกันระดับ Data Record)
|
||||
|
||||
#### Idempotency
|
||||
ทุก API ที่แก้ไขสถานะจะต้องส่ง `Idempotency-Key` ป้องกันผู้ใช้กดยืนยันซ้ำสองรอบ
|
||||
|
||||
## 2. 📊 Data Flow & Processes
|
||||
|
||||
### 2.1 Main Request Flow
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
participant Client as Client
|
||||
participant NPM as Nginx Proxy
|
||||
participant BE as Backend (NestJS)
|
||||
participant Redis as Redis Cache
|
||||
participant DB as MariaDB
|
||||
|
||||
Client->>NPM: HTTP Request + JWT
|
||||
NPM->>BE: Forward Request
|
||||
|
||||
BE->>BE: Rate Limit Check & Validate Input
|
||||
BE->>Redis: Get User Permissions (RBAC Cache)
|
||||
Redis-->>BE: Permission Data
|
||||
BE->>BE: Verify Permission
|
||||
|
||||
BE->>DB: Process Logic & Save
|
||||
BE->>Redis: Invalidate affected Cache
|
||||
|
||||
BE-->>Client: JSON Response
|
||||
```
|
||||
|
||||
### 2.2 File Upload Flow (Two-Phase Storage)
|
||||
ใช้แบบ **Two-Phase** เพื่อลดความเสี่ยงเกิดไฟล์ขยะ (Orphan Files):
|
||||
1. **[Phase 1]:** Client อัปโหลดไฟล์ -> ตรวจ Virus -> วางไว้ที่โฟลเดอร์ `temp/` -> ส่ง `temp_id` กลับให้ Client
|
||||
2. **[Phase 2]:** Client สั่ง Create Document (แนบ `temp_id`) -> Backend บันทึกฐานข้อมูล -> ย้ายไฟล์จาก `temp/` ไปที่ `permanent/` -> สร้างตาราง Attachment -> Commit Transaction
|
||||
3. **[Cleanup Job]:** ครอนจ็อบตามลบไฟล์ที่ค้างอยู่ใน `temp/` เกิน 24 ชั่วโมง
|
||||
|
||||
## 3. 🛡️ Security Architecture
|
||||
|
||||
### 3.1 Rate Limiting (Redis-backed)
|
||||
- Anonymous: 100 req/hour
|
||||
- File Upload: 50 req/hour
|
||||
- Document Control: 2000 req/hour
|
||||
- Admin: 5000 req/hour
|
||||
|
||||
### 3.2 Authorization checking flow (CASL)
|
||||
1. ดึง JWT Token ตรวจสอบความถูกต้อง
|
||||
2. โหลด User Permissions จาก Redis (`user:{user_id}:permissions`)
|
||||
3. ตรวจสอบเงื่อนไขตาม Context:
|
||||
- Superadmin Override
|
||||
- Orgnization Level
|
||||
- Project Level
|
||||
- Contract Level
|
||||
4. พิจารณาอนุญาตหากระดับใดระดับหนึ่งอนุญาต (Most Permissive approach)
|
||||
|
||||
### 3.3 OWASP Top 10 Protections implemented
|
||||
- **SQL Injection:** Parameterized Queries via TypeORM
|
||||
- **XSS/CSRF:** Input Sanitization, CSRF Tokens
|
||||
- **Insecure File Upload:** Magic Number Validation (ไม่ใช่แค่ extension), ไวรัสสแกน, สิทธิเข้าถึงไฟล์ถูกห่อหุ้มด้วย Authorization endpoint เสมอ ไม่ปล่อย public link
|
||||
|
||||
## 4. 🔄 Resilience & Error Handling
|
||||
|
||||
- **Circuit Breaker:** ใช้งานครอบ API ภายนอก (Email, LINE Notify). หาก fail ติดต่อกัน 5 ครั้งใน 1 นาที ให้หยุดส่ง (Timeout 30s) แล้วใช้โหมด Half-open
|
||||
- **Retry Mechanism (Exponential Backoff):** สำหรับกระบวนการสำคัญชั่วคราว เช่น จังหวะล็อก Database ล้มเหลวตอน Generate Number
|
||||
- **Graceful Degradation:** หาก Search Engine ล่ม (Elasticsearch down), ระบบต้องสลับไปใช้ Database Query พื้นฐานชั่วคราวได้ หรือตัดฟีเจอร์บางส่วนโดยไม่กระทบ CRUD หลัก
|
||||
188
specs/02-architecture/02-03-network-design.md
Normal file
188
specs/02-architecture/02-03-network-design.md
Normal file
@@ -0,0 +1,188 @@
|
||||
# 02.4 Network Design & Security (การออกแบบเครือข่ายและความปลอดภัย)
|
||||
|
||||
---
|
||||
|
||||
title: 'Network Design & Security'
|
||||
version: 1.8.0
|
||||
status: first-draft
|
||||
owner: Nattanin Peancharoen
|
||||
last_updated: 2026-02-23
|
||||
related:
|
||||
- specs/02-Architecture/00-01-system-context.md
|
||||
- specs/02-Architecture/02-03-software-architecture.md
|
||||
|
||||
---
|
||||
|
||||
## 1. 🌐 Network Segmentation (VLANs) และหลักการ Zero Trust
|
||||
|
||||
ระบบ LCBP3-DMS จัดแบ่งเครือข่ายออกเป็นเครือข่ายย่อย (VLANs) เพื่อการควบคุมการเข้าถึง (Access Control) ตามหลักการ Zero Trust โดยใช้อุปกรณ์ Network ของ Omada (ER7206 Router & SG2428P Core Switch) และ Switch ต่างๆ ในเครือข่าย
|
||||
|
||||
| VLAN ID | Name | Purpose | Subnet | Gateway | Notes |
|
||||
| ------- | -------------- | ----------------------- | --------------- | ------------ | ---------------------------------------------------- |
|
||||
| 10 | SERVER | Server & Storage | 192.168.10.0/24 | 192.168.10.1 | Servers (QNAP, ASUSTOR). Static IPs ONLY. |
|
||||
| 20 | MGMT (Default) | Management & Admin | 192.168.20.0/24 | 192.168.20.1 | Network devices (ER7206, OC200, Switches), Admin PC. |
|
||||
| 30 | USER | User Devices | 192.168.30.0/24 | 192.168.30.1 | Staff PC, Notebooks, Wi-Fi. |
|
||||
| 40 | CCTV | Surveillance | 192.168.40.0/24 | 192.168.40.1 | Cameras, NVR. Isolated. |
|
||||
| 50 | VOICE | IP Phones | 192.168.50.0/24 | 192.168.50.1 | SIP traffic. Isolated. |
|
||||
| 60 | DMZ | Public Services | 192.168.60.0/24 | 192.168.60.1 | DMZ. Isolated from Internal. |
|
||||
| 70 | GUEST | Guest Wi-Fi (Untrusted) | 192.168.70.0/24 | 192.168.70.1 | Guest Wi-Fi. Isolated Internet Access only. |
|
||||
|
||||
## 2. 🔐 Security Zones และสิทธิการเข้าถึงของ Container
|
||||
|
||||
```mermaid
|
||||
flowchart TB
|
||||
subgraph PublicZone["🌐 PUBLIC ZONE"]
|
||||
direction LR
|
||||
NPM["NPM (Reverse Proxy)<br/>Ports: 80, 443"]
|
||||
SSL["SSL/TLS Termination"]
|
||||
end
|
||||
|
||||
subgraph AppZone["📱 APPLICATION ZONE (Docker Network 'lcbp3' on QNAP)"]
|
||||
direction LR
|
||||
Frontend["Next.js"]
|
||||
Backend["NestJS"]
|
||||
N8N["n8n"]
|
||||
Gitea["Gitea"]
|
||||
end
|
||||
|
||||
subgraph DataZone["💾 DATA ZONE (QNAP - Internal Only)"]
|
||||
direction LR
|
||||
MariaDB["MariaDB"]
|
||||
Redis["Redis"]
|
||||
ES["Elasticsearch"]
|
||||
end
|
||||
|
||||
subgraph InfraZone["🛠️ INFRASTRUCTURE ZONE (ASUSTOR)"]
|
||||
direction LR
|
||||
Backup["Backup Services"]
|
||||
Registry["Docker Registry"]
|
||||
Monitoring["Prometheus + Grafana"]
|
||||
Logs["Loki / Syslog"]
|
||||
end
|
||||
|
||||
PublicZone -->|HTTPS Only| AppZone
|
||||
AppZone -->|Internal API| DataZone
|
||||
DataZone -.->|Backup| InfraZone
|
||||
AppZone -.->|Metrics| InfraZone
|
||||
```
|
||||
|
||||
### 2.1 กฎเหล็ก: การเข้าถึงระบบฐานข้อมูล (Database Access Restriction)
|
||||
> [!CAUTION]
|
||||
> **MariaDB และ Redis ตั้งอยู่ใน DATA ZONE ภายใต้ Docker Network ภายในชื่อ `lcbp3` เท่านั้น**
|
||||
- **ห้าม Expose Port ออกสู่ Host โดยตรง:** `mariadb:3306` และ `redis:6379` จะต้องไม่ถูกเปิดสิทธิออกสู่ภายนอก Container Station
|
||||
- **การเข้าถึงจากระบบอื่น:** เฉพาะ Service ใน **APPLICATION ZONE** (เช่น NestJS Backend) และ Service อื่นบน Network `lcbp3` เท่านั้นที่จะสามารถเรียกใช้งาน Database ได้
|
||||
- **การจัดการโดย Admin:** หากผู้ดูแลระบบต้องการเข้าไปจัดการฐานข้อมูล จะต้องใช้งานผ่าน **phpMyAdmin** (`pma.np-dms.work`) ซึ่งถูกจำกัดสิทธิเข้าถึงผ่าน Nginx Proxy Manager อีกชั้น หรือผ่าน SSH Tunnel เข้าสู่เซิร์ฟเวอร์เท่านั้น
|
||||
|
||||
## 3. 🗺️ Network Topology & Switch Profiles
|
||||
|
||||
```mermaid
|
||||
graph TB
|
||||
subgraph Internet
|
||||
WAN[("🌐 Internet<br/>WAN")]
|
||||
end
|
||||
|
||||
subgraph Router["ER7206 Router"]
|
||||
R[("🔲 ER7206<br/>192.168.20.1")]
|
||||
end
|
||||
|
||||
subgraph CoreSwitch["SG2428P Core Switch"]
|
||||
CS[("🔲 SG2428P<br/>192.168.20.2")]
|
||||
end
|
||||
|
||||
subgraph ServerSwitch["AMPCOM 2.5G Switch"]
|
||||
SS[("🔲 AMPCOM<br/>192.168.20.3")]
|
||||
end
|
||||
|
||||
subgraph Servers["VLAN 10 - Servers"]
|
||||
QNAP[("💾 QNAP<br/>192.168.10.8")]
|
||||
ASUSTOR[("💾 ASUSTOR<br/>192.168.10.9")]
|
||||
end
|
||||
|
||||
subgraph AccessPoints["EAP610 x16"]
|
||||
AP[("📶 WiFi APs")]
|
||||
end
|
||||
|
||||
WAN --> R
|
||||
R -->|Port 3| CS
|
||||
CS -->|LAG Port 3-4| SS
|
||||
SS -->|Port 3-4 LACP| QNAP
|
||||
SS -->|Port 5-6 LACP| ASUSTOR
|
||||
CS -->|Port 5-20| AP
|
||||
```
|
||||
|
||||
### 3.1 Switch Profiles & Interfaces
|
||||
- **01_CORE_TRUNK:** Router & switch uplinks (Native: 20, Tagged: All)
|
||||
- **02_MGMT_ONLY:** Management only (Native: 20, Untagged: 20)
|
||||
- **03_SERVER_ACCESS:** QNAP / ASUSTOR (Native: 10, Untagged: 10)
|
||||
- **04_CCTV_ACCESS:** CCTV cameras (Native: 40, Untagged: 40)
|
||||
- **05_USER_ACCESS:** PC / Printer (Native: 30, Untagged: 30)
|
||||
- **06_AP_TRUNK:** EAP610 Access Points (Native: 20, Tagged: 30, 70)
|
||||
- **07_VOICE_ACCESS:** IP Phones (Native: 30, Tagged: 50, Untagged: 30)
|
||||
|
||||
### 3.2 NAS NIC Bonding Configuration
|
||||
| Device | Bonding Mode | Member Ports | VLAN Mode | Tagged VLAN | IP Address | Gateway | Notes |
|
||||
| ------- | ------------------- | ------------ | --------- | ----------- | --------------- | ------------ | ---------------------- |
|
||||
| QNAP | IEEE 802.3ad (LACP) | Adapter 1, 2 | Untagged | 10 (SERVER) | 192.168.10.8/24 | 192.168.10.1 | Primary NAS for DMS |
|
||||
| ASUSTOR | IEEE 802.3ad (LACP) | Port 1, 2 | Untagged | 10 (SERVER) | 192.168.10.9/24 | 192.168.10.1 | Backup / Secondary NAS |
|
||||
|
||||
## 4. 🔥 Firewall Rules (ACLs) & Port Forwarding
|
||||
|
||||
กฎของ Firewall จะถูกกำหนดบน Omada Controller และอุปกรณ์ Gateway (ER7206) ตามหลักการอนุญาตแค่สิ่งที่ต้องการ (Default Deny)
|
||||
|
||||
### 4.1 IP Groups & Port Groups (อ้างอิงบ่อย)
|
||||
**IP Groups:**
|
||||
- `Server`: 192.168.10.8, 192.168.10.9, 192.168.10.111
|
||||
- `Omada-Controller`: 192.168.20.250
|
||||
- `DHCP-Gateways`: 192.168.30.1, 192.168.70.1
|
||||
- `QNAP_Services`: 192.168.10.8
|
||||
- `Internal`: 192.168.10.0/24, 192.168.20.0/24, 192.168.30.0/24
|
||||
- `Blacklist`: (เพิ่ม IP ประสงค์ร้าย)
|
||||
|
||||
**Port Groups:**
|
||||
- `Web`: TCP 443, 8443, 80, 81, 2222
|
||||
- `Omada-Auth`: TCP 443, 8043, 8088, 8843, 29810-29814
|
||||
- `VoIP`: UDP 5060, 5061, 10000-20000 (SIP + RTP)
|
||||
- `DHCP`: UDP 67, 68
|
||||
|
||||
### 4.2 Switch ACL (สำหรับ Omada OC200)
|
||||
> ⚠️ **ลำดับความสำคัญ (Priority Level):** (1) Allow rules (DHCP, Auth) -> (2) Isolate/Deny rules -> (3) Allow specific services -> (4) Default Deny
|
||||
|
||||
| ลำดับ | Name | Policy | Source | Destination | Ports |
|
||||
| :--- | :------------------------ | :----- | :---------------- | :---------------------------- | :---------------------------------------- |
|
||||
| 1 | 01 Allow-User-DHCP | Allow | Network → VLAN 30 | IP → 192.168.30.1 | Port Group → DHCP |
|
||||
| 2 | 02 Allow-Guest-DHCP | Allow | Network → VLAN 70 | IP → 192.168.70.1 | Port Group → DHCP |
|
||||
| 3 | 03 Allow-WiFi-Auth | Allow | Network → VLAN 30 | IP Group → Omada-Controller | Port Group → Omada-Auth |
|
||||
| 4 | 04 Allow-Guest-WiFi-Auth | Allow | Network → VLAN 70 | IP Group → Omada-Controller | Port Group → Omada-Auth |
|
||||
| 5 | 05 Isolate-Guests | Deny | Network → VLAN 70 | Network → VLAN 10, 20, 30, 60 | All |
|
||||
| 6 | 06 Isolate-Servers | Deny | Network → VLAN 10 | Network → VLAN 30 (USER) | All |
|
||||
| 7 | 07 Block-User-to-Mgmt | Deny | Network → VLAN 30 | Network → VLAN 20 (MGMT) | All |
|
||||
| 8 | 08 Allow-User-to-Services | Allow | Network → VLAN 30 | IP → QNAP (192.168.10.8) | Port Group → Web (443,8443, 80, 81, 2222) |
|
||||
| 9 | 09 Allow-Voice-to-User | Allow | Network → VLAN 50 | Network → VLAN 30,50 | All |
|
||||
| 10 | 10 Allow-MGMT-to-All | Allow | Network → VLAN 20 | Any | All |
|
||||
| 11 | 11 Allow-Server-Internal | Allow | IP Group : Server | IP Group : Server | All |
|
||||
| 12 | 12 Allow-Server → CCTV | Allow | IP Group : Server | Network → VLAN 40 (CCTV) | All |
|
||||
| 13 | 100 (Default) | Deny | Any | Any | All |
|
||||
|
||||
### 4.3 Gateway ACL (สำหรับ ER7206)
|
||||
| ลำดับ | Name | Policy | Direction | PROTOCOLS | Source | Destination |
|
||||
| :--- | :---------------------- | :----- | :-------- | :-------- | :------------------- | :--------------------------- |
|
||||
| 1 | 01 Blacklist | Deny | [WAN2] IN | All | IP Group:Blacklist | IP Group:Internal |
|
||||
| 2 | 02 Geo | Permit | [WAN2] IN | All | Location Group:Allow | IP Group:Internal |
|
||||
| 3 | 03 Allow-Voice-Internet | Permit | LAN->WAN | UDP | Network → VLAN 50 | Any |
|
||||
| 4 | 04 Internal → Internet | Permit | LAN->WAN | All | IP Group:Internal | Domain Group:DomainGroup_Any |
|
||||
|
||||
### 4.4 Port Forwarding
|
||||
Traffic สาธารณะ (WAN) จะถูกเชื่อมต่อไปยัง Nginx Proxy Manager เพียงจุดเดียว
|
||||
- **Allow-NPM-HTTPS:** External Port 443 -> QNAP (192.168.10.8) Port 443 (TCP)
|
||||
- **Allow-NPM-HTTP (สำหรับ Let's Encrypt):** External Port 80 -> QNAP (192.168.10.8) Port 80 (TCP)
|
||||
|
||||
## 5. 📡 EAP ACL (Wireless Data Flow Rules)
|
||||
|
||||
ตั้งค่าสำหรับ Access Points ให้ป้องกันการ Broadcast ลดทอนกันเอง หรือรบกวนโซนอื่นๆ
|
||||
* **SSID: PSLCBP3 (Staff WiFi) - VLAN 30**
|
||||
- อนุญาต DNS, 192.168.10.0/24 (Servers), Printer, Internet
|
||||
- **บล็อค** การเข้าสู่ 192.168.20.0/24 (MGMT), 192.168.40.0/24 (CCTV), และ **Client Isolation (Client-2-Client Deny)**
|
||||
|
||||
* **SSID: GUEST (Guest WiFi) - VLAN 70**
|
||||
- อนุญาต DNS, Internet (HTTP/HTTPS)
|
||||
- **บล็อคเครือข่ายส่วนตัวทั้งหมด (RFC1918):** 10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16 และสั่ง **Client Isolation**
|
||||
339
specs/02-architecture/02-04-api-design.md
Normal file
339
specs/02-architecture/02-04-api-design.md
Normal file
@@ -0,0 +1,339 @@
|
||||
# 02.4 API Design & Error Handling (การออกแบบ API และการจัดการข้อผิดพลาด)
|
||||
|
||||
---
|
||||
|
||||
**title:** 'API Design & Error Handling'
|
||||
**version:** 1.8.0
|
||||
**status:** active
|
||||
**owner:** Nattanin Peancharoen
|
||||
**last_updated:** 2026-02-23
|
||||
**related:**
|
||||
- specs/02-Architecture/00-01-system-context.md
|
||||
- specs/02-Architecture/02-03-software-architecture.md
|
||||
- specs/03-Implementation/03-01-fullstack-js-v1.7.0.md
|
||||
|
||||
---
|
||||
|
||||
## 1. 📋 ภาพรวม (Overview)
|
||||
|
||||
เอกสารนี้กำหนดมาตรฐานการออกแบบ API สำหรับระบบ LCBP3-DMS โดยใช้แนวทาง **API-First Design** ที่เน้นความชัดเจน ความสอดคล้อง และความปลอดภัย รวมถึงกลยุทธ์การจัดการ Error (Error Handling) สำหรับ Backend (NestJS)
|
||||
|
||||
## 2. 🎯 หลักการออกแบบ API (API Design Principles)
|
||||
|
||||
### 2.1 API-First Approach
|
||||
- **ออกแบบ API ก่อนการ Implement:** ทำการออกแบบ API Endpoint และ Data Contract ให้ชัดเจนก่อนเริ่มเขียนโค้ด
|
||||
- **Documentation-Driven:** ใช้ OpenAPI/Swagger เป็นเอกสารอ้างอิงหลัก
|
||||
- **Contract Testing:** ทดสอบ API ตาม Contract ที่กำหนดไว้
|
||||
|
||||
### 2.2 RESTful Principles
|
||||
- ใช้ HTTP Methods อย่างถูกต้อง: `GET`, `POST`, `PUT`, `PATCH`, `DELETE`
|
||||
- ใช้ HTTP Status Codes ที่เหมาะสม
|
||||
- Resource-Based URL Design
|
||||
- Stateless Communication
|
||||
|
||||
### 2.3 Consistency & Predictability
|
||||
- **Naming Conventions:** ใช้ `kebab-case` สำหรับ URL paths
|
||||
- **Property Naming:** ใช้ `camelCase` สำหรับ JSON properties และ query parameters (สอดคล้องกับ TypeScript/JavaScript conventions)
|
||||
- **Database Columns:** Database ใช้ `snake_case` (mapped via TypeORM decorators)
|
||||
- **Versioning:** รองรับการ Version API ผ่าน URL path (`/api/v1/...`)
|
||||
|
||||
## 3. 🔐 Authentication & Authorization
|
||||
|
||||
### 3.1 Authentication
|
||||
- **JWT-Based Authentication:** ใช้ JSON Web Token สำหรับการยืนยันตัวตน
|
||||
- **Token Management:**
|
||||
- Access Token Expiration: 8 ชั่วโมง
|
||||
- Refresh Token Expiration: 7 วัน
|
||||
- Token Rotation: รองรับการหมุนเวียน Refresh Token
|
||||
- Token Revocation: บันทึก Revoked Tokens จนกว่าจะหมดอายุ
|
||||
|
||||
**Endpoints คอร์:**
|
||||
```typescript
|
||||
POST /api/v1/auth/login
|
||||
POST /api/v1/auth/logout
|
||||
POST /api/v1/auth/refresh
|
||||
POST /api/v1/auth/change-password
|
||||
```
|
||||
|
||||
### 3.2 Authorization (RBAC) (CASL)
|
||||
ใช้ระบบ 4-Level Permission Hierarchy (Global, Organization, Project, Contract)
|
||||
- **Permission Checking:** ใช้ Decorator `@RequirePermission('resource.action')`
|
||||
|
||||
**Example:**
|
||||
```typescript
|
||||
@RequirePermission('correspondence.create')
|
||||
@Post('correspondences')
|
||||
async createCorrespondence(@Body() dto: CreateCorrespondenceDto) {
|
||||
// Implementation
|
||||
}
|
||||
```
|
||||
|
||||
### 3.3 Token Payload Optimization
|
||||
- JWT Payload เก็บเฉพาะ `userId` และ `scope` ปัจจุบัน
|
||||
- **Permissions Caching:** เก็บ Permission List ใน Redis และดึงมาตรวจสอบเมื่อมี Request
|
||||
|
||||
## 4. 📡 API Conventions
|
||||
|
||||
### 4.1 Base URL Structure
|
||||
```
|
||||
https://backend.np-dms.work/api/v1/{resource}
|
||||
```
|
||||
|
||||
### 4.2 HTTP Methods & Usage
|
||||
| Method | Usage | Idempotent | Example |
|
||||
| :------- | :--------------------------- | :--------- | :----------------------------------- |
|
||||
| `GET` | ดึงข้อมูล (Read) | ✅ Yes | `GET /api/v1/correspondences` |
|
||||
| `POST` | สร้างข้อมูลใหม่ (Create) | ❌ No\* | `POST /api/v1/correspondences` |
|
||||
| `PUT` | อัปเดตทั้งหมด (Full Update) | ✅ Yes | `PUT /api/v1/correspondences/:id` |
|
||||
| `PATCH` | อัปเดตบางส่วน (Partial Update) | ✅ Yes | `PATCH /api/v1/correspondences/:id` |
|
||||
| `DELETE` | ลบข้อมูล (Soft Delete) | ✅ Yes | `DELETE /api/v1/correspondences/:id` |
|
||||
|
||||
* **Note:** `POST` เป็น Idempotent ได้เมื่อใช้ `Idempotency-Key` Header
|
||||
|
||||
### 4.3 Request Format
|
||||
**Request Headers:**
|
||||
```http
|
||||
Content-Type: application/json
|
||||
Authorization: Bearer <access_token>
|
||||
Idempotency-Key: <uuid> # สำหรับ POST/PUT/DELETE
|
||||
```
|
||||
|
||||
### 4.4 HTTP Status Codes
|
||||
| Status | Use Case |
|
||||
| ------------------------- | ------------------------------------------- |
|
||||
| 200 OK | Successful GET, PUT, PATCH |
|
||||
| 201 Created | Successful POST |
|
||||
| 204 No Content | Successful DELETE |
|
||||
| 400 Bad Request | Validation error, Invalid input |
|
||||
| 401 Unauthorized | Missing or invalid JWT token |
|
||||
| 403 Forbidden | Insufficient permissions (RBAC) |
|
||||
| 404 Not Found | Resource not found |
|
||||
| 409 Conflict | Duplicate resource, Business rule violation |
|
||||
| 422 Unprocessable Entity | Business logic error |
|
||||
| 429 Too Many Requests | Rate limit exceeded |
|
||||
| 500 Internal Server Error | Unexpected server error |
|
||||
|
||||
## 5. 🔄 Response Formats (Standard REST with Meta Data)
|
||||
|
||||
เราใช้ Standard REST with Custom Error Format ซึ่งเรียบง่ายและยืดหยุ่นกว่า
|
||||
|
||||
### 5.1 Success Response
|
||||
|
||||
**Single Resource:**
|
||||
```typescript
|
||||
{
|
||||
"data": {
|
||||
"id": 1,
|
||||
"document_number": "CORR-2024-0001",
|
||||
"subject": "...",
|
||||
},
|
||||
"meta": {
|
||||
"timestamp": "2024-01-01T00:00:00Z",
|
||||
"version": "1.0"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Collection (Pagination):**
|
||||
```typescript
|
||||
{
|
||||
"data": [
|
||||
{ "id": 1, ... },
|
||||
{ "id": 2, ... }
|
||||
],
|
||||
"meta": {
|
||||
"pagination": {
|
||||
"page": 1,
|
||||
"limit": 20,
|
||||
"total": 100,
|
||||
"totalPages": 5
|
||||
},
|
||||
"timestamp": "2024-01-01T00:00:00Z"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 5.2 Error Response Format
|
||||
```typescript
|
||||
{
|
||||
"error": {
|
||||
"code": "VALIDATION_ERROR",
|
||||
"message": "Validation failed on input data",
|
||||
"statusCode": 400,
|
||||
"timestamp": "2024-01-01T00:00:00Z",
|
||||
"path": "/api/correspondences",
|
||||
"details": [
|
||||
{
|
||||
"field": "subject",
|
||||
"message": "Subject is required",
|
||||
"value": null
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## 6. 🛠️ NestJS Implementation Details
|
||||
|
||||
### 6.1 Global Exception Filter
|
||||
คลาสจัดการ Error หลักที่จะจับและดัดแปลง Error ส่งคืน Client อย่างสม่ำเสมอ
|
||||
|
||||
```typescript
|
||||
// File: backend/src/common/filters/global-exception.filter.ts
|
||||
import { ExceptionFilter, Catch, ArgumentsHost, HttpException, HttpStatus } from '@nestjs/common';
|
||||
|
||||
@Catch()
|
||||
export class GlobalExceptionFilter implements ExceptionFilter {
|
||||
catch(exception: unknown, host: ArgumentsHost) {
|
||||
const ctx = host.switchToHttp();
|
||||
const response = ctx.getResponse();
|
||||
const request = ctx.getRequest();
|
||||
|
||||
let status = HttpStatus.INTERNAL_SERVER_ERROR;
|
||||
let code = 'INTERNAL_SERVER_ERROR';
|
||||
let message = 'An unexpected error occurred';
|
||||
let details = null;
|
||||
|
||||
if (exception instanceof HttpException) {
|
||||
status = exception.getStatus();
|
||||
const exceptionResponse = exception.getResponse();
|
||||
|
||||
if (typeof exceptionResponse === 'object') {
|
||||
code = (exceptionResponse as any).error || exception.name;
|
||||
message = (exceptionResponse as any).message || exception.message;
|
||||
details = (exceptionResponse as any).details;
|
||||
} else {
|
||||
message = exceptionResponse;
|
||||
}
|
||||
}
|
||||
|
||||
// Log error (but don't expose internal details to client)
|
||||
console.error('Exception:', exception);
|
||||
|
||||
response.status(status).json({
|
||||
error: {
|
||||
code,
|
||||
message,
|
||||
statusCode: status,
|
||||
timestamp: new Date().toISOString(),
|
||||
path: request.url,
|
||||
...(details && { details }),
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 6.2 Custom Business Exception
|
||||
สำหรับจัดการข้อผิดพลาดเชิงความสัมพันธ์ หรือเงื่อนไขธุรกิจ เช่น State Conflict.
|
||||
|
||||
```typescript
|
||||
// File: backend/src/common/exceptions/business.exception.ts
|
||||
export class BusinessException extends HttpException {
|
||||
constructor(message: string, code: string = 'BUSINESS_ERROR') {
|
||||
super(
|
||||
{
|
||||
error: code,
|
||||
message,
|
||||
},
|
||||
HttpStatus.UNPROCESSABLE_ENTITY
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// Usage Example:
|
||||
throw new BusinessException(
|
||||
'Cannot approve correspondence in current status',
|
||||
'INVALID_WORKFLOW_TRANSITION'
|
||||
);
|
||||
```
|
||||
|
||||
### 6.3 Validation Pipe Configuration
|
||||
บังคับ Validation Pipe ก่อนส่งพารามิเตอร์ให้กับ Controller
|
||||
|
||||
```typescript
|
||||
// File: backend/src/main.ts
|
||||
app.useGlobalPipes(
|
||||
new ValidationPipe({
|
||||
whitelist: true, // Strip properties not in DTO
|
||||
forbidNonWhitelisted: true, // Throw error if unknown properties
|
||||
transform: true, // Auto-transform payloads to DTO instances
|
||||
transformOptions: {
|
||||
enableImplicitConversion: true,
|
||||
},
|
||||
exceptionFactory: (errors) => {
|
||||
const details = errors.map((error) => ({
|
||||
field: error.property,
|
||||
message: Object.values(error.constraints || {}).join(', '),
|
||||
value: error.value,
|
||||
}));
|
||||
|
||||
return new HttpException(
|
||||
{
|
||||
error: 'VALIDATION_ERROR',
|
||||
message: 'Validation failed',
|
||||
details,
|
||||
},
|
||||
HttpStatus.BAD_REQUEST
|
||||
);
|
||||
},
|
||||
})
|
||||
);
|
||||
```
|
||||
|
||||
## 7. 🛡️ API Security & Rate Limiting
|
||||
|
||||
### 7.1 Rate Limiting (Redis-backed)
|
||||
|
||||
| Endpoint Type | Limit | Scope |
|
||||
| :------------------ | :----------------- | :---- |
|
||||
| Anonymous Endpoints | 100 requests/hour | IP |
|
||||
| Viewer | 500 requests/hour | User |
|
||||
| Editor | 1000 requests/hour | User |
|
||||
| Document Control | 2000 requests/hour | User |
|
||||
| Admin/Superadmin | 5000 requests/hour | User |
|
||||
| File Upload | 50 requests/hour | User |
|
||||
| Search | 500 requests/hour | User |
|
||||
| Authentication | 10 requests/minute | IP |
|
||||
|
||||
### 7.2 File Upload Security
|
||||
- **Virus Scanning:** ใช้ ClamAV scan ทุกไฟล์
|
||||
- **File Type Validation:** White-list (PDF, DWG, DOCX, XLSX, ZIP)
|
||||
- **File Size Limit:** 50MB per file
|
||||
- **Two-Phase Storage:**
|
||||
1. Upload to `temp/` folder
|
||||
2. Commit to `permanent/{YYYY}/{MM}/` when operation succeeds
|
||||
|
||||
## 8. 🔄 Idempotency
|
||||
|
||||
- **ทุก Critical Operation** (Create, Update, Delete) ต้องรองรับ Idempotency
|
||||
- Client ส่ง Header: `Idempotency-Key: <uuid>`
|
||||
- Server เช็คว่า Key นี้เคยประมวลผลสำเร็จแล้วหรือไม่
|
||||
- ถ้าเคยทำแล้ว: ส่งผลลัพธ์เดิมกลับไป (ไม่ทำซ้ำ)
|
||||
|
||||
## 9. 📈 Optimization & Additional Guidelines
|
||||
|
||||
### 9.1 Caching Strategy
|
||||
- Master Data: 1 hour
|
||||
- User Sessions: 30 minutes
|
||||
- Search Results: 15 minutes
|
||||
- File Metadata: 1 hour
|
||||
|
||||
### 9.2 API Versioning
|
||||
- **URL-Based Versioning:** `/api/v1/...`, `/api/v2/...`
|
||||
- **Backward Compatibility:** รองรับ API เวอร์ชันเก่าอย่างน้อย 1 เวอร์ชัน
|
||||
- ใช้ Deprecation Headers เมื่อมีการยกเลิก Endpoints
|
||||
|
||||
### 9.3 Documentation
|
||||
- **Swagger/OpenAPI:** Auto-generated จาก NestJS Decorators
|
||||
- **URL:** `https://backend.np-dms.work/api/docs`
|
||||
|
||||
## 🎯 สรุป Best Practices
|
||||
1. **ใช้ DTOs สำหรับ Validation ทุก Request**
|
||||
2. **ส่ง Idempotency-Key สำหรับ Critical Operations**
|
||||
3. **ใช้ Proper HTTP Status Codes**
|
||||
4. **Implement Rate Limiting บน Client Side**
|
||||
5. **Handle Errors Gracefully และอย่าเปิดเผยข้อผิดพลาดภายใน (DB Errors) สู่ Client**
|
||||
6. **Cache Frequently-Accessed Data**
|
||||
7. **Use Pagination สำหรับ Large Datasets เสมอ**
|
||||
8. **Document ทุก Endpoint ด้วย Swagger**
|
||||
@@ -1,149 +0,0 @@
|
||||
# การตั้งค่า Network Segmentation และ Firewall Rules
|
||||
|
||||
สำหรับอุปกรณ์ Omada (ER7206 + OC200) กลยุทธ์หลักคือการใช้ **VLANs (Virtual LANs)** เพื่อแบ่งกลุ่มอุปกรณ์ และใช้ **Firewall ACLs (Access Control Lists)** เพื่อควบคุมการจราจรระหว่างกลุ่มเหล่านั้น
|
||||
|
||||
นี่คือคำแนะนำตามแนวทาง "Zero Trust" ที่ปรับให้เข้ากับสถาปัตยกรรมของคุณครับ
|
||||
|
||||
---
|
||||
|
||||
## 1. 🌐 การแบ่งส่วนเครือข่าย (VLAN Segmentation)
|
||||
|
||||
ใน Omada Controller (OC200) ให้คุณไปที่ `Settings > Wired Networks > LAN` และสร้างเครือข่ายย่อย (VLANs) ดังนี้:
|
||||
|
||||
* **VLAN 10: SERVER**
|
||||
* **IP Range:** 192.168.10.x
|
||||
* **วัตถุประสงค์:** ใช้สำหรับอุปกรณ์ Server (QNAP และ ASUSTOR)
|
||||
|
||||
* **VLAN 20 MGMT(Default): Management**
|
||||
* **IP Range:** 192.168.20.x
|
||||
* **วัตถุประสงค์:** ใช้สำหรับอุปกรณ์ Network (ER7206, OC200, Switches) และ PC ของผู้ดูแลระบบ (Admin) เท่านั้น
|
||||
|
||||
* **VLAN 30: USER**
|
||||
* **IP Range:** 192.168.30.x
|
||||
* **วัตถุประสงค์:** สำหรับ PC, Notebook, และ Wi-Fi ของพนักงานทั่วไปที่ต้องเข้าใช้งานระบบ (เช่น `lcbp3.np-dms.work`)
|
||||
|
||||
* **VLAN 40: CCTV**
|
||||
* **IP Range:** 192.168.40.x
|
||||
* **วัตถุประสงค์:** ใช้สำหรับอุปกรณ์ CCTV เท่านั้น
|
||||
|
||||
* **VLAN 50 VOICEt**
|
||||
* **IP Range:** 192.168.50.x
|
||||
* **วัตถุประสงค์:** ใช้สำหรับอุปกรณ์ IP Phone เท่านั้น
|
||||
|
||||
* **VLAN 60 DMZ**
|
||||
* **IP Range:** 192.168.60.x
|
||||
* **วัตถุประสงค์:** ใช้สำหรับ Network DMZ เท่านั้น
|
||||
|
||||
* **VLAN 70: GUEST / Untrusted** (สำหรับ Wi-Fi แขก)
|
||||
* **IP Range:** 192.168.70.x
|
||||
* **วัตถุประสงค์:** สำหรับ Wi-Fi แขก (Guest) ห้ามเข้าถึงเครือข่ายภายในโดยเด็ดขาด
|
||||
|
||||
**การตั้งค่า Port Switch:**
|
||||
หลังจากสร้าง VLANs แล้ว ให้ไปที่ `Devices` > เลือก Switch ของคุณ > `Ports` > กำหนด Port Profile:
|
||||
|
||||
* Port ที่เสียบ QNAP NAS: ตั้งค่า Profile เป็น **VLAN 10**
|
||||
* Port ที่เสียบ PC พนักงาน: ตั้งค่า Profile เป็น **VLAN 20**
|
||||
|
||||
---
|
||||
|
||||
## 2. 🔥 Firewall Rules (ACLs)
|
||||
|
||||
นี่คือหัวใจสำคัญครับ ไปที่ `Settings > Network Security > ACL (Access Control)`
|
||||
|
||||
กฎของ Firewall จะทำงานจากบนลงล่าง (ข้อ 1 ทำก่อนข้อ 2)
|
||||
|
||||
---
|
||||
|
||||
### 2.1 IP Groups & Port Groups
|
||||
|
||||
**IP Groups** (สร้างใน `Settings > Network Security > Groups`):
|
||||
|
||||
| Group Name | Members |
|
||||
| :----------------- | :------------------------------------------------ |
|
||||
| `Server` | 192.168.10.8, 192.168.10.9, 192.168.10.111 |
|
||||
| `Omada-Controller` | 192.168.20.250 (OC200 IP) |
|
||||
| `DHCP-Gateways` | 192.168.30.1, 192.168.70.1 |
|
||||
| `QNAP_Services` | 192.168.10.8 |
|
||||
| `Internal` | 192.168.10.0/24, 192.168.20.0/24, 192.168.30.0/24 |
|
||||
| `Blacklist` | (Add malicious IPs as needed) |
|
||||
|
||||
**Port Groups**:
|
||||
|
||||
| Group Name | Ports |
|
||||
| :----------- | :-------------------------------------- |
|
||||
| `Web` | TCP 443, 8443, 80, 81, 2222 |
|
||||
| `Omada-Auth` | TCP 443, 8043, 8088, 8843, 29810-29814 |
|
||||
| `VoIP` | UDP 5060, 5061, 10000-20000 (SIP + RTP) |
|
||||
| `DHCP` | UDP 67, 68 |
|
||||
|
||||
---
|
||||
|
||||
### 2.2 Switch ACL (สำหรับ Omada OC200)
|
||||
|
||||
| ลำดับ | Name | Policy | Source | Destination | Ports |
|
||||
| :--- | :------------------------ | :----- | :---------------- | :---------------------------- | :---------------------------------------- |
|
||||
| 1 | 01 Allow-User-DHCP | Allow | Network → VLAN 30 | IP → 192.168.30.1 | Port Group → DHCP |
|
||||
| 2 | 02 Allow-Guest-DHCP | Allow | Network → VLAN 70 | IP → 192.168.70.1 | Port Group → DHCP |
|
||||
| 3 | 03 Allow-WiFi-Auth | Allow | Network → VLAN 30 | IP Group → Omada-Controller | Port Group → Omada-Auth |
|
||||
| 4 | 04 Allow-Guest-WiFi-Auth | Allow | Network → VLAN 70 | IP Group → Omada-Controller | Port Group → Omada-Auth |
|
||||
| 5 | 05 Isolate-Guests | Deny | Network → VLAN 70 | Network → VLAN 10, 20, 30, 60 | All |
|
||||
| 6 | 06 Isolate-Servers | Deny | Network → VLAN 10 | Network → VLAN 30 (USER) | All |
|
||||
| 7 | 07 Block-User-to-Mgmt | Deny | Network → VLAN 30 | Network → VLAN 20 (MGMT) | All |
|
||||
| 8 | 08 Allow-User-to-Services | Allow | Network → VLAN 30 | IP → QNAP (192.168.10.8) | Port Group → Web (443,8443, 80, 81, 2222) |
|
||||
| 9 | 09 Allow-Voice-to-User | Allow | Network → VLAN 50 | Network → VLAN 30,50 | All |
|
||||
| 10 | 10 Allow-MGMT-to-All | Allow | Network → VLAN 20 | Any | All |
|
||||
| 11 | 11 Allow-Server-Internal | Allow | IP Group : Server | IP Group : Server | All |
|
||||
| 12 | 12 Allow-Server → CCTV | Allow | IP Group : Server | Network → VLAN 40 (CCTV) | All |
|
||||
| 13 | 100 (Default) | Deny | Any | Any | All |
|
||||
|
||||
> ⚠️ **หมายเหตุสำคัญ - ลำดับ ACL:**
|
||||
> 1. **Allow rules ก่อน** - DHCP (#1-2) และ WiFi-Auth (#3-4) ต้องอยู่ **บนสุด**
|
||||
> 2. **Isolate/Deny rules ถัดมา** - (#5-7) block traffic ที่ไม่ต้องการ
|
||||
> 3. **Allow specific rules** - (#8-12) อนุญาต traffic ที่เหลือ
|
||||
> 4. **Default Deny ล่าสุด** - (#13) block ทุกอย่างที่ไม่ match
|
||||
|
||||
---
|
||||
|
||||
### 2.3 Gateway ACL (สำหรับ Omada ER7206)
|
||||
|
||||
| ลำดับ | Name | Policy | Direction | PROTOCOLS | Source | Destination |
|
||||
| :--- | :---------------------- | :----- | :-------- | :-------- | :------------------- | :--------------------------- |
|
||||
| 1 | 01 Blacklist | Deny | [WAN2] IN | All | IP Group:Blacklist | IP Group:Internal |
|
||||
| 2 | 02 Geo | Permit | [WAN2] IN | All | Location Group:Allow | IP Group:Internal |
|
||||
| 3 | 03 Allow-Voice-Internet | Permit | LAN->WAN | UDP | Network → VLAN 50 | Any |
|
||||
| 4 | 04 Internal → Internet | Permit | LAN->WAN | All | IP Group:Internal | Domain Group:DomainGroup_Any |
|
||||
|
||||
> 💡 **หมายเหตุ:** Rule #3 `Allow-Voice-Internet` อนุญาต IP Phone (VLAN 50) เชื่อมต่อ Cloud PBX ภายนอก ผ่าน Port Group → VoIP (UDP 5060, 5061, 10000-20000)
|
||||
|
||||
---
|
||||
|
||||
## 3. 🚪 Port Forwarding (การเปิด Service สู่สาธารณะ)
|
||||
|
||||
ส่วนนี้ไม่ใช่ Firewall ACL แต่จำเป็นเพื่อให้คนนอกเข้าใช้งานได้ครับ
|
||||
ไปที่ `Settings > Transmission > Port Forwarding`
|
||||
|
||||
สร้างกฎเพื่อส่งต่อการจราจรจาก WAN (อินเทอร์เน็ต) ไปยัง Nginx Proxy Manager (NPM) ที่อยู่บน QNAP (VLAN 10)
|
||||
|
||||
* **Name:** Allow-NPM-HTTPS
|
||||
* **External Port:** 443
|
||||
* **Internal Port:** 443
|
||||
* **Internal IP:** `192.168.10.8` (IP ของ QNAP)
|
||||
* **Protocol:** TCP
|
||||
|
||||
* **Name:** Allow-NPM-HTTP (สำหรับ Let's Encrypt)
|
||||
* **External Port:** 80
|
||||
* **Internal Port:** 80
|
||||
* **Internal IP:** `192.168.10.8` (IP ของ QNAP)
|
||||
* **Protocol:** TCP
|
||||
|
||||
### สรุปผังการเชื่อมต่อ
|
||||
|
||||
1. **ผู้ใช้ภายนอก** -> `https://lcbp3.np-dms.work`
|
||||
2. **ER7206** รับที่ Port 443
|
||||
3. **Port Forwarding** ส่งต่อไปยัง `192.168.10.8:443` (QNAP NPM)
|
||||
4. **NPM** (บน QNAP) ส่งต่อไปยัง `backend:3000` หรือ `frontend:3000` ภายใน Docker
|
||||
5. **ผู้ใช้ภายใน (Office)** -> `https://lcbp3.np-dms.work`
|
||||
6. **Firewall ACL** (กฎข้อ 4) อนุญาตให้ VLAN 30 คุยกับ `192.168.10.8:443`
|
||||
7. (ขั้นตอนที่ 3-4 ทำงานเหมือนเดิม)
|
||||
|
||||
การตั้งค่าตามนี้จะช่วยแยกส่วน Server ของคุณออกจากเครือข่ายพนักงานอย่างชัดเจน ซึ่งปลอดภัยกว่าการวางทุกอย่างไว้ในวง LAN เดียวกันมากครับ
|
||||
@@ -1,352 +0,0 @@
|
||||
# ADR-007: API Design & Error Handling Strategy
|
||||
|
||||
**Status:** ✅ Accepted
|
||||
**Date:** 2025-12-01
|
||||
**Decision Makers:** Backend Team, System Architect
|
||||
**Related Documents:** [Backend Guidelines](../03-implementation/03-02-backend-guidelines.md), [ADR-005: Technology Stack](./ADR-005-technology-stack.md)
|
||||
|
||||
---
|
||||
|
||||
## Context and Problem Statement
|
||||
|
||||
ระบบ LCBP3-DMS ต้องการมาตรฐานการออกแบบ API ที่ชัดเจนและสม่ำเสมอทั้งระบบ รวมถึงกลยุทธ์การจัดการ Error และ Validation ที่เหมาะสม
|
||||
|
||||
### ปัญหาที่ต้องแก้:
|
||||
|
||||
1. **API Consistency:** ทำอย่างไรให้ API response format สม่ำเสมอทั้งระบบ
|
||||
2. **Error Handling:** จัดการ error อย่างไรให้ client เข้าใจและแก้ไขได้
|
||||
3. **Validation:** Validate request อย่างไรให้ครอบคลุมและให้ feedback ที่ดี
|
||||
4. **Status Codes:** ใช้ HTTP status codes อย่างไรให้ถูกต้องและสม่ำเสมอ
|
||||
|
||||
---
|
||||
|
||||
## Decision Drivers
|
||||
|
||||
- 🎯 **Developer Experience:** Frontend developers ต้องใช้ API ได้ง่าย
|
||||
- 🔒 **Security:** ป้องกัน Information Leakage จาก Error messages
|
||||
- 📊 **Debuggability:** ต้องหา Root cause ของ Error ได้ง่าย
|
||||
- 🌍 **Internationalization:** รองรับภาษาไทยและอังกฤษ
|
||||
- 📝 **Standards Compliance:** ใช้มาตรฐานที่เป็นที่ยอมรับ (REST, JSON:API)
|
||||
|
||||
---
|
||||
|
||||
## Considered Options
|
||||
|
||||
### Option 1: Standard REST with Custom Error Format
|
||||
|
||||
**รูปแบบ:**
|
||||
|
||||
```typescript
|
||||
// Success
|
||||
{
|
||||
"data": { ... },
|
||||
"meta": { "timestamp": "..." }
|
||||
}
|
||||
|
||||
// Error
|
||||
{
|
||||
"error": {
|
||||
"code": "VALIDATION_ERROR",
|
||||
"message": "Validation failed",
|
||||
"details": [...]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Pros:**
|
||||
|
||||
- ✅ Simple และเข้าใจง่าย
|
||||
- ✅ Flexible สำหรับ Custom needs
|
||||
- ✅ ไม่ต้อง Follow spec ที่ซับซ้อน
|
||||
|
||||
**Cons:**
|
||||
|
||||
- ❌ ไม่มี Standard specification
|
||||
- ❌ ต้องสื่อสารภายในทีมให้ชัดเจน
|
||||
- ❌ อาจไม่สม่ำเสมอหากไม่ระวัง
|
||||
|
||||
### Option 2: JSON:API Specification
|
||||
|
||||
**รูปแบบ:**
|
||||
|
||||
```typescript
|
||||
{
|
||||
"data": {
|
||||
"type": "correspondences",
|
||||
"id": "1",
|
||||
"attributes": { ... },
|
||||
"relationships": { ... }
|
||||
},
|
||||
"included": [...]
|
||||
}
|
||||
```
|
||||
|
||||
**Pros:**
|
||||
|
||||
- ✅ มาตรฐานที่เป็นที่ยอมรับ
|
||||
- ✅ มี Libraries ช่วย
|
||||
- ✅ รองรับ Relationships ได้ดี
|
||||
|
||||
**Cons:**
|
||||
|
||||
- ❌ ซับซ้อนเกินความจำเป็น
|
||||
- ❌ Verbose (ข้อมูลซ้ำซ้อน)
|
||||
- ❌ Learning curve สูง
|
||||
|
||||
### Option 3: GraphQL
|
||||
|
||||
**Pros:**
|
||||
|
||||
- ✅ Client เลือกข้อมูลที่ต้องการได้
|
||||
- ✅ ลด Over-fetching/Under-fetching
|
||||
- ✅ Strong typing
|
||||
|
||||
**Cons:**
|
||||
|
||||
- ❌ Complexity สูง
|
||||
- ❌ Caching ยาก
|
||||
- ❌ ไม่เหมาะกับ Document-heavy system
|
||||
- ❌ Team ยังไม่มีประสบการณ์
|
||||
|
||||
---
|
||||
|
||||
## Decision Outcome
|
||||
|
||||
**Chosen Option:** **Option 1 - Standard REST with Custom Error Format + NestJS Exception Filters**
|
||||
|
||||
### Rationale
|
||||
|
||||
1. **Simplicity:** ทีมคุ้นเคยกับ REST API และ NestJS มี Built-in support ที่ดี
|
||||
2. **Flexibility:** สามารถปรับแต่งตาม Business needs ได้ง่าย
|
||||
3. **Performance:** Lightweight กว่า JSON:API และ GraphQL
|
||||
4. **Team Capability:** ทีมมีประสบการณ์ REST มากกว่า GraphQL
|
||||
|
||||
---
|
||||
|
||||
## Implementation Details
|
||||
|
||||
### 1. Success Response Format
|
||||
|
||||
```typescript
|
||||
// Single resource
|
||||
{
|
||||
"data": {
|
||||
"id": 1,
|
||||
"document_number": "CORR-2024-0001",
|
||||
"subject": "...",
|
||||
...
|
||||
},
|
||||
"meta": {
|
||||
"timestamp": "2024-01-01T00:00:00Z",
|
||||
"version": "1.0"
|
||||
}
|
||||
}
|
||||
|
||||
// Collection with pagination
|
||||
{
|
||||
"data": [
|
||||
{ "id": 1, ... },
|
||||
{ "id": 2, ... }
|
||||
],
|
||||
"meta": {
|
||||
"pagination": {
|
||||
"page": 1,
|
||||
"limit": 20,
|
||||
"total": 100,
|
||||
"totalPages": 5
|
||||
},
|
||||
"timestamp": "2024-01-01T00:00:00Z"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 2. Error Response Format
|
||||
|
||||
```typescript
|
||||
{
|
||||
"error": {
|
||||
"code": "VALIDATION_ERROR",
|
||||
"message": "Validation failed on input data",
|
||||
"statusCode": 400,
|
||||
"timestamp": "2024-01-01T00:00:00Z",
|
||||
"path": "/api/correspondences",
|
||||
"details": [
|
||||
{
|
||||
"field": "subject",
|
||||
"message": "Subject is required",
|
||||
"value": null
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 3. HTTP Status Codes
|
||||
|
||||
| Status | Use Case |
|
||||
| ------------------------- | ------------------------------------------- |
|
||||
| 200 OK | Successful GET, PUT, PATCH |
|
||||
| 201 Created | Successful POST |
|
||||
| 204 No Content | Successful DELETE |
|
||||
| 400 Bad Request | Validation error, Invalid input |
|
||||
| 401 Unauthorized | Missing or invalid JWT token |
|
||||
| 403 Forbidden | Insufficient permissions (RBAC) |
|
||||
| 404 Not Found | Resource not found |
|
||||
| 409 Conflict | Duplicate resource, Business rule violation |
|
||||
| 422 Unprocessable Entity | Business logic error |
|
||||
| 429 Too Many Requests | Rate limit exceeded |
|
||||
| 500 Internal Server Error | Unexpected server error |
|
||||
|
||||
### 4. Global Exception Filter
|
||||
|
||||
```typescript
|
||||
// File: backend/src/common/filters/global-exception.filter.ts
|
||||
import {
|
||||
ExceptionFilter,
|
||||
Catch,
|
||||
ArgumentsHost,
|
||||
HttpException,
|
||||
HttpStatus,
|
||||
} from '@nestjs/common';
|
||||
|
||||
@Catch()
|
||||
export class GlobalExceptionFilter implements ExceptionFilter {
|
||||
catch(exception: unknown, host: ArgumentsHost) {
|
||||
const ctx = host.switchToHttp();
|
||||
const response = ctx.getResponse();
|
||||
const request = ctx.getRequest();
|
||||
|
||||
let status = HttpStatus.INTERNAL_SERVER_ERROR;
|
||||
let code = 'INTERNAL_SERVER_ERROR';
|
||||
let message = 'An unexpected error occurred';
|
||||
let details = null;
|
||||
|
||||
if (exception instanceof HttpException) {
|
||||
status = exception.getStatus();
|
||||
const exceptionResponse = exception.getResponse();
|
||||
|
||||
if (typeof exceptionResponse === 'object') {
|
||||
code = (exceptionResponse as any).error || exception.name;
|
||||
message = (exceptionResponse as any).message || exception.message;
|
||||
details = (exceptionResponse as any).details;
|
||||
} else {
|
||||
message = exceptionResponse;
|
||||
}
|
||||
}
|
||||
|
||||
// Log error (but don't expose internal details to client)
|
||||
console.error('Exception:', exception);
|
||||
|
||||
response.status(status).json({
|
||||
error: {
|
||||
code,
|
||||
message,
|
||||
statusCode: status,
|
||||
timestamp: new Date().toISOString(),
|
||||
path: request.url,
|
||||
...(details && { details }),
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 5. Custom Business Exception
|
||||
|
||||
```typescript
|
||||
// File: backend/src/common/exceptions/business.exception.ts
|
||||
export class BusinessException extends HttpException {
|
||||
constructor(message: string, code: string = 'BUSINESS_ERROR') {
|
||||
super(
|
||||
{
|
||||
error: code,
|
||||
message,
|
||||
},
|
||||
HttpStatus.UNPROCESSABLE_ENTITY
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// Usage
|
||||
throw new BusinessException(
|
||||
'Cannot approve correspondence in current status',
|
||||
'INVALID_WORKFLOW_TRANSITION'
|
||||
);
|
||||
```
|
||||
|
||||
### 6. Validation Pipe Configuration
|
||||
|
||||
```typescript
|
||||
// File: backend/src/main.ts
|
||||
app.useGlobalPipes(
|
||||
new ValidationPipe({
|
||||
whitelist: true, // Strip properties not in DTO
|
||||
forbidNonWhitelisted: true, // Throw error if unknown properties
|
||||
transform: true, // Auto-transform payloads to DTO instances
|
||||
transformOptions: {
|
||||
enableImplicitConversion: true,
|
||||
},
|
||||
exceptionFactory: (errors) => {
|
||||
const details = errors.map((error) => ({
|
||||
field: error.property,
|
||||
message: Object.values(error.constraints || {}).join(', '),
|
||||
value: error.value,
|
||||
}));
|
||||
|
||||
return new HttpException(
|
||||
{
|
||||
error: 'VALIDATION_ERROR',
|
||||
message: 'Validation failed',
|
||||
details,
|
||||
},
|
||||
HttpStatus.BAD_REQUEST
|
||||
);
|
||||
},
|
||||
})
|
||||
);
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Consequences
|
||||
|
||||
### Positive Consequences
|
||||
|
||||
1. ✅ **Consistency:** API responses มีรูปแบบสม่ำเสมอทั้งระบบ
|
||||
2. ✅ **Developer Friendly:** Frontend developers ใช้งาน API ได้ง่าย
|
||||
3. ✅ **Debuggability:** Error messages ให้ข้อมูลเพียงพอสำหรับ Debug
|
||||
4. ✅ **Security:** ไม่เปิดเผย Internal error details ให้ Client
|
||||
5. ✅ **Maintainability:** ใช้ NestJS built-in features ทำให้ Maintain ง่าย
|
||||
|
||||
### Negative Consequences
|
||||
|
||||
1. ❌ **No Standard Spec:** ไม่ใช่ Standard เช่น JSON:API จึงต้องเขียน Documentation ชัดเจน
|
||||
2. ❌ **Manual Documentation:** ต้อง Document API response format เอง
|
||||
3. ❌ **Learning Curve:** Team members ใหม่ต้องเรียนรู้ Error code conventions
|
||||
|
||||
### Mitigation Strategies
|
||||
|
||||
- **Documentation:** ใช้ Swagger/OpenAPI เพื่อ Auto-generate API docs
|
||||
- **Code Generation:** Generate TypeScript interfaces สำหรับ Frontend จาก DTOs
|
||||
- **Error Code Registry:** มี Centralized list ของ Error codes พร้อมคำอธิบาย
|
||||
- **Testing:** เขียน Integration tests เพื่อ Validate response formats
|
||||
|
||||
---
|
||||
|
||||
## Related ADRs
|
||||
|
||||
- [ADR-005: Technology Stack](./ADR-005-technology-stack.md) - เลือกใช้ NestJS
|
||||
- [ADR-004: RBAC Implementation](./ADR-004-rbac-implementation.md) - Error 403 Forbidden
|
||||
|
||||
---
|
||||
|
||||
## References
|
||||
|
||||
- [NestJS Exception Filters](https://docs.nestjs.com/exception-filters)
|
||||
- [HTTP Status Codes](https://httpstatuses.com/)
|
||||
- [REST API Best Practices](https://restfulapi.net/)
|
||||
|
||||
---
|
||||
|
||||
**Last Updated:** 2025-12-01
|
||||
**Next Review:** 2025-06-01
|
||||
Reference in New Issue
Block a user