260223:1415 20260223 nextJS & nestJS Best pratices
All checks were successful
Build and Deploy / deploy (push) Successful in 4m44s

This commit is contained in:
admin
2026-02-23 14:15:06 +07:00
parent c90a664f53
commit ef16817f38
164 changed files with 24815 additions and 311 deletions

View File

@@ -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

View File

@@ -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
```
```

View 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 ชัดเจน

View File

@@ -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

View 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 หลัก

View 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**

View 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**

View File

@@ -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 เดียวกันมากครับ

View File

@@ -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