690517:1449 204 and 302 refactor #03
This commit is contained in:
@@ -175,81 +175,3 @@ services:
|
||||
backend:
|
||||
condition: service_healthy
|
||||
|
||||
# ----------------------------------------------------------------
|
||||
# 3. ClamAV (Antivirus scanning for file uploads — ADR-016)
|
||||
# Service Name: clamav (Backend อ้างอิง CLAMAV_HOST=clamav, port 3310)
|
||||
# ----------------------------------------------------------------
|
||||
clamav:
|
||||
<<: [*restart_policy, *default_logging]
|
||||
image: clamav/clamav:1.4.4
|
||||
container_name: clamav
|
||||
deploy:
|
||||
resources:
|
||||
limits:
|
||||
cpus: '1.0'
|
||||
memory: 2G
|
||||
reservations:
|
||||
cpus: '0.25'
|
||||
memory: 1G
|
||||
environment:
|
||||
CLAMAV_NO_LOG_FILE: 'true' # ปิดการเขียนไฟล์ clamd.log
|
||||
FRESHCLAM_NO_LOG_FILE: 'true' # ปิดการเขียนไฟล์ freshclam.log
|
||||
TZ: 'Asia/Bangkok'
|
||||
CLAMAV_NO_FRESHCLAMD: 'false'
|
||||
CLAMAV_NO_CLAMD: 'false'
|
||||
CLAMD_STARTUP_TIMEOUT: '1800'
|
||||
networks:
|
||||
- lcbp3
|
||||
volumes:
|
||||
# cache definitions เพื่อไม่ต้อง download ทุกครั้งที่ restart
|
||||
- '/share/np-dms/clamav/data:/var/lib/clamav'
|
||||
- '/share/np-dms/data/logs/clamav:/var/log/clamav'
|
||||
healthcheck:
|
||||
test: ['CMD', 'clamdcheck.sh']
|
||||
interval: 60s
|
||||
timeout: 30s
|
||||
retries: 3
|
||||
start_period: 300s
|
||||
|
||||
# ----------------------------------------------------------------
|
||||
# 4. Qdrant (Vector Database for RAG — ADR-023A)
|
||||
# Service Name: qdrant (Backend อ้างอิง QDRANT_HOST=qdrant, port 6333)
|
||||
# ----------------------------------------------------------------
|
||||
qdrant:
|
||||
<<: [*restart_policy, *default_logging]
|
||||
image: qdrant/qdrant:v1.7.0
|
||||
container_name: qdrant
|
||||
deploy:
|
||||
resources:
|
||||
limits:
|
||||
cpus: '1.0'
|
||||
memory: 2G
|
||||
reservations:
|
||||
cpus: '0.25'
|
||||
memory: 512M
|
||||
environment:
|
||||
TZ: 'Asia/Bangkok'
|
||||
QDRANT__SERVICE__GRPC_PORT: '6334'
|
||||
QDRANT__LOG_LEVEL: 'INFO'
|
||||
networks:
|
||||
- lcbp3
|
||||
ports:
|
||||
- '6333:6333' # HTTP API
|
||||
- '6334:6334' # gRPC API
|
||||
volumes:
|
||||
- '/share/np-dms/qdrant/storage:/qdrant/storage'
|
||||
healthcheck:
|
||||
test: ['CMD', 'curl', '-f', 'http://localhost:6333/health']
|
||||
interval: 30s
|
||||
timeout: 10s
|
||||
retries: 3
|
||||
start_period: 30s
|
||||
|
||||
# sudo chown -R 100:101 /share/np-dms/data/logs/clamav
|
||||
# sudo chmod -R 755 /share/np-dms/data/logs/climax
|
||||
|
||||
# sudo chown -R 100:101 /share/np-dms/clamav/data
|
||||
# sudo chmod -R 775 /share/np-dms/clamav/data
|
||||
|
||||
# sudo mkdir -p /share/np-dms/qdrant/storage
|
||||
# sudo chown -R 100:101 /share/np-dms/qdrant/storage
|
||||
|
||||
@@ -124,3 +124,83 @@ services:
|
||||
interval: 30s
|
||||
timeout: 10s
|
||||
retries: 5
|
||||
|
||||
|
||||
# ----------------------------------------------------------------
|
||||
# 3. ClamAV (Antivirus scanning for file uploads — ADR-016)
|
||||
# Service Name: clamav (Backend อ้างอิง CLAMAV_HOST=clamav, port 3310)
|
||||
# ----------------------------------------------------------------
|
||||
clamav:
|
||||
<<: [*restart_policy, *default_logging]
|
||||
image: clamav/clamav:1.4.4
|
||||
container_name: clamav
|
||||
deploy:
|
||||
resources:
|
||||
limits:
|
||||
cpus: '1.0'
|
||||
memory: 2G
|
||||
reservations:
|
||||
cpus: '0.25'
|
||||
memory: 1G
|
||||
environment:
|
||||
CLAMAV_NO_LOG_FILE: 'true' # ปิดการเขียนไฟล์ clamd.log
|
||||
FRESHCLAM_NO_LOG_FILE: 'true' # ปิดการเขียนไฟล์ freshclam.log
|
||||
TZ: 'Asia/Bangkok'
|
||||
CLAMAV_NO_FRESHCLAMD: 'false'
|
||||
CLAMAV_NO_CLAMD: 'false'
|
||||
CLAMD_STARTUP_TIMEOUT: '1800'
|
||||
networks:
|
||||
- lcbp3
|
||||
volumes:
|
||||
# cache definitions เพื่อไม่ต้อง download ทุกครั้งที่ restart
|
||||
- '/share/np-dms/clamav/data:/var/lib/clamav'
|
||||
- '/share/np-dms/data/logs/clamav:/var/log/clamav'
|
||||
healthcheck:
|
||||
test: ['CMD', 'clamdcheck.sh']
|
||||
interval: 60s
|
||||
timeout: 30s
|
||||
retries: 3
|
||||
start_period: 300s
|
||||
|
||||
# ----------------------------------------------------------------
|
||||
# 4. Qdrant (Vector Database for RAG — ADR-023A)
|
||||
# Service Name: qdrant (Backend อ้างอิง QDRANT_HOST=qdrant, port 6333)
|
||||
# ----------------------------------------------------------------
|
||||
qdrant:
|
||||
<<: [*restart_policy, *default_logging]
|
||||
image: qdrant/qdrant:v1.7.0
|
||||
container_name: qdrant
|
||||
deploy:
|
||||
resources:
|
||||
limits:
|
||||
cpus: '1.0'
|
||||
memory: 2G
|
||||
reservations:
|
||||
cpus: '0.25'
|
||||
memory: 512M
|
||||
environment:
|
||||
TZ: 'Asia/Bangkok'
|
||||
QDRANT__SERVICE__GRPC_PORT: '6334'
|
||||
QDRANT__LOG_LEVEL: 'INFO'
|
||||
networks:
|
||||
- lcbp3
|
||||
ports:
|
||||
- '6333:6333' # HTTP API
|
||||
- '6334:6334' # gRPC API
|
||||
volumes:
|
||||
- '/share/np-dms/qdrant/storage:/qdrant/storage'
|
||||
healthcheck:
|
||||
test: ['CMD', 'curl', '-f', 'http://localhost:6333/health']
|
||||
interval: 30s
|
||||
timeout: 10s
|
||||
retries: 3
|
||||
start_period: 30s
|
||||
|
||||
# sudo chown -R 100:101 /share/np-dms/data/logs/clamav
|
||||
# sudo chmod -R 755 /share/np-dms/data/logs/climax
|
||||
|
||||
# sudo chown -R 100:101 /share/np-dms/clamav/data
|
||||
# sudo chmod -R 775 /share/np-dms/clamav/data
|
||||
|
||||
# sudo mkdir -p /share/np-dms/qdrant/storage
|
||||
# sudo chown -R 100:101 /share/np-dms/qdrant/storage
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
# 04.1 Infrastructure Setup & Docker Compose
|
||||
|
||||
**Project:** LCBP3-DMS
|
||||
**Version:** 1.8.0
|
||||
**Version:** 1.9.0
|
||||
**Status:** Active
|
||||
**Owner:** Nattanin Peancharoen / DevOps Team
|
||||
**Last Updated:** 2026-02-23
|
||||
**Owner:** Nattanin Peancharoen (System Architect / Release Manager / Product Owner)
|
||||
**Last Updated:** 2026-05-16
|
||||
|
||||
> 📍 **Primary Server:** QNAP TS-473A (Application & Database)
|
||||
> 💾 **Backup Server:** ASUSTOR AS5403T (Infrastructure & Backup)
|
||||
@@ -17,11 +17,73 @@ This document serves as the authoritative guide for the environment setup, Docke
|
||||
|
||||
---
|
||||
|
||||
# Infrastructure Distribution
|
||||
|
||||
> 📍 **Multi-Server Architecture:** LCBP3-DMS ใช้ distributed infrastructure แบ่งการทำงานระหว่าง 2 เครื่องหลัก
|
||||
|
||||
## Server Roles
|
||||
|
||||
| Server | IP Address | Role | Services |
|
||||
|--------|------------|------|----------|
|
||||
| **QNAP TS-473A** | 192.168.10.8 | Primary Application & Database Server | Backend, Frontend, MariaDB, Redis (cache), Elasticsearch (search), Qdrant, ClamAV, Monitoring Exporters |
|
||||
| **ASUSTOR AS5403T** | 192.168.10.9 | Infrastructure & Backup Server | Prometheus, Grafana, Gitea Runner |
|
||||
|
||||
## Service Distribution
|
||||
|
||||
### QNAP (192.168.10.8)
|
||||
|
||||
**Application Stack** (`/share/np-dms/app/docker-compose-app.yml`):
|
||||
- `backend` - NestJS API
|
||||
- `frontend` - Next.js Web App
|
||||
- `qdrant` - Vector Database (ADR-023A)
|
||||
- `clamav` - Antivirus Scanner (ADR-016)
|
||||
|
||||
**Infrastructure Services** (`/share/np-dms/services/docker-compose.yml`):
|
||||
- `cache` - Redis (Caching + Distributed Lock + BullMQ queues)
|
||||
- `search` - Elasticsearch (Full-text Search)\n- `qdrant` - Vector Database for RAG (ADR-023A)\n- `clamav` - Antivirus Scanner (ADR-016)
|
||||
|
||||
**Database Stack** (`/share/np-dms/mariadb/docker-compose.yml`):
|
||||
- `mariadb` - MariaDB Database
|
||||
- `pma` - phpMyAdmin (via NPM proxy only)\n\n**MariaDB Configuration:**\n- Config file location: `/share/np-dms/mariadb/my.cnf` (mount to `/etc/mysql/conf.d/my.cnf:ro`)\n- Backup directory: `/share/dms-data/mariadb/backup`
|
||||
|
||||
**Monitoring Exporters** (`/share/np-dms/monitoring/docker-compose.yml`):
|
||||
- `node-exporter` - System metrics
|
||||
- `cadvisor` - Container metrics
|
||||
- `mysqld-exporter` - Database metrics
|
||||
|
||||
**Supporting Services**:
|
||||
- NPM (Nginx Proxy Manager) - Reverse Proxy & SSL
|
||||
- Gitea - Git Repository
|
||||
- n8n - Workflow Automation
|
||||
- RocketChat - Team Communication
|
||||
|
||||
### ASUSTOR (192.168.10.9)
|
||||
|
||||
**Monitoring Stack**:
|
||||
- `prometheus` - Metrics Collection & Alerting
|
||||
- `grafana` - Visualization & Dashboards
|
||||
- Gitea Runner - CI/CD Pipeline Execution
|
||||
|
||||
## Network Architecture
|
||||
|
||||
**Docker Network:** `lcbp3` (external bridge network)
|
||||
|
||||
- ทุก service บน QNAP เชื่อมต่อผ่าน network `lcbp3` เดียวกัน
|
||||
- Services สื่อสารกันด้วย DNS names (เช่น `backend:3000`, `cache:6379`)
|
||||
- ASUSTOR scrape metrics ผ่าน QNAP exporters ผ่าน network DNS
|
||||
|
||||
**Network Security:**
|
||||
- Elasticsearch และ Redis ไม่ publish ports ออก LAN (ใช้ `expose` เฉพาะภายใน network)
|
||||
- MariaDB publish port 3306 แต่ bind เฉพาะ loopback (127.0.0.1:3306) สำหรับ backup/migration
|
||||
- Public access ผ่าน NPM reverse proxy เท่านั้น
|
||||
|
||||
---
|
||||
|
||||
# Environment Setup & Configuration
|
||||
|
||||
**Project:** LCBP3-DMS
|
||||
**Version:** 1.8.0
|
||||
**Last Updated:** 2025-12-02
|
||||
**Version:** 1.9.0
|
||||
**Last Updated:** 2026-05-16
|
||||
|
||||
---
|
||||
|
||||
@@ -37,34 +99,35 @@ This document describes environment variables, configuration files, and secrets
|
||||
|
||||
```bash
|
||||
# File: backend/.env (DO NOT commit to Git)
|
||||
# Location: /share/np-dms/app/.env (ใช้ร่วมกับ docker-compose-app.yml)
|
||||
|
||||
# Application
|
||||
NODE_ENV=production
|
||||
APP_PORT=3000
|
||||
APP_URL=https://lcbp3-dms.example.com
|
||||
APP_URL=https://lcbp3.np-dms.work
|
||||
|
||||
# Database
|
||||
DB_HOST=lcbp3-mariadb
|
||||
DB_HOST=mariadb
|
||||
DB_PORT=3306
|
||||
DB_USER=lcbp3_user
|
||||
DB_PASS=<STRONG_PASSWORD>
|
||||
DB_NAME=lcbp3_dms
|
||||
DB_DATABASE=lcbp3
|
||||
DB_USERNAME=center
|
||||
DB_PASSWORD=<STRONG_PASSWORD>
|
||||
|
||||
# Redis
|
||||
REDIS_HOST=lcbp3-redis
|
||||
REDIS_HOST=cache
|
||||
REDIS_PORT=6379
|
||||
REDIS_PASSWORD=<STRONG_PASSWORD>
|
||||
|
||||
# JWT Authentication
|
||||
JWT_SECRET=<RANDOM_256_BIT_SECRET>
|
||||
JWT_EXPIRATION=1h
|
||||
JWT_EXPIRATION=8h
|
||||
JWT_REFRESH_SECRET=<RANDOM_256_BIT_SECRET>
|
||||
JWT_REFRESH_EXPIRATION=7d
|
||||
|
||||
# File Storage
|
||||
UPLOAD_DIR=/app/uploads
|
||||
TEMP_UPLOAD_DIR=/app/uploads/temp
|
||||
MAX_FILE_SIZE=104857600 # 100MB
|
||||
# File Storage (Two-Phase Storage - ADR-016)
|
||||
UPLOAD_TEMP_DIR=/app/uploads/temp
|
||||
UPLOAD_PERMANENT_DIR=/app/uploads/permanent
|
||||
MAX_FILE_SIZE=52428800 # 50MB
|
||||
ALLOWED_FILE_TYPES=pdf,doc,docx,xls,xlsx,dwg,jpg,png
|
||||
|
||||
# SMTP Email
|
||||
@@ -77,20 +140,31 @@ SMTP_FROM="LCBP3-DMS System <noreply@example.com>"
|
||||
# LINE Notify (Optional)
|
||||
LINE_NOTIFY_ENABLED=true
|
||||
|
||||
# ClamAV Virus Scanner
|
||||
# ClamAV Virus Scanner (ADR-016)
|
||||
CLAMAV_HOST=clamav
|
||||
CLAMAV_PORT=3310
|
||||
|
||||
# Elasticsearch
|
||||
ELASTICSEARCH_NODE=http://lcbp3-elasticsearch:9200
|
||||
ELASTICSEARCH_INDEX_PREFIX=lcbp3_
|
||||
ELASTICSEARCH_HOST=search
|
||||
ELASTICSEARCH_PORT=9200
|
||||
ELASTICSEARCH_USERNAME=elastic
|
||||
ELASTICSEARCH_PASSWORD=<STRONG_PASSWORD>
|
||||
|
||||
# Qdrant Vector Database (ADR-023A)
|
||||
QDRANT_HOST=qdrant
|
||||
QDRANT_PORT=6333
|
||||
QDRANT_GRPC_PORT=6334
|
||||
|
||||
# Document Numbering (ADR-002)
|
||||
NUMBERING_LOCK_TIMEOUT=5000
|
||||
NUMBERING_RESERVATION_TTL=300
|
||||
|
||||
# Logging
|
||||
LOG_LEVEL=info
|
||||
LOG_FILE_PATH=/app/logs
|
||||
|
||||
# Frontend URL (for email links)
|
||||
FRONTEND_URL=https://lcbp3-dms.example.com
|
||||
FRONTEND_URL=https://lcbp3.np-dms.work
|
||||
|
||||
# Rate Limiting
|
||||
RATE_LIMIT_TTL=60
|
||||
@@ -101,13 +175,14 @@ RATE_LIMIT_MAX=100
|
||||
|
||||
```bash
|
||||
# File: frontend/.env.local (DO NOT commit to Git)
|
||||
# Location: /share/np-dms/app/.env (ใช้ร่วมกับ docker-compose-app.yml)
|
||||
|
||||
# API Backend
|
||||
NEXT_PUBLIC_API_URL=https://lcbp3-dms.example.com/api
|
||||
NEXT_PUBLIC_API_URL=https://backend.np-dms.work/api
|
||||
|
||||
# Application
|
||||
NEXT_PUBLIC_APP_NAME=LCBP3-DMS
|
||||
NEXT_PUBLIC_APP_VERSION=1.8.0
|
||||
NEXT_PUBLIC_APP_VERSION=1.9.0
|
||||
|
||||
# Feature Flags
|
||||
NEXT_PUBLIC_ENABLE_NOTIFICATIONS=true
|
||||
@@ -118,134 +193,88 @@ NEXT_PUBLIC_ENABLE_LINE_NOTIFY=true
|
||||
|
||||
## 🐳 Docker Compose Configuration
|
||||
|
||||
### Production docker-compose.yml
|
||||
> **⚠️ สำคัญ:** LCBP3-DMS ใช้ **Split Files Architecture** ไม่ใช่ monolithic docker-compose.yml เดียว
|
||||
>
|
||||
> ดูรายละเอียดและไฟล์จริงได้ที่: [Appendix A - Live QNAP Production Configs](#appendix-a--live-qnap-production-configs)
|
||||
|
||||
### Split Files Structure
|
||||
|
||||
```
|
||||
/share/np-dms/
|
||||
├── app/docker-compose-app.yml # Application Stack (backend, frontend)
|
||||
├── mariadb/docker-compose.yml # Database Stack (mariadb, pma)
|
||||
├── services/docker-compose.yml # Infrastructure Services (cache/redis, search/elasticsearch, qdrant, clamav)
|
||||
├── monitoring/docker-compose.yml # Monitoring Exporters (node-exporter, cadvisor, mysqld-exporter)
|
||||
├── npm/docker-compose.yml # Nginx Proxy Manager
|
||||
├── git/docker-compose.yml # Gitea
|
||||
└── n8n/docker-compose.yml # n8n Automation
|
||||
```
|
||||
|
||||
### Production Configuration Overview
|
||||
|
||||
**Application Stack** (`app/docker-compose-app.yml`):
|
||||
- `backend` - NestJS API (port 3000 internal)
|
||||
- `frontend` - Next.js Web App (port 3000 internal)
|
||||
- `qdrant` - Vector Database for RAG (ADR-023A)
|
||||
- `clamav` - Antivirus Scanner (ADR-016)
|
||||
|
||||
**Infrastructure Services** (`services/docker-compose.yml`):
|
||||
- `cache` - Redis 7-alpine (Caching + Distributed Lock + BullMQ)
|
||||
- `search` - Elasticsearch 8.11.1 (Full-text Search)\n- `qdrant` - Vector Database for RAG (ADR-023A)\n- `clamav` - Antivirus Scanner (ADR-016)
|
||||
|
||||
**Database Stack** (`mariadb/docker-compose.yml`):
|
||||
- `mariadb` - MariaDB 11.8
|
||||
- `pma` - phpMyAdmin (via NPM proxy only)\n\n**MariaDB Configuration:**\n- Config file location: `/share/np-dms/mariadb/my.cnf` (mount to `/etc/mysql/conf.d/my.cnf:ro`)\n- Backup directory: `/share/dms-data/mariadb/backup`
|
||||
|
||||
**Network:** ทุก stack ใช้ external network `lcbp3` เดียวกัน
|
||||
|
||||
> **ดู configuration ทั้งหมดได้ที่:** [Appendix A](#appendix-a--live-qnap-production-configs)
|
||||
|
||||
### Legacy Monolithic Example (Deprecated)
|
||||
|
||||
⚠️ **ข้อมูลด้านล่างนี้เป็นตัวอย่างเท่านั้น - อย่าใช้ใน production**
|
||||
|
||||
เอกสารเดิมแสดง monolithic docker-compose.yml แต่ production จริงใช้ split files ดังนี้:
|
||||
|
||||
```yaml
|
||||
# File: docker-compose.yml
|
||||
# ❌ DEPRECATED: อย่าใช้ monolithic approach นี้
|
||||
version: '3.8'
|
||||
|
||||
services:
|
||||
# NGINX Reverse Proxy
|
||||
nginx:
|
||||
image: nginx:alpine
|
||||
container_name: lcbp3-nginx
|
||||
ports:
|
||||
- '80:80'
|
||||
- '443:443'
|
||||
volumes:
|
||||
- ./nginx/nginx.conf:/etc/nginx/nginx.conf:ro
|
||||
- ./nginx/ssl:/etc/nginx/ssl:ro
|
||||
- nginx-logs:/var/log/nginx
|
||||
depends_on:
|
||||
- backend
|
||||
- frontend
|
||||
restart: unless-stopped
|
||||
networks:
|
||||
- lcbp3-network
|
||||
# ... (ใช้ NPM แทน)
|
||||
|
||||
# NestJS Backend
|
||||
backend:
|
||||
image: lcbp3-backend:latest
|
||||
container_name: lcbp3-backend
|
||||
environment:
|
||||
- NODE_ENV=production
|
||||
env_file:
|
||||
- ./backend/.env
|
||||
volumes:
|
||||
- uploads:/app/uploads
|
||||
- backend-logs:/app/logs
|
||||
depends_on:
|
||||
- mariadb
|
||||
- redis
|
||||
- elasticsearch
|
||||
restart: unless-stopped
|
||||
networks:
|
||||
- lcbp3-network
|
||||
healthcheck:
|
||||
test: ['CMD', 'curl', '-f', 'http://localhost:3000/health']
|
||||
interval: 30s
|
||||
timeout: 10s
|
||||
retries: 3
|
||||
# ... (ดู app/docker-compose-app.yml จริง)
|
||||
|
||||
# Next.js Frontend
|
||||
frontend:
|
||||
image: lcbp3-frontend:latest
|
||||
container_name: lcbp3-frontend
|
||||
environment:
|
||||
- NODE_ENV=production
|
||||
env_file:
|
||||
- ./frontend/.env.local
|
||||
restart: unless-stopped
|
||||
networks:
|
||||
- lcbp3-network
|
||||
# ... (ดู app/docker-compose-app.yml จริง)
|
||||
|
||||
# MariaDB Database
|
||||
mariadb:
|
||||
image: mariadb:11.8
|
||||
container_name: lcbp3-mariadb
|
||||
environment:
|
||||
MYSQL_ROOT_PASSWORD: ${DB_ROOT_PASS}
|
||||
MYSQL_DATABASE: ${DB_NAME}
|
||||
MYSQL_USER: ${DB_USER}
|
||||
MYSQL_PASSWORD: ${DB_PASS}
|
||||
volumes:
|
||||
- mariadb-data:/var/lib/mysql
|
||||
- ./mariadb/init:/docker-entrypoint-initdb.d:ro
|
||||
ports:
|
||||
- '3306:3306'
|
||||
restart: unless-stopped
|
||||
networks:
|
||||
- lcbp3-network
|
||||
command: --character-set-server=utf8mb4 --collation-server=utf8mb4_unicode_ci
|
||||
# ... (ดู mariadb/docker-compose.yml จริง)
|
||||
|
||||
# Redis Cache & Queue
|
||||
redis:
|
||||
image: redis:7.2-alpine
|
||||
container_name: lcbp3-redis
|
||||
command: redis-server --requirepass ${REDIS_PASSWORD}
|
||||
volumes:
|
||||
- redis-data:/data
|
||||
ports:
|
||||
- '6379:6379'
|
||||
restart: unless-stopped
|
||||
networks:
|
||||
- lcbp3-network
|
||||
container_name: cache
|
||||
# ... (ดู services/docker-compose.yml จริง - service name: cache)
|
||||
|
||||
# Elasticsearch
|
||||
elasticsearch:
|
||||
image: docker.elastic.co/elasticsearch/elasticsearch:8.11.0
|
||||
container_name: lcbp3-elasticsearch
|
||||
environment:
|
||||
- discovery.type=single-node
|
||||
- 'ES_JAVA_OPTS=-Xms512m -Xmx512m'
|
||||
- xpack.security.enabled=false
|
||||
volumes:
|
||||
- elasticsearch-data:/usr/share/elasticsearch/data
|
||||
ports:
|
||||
- '9200:9200'
|
||||
restart: unless-stopped
|
||||
networks:
|
||||
- lcbp3-network
|
||||
container_name: search
|
||||
# ... (ดู services/docker-compose.yml จริง - service name: search)
|
||||
|
||||
# ClamAV (Optional - for virus scanning)
|
||||
clamav:
|
||||
image: clamav/clamav:latest
|
||||
container_name: lcbp3-clamav
|
||||
restart: unless-stopped
|
||||
networks:
|
||||
- lcbp3-network
|
||||
|
||||
networks:
|
||||
lcbp3-network:
|
||||
driver: bridge
|
||||
|
||||
volumes:
|
||||
mariadb-data:
|
||||
redis-data:
|
||||
elasticsearch-data:
|
||||
uploads:
|
||||
backend-logs:
|
||||
nginx-logs:
|
||||
# ... (ดู app/docker-compose-app.yml จริง)
|
||||
```
|
||||
|
||||
### Development docker-compose.override.yml
|
||||
@@ -291,49 +320,169 @@ services:
|
||||
|
||||
elasticsearch:
|
||||
environment:
|
||||
- 'ES_JAVA_OPTS=-Xms256m -Xmx256m' # Lower memory for dev
|
||||
- 'ES_JAVA_OPTS=-Xms512m -Xmx512m' # Lower memory for dev
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🔑 Secrets Management
|
||||
## 🔒 Secrets Management\n\n> **🔄 Update:** LCBP3-DMS ใช้ **.env files** แทน Docker secrets เพื่อความเข้ากันได้กับมาตราฐานทั่วไปและ CI/CD pipeline\n\n### Why .env Files?\n\n- **Industry Standard:** .env files เป็นมาตราฐานทั่วไปสำหรับ environment configuration\n- **CI/CD Friendly:** ง่ายต่อการจัดการผ่าน GitHub Actions secrets และ Gitea secrets\n- **QNAP Container Station 3.x:** รองรับ `env_file` อย่างเต็มที่\n- **Simplicity:** ไม่ต้องจัดการกับ Docker secrets ที่ซับซ้อน\n\n### .env Files Structure\n\n```\n/share/np-dms/\n├── app/.env # Application stack secrets (backend, frontend, qdrant, clamav)\n├── mariadb/.env # Database secrets\n├── services/.env # Infrastructure services secrets (cache, search)\n├── npm/.env # NPM secrets\n├── git/.env # Gitea secrets\n└── n8n/.env # n8n secrets\n```\n\n### Creating .env Files\n\n```bash\n# Copy example template\ncp .env.example .env\n\n# Edit with actual values\nnano .env\n\n# Set restrictive permissions\nchmod 600 .env\n```\n\n### Security Best Practices\n\n- **File Permissions:** `chmod 600 .env` (read/write for owner only)\n- **Git Ignore:** เพิ่ม `.env` ใน `.gitignore`\n- **Environment-Specific:** ใช้ `.env.production`, `.env.staging` สำหรับ environment ต่างกัน\n- **CI/CD Secrets:** ใช้ GitHub Actions secrets หรือ Gitea secrets สำหรับ CI/CD pipeline\n- **Rotation:** Rotate secrets ทุก 90 วัน (ตาม ADR-016)\n\n### Alternative: Docker Secrets (Legacy)\n\nเอกสารเดิมแนะนำ Docker secrets แต่ LCBP3-DMS เลือกใช้ .env files แทน:\n\n```yaml\n# ❌ DEPRECATED: Docker secrets approach\nsecrets:\n db_password:\n file: ./secrets/db_password.txt\n\nservices:\n backend:\n secrets:\n - db_password\n```\n\n> **หากต้องการใช้ Docker secrets:** ดู Docker documentation สำหรับการใช้งาน แต่ไม่แนะนำสำหรับ LCBP3-DMS
|
||||
|
||||
### Using Docker Secrets (Recommended for Production)
|
||||
|
||||
```yaml
|
||||
# docker-compose.yml
|
||||
services:
|
||||
backend:
|
||||
secrets:
|
||||
- db_password
|
||||
- jwt_secret
|
||||
environment:
|
||||
DB_PASS_FILE: /run/secrets/db_password
|
||||
JWT_SECRET_FILE: /run/secrets/jwt_secret
|
||||
|
||||
secrets:
|
||||
db_password:
|
||||
file: ./secrets/db_password.txt
|
||||
jwt_secret:
|
||||
file: ./secrets/jwt_secret.txt
|
||||
```
|
||||
|
||||
### Generate Strong Secrets
|
||||
### Using .env Files (Recommended for Production)
|
||||
|
||||
```bash
|
||||
# Generate JWT Secret
|
||||
openssl rand -base64 64
|
||||
# File: /share/np-dms/app/.env
|
||||
# Location: /share/np-dms/app/.env (ใช้ร่วมกับ docker-compose-app.yml)
|
||||
# ⚠️ DO NOT commit to Git
|
||||
|
||||
# Generate Database Password
|
||||
openssl rand -base64 32
|
||||
# Database
|
||||
DB_PASSWORD=<STRONG_RANDOM_PASSWORD>
|
||||
|
||||
# Generate Redis Password
|
||||
openssl rand -base64 32
|
||||
# Redis
|
||||
REDIS_PASSWORD=<STRONG_RANDOM_PASSWORD>
|
||||
|
||||
# JWT Authentication
|
||||
JWT_SECRET=<RANDOM_256_BIT_SECRET>
|
||||
JWT_REFRESH_SECRET=<RANDOM_256_BIT_SECRET>
|
||||
|
||||
# SMTP Email
|
||||
SMTP_PASS=<APP_PASSWORD>
|
||||
|
||||
# Elasticsearch
|
||||
ELASTICSEARCH_PASSWORD=<STRONG_PASSWORD>
|
||||
|
||||
# Qdrant
|
||||
QDRANT_API_KEY=<OPTIONAL_API_KEY>
|
||||
```
|
||||
|
||||
### Docker Compose Integration
|
||||
|
||||
```yaml
|
||||
# File: docker-compose-app.yml
|
||||
services:
|
||||
backend:
|
||||
env_file:
|
||||
- .env
|
||||
environment:
|
||||
TZ: 'Asia/Bangkok'
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 📁 Directory Structure
|
||||
## � Volume/Storage Architecture (QNAP-Specific)
|
||||
|
||||
> **QNAP NAS Storage:** LCBP3-DMS ใช้ QNAP absolute paths แทน Docker named volumes เพื่อประโยชน์จาก NAS storage management
|
||||
|
||||
### Storage Paths on QNAP
|
||||
|
||||
```
|
||||
/share/np-dms/
|
||||
├── app/
|
||||
│ ├── .env # Environment variables (gitignored)
|
||||
│ └── docker-compose-app.yml # Application stack
|
||||
│
|
||||
├── mariadb/
|
||||
│ ├── data/ # MariaDB database files
|
||||
│ ├── my.cnf # MariaDB configuration (mount to /etc/mysql/conf.d/my.cnf:ro)
|
||||
│ └── init/ # Database initialization scripts
|
||||
│
|
||||
├── services/
|
||||
│ ├── cache/
|
||||
│ │ └── data/ # Redis persistence
|
||||
│ └── search/
|
||||
│ └── data/ # Elasticsearch indices
|
||||
│
|
||||
├── monitoring/
|
||||
│ └── docker-compose.yml # Monitoring exporters
|
||||
│
|
||||
├── npm/
|
||||
│ ├── data/ # NPM configuration
|
||||
│ ├── letsencrypt/ # SSL certificates
|
||||
│ └── custom/ # Custom nginx configs
|
||||
│
|
||||
├── git/
|
||||
│ ├── etc/ # Gitea configuration
|
||||
│ ├── lib/ # Gitea libraries
|
||||
│ ├── gitea_repos/ # Git repositories
|
||||
│ └── gitea_registry/ # Container registry
|
||||
│
|
||||
├── n8n/
|
||||
│ ├── /home/node/.n8n # n8n workflow data
|
||||
│ ├── cache/ # n8n cache
|
||||
│ └── scripts/ # n8n custom scripts
|
||||
│
|
||||
└── data/
|
||||
├── uploads/
|
||||
│ ├── temp/ # Temporary upload storage (Two-Phase Storage - ADR-016)
|
||||
│ └── permanent/ # Permanent document storage
|
||||
└── logs/
|
||||
├── backend/ # Backend application logs
|
||||
└── frontend/ # Frontend application logs
|
||||
```
|
||||
|
||||
### Volume Mounts in Docker Compose
|
||||
|
||||
**Application Stack** (`app/docker-compose-app.yml`):
|
||||
```yaml
|
||||
volumes:
|
||||
- '/share/np-dms/data/uploads/temp:/app/uploads/temp'
|
||||
- '/share/np-dms/data/uploads/permanent:/app/uploads/permanent'
|
||||
- '/share/np-dms/data/logs/backend:/app/logs'
|
||||
```
|
||||
|
||||
**Database Stack** (`mariadb/docker-compose.yml`):
|
||||
```yaml
|
||||
volumes:
|
||||
- '/share/np-dms/mariadb/data:/var/lib/mysql'
|
||||
- '/share/np-dms/mariadb/my.cnf:/etc/mysql/conf.d/my.cnf:ro'
|
||||
- '/share/np-dms/mariadb/init:/docker-entrypoint-initdb.d:ro'
|
||||
- '/share/dms-data/mariadb/backup:/backup'
|
||||
```
|
||||
|
||||
**Infrastructure Services** (`services/docker-compose.yml`):
|
||||
```yaml
|
||||
volumes:
|
||||
- '/share/np-dms/services/cache/data:/data' # Redis
|
||||
- '/share/np-dms/services/search/data:/usr/share/elasticsearch/data' # Elasticsearch
|
||||
```
|
||||
|
||||
### Storage Permissions
|
||||
|
||||
QNAP requires specific UID/GID for container access:
|
||||
|
||||
- **MariaDB:** UID 999
|
||||
- **Redis:** UID 999
|
||||
- **Elasticsearch:** UID 1000
|
||||
- **phpMyAdmin:** UID 33
|
||||
|
||||
Example setup:
|
||||
```bash
|
||||
# MariaDB
|
||||
chown -R 999:999 /share/np-dms/mariadb
|
||||
chmod -R 755 /share/np-dms/mariadb
|
||||
|
||||
# Redis
|
||||
chown -R 999:999 /share/np-dms/services/cache/data
|
||||
chmod -R 750 /share/np-dms/services/cache/data
|
||||
|
||||
# Elasticsearch
|
||||
chown -R 1000:1000 /share/np-dms/services/search/data
|
||||
chmod -R 750 /share/np-dms/services/search/data
|
||||
```
|
||||
|
||||
### Backup Strategy
|
||||
|
||||
QNAP provides native snapshot backup for `/share/np-dms/` directory:
|
||||
- **Frequency:** Daily snapshots (configurable in QNAP Hybrid Backup Sync)
|
||||
- **Retention:** 7-30 days (configurable)
|
||||
- **Target:** External USB drive or remote NAS
|
||||
|
||||
Additional application-level backups:
|
||||
- **Database:** mysqldump scripts (see Backup & Recovery section)
|
||||
- **Configuration:** `/share/np-dms/*.env` files (manual backup)
|
||||
|
||||
---
|
||||
|
||||
## �� Directory Structure
|
||||
|
||||
```
|
||||
lcbp3/
|
||||
@@ -451,7 +600,7 @@ docker exec lcbp3-backend env | grep DB_
|
||||
|
||||
```bash
|
||||
# Test Redis connection
|
||||
docker exec lcbp3-redis redis-cli -a <PASSWORD> ping
|
||||
docker exec cache redis-cli -a <PASSWORD> ping
|
||||
# Should return: PONG
|
||||
```
|
||||
|
||||
@@ -475,15 +624,15 @@ docker exec lcbp3-backend env | grep NODE_ENV
|
||||
|
||||
---
|
||||
|
||||
**Version:** 1.8.0
|
||||
**Last Review:** 2025-12-01
|
||||
**Next Review:** 2026-03-01
|
||||
**Version:** 1.9.0
|
||||
**Last Review:** 2026-05-16
|
||||
**Next Review:** 2026-08-16
|
||||
|
||||
---
|
||||
|
||||
# Infrastructure Setup
|
||||
|
||||
> 📍 **Document Version:** v1.8.0
|
||||
> 📍 **Document Version:** v1.9.0
|
||||
> 🖥️ **Primary Server:** QNAP TS-473A (Application & Database)
|
||||
> 💾 **Backup Server:** ASUSTOR AS5403T (Infrastructure & Backup)
|
||||
|
||||
@@ -508,7 +657,7 @@ version: '3.8'
|
||||
services:
|
||||
redis:
|
||||
image: 'redis:7.2-alpine'
|
||||
container_name: lcbp3-redis
|
||||
container_name: cache
|
||||
restart: unless-stopped
|
||||
# AOF: Enabled for durability
|
||||
# Maxmemory: Prevent OOM
|
||||
@@ -840,7 +989,7 @@ curl -X POST http://admin:admin@localhost:3000/api/dashboards/db \
|
||||
|
||||
### 5.1 Database Backup Strategy
|
||||
|
||||
#### Automated Backup Script
|
||||
> **⚠️ Note:** Scripts ด้านล่างนี้เป็นตัวอย่าง/เทมเพลตเท่านั้น - วิธีการจริงจะตัดสินใจภายหลังตามความเหมาะสมกับ infrastructure\n\n#### Automated Backup Script (Example)
|
||||
|
||||
```bash
|
||||
#!/bin/bash
|
||||
@@ -998,7 +1147,7 @@ docker exec cache redis-cli ping
|
||||
|
||||
---
|
||||
|
||||
## 6. Maintenance Procedures
|
||||
## 6. Maintenance Procedures\n\n> **⚠️ Note:** Procedures ด้านล่างนี้เป็น template/guidelines เท่านั้น - วิธีการจริงจะตัดสินใจภายหลังตามความเหมาะสมกับ infrastructure
|
||||
|
||||
### 6.1 Sequence Adjustment
|
||||
|
||||
@@ -1107,16 +1256,16 @@ LINES TERMINATED BY '\n';
|
||||
echo "🧹 Cleaning up expired reservations..."
|
||||
|
||||
# Get all reservation keys
|
||||
KEYS=$(docker exec lcbp3-redis-1 redis-cli --cluster call 172.20.0.2:6379 KEYS "reservation:*" | grep -v "(error)")
|
||||
KEYS=$(docker exec cache-1 redis-cli --cluster call 172.20.0.2:6379 KEYS "reservation:*" | grep -v "(error)")
|
||||
|
||||
COUNT=0
|
||||
for KEY in $KEYS; do
|
||||
# Check TTL
|
||||
TTL=$(docker exec lcbp3-redis-1 redis-cli TTL "$KEY")
|
||||
TTL=$(docker exec cache-1 redis-cli TTL "$KEY")
|
||||
|
||||
if [ "$TTL" -lt 0 ]; then
|
||||
# Delete expired key
|
||||
docker exec lcbp3-redis-1 redis-cli DEL "$KEY"
|
||||
docker exec cache-1 redis-cli DEL "$KEY"
|
||||
((COUNT++))
|
||||
fi
|
||||
done
|
||||
@@ -1236,8 +1385,8 @@ echo "⚠️ Please verify system functionality manually"
|
||||
1. Check Redis cluster health
|
||||
|
||||
```bash
|
||||
docker exec lcbp3-redis-1 redis-cli cluster info
|
||||
docker exec lcbp3-redis-1 redis-cli cluster nodes
|
||||
docker exec cache-1 redis-cli cluster info
|
||||
docker exec cache-1 redis-cli cluster nodes
|
||||
```
|
||||
|
||||
2. Check database locks
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
# 🚀 Release Management Policy — LCBP3-DMS v1.8.0
|
||||
# 🚀 Release Management Policy — LCBP3-DMS v1.9.0
|
||||
|
||||
---
|
||||
|
||||
title: 'Release Management Policy, Versioning Strategy, and Deployment Gates'
|
||||
version: 1.0.0
|
||||
version: 1.1.0
|
||||
status: DRAFT
|
||||
owner: Nattanin Peancharoen (System Architect / Release Manager)
|
||||
last_updated: 2026-03-11
|
||||
owner: Nattanin Peancharoen (System Architect / Release Manager / Product Owner)
|
||||
last_updated: 2026-05-16
|
||||
related:
|
||||
|
||||
- specs/04-Infrastructure-OPS/04-04-deployment-guide.md ← Blue-Green Deployment Detail
|
||||
@@ -17,7 +17,7 @@ related:
|
||||
---
|
||||
|
||||
> [!IMPORTANT]
|
||||
> ทุก Release สู่ Production **ต้องผ่าน Release Gate** — ไม่มีข้อยกเว้น
|
||||
> ทุก Release สู่ Production **ต้องผ่าน Release Gate** — มีข้อยกเว้นเฉพาะ P0 Emergency เท่านั้น
|
||||
> เอกสารนี้กำหนด Policy ที่ทุกคนในทีมต้องปฏิบัติตาม
|
||||
|
||||
---
|
||||
@@ -74,12 +74,14 @@ lcbp3-backend:v1.8.0-rc.1 ← Release Candidate
|
||||
|
||||
| Release Type | Cadence | Who Approves | Notes |
|
||||
| -------------------------- | ------------- | ---------------------------- | --------------------- |
|
||||
| **Sprint Release** (Minor) | ทุก 2 สัปดาห์ | PO + Lead Dev | ตามแผน Sprint |
|
||||
| **Hotfix** (Patch) | ตามเหตุการณ์ | Lead Dev (P0/P1) → PO Notify | ไม่รอ Sprint |
|
||||
| **Emergency Hotfix** | ทันที (P0) | Lead Dev → แจ้ง PO พร้อมกัน | Security, System Down |
|
||||
| **Sprint Release** (Minor) | ทุก 2 สัปดาห์ (Ideal State) | PO + Lead Dev | ตามแผน Sprint |
|
||||
| **Hotfix** (Patch) | ตามเหตุการณ์ | System Architect/DevOps → PO Notify | ไม่รอ Sprint |
|
||||
| **Emergency Hotfix** | ทันที (P0) | System Architect/DevOps → แจ้ง PO พร้อมกัน | Security, System Down |
|
||||
| **Major Release** | กำหนดโดย PO | PO + กทท. Sign-off | Phase Change |
|
||||
|
||||
### Sprint Release Calendar (ตัวอย่าง)
|
||||
### Sprint Release Calendar (Ideal State - Not Yet Implemented)
|
||||
|
||||
> **หมายเหตุ:** Sprint cadence ทุก 2 สัปดาห์เป็น ideal state ยังไม่ได้ทำจริงในปัจจุบัน ปัจจุบะทำ release แบบ as-needed
|
||||
|
||||
```
|
||||
Sprint 1: 01–14 มี.ค. 2569 → Release v1.9.0 (28 มี.ค.)
|
||||
@@ -105,14 +107,16 @@ Sprint 2: 15–28 มี.ค. 2569 → Release v1.10.0 (11 เม.ย.)
|
||||
| -------------------------- | ------------------ | ----------------------------------- |
|
||||
| **TypeScript Compile** | `tsc --noEmit` | 0 Errors |
|
||||
| **Unit Tests Pass** | Jest | ≥ 80% Pass Rate |
|
||||
| **E2E Tests (Core Flows)** | Playwright/Cypress | 100% Core Flows ผ่าน |
|
||||
| **E2E Tests (Core Flows)** | Playwright | 100% Core Flows ผ่าน |
|
||||
| **Security Scan** | `npm audit` | 0 Critical/High Vulnerabilities |
|
||||
| **UUID Misuse Check** | grep script | 0 parseInt on UUID (ADR-019) |
|
||||
| **Console.log Check** | grep script | 0 console.log in committed code |
|
||||
| **Lint** | ESLint | 0 Errors (Warnings ยอมรับได้) |
|
||||
| **Build Success** | Docker Build | Exit 0 |
|
||||
| **Image Size** | Docker inspect | < 2GB (Backend), < 1.5GB (Frontend) |
|
||||
|
||||
**Owner:** Lead Dev
|
||||
**Tool:** Gitea CI/CD Pipeline (ADR-015)
|
||||
**Owner:** System Architect/DevOps
|
||||
**Tool:** Gitea CI/CD Pipeline (.gitea/workflows/ci-deploy.yml)
|
||||
|
||||
---
|
||||
|
||||
@@ -123,10 +127,10 @@ Sprint 2: 15–28 มี.ค. 2569 → Release v1.10.0 (11 เม.ย.)
|
||||
| Deploy to Staging Environment | สำเร็จ, ไม่มี Error | DevOps |
|
||||
| Health Check `/health` → 200 | ✅ | Automated |
|
||||
| Smoke Test (Manual): Login → Create Correspondence → Submit | ผ่าน | Dev หรือ QA |
|
||||
| Migration Script (ถ้ามี Schema Change) | รันสำเร็จบน Staging Schema | DBA / Dev |
|
||||
| Migration Script (ถ้ามี Schema Change) | รันสำเร็จบน Staging Schema | System Architect/DevOps |
|
||||
| Rollback Test: Deploy → Rollback → Verify | ระบบ Rollback ได้ใน < 5 นาที | DevOps |
|
||||
|
||||
**Owner:** Nattanin P.
|
||||
**Owner:** Nattanin P. (System Architect / Release Manager / Product Owner)
|
||||
|
||||
---
|
||||
|
||||
@@ -138,11 +142,11 @@ PO Review: ✅ ไม่มี Known Blocker Issues?
|
||||
PO Sign-off: ✅ อนุมัติ Release
|
||||
|
||||
ถ้ามี Schema Change:
|
||||
DBA Confirm: ✅ Schema SQL พร้อม Apply บน Production
|
||||
DBA Confirm: ✅ Rollback SQL พร้อม (ถ้าจำเป็น)
|
||||
System Architect/DevOps Confirm: ✅ Schema SQL พร้อม Apply บน Production
|
||||
System Architect/DevOps Confirm: ✅ Rollback SQL พร้อม (ถ้าจำเป็น)
|
||||
```
|
||||
|
||||
**Owner:** Nattanin P. (PO + Release Manager)
|
||||
**Owner:** Nattanin P. (System Architect / Release Manager / Product Owner)
|
||||
|
||||
---
|
||||
|
||||
@@ -155,8 +159,8 @@ PO Sign-off: ✅ อนุมัติ Release
|
||||
2. Post-Deploy Verification (15 นาที):
|
||||
✅ Health Check: All containers healthy
|
||||
✅ Smoke Test: Login + Core Feature
|
||||
✅ Error Rate: < 1% (Grafana) ใน 15 นาทีแรก
|
||||
✅ Response Time: P90 < 500ms (Grafana)
|
||||
✅ Error Rate: < 1% (Grafana on ASUSTOR) ใน 15 นาทีแรก
|
||||
✅ Response Time: P90 < 500ms (Grafana on ASUSTOR)
|
||||
|
||||
3. ถ้าผ่าน → RELEASE COMPLETE ✅
|
||||
4. ถ้าไม่ผ่าน → ROLLBACK ทันที (rollback.sh)
|
||||
@@ -357,7 +361,7 @@ Security Check: npm audit (ถ้าเป็น Security Bug)
|
||||
| **Mean Time to Restore (MTTR)** | < 4 ชั่วโมง (P0) / < 8 ชั่วโมง (P1) | Incident Log |
|
||||
| **Time to Rollback** | < 5 นาที (Blue-Green Switch) | Deploy Log |
|
||||
|
||||
> **หมายเหตุ:** Metrics เหล่านี้คือ **DORA Metrics** (DevOps Research and Assessment)
|
||||
> **หมายเหตุ:** Metrics เหล่านี้คือ **DORA Metrics** (DevOps Research and Assessment)
|
||||
> ติดตามใน Monthly Engineering Review
|
||||
|
||||
---
|
||||
@@ -367,50 +371,39 @@ Security Check: npm audit (ถ้าเป็น Security Bug)
|
||||
### Pipeline Stages (ทุก PR เข้า `develop` หรือ `release/*`)
|
||||
|
||||
```yaml
|
||||
# .gitea/workflows/ci.yml (ตัวอย่าง Structure)
|
||||
# .gitea/workflows/ci-deploy.yml (Actual Implementation)
|
||||
|
||||
stages:
|
||||
- name: "1. Code Quality"
|
||||
jobs:
|
||||
- typecheck # tsc --noEmit
|
||||
- lint # ESLint
|
||||
- unit-test # Jest (coverage report)
|
||||
jobs:
|
||||
# JOB 1: CI & Quality Gate
|
||||
build:
|
||||
steps:
|
||||
- pnpm install --frozen-lockfile
|
||||
- pnpm lint
|
||||
- Security checks:
|
||||
- UUID misuse check (grep parseInt.*uuid)
|
||||
- Console.log check (grep console.log)
|
||||
- pnpm test (backend + frontend)
|
||||
|
||||
- name: "2. Security"
|
||||
jobs:
|
||||
- dependency-audit # npm audit
|
||||
- secret-scan # gitleaks (ตรวจ Secret ใน Code)
|
||||
|
||||
- name: "3. Build"
|
||||
jobs:
|
||||
- build-backend # docker build lcbp3-backend:${BRANCH_SHA}
|
||||
- build-frontend # docker build lcbp3-frontend:${BRANCH_SHA}
|
||||
|
||||
- name: "4. Integration Test" (เฉพาะ release/* branch)
|
||||
jobs:
|
||||
- deploy-staging # Deploy to Staging Environment
|
||||
- smoke-test # Playwright Smoke Test
|
||||
- api-test # Postman/Newman Core API Tests
|
||||
|
||||
- name: "5. Release" (เฉพาะ main branch, Manual Trigger)
|
||||
jobs:
|
||||
- tag-version # git tag vX.Y.Z
|
||||
- push-registry # Push image ไปยัง Internal Registry
|
||||
- deploy-prod # deploy.sh (Blue-Green)
|
||||
- notify # LINE Notification
|
||||
# JOB 2: Deploy — Trigger Blue-Green on QNAP (main branch only)
|
||||
deploy:
|
||||
needs: build
|
||||
if: github.ref == 'refs/heads/main'
|
||||
steps:
|
||||
- SSH to QNAP
|
||||
- git pull origin main
|
||||
- ./scripts/deploy.sh
|
||||
```
|
||||
|
||||
> **หมายเหตุ:** Pipeline จริงมี 2 jobs เท่านั้น ไม่มี stages แยก 5 ขั้นตอนตามที่เคยระบุในเอกสารเดิม
|
||||
|
||||
### Environment Variables ที่ CI/CD ใช้
|
||||
|
||||
```bash
|
||||
# Gitea Secrets (ตั้งค่าใน Gitea Settings → Secrets)
|
||||
REGISTRY_URL=registry.internal.example.com
|
||||
REGISTRY_USERNAME=ci-bot
|
||||
REGISTRY_PASSWORD=<secret>
|
||||
QNAP_SSH_KEY=<private key>
|
||||
QNAP_HOST=192.168.1.x
|
||||
LINE_NOTIFY_TOKEN=<secret>
|
||||
STAGING_URL=https://staging.lcbp3-dms.internal
|
||||
SSH_KEY=<private key for QNAP>
|
||||
HOST=192.168.10.8
|
||||
PORT=22
|
||||
USERNAME=admin
|
||||
```
|
||||
|
||||
---
|
||||
@@ -421,12 +414,14 @@ STAGING_URL=https://staging.lcbp3-dms.internal
|
||||
|
||||
```
|
||||
✅ npm audit: 0 Critical, 0 High vulnerabilities
|
||||
✅ ไม่มี Secret/Credential hardcoded ใน Code (Gitleaks)
|
||||
✅ ไม่มี Secret/Credential hardcoded ใน Code (git secrets check)
|
||||
✅ .env ไม่ถูก Commit (gitignore check)
|
||||
✅ JWT_SECRET และ DB_PASSWORD ไม่ใช่ Default Values
|
||||
✅ Docker Image ไม่มี Root User (USER node)
|
||||
✅ Helmet.js Security Headers ยังทำงาน (Smoke Test)
|
||||
✅ Rate Limiting ยังทำงาน (Login endpoint test)
|
||||
✅ UUID misuse check: 0 parseInt on UUID (ADR-019)
|
||||
✅ Console.log check: 0 console.log in committed code
|
||||
```
|
||||
|
||||
### ข้อห้ามเด็ดขาด (Forbidden in Release)
|
||||
@@ -467,6 +462,8 @@ STAGING_URL=https://staging.lcbp3-dms.internal
|
||||
- [ ] TypeScript 0 Errors
|
||||
- [ ] ESLint 0 Errors
|
||||
- [ ] `npm audit` 0 Critical/High
|
||||
- [ ] UUID misuse check: 0 parseInt on UUID
|
||||
- [ ] Console.log check: 0 console.log in committed code
|
||||
- [ ] Docker Build Success
|
||||
- [ ] CHANGELOG.md Updated
|
||||
- [ ] Delta SQL file ready (ถ้ามี Schema Change)
|
||||
@@ -496,7 +493,15 @@ STAGING_URL=https://staging.lcbp3-dms.internal
|
||||
|
||||
## 📝 Document Control
|
||||
|
||||
- **Version:** 1.0.0 | **Status:** DRAFT
|
||||
- **Created:** 2026-03-11 | **Owner:** Nattanin Peancharoen
|
||||
- **Version:** 1.1.0 | **Status:** DRAFT
|
||||
- **Created:** 2026-03-11 | **Owner:** Nattanin Peancharoen (System Architect / Release Manager / Product Owner)
|
||||
- **Last Updated:** 2026-05-16 | Updated CI/CD pipeline to match actual implementation
|
||||
- **Next Review:** Pre Sprint 1 (T-2 สัปดาห์ก่อน Go-Live)
|
||||
- **Classification:** Internal — Developer + DevOps + PO Only
|
||||
|
||||
## 📝 Change History
|
||||
|
||||
| Version | Date | Changes | Author |
|
||||
|---------|------|---------|---------|
|
||||
| 1.0.0 | 2026-03-11 | Initial release policy document | Nattanin P. |
|
||||
| 1.1.0 | 2026-05-16 | Updated to v1.9.0: Fixed CI/CD pipeline description, clarified monitoring infrastructure (ASUSTOR vs QNAP), updated role references to reflect actual team structure, marked Sprint cadence as ideal state | Nattanin P. |
|
||||
|
||||
Reference in New Issue
Block a user