From 5eff8861e16dbdda545a240049704beb3df63b92 Mon Sep 17 00:00:00 2001 From: admin Date: Mon, 23 Feb 2026 15:03:35 +0700 Subject: [PATCH] refactor(specs): merge 08-infrastructure into canonical 04-06 dirs - Append live QNAP configs to 04-01-docker-compose.md (Appendix A) - MariaDB+PMA, Redis+Elasticsearch, NPM, Gitea, n8n, App stack - Append SSH setup + Secrets management to 04-06-security-operations.md - Appendix A: SSH key setup, config, hardening, port forwarding - Appendix B: .env structure, secret generation, rotation, GPG backup - Append QNAP/Gitea CI/CD docs to 04-04-deployment-guide.md - Appendix A: Container Station deployment steps - Appendix B: Gitea Actions CI/CD pipeline setup - Appendix C: act_runner (ASUSTOR) installation - Move Git_command.md -> 05-Engineering-Guidelines/05-05-git-cheatsheet.md - Move docker-compose-app.yml, lcbp3-monitoring.yml, lcbp3-registry.yml, grafana/ -> 04-Infrastructure-OPS/ - Archive lcbp3-db.md -> 99-archives/ - Remove all legacy 08-infrastructure/* files from git - Remove Google OAuth client_secret JSON from git index (security) - Add .gitignore rules: *client_secret*.json, *service_account*.json, specs/08-infrastructure/ - Update 04-Infrastructure-OPS/README.md with new file index --- .gitignore | 4 + .../04-01-docker-compose.md | 453 ++++++++++++++++++ .../04-04-deployment-guide.md | 156 ++++++ .../04-06-security-operations.md | 177 +++++++ specs/04-Infrastructure-OPS/README.md | 27 +- .../docker-compose-app.yml | 0 .../dashboards/lcbp3-docker-monitoring.json | 0 .../lcbp3-monitoring.yml | 0 .../lcbp3-registry.yml | 0 .../05-05-git-cheatsheet.md} | 0 specs/08-infrastructure/04_Service_setting.md | 148 ------ .../08_secrets_management.md | 201 -------- specs/08-infrastructure/09_app_deployment.md | 256 ---------- specs/08-infrastructure/10_gitea_runner.md | 74 --- specs/08-infrastructure/11_Chat.md | 224 --------- specs/08-infrastructure/Gitea_setting.md | 92 ---- specs/08-infrastructure/MariaDB_setting.md | 176 ------- specs/08-infrastructure/NPM_setting.md | 101 ---- specs/08-infrastructure/README.md | 379 --------------- specs/08-infrastructure/SSH_setting.md | 219 --------- ...asjvfrbbgo.apps.googleusercontent.com.json | 1 - specs/08-infrastructure/n8n_setting.md | 91 ---- .../lcbp3-db.md | 0 23 files changed, 808 insertions(+), 1971 deletions(-) rename specs/{08-infrastructure => 04-Infrastructure-OPS}/docker-compose-app.yml (100%) rename specs/{08-infrastructure => 04-Infrastructure-OPS}/grafana/dashboards/lcbp3-docker-monitoring.json (100%) rename specs/{08-infrastructure => 04-Infrastructure-OPS}/lcbp3-monitoring.yml (100%) rename specs/{08-infrastructure => 04-Infrastructure-OPS}/lcbp3-registry.yml (100%) rename specs/{08-infrastructure/Git_command.md => 05-Engineering-Guidelines/05-05-git-cheatsheet.md} (100%) delete mode 100644 specs/08-infrastructure/04_Service_setting.md delete mode 100644 specs/08-infrastructure/08_secrets_management.md delete mode 100644 specs/08-infrastructure/09_app_deployment.md delete mode 100644 specs/08-infrastructure/10_gitea_runner.md delete mode 100644 specs/08-infrastructure/11_Chat.md delete mode 100644 specs/08-infrastructure/Gitea_setting.md delete mode 100644 specs/08-infrastructure/MariaDB_setting.md delete mode 100644 specs/08-infrastructure/NPM_setting.md delete mode 100644 specs/08-infrastructure/README.md delete mode 100644 specs/08-infrastructure/SSH_setting.md delete mode 100644 specs/08-infrastructure/client_secret_1028957954367-93vi8kcnim3m28mnaqjbclasjvfrbbgo.apps.googleusercontent.com.json delete mode 100644 specs/08-infrastructure/n8n_setting.md rename specs/{08-infrastructure => 99-archives}/lcbp3-db.md (100%) diff --git a/.gitignore b/.gitignore index d100678..0ace1c1 100644 --- a/.gitignore +++ b/.gitignore @@ -70,6 +70,10 @@ report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json .env.test.local .env.production.local *.pem +# Secrets — NEVER commit OAuth credentials or service account keys +*client_secret*.json +*service_account*.json +specs/08-infrastructure/ # ============================================ # Testing & Coverage diff --git a/specs/04-Infrastructure-OPS/04-01-docker-compose.md b/specs/04-Infrastructure-OPS/04-01-docker-compose.md index 6c1e47c..86fbea2 100644 --- a/specs/04-Infrastructure-OPS/04-01-docker-compose.md +++ b/specs/04-Infrastructure-OPS/04-01-docker-compose.md @@ -1387,3 +1387,456 @@ networks: -- Export audit logs for compliance SELECT * FROM document_numbering + +--- + +# Appendix A — Live QNAP Production Configs + +> 🖥️ **Server:** QNAP TS-473A · Path: `/share/np-dms/` +> These are the **actual running Docker Compose configurations** on QNAP Container Station (v1.8.0). +> They differ from the generic Blue-Green templates above in that they use the real `lcbp3` Docker external network. + +--- + +## A.1 Database Stack (`lcbp3-db`) + +> **Path:** `/share/np-dms/mariadb/docker-compose.yml` +> **Application name:** `lcbp3-db` — Services: `mariadb`, `pma` + +### Pre-requisite: File Permissions + +```bash +chown -R 999:999 /share/np-dms/mariadb +chmod -R 755 /share/np-dms/mariadb +setfacl -R -m u:999:rwx /share/np-dms/mariadb + +# phpMyAdmin (UID 33) +chown -R 33:33 /share/np-dms/pma/tmp +chmod 755 /share/np-dms/pma/tmp + +# Add Prometheus exporter user +# docker exec -it mariadb mysql -u root -p +# CREATE USER 'exporter'@'%' IDENTIFIED BY '' WITH MAX_USER_CONNECTIONS 3; +# GRANT PROCESS, REPLICATION CLIENT, SELECT ON *.* TO 'exporter'@'%'; +# FLUSH PRIVILEGES; +``` + +### Add Databases for NPM & Gitea + +```sql +-- Nginx Proxy Manager +CREATE DATABASE npm; +CREATE USER 'npm'@'%' IDENTIFIED BY 'npm'; +GRANT ALL PRIVILEGES ON npm.* TO 'npm'@'%'; +FLUSH PRIVILEGES; + +-- Gitea +CREATE DATABASE gitea CHARACTER SET 'utf8mb4' COLLATE 'utf8mb4_unicode_ci'; +CREATE USER 'gitea'@'%' IDENTIFIED BY ''; +GRANT ALL PRIVILEGES ON gitea.* TO 'gitea'@'%'; +FLUSH PRIVILEGES; +``` + +```yaml +# /share/np-dms/mariadb/docker-compose.yml +x-restart: &restart_policy + restart: unless-stopped + +x-logging: &default_logging + logging: + driver: "json-file" + options: + max-size: "10m" + max-file: "5" + +networks: + lcbp3: + external: true + +services: + mariadb: + <<: [*restart_policy, *default_logging] + image: mariadb:11.8 + container_name: mariadb + stdin_open: true + tty: true + deploy: + resources: + limits: + cpus: "2.0" + memory: 4G + reservations: + cpus: "0.5" + memory: 1G + environment: + MYSQL_ROOT_PASSWORD: "" + MYSQL_DATABASE: "lcbp3" + MYSQL_USER: "center" + MYSQL_PASSWORD: "" + TZ: "Asia/Bangkok" + ports: + - "3306:3306" + networks: + - lcbp3 + 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" + healthcheck: + test: ["CMD", "healthcheck.sh", "--connect", "--innodb_initialized"] + interval: 10s + timeout: 5s + retries: 3 + start_period: 30s + + pma: + <<: [*restart_policy, *default_logging] + image: phpmyadmin:5-apache + container_name: pma + deploy: + resources: + limits: + cpus: "0.25" + memory: 256M + environment: + TZ: "Asia/Bangkok" + PMA_HOST: "mariadb" + PMA_PORT: "3306" + PMA_ABSOLUTE_URI: "https://pma.np-dms.work/" + UPLOAD_LIMIT: "1G" + MEMORY_LIMIT: "512M" + ports: + - "89:80" + networks: + - lcbp3 + volumes: + - "/share/np-dms/pma/config.user.inc.php:/etc/phpmyadmin/config.user.inc.php:ro" + - "/share/np-dms/pma/tmp:/var/lib/phpmyadmin/tmp:rw" + depends_on: + mariadb: + condition: service_healthy +``` + +--- + +## A.2 Cache + Search Stack (`services`) + +> **Path:** `/share/np-dms/services/docker-compose.yml` +> **Application name:** `services` — Services: `cache` (Redis 7.2), `search` (Elasticsearch 8.11) + +### Pre-requisite: File Permissions + +```bash +mkdir -p /share/np-dms/services/cache/data +mkdir -p /share/np-dms/services/search/data + +# Redis (UID 999) +chown -R 999:999 /share/np-dms/services/cache/data +chmod -R 750 /share/np-dms/services/cache/data + +# Elasticsearch (UID 1000) +chown -R 1000:1000 /share/np-dms/services/search/data +chmod -R 750 /share/np-dms/services/search/data +``` + +```yaml +# /share/np-dms/services/docker-compose.yml +x-restart: &restart_policy + restart: unless-stopped + +x-logging: &default_logging + logging: + driver: "json-file" + options: + max-size: "10m" + max-file: "5" + +networks: + lcbp3: + external: true + +services: + cache: + <<: [*restart_policy, *default_logging] + image: redis:7-alpine + container_name: cache + deploy: + resources: + limits: + cpus: "1.0" + memory: 2G + reservations: + cpus: "0.25" + memory: 512M + environment: + TZ: "Asia/Bangkok" + ports: + - "6379:6379" + networks: + - lcbp3 + volumes: + - "/share/np-dms/services/cache/data:/data" + healthcheck: + test: ["CMD", "redis-cli", "ping"] + interval: 10s + timeout: 5s + retries: 5 + + search: + <<: [*restart_policy, *default_logging] + image: elasticsearch:8.11.1 + container_name: search + deploy: + resources: + limits: + cpus: "2.0" + memory: 4G + reservations: + cpus: "0.5" + memory: 2G + environment: + TZ: "Asia/Bangkok" + discovery.type: "single-node" + xpack.security.enabled: "false" + # Heap locked at 1GB per ADR-005 + ES_JAVA_OPTS: "-Xms1g -Xmx1g" + ports: + - "9200:9200" + networks: + - lcbp3 + volumes: + - "/share/np-dms/services/search/data:/usr/share/elasticsearch/data" + healthcheck: + test: ["CMD-SHELL", "curl -s http://localhost:9200/_cluster/health | grep -q '\"status\":\"green\"\\|\"status\":\"yellow\"'"] + interval: 30s + timeout: 10s + retries: 5 +``` + +--- + +## A.3 Nginx Proxy Manager (`lcbp3-npm`) + +> **Path:** `/share/np-dms/npm/docker-compose.yml` +> **Application name:** `lcbp3-npm` — Services: `npm`, `landing` + +### NPM Proxy Host Config Reference + +| Domain | Forward Host | Port | Cache | Block Exploits | WebSocket | SSL | +| :-------------------- | :----------- | :--- | :---- | :------------- | :-------- | :--- | +| `backend.np-dms.work` | `backend` | 3000 | ❌ | ✅ | ❌ | ✅ | +| `lcbp3.np-dms.work` | `frontend` | 3000 | ✅ | ✅ | ✅ | ✅ | +| `git.np-dms.work` | `gitea` | 3000 | ✅ | ✅ | ✅ | ✅ | +| `n8n.np-dms.work` | `n8n` | 5678 | ✅ | ✅ | ✅ | ✅ | +| `chat.np-dms.work` | `rocketchat` | 3000 | ✅ | ✅ | ✅ | ✅ | +| `npm.np-dms.work` | `npm` | 81 | ❌ | ✅ | ✅ | ✅ | +| `pma.np-dms.work` | `pma` | 80 | ✅ | ✅ | ❌ | ✅ | + +```yaml +# /share/np-dms/npm/docker-compose.yml +x-restart: &restart_policy + restart: unless-stopped + +x-logging: &default_logging + logging: + driver: "json-file" + options: + max-size: "10m" + max-file: "5" + +networks: + lcbp3: + external: true + giteanet: + external: true + name: gitnet + +services: + npm: + <<: [*restart_policy, *default_logging] + image: jc21/nginx-proxy-manager:latest + container_name: npm + deploy: + resources: + limits: + cpus: "1.0" + memory: 512M + ports: + - "80:80" + - "443:443" + - "81:81" + environment: + TZ: "Asia/Bangkok" + DB_MYSQL_HOST: "mariadb" + DB_MYSQL_PORT: 3306 + DB_MYSQL_USER: "npm" + DB_MYSQL_PASSWORD: "npm" + DB_MYSQL_NAME: "npm" + DISABLE_IPV6: "true" + networks: + - lcbp3 + - giteanet + volumes: + - "/share/np-dms/npm/data:/data" + - "/share/np-dms/npm/letsencrypt:/etc/letsencrypt" + - "/share/np-dms/npm/custom:/data/nginx/custom" + + landing: + image: nginx:1.27-alpine + container_name: landing + restart: unless-stopped + volumes: + - "/share/np-dms/npm/landing:/usr/share/nginx/html:ro" + networks: + - lcbp3 +``` + +--- + +## A.4 Gitea (`git`) + +> **Path:** `/share/np-dms/git/docker-compose.yml` +> **Application name:** `git` — Service: `gitea` (rootless) + +### Pre-requisite: File Permissions + +```bash +chown -R 1000:1000 /share/np-dms/gitea/ +setfacl -m u:1000:rwx /share/np-dms/gitea/etc \ + /share/np-dms/gitea/lib \ + /share/np-dms/gitea/backup +``` + +```yaml +# /share/np-dms/git/docker-compose.yml +networks: + lcbp3: + external: true + giteanet: + external: true + name: gitnet + +services: + gitea: + image: gitea/gitea:latest-rootless + container_name: gitea + restart: always + environment: + USER_UID: "1000" + USER_GID: "1000" + TZ: Asia/Bangkok + GITEA__server__ROOT_URL: https://git.np-dms.work/ + GITEA__server__DOMAIN: git.np-dms.work + GITEA__server__SSH_DOMAIN: git.np-dms.work + GITEA__server__START_SSH_SERVER: "true" + GITEA__server__SSH_PORT: "22" + GITEA__server__SSH_LISTEN_PORT: "22" + GITEA__server__LFS_START_SERVER: "true" + GITEA__server__HTTP_PORT: "3000" + GITEA__server__TRUSTED_PROXIES: "127.0.0.1/8,10.0.0.0/8,172.16.0.0/12,192.168.0.0/16" + GITEA__database__DB_TYPE: mysql + GITEA__database__HOST: mariadb:3306 + GITEA__database__NAME: "gitea" + GITEA__database__USER: "gitea" + GITEA__database__PASSWD: "" + GITEA__packages__ENABLED: "true" + GITEA__security__INSTALL_LOCK: "true" + volumes: + - /share/np-dms/gitea/backup:/backup + - /share/np-dms/gitea/etc:/etc/gitea + - /share/np-dms/gitea/lib:/var/lib/gitea + - /share/np-dms/gitea/gitea_repos:/var/lib/gitea/git/repositories + - /share/np-dms/gitea/gitea_registry:/data/registry + - /etc/timezone:/etc/timezone:ro + - /etc/localtime:/etc/localtime:ro + ports: + - "3003:3000" + - "2222:22" + networks: + - lcbp3 + - giteanet +``` + +--- + +## A.5 n8n Automation + +> **Path:** `/share/np-dms/n8n/docker-compose.yml` + +```bash +chown -R 1000:1000 /share/np-dms/n8n +chmod -R 755 /share/np-dms/n8n +``` + +```yaml +# /share/np-dms/n8n/docker-compose.yml +x-restart: &restart_policy + restart: unless-stopped + +x-logging: &default_logging + logging: + driver: "json-file" + options: + max-size: "10m" + max-file: "5" + +networks: + lcbp3: + external: true + +services: + n8n: + <<: [*restart_policy, *default_logging] + image: n8nio/n8n:latest + container_name: n8n + deploy: + resources: + limits: + cpus: "1.5" + memory: 2G + reservations: + cpus: "0.25" + memory: 512M + environment: + TZ: "Asia/Bangkok" + NODE_ENV: "production" + N8N_PUBLIC_URL: "https://n8n.np-dms.work/" + WEBHOOK_URL: "https://n8n.np-dms.work/" + N8N_HOST: "n8n.np-dms.work" + N8N_PORT: 5678 + N8N_PROTOCOL: "https" + N8N_PROXY_HOPS: "1" + N8N_DIAGNOSTICS_ENABLED: 'false' + N8N_SECURE_COOKIE: 'true' + # N8N_ENCRYPTION_KEY should be kept in .env (gitignored) + N8N_ENFORCE_SETTINGS_FILE_PERMISSIONS: 'true' + DB_TYPE: mysqldb + DB_MYSQLDB_DATABASE: "n8n" + DB_MYSQLDB_USER: "center" + DB_MYSQLDB_HOST: "mariadb" + DB_MYSQLDB_PORT: 3306 + ports: + - "5678:5678" + networks: + lcbp3: {} + volumes: + - "/share/np-dms/n8n:/home/node/.n8n" + - "/share/np-dms/n8n/cache:/home/node/.cache" + - "/share/np-dms/n8n/scripts:/scripts" + healthcheck: + test: ["CMD", "wget", "-qO-", "http://127.0.0.1:5678/"] + interval: 15s + timeout: 5s + retries: 30 +``` + +--- + +## A.6 Application Stack (`lcbp3-app`) + +> **Path:** `/share/np-dms/app/docker-compose.yml` +> See the annotated version in [`04-Infrastructure-OPS/docker-compose-app.yml`](./docker-compose-app.yml) + +This stack contains `backend` (NestJS) and `frontend` (Next.js). +Refer to [04-04-deployment-guide.md](./04-04-deployment-guide.md) for full deployment steps and CI/CD pipeline details. + diff --git a/specs/04-Infrastructure-OPS/04-04-deployment-guide.md b/specs/04-Infrastructure-OPS/04-04-deployment-guide.md index 88dca9b..8c3ade2 100644 --- a/specs/04-Infrastructure-OPS/04-04-deployment-guide.md +++ b/specs/04-Infrastructure-OPS/04-04-deployment-guide.md @@ -935,3 +935,159 @@ docker exec lcbp3-mariadb mysql -u root -p -e " **Version:** 1.8.0 **Last Updated:** 2025-12-02 **Next Review:** 2026-06-01 + +--- + +# Appendix A — QNAP Container Station Deployment + +> 🖥️ **Platform:** QNAP TS-473A · Container Station · Docker Compose App path: `/share/np-dms/app/` + +## A.1 Prerequisites Checklist + +Before deploying `lcbp3-app`, ensure these services are **healthy**: + +| Service | Container Name | Stack | +| -------------- | -------------- | ----------------------------- | +| MariaDB | `mariadb` | `lcbp3-db` | +| Redis | `cache` | `services` | +| Elasticsearch | `search` | `services` | +| NPM | `npm` | `lcbp3-npm` | +| Docker Network | `lcbp3` | `docker network create lcbp3` | + +## A.2 Directory Setup (QNAP SSH) + +```bash +mkdir -p /share/np-dms/data/uploads/temp +mkdir -p /share/np-dms/data/uploads/permanent +mkdir -p /share/np-dms/data/logs/backend +mkdir -p /share/np-dms/app + +# UID 1001 = non-root nestjs user in container +chown -R 1001:1001 /share/np-dms/data/uploads +chown -R 1001:1001 /share/np-dms/data/logs/backend +chmod -R 750 /share/np-dms/data/uploads +``` + +## A.3 Deploy via Container Station UI + +1. เปิด **Container Station** → **Applications** → **Create** +2. ตั้งชื่อ Application: `lcbp3-app` +3. วาง content จาก `specs/04-Infrastructure-OPS/docker-compose-app.yml` +4. แก้ไข Environment Variables ตามต้องการ (secrets ต้องไม่อยู่ใน git) +5. กด **Create** + +ตรวจสอบ Container Status: Applications → `lcbp3-app` +- ✅ `backend` → Running (healthy) +- ✅ `frontend` → Running (healthy) + +## A.4 Verify Deployment + +```bash +# Backend health (inside Docker network) +docker exec frontend wget -qO- http://backend:3000/health + +# Via NPM +curl -I https://lcbp3.np-dms.work +curl -I https://backend.np-dms.work/api +``` + +--- + +# Appendix B — Gitea Actions CI/CD Pipeline + +> 🔄 Automated Build + Deploy on every push to `main` + +## B.1 Setup Gitea Secrets + +Gitea → Repository → Settings → Actions → Secrets → **Add New Secret**: + +| Secret Name | Value | Description | +| ----------- | -------------- | ----------------------------- | +| `HOST` | `192.168.10.8` | QNAP IP (VLAN 10) | +| `PORT` | `22` | SSH Port | +| `USERNAME` | `admin` | SSH user with Docker access | +| `PASSWORD` | `***` | SSH password (or use SSH Key) | + +## B.2 Pipeline Flow + +```mermaid +graph TD + A[Push to main] --> B[Gitea Runner picks up job] + B --> C[SSH to QNAP] + C --> D[git pull latest code] + D --> E[Build Backend Image] + E --> F[Build Frontend Image] + F --> G[docker compose up -d] + G --> H[Cleanup old images] + H --> I[Deploy complete ✅] +``` + +## B.3 Manual Trigger (Re-deploy without code change) + +1. Go to repository → **Actions** tab (top menu) +2. Select workflow **"Build and Deploy"** +3. Click **"Run workflow"** → Select branch `main` → **Run** + +## B.4 Troubleshooting + +| Error | Cause | Fix | +| ---------------------------------------------- | ------------------------------- | --------------------------------------------- | +| `No matching runner with label: ubuntu-latest` | Runner not registered / offline | Register act_runner per Appendix C | +| `SSH Timeout` | QNAP firewall / ACL | Check VLAN 10 ACL allows runner IP on port 22 | +| `Disk Full` | Old images accumulate | `docker image prune -a` on QNAP | +| `Build failed: ENOENT .bin/ts-script` | pnpm deploy symlink error | Use `--shamefully-hoist` flag in Dockerfile | + +--- + +# Appendix C — Gitea Runner (act_runner) on ASUSTOR + +> **Platform:** ASUSTOR AS5403T · Path: `/volume1/np-dms/gitea-runner/` +> **Note:** Gitea is on QNAP, Runner is on ASUSTOR (per Server Role Separation) + +## C.1 Get Registration Token + +Gitea Web UI → **Site Administration** → **Actions** → **Runners** → **Create new Runner** → Copy token + +## C.2 Setup Directory + +```bash +ssh asustor +mkdir -p /volume1/np-dms/gitea-runner/data +``` + +## C.3 Docker Compose + +```yaml +# /volume1/np-dms/gitea-runner/docker-compose.yml +services: + runner: + image: gitea/act_runner:latest + container_name: gitea-runner + restart: always + environment: + GITEA_INSTANCE_URL: https://git.np-dms.work + GITEA_RUNNER_REGISTRATION_TOKEN: + GITEA_RUNNER_NAME: asustor-runner + # Label must match runs-on in deploy.yaml + GITEA_RUNNER_LABELS: ubuntu-latest:docker://node:18-bullseye,self-hosted:docker://node:18-bullseye + volumes: + - /volume1/np-dms/gitea-runner/data:/data + - /var/run/docker.sock:/var/run/docker.sock +``` + +```bash +cd /volume1/np-dms/gitea-runner +docker compose up -d +``` + +## C.4 Verify + +Gitea → **Settings** → **Actions** → **Runners** — should show **Total: 1** with green indicator next to `asustor-runner`. + +## C.5 Maintenance + +```bash +# Cleanup old build images periodically +docker image prune -a # on ASUSTOR (runner images) +ssh qnap "docker image prune -a" # on QNAP (app images) +``` diff --git a/specs/04-Infrastructure-OPS/04-06-security-operations.md b/specs/04-Infrastructure-OPS/04-06-security-operations.md index 2f411ad..9146163 100644 --- a/specs/04-Infrastructure-OPS/04-06-security-operations.md +++ b/specs/04-Infrastructure-OPS/04-06-security-operations.md @@ -442,3 +442,180 @@ echo "Account compromise response completed for User ID: $USER_ID" **Version:** 1.8.0 **Last Review:** 2025-12-01 **Next Review:** 2026-03-01 + +--- + +# Appendix A — SSH Setup (QNAP & ASUSTOR) + +> คู่มือการตั้งค่าและใช้งาน SSH สำหรับ NAS ทั้ง 2 เครื่อง + +## A.1 Connection Info + +| Item | QNAP (TS-473A) | ASUSTOR (AS5403T) | +| ------------- | ------------------ | ----------------------- | +| **Role** | Application Server | Infrastructure / Backup | +| **IP** | `192.168.10.8` | `192.168.10.9` | +| **SSH Port** | `22` | `22` | +| **SSH Alias** | `qnap` | `asustor` | + +## A.2 Enable SSH on NAS + +**QNAP:** QTS Web UI → Control Panel → Network & File Services → Telnet/SSH → Enable SSH (port 22) + +**ASUSTOR:** ADM Web UI → Settings → Terminal & SNMP → Enable SSH service (port 22) + +## A.3 SSH Key Setup (Client → NAS) + +```powershell +# Create key (once on dev machine) +ssh-keygen -t ed25519 -C "nattanin@np-dms" + +# Copy public key to NAS +ssh-copy-id -i ~/.ssh/id_ed25519.pub nattanin@192.168.10.8 # QNAP +ssh-copy-id -i ~/.ssh/id_ed25519.pub nattanin@192.168.10.9 # ASUSTOR +``` + +## A.4 SSH Config (`~/.ssh/config`) + +```ssh-config +Host gitea + HostName git.np-dms.work + User git + Port 2222 + IdentityFile ~/.ssh/id_ed25519 + IdentitiesOnly yes + +Host qnap + HostName 192.168.10.8 + User nattanin + Port 22 + IdentityFile ~/.ssh/id_ed25519 + IdentitiesOnly yes + +Host asustor + HostName 192.168.10.9 + User nattanin + Port 22 + IdentityFile ~/.ssh/id_ed25519 + IdentitiesOnly yes +``` + +## A.5 SSH Hardening (`/etc/ssh/sshd_config` on NAS) + +```bash +PasswordAuthentication no # Key-only login +PermitRootLogin no +AllowUsers nattanin + +# Restart SSH +/etc/init.d/login_server.sh restart # QNAP +/etc/init.d/sshd restart # ASUSTOR +``` + +> ⚠️ **ตรวจสอบว่า SSH Key ใช้งานได้ก่อนปิด PasswordAuthentication** + +## A.6 SSH Port Forwarding (Useful Tunnels) + +```powershell +# MariaDB local tunnel +ssh -L 3306:localhost:3306 qnap + +# Elasticsearch tunnel +ssh -L 9200:localhost:9200 qnap + +# Grafana tunnel (from ASUSTOR) +ssh -L 3000:localhost:3000 asustor +``` + +## A.7 SSH Troubleshooting + +| Problem | Cause | Fix | +| ------------------------------- | ------------------------ | ------------------------------------------------------ | +| `Connection refused` | SSH not enabled on NAS | Enable SSH via Web UI | +| `Permission denied (publickey)` | Wrong key or permissions | `chmod 700 ~/.ssh`, `chmod 600 ~/.ssh/authorized_keys` | +| `Host key verification failed` | IP changed, stale key | `ssh-keygen -R 192.168.10.8` | +| `Connection timed out` | Firewall / wrong IP | Check ACL and ping | + +--- + +# Appendix B — Secrets Management (QNAP Deployment) + +> ⚠️ **Security Level: CONFIDENTIAL** — ห้าม commit secrets ลง Git + +## B.1 Secret Categories + +| Category | Examples | Storage | +| -------------------- | ------------------------------ | ---------------------- | +| Database Credentials | `MYSQL_ROOT_PASSWORD` | `.env` (gitignored) | +| API Keys | `JWT_SECRET`, `REDIS_PASSWORD` | `.env` (gitignored) | +| SSL Certificates | Let's Encrypt | NPM volume | +| SSH Keys | Backup access keys | ASUSTOR secure storage | + +## B.2 Environment File (QNAP) + +```bash +# File: /share/np-dms/.env +# ⚠️ MUST be in .gitignore + +# === Database === +MYSQL_ROOT_PASSWORD= +MYSQL_DATABASE=lcbp3 +MYSQL_USER=center +MYSQL_PASSWORD= + +# === Redis === +REDIS_PASSWORD= + +# === Application === +JWT_SECRET= # openssl rand -hex 64 +JWT_REFRESH_SECRET= + +# === Monitoring === +GRAFANA_PASSWORD= + +# === External Services === +LINE_CHANNEL_SECRET= +LINE_CHANNEL_ACCESS_TOKEN= +``` + +## B.3 Generate Strong Secrets + +```bash +openssl rand -base64 32 # Strong password (24+ chars) +openssl rand -hex 64 # JWT Secret (64 hex chars) +``` + +## B.4 Rotation Schedule + +| Secret | Period | Impact | +| ----------------- | -------------------- | ----------------------- | +| JWT Secret | 90 days | Users re-login required | +| Database Password | 180 days | Service restart needed | +| Redis Password | 180 days | Service restart needed | +| SSL Certificates | Auto (Let's Encrypt) | None | + +## B.5 Encrypted Secret Backup + +```bash +# Encrypt and back up to ASUSTOR +gpg --symmetric --cipher-algo AES256 /share/np-dms/.env -o /tmp/env.gpg +scp /tmp/env.gpg admin@192.168.10.9:/volume1/backup/secrets/ +rm /tmp/env.gpg + +# Restore +scp admin@192.168.10.9:/volume1/backup/secrets/env.gpg /tmp/ +gpg --decrypt /tmp/env.gpg > /share/np-dms/.env +rm /tmp/env.gpg +``` + +## B.6 Gitea Actions Secrets (CI/CD) + +Configure at: Gitea → Repository → Settings → Actions → Secrets + +| Secret Name | Description | +| ----------- | ----------------------------- | +| `HOST` | QNAP IP (`192.168.10.8`) | +| `PORT` | SSH Port (`22`) | +| `USERNAME` | SSH user with Docker access | +| `PASSWORD` | SSH password (prefer SSH Key) | + diff --git a/specs/04-Infrastructure-OPS/README.md b/specs/04-Infrastructure-OPS/README.md index 6c0aa01..4481039 100644 --- a/specs/04-Infrastructure-OPS/README.md +++ b/specs/04-Infrastructure-OPS/README.md @@ -16,15 +16,24 @@ It consolidates what was previously split across multiple operations and specifi ## 📂 Document Index -| File | Purpose | Key Contents | -| ------------------------------------------------------------------------ | ---------------------- | ------------------------------------------------------------------------------------------- | -| **[04-01-docker-compose.md](./04-01-docker-compose.md)** | Core Environment Setup | `.env` configs, Blue/Green Docker Compose, MariaDB & Redis optimization | -| **[04-02-backup-recovery.md](./04-02-backup-recovery.md)** | Disaster Recovery | RTO/RPO strategies, QNAP to ASUSTOR backup scripts, Restic/Mysqldump config | -| **[04-03-monitoring.md](./04-03-monitoring.md)** | Observability | Prometheus metrics, AlertManager rules (inclusive of Document Numbering DB), Grafana alerts | -| **[04-04-deployment-guide.md](./04-04-deployment-guide.md)** | Production Rollout | Step-by-step Blue-Green deployment scripts, rollback playbooks, Nginx Reverse Proxy | -| **[04-05-maintenance-procedures.md](./04-05-maintenance-procedures.md)** | Routine Care | Log rotation, dependency zero-downtime updates, scheduled DB optimizations | -| **[04-06-security-operations.md](./04-06-security-operations.md)** | Hardening & Audit | User access review scripts, SSL renewals, vulnerability scanning procedures | -| **[04-07-incident-response.md](./04-07-incident-response.md)** | Escalation | P0-P3 classifications, incident commander roles, Post-Incident Review (PIR) | +| File | Purpose | Key Contents | +| ------------------------------------------------------------------------ | ---------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------- | +| **[04-01-docker-compose.md](./04-01-docker-compose.md)** | Core Environment Setup | `.env` configs, Blue/Green Docker Compose, MariaDB & Redis optimization, **Appendix A: Live QNAP configs** (MariaDB, Redis/ES, NPM, Gitea, n8n) | +| **[04-02-backup-recovery.md](./04-02-backup-recovery.md)** | Disaster Recovery | RTO/RPO strategies, QNAP to ASUSTOR backup scripts, Restic/Mysqldump config | +| **[04-03-monitoring.md](./04-03-monitoring.md)** | Observability | Prometheus metrics, AlertManager rules, Grafana alerts | +| **[04-04-deployment-guide.md](./04-04-deployment-guide.md)** | Production Rollout | Blue-Green deployment scripts, **Appendix A: QNAP Container Station**, **Appendix B: Gitea Actions CI/CD**, **Appendix C: act_runner setup** | +| **[04-05-maintenance-procedures.md](./04-05-maintenance-procedures.md)** | Routine Care | Log rotation, dependency updates, scheduled DB optimizations | +| **[04-06-security-operations.md](./04-06-security-operations.md)** | Hardening & Audit | User access review, SSL renewals, vulnerability scanning, **Appendix A: SSH Setup**, **Appendix B: Secrets Management** | +| **[04-07-incident-response.md](./04-07-incident-response.md)** | Escalation | P0-P3 classifications, incident commander roles, Post-Incident Review | + +### 🐳 Live Docker Compose Files (QNAP) + +| File | Application | Path on QNAP | +| ------------------------------------------------------ | ---------------------------------------------- | ----------------------------- | +| **[docker-compose-app.yml](./docker-compose-app.yml)** | `lcbp3-app` (backend + frontend) | `/share/np-dms/app/` | +| **[lcbp3-monitoring.yml](./lcbp3-monitoring.yml)** | `lcbp3-monitoring` (Prometheus, Grafana, etc.) | `/volume1/np-dms/monitoring/` | +| **[lcbp3-registry.yml](./lcbp3-registry.yml)** | `lcbp3-registry` (Docker Registry) | `/volume1/np-dms/registry/` | +| **[grafana/](./grafana/)** | Grafana dashboard JSON configs | Imported via Grafana UI | --- diff --git a/specs/08-infrastructure/docker-compose-app.yml b/specs/04-Infrastructure-OPS/docker-compose-app.yml similarity index 100% rename from specs/08-infrastructure/docker-compose-app.yml rename to specs/04-Infrastructure-OPS/docker-compose-app.yml diff --git a/specs/08-infrastructure/grafana/dashboards/lcbp3-docker-monitoring.json b/specs/04-Infrastructure-OPS/grafana/dashboards/lcbp3-docker-monitoring.json similarity index 100% rename from specs/08-infrastructure/grafana/dashboards/lcbp3-docker-monitoring.json rename to specs/04-Infrastructure-OPS/grafana/dashboards/lcbp3-docker-monitoring.json diff --git a/specs/08-infrastructure/lcbp3-monitoring.yml b/specs/04-Infrastructure-OPS/lcbp3-monitoring.yml similarity index 100% rename from specs/08-infrastructure/lcbp3-monitoring.yml rename to specs/04-Infrastructure-OPS/lcbp3-monitoring.yml diff --git a/specs/08-infrastructure/lcbp3-registry.yml b/specs/04-Infrastructure-OPS/lcbp3-registry.yml similarity index 100% rename from specs/08-infrastructure/lcbp3-registry.yml rename to specs/04-Infrastructure-OPS/lcbp3-registry.yml diff --git a/specs/08-infrastructure/Git_command.md b/specs/05-Engineering-Guidelines/05-05-git-cheatsheet.md similarity index 100% rename from specs/08-infrastructure/Git_command.md rename to specs/05-Engineering-Guidelines/05-05-git-cheatsheet.md diff --git a/specs/08-infrastructure/04_Service_setting.md b/specs/08-infrastructure/04_Service_setting.md deleted file mode 100644 index 4e2ef9f..0000000 --- a/specs/08-infrastructure/04_Service_setting.md +++ /dev/null @@ -1,148 +0,0 @@ -# การตั้งค่า Redis และ Elasticsearch - ---- - -## **📝 คำอธิบายและข้อควรพิจารณา** - -* 1 Redis (Service: cache) - - * Image: redis:7-alpine มีขนาดเล็กและทันสมัย - - * Port: expose port 6379 ออกมาที่ Host QNAP เพื่อให้ Uptime Kuma (ASUSTOR) สามารถ monitor ได้ — Backend (NestJS) ยังคงคุยผ่าน lcbp3 network ภายในโดยตรง - - * Volume: map data ไปที่ /share/Container/cache/data เผื่อใช้ Redis ในการทำ Persistent Cache (ถ้าต้องการแค่ Locking อาจจะไม่จำเป็นต้อง map volume ก็ได้ครับ) - - * User ID: Image redis:7-alpine รันด้วย user redis (UID 999) - -* 2 Elasticsearch (Service: search) - - * Image: elasticsearch:8.11.1 ผมเลือกเวอร์ชัน 8 ที่ใหม่และระบุชัดเจน (ไม่ใช้ latest) เพื่อความเสถียรครับ - - * Port: expose port 9200 ออกมาที่ Host QNAP เพื่อให้ Uptime Kuma (ASUSTOR) และ Prometheus สามารถ monitor ได้ - - * Environment (สำคัญมาก): - - * discovery.type: "single-node": ต้องมี ไม่อย่างนั้น Elasticsearch V.8 จะไม่ยอม start ถ้าไม่พบ node อื่นใน cluster - - * xpack.security.enabled: "false": เพื่อความสะดวกในการพัฒนาระยะแรก NestJS จะได้เชื่อมต่อ API port 9200 ได้เลย (หากเปิดใช้งานจะต้องตั้งค่า SSL และ Token ซึ่งซับซ้อนกว่ามาก) - - * ES_JAVA_OPTS: "-Xms1g -Xmx1g": เป็น Best Practice ที่ต้องกำหนด Heap Size ให้ Elasticsearch (ในที่นี้คือ 1GB) - - * User ID: Image elasticsearch รันด้วย user elasticsearch (UID 1000) - ---- - -## กำหนดสิทธิ - -```bash -# สร้าง Directory -mkdir -p /share/np-dms/services/cache/data -mkdir -p /share/np-dms/services/search/data - -# กำหนดสิทธิ์ให้ตรงกับ User ID ใน Container -# Redis (UID 999) -chown -R 999:999 /share/np-dms/services/cache/data -chmod -R 750 /share/np-dms/services/cache/data - -# Elasticsearch (UID 1000) -chown -R 1000:1000 /share/np-dms/services/search/data -chmod -R 750 /share/np-dms/services/search/data -``` - -## Docker file - -```yml -# File: /share/np-dms/services/docker-compose.yml (หรือไฟล์ที่คุณใช้รวม) -# DMS Container v1_7_0: เพิ่ม Application name: services -#Services 'cache' (Redis) และ 'search' (Elasticsearch) - -x-restart: &restart_policy - restart: unless-stopped - -x-logging: &default_logging - logging: - driver: "json-file" - options: - max-size: "10m" - max-file: "5" - -networks: - lcbp3: - external: true - -services: - # ---------------------------------------------------------------- - # 1. Redis (สำหรับ Caching และ Distributed Lock) - # Service Name: cache (ตามที่ NPM และ Backend Plan อ้างอิง) - # ---------------------------------------------------------------- - cache: - <<: [*restart_policy, *default_logging] - image: redis:7-alpine # ใช้ Alpine image เพื่อให้มีขนาดเล็ก - container_name: cache - stdin_open: true - tty: true - deploy: - resources: - limits: - cpus: "1.0" - memory: 2G # Redis เป็น in-memory, ให้ memory เพียงพอต่อการใช้งาน - reservations: - cpus: "0.25" - memory: 512M - environment: - TZ: "Asia/Bangkok" - ports: - - "6379:6379" - networks: - - lcbp3 - volumes: - - "/share/np-dms/services/cache/data:/data" # Map volume สำหรับเก็บข้อมูล (ถ้าต้องการ persistence) - healthcheck: - test: ["CMD", "redis-cli", "ping"] # ตรวจสอบว่า service พร้อมใช้งาน - interval: 10s - timeout: 5s - retries: 5 - - # ---------------------------------------------------------------- - # 2. Elasticsearch (สำหรับ Advanced Search) - # Service Name: search (ตามที่ NPM และ Backend Plan อ้างอิง) - # ---------------------------------------------------------------- - search: - <<: [*restart_policy, *default_logging] - image: elasticsearch:8.11.1 # แนะนำให้ระบุเวอร์ชันชัดเจน (V.8) - container_name: search - stdin_open: true - tty: true - deploy: - resources: - limits: - cpus: "2.0" # Elasticsearch ใช้ CPU และ Memory ค่อนข้างหนัก - memory: 4G - reservations: - cpus: "0.5" - memory: 2G - environment: - TZ: "Asia/Bangkok" - # --- Critical Settings for Single-Node --- - discovery.type: "single-node" # สำคัญมาก: กำหนดให้รันแบบ 1 node - # --- Security (Disable for Development) --- - # ปิด xpack security เพื่อให้ NestJS เชื่อมต่อง่าย (backend -> search:9200) - # หากเป็น Production จริง ควรเปิดใช้งานและตั้งค่า token/cert ครับ - xpack.security.enabled: "false" - # --- Performance Tuning --- - # กำหนด Heap size (1GB) ให้เหมาะสมกับ memory limit (4GB) - ES_JAVA_OPTS: "-Xms1g -Xmx1g" - ports: - - "9200:9200" - networks: - - lcbp3 - volumes: - - "/share/np-dms/services/search/data:/usr/share/elasticsearch/data" # Map volume สำหรับเก็บ data/indices - healthcheck: - # รอจนกว่า cluster health จะเป็น yellow หรือ green - test: ["CMD-SHELL", "curl -s http://localhost:9200/_cluster/health | grep -q '\"status\":\"green\"\\|\\\"status\":\"yellow\"'"] - interval: 30s - timeout: 10s - retries: 5 - -``` diff --git a/specs/08-infrastructure/08_secrets_management.md b/specs/08-infrastructure/08_secrets_management.md deleted file mode 100644 index d87b8b7..0000000 --- a/specs/08-infrastructure/08_secrets_management.md +++ /dev/null @@ -1,201 +0,0 @@ -# Secrets Management สำหรับ LCBP3-DMS - -> 📍 **Version:** v1.8.0 -> ⚠️ **Security Level:** CONFIDENTIAL - ---- - -## Overview - -เอกสารนี้อธิบายวิธีการจัดการ Secrets และ Sensitive Data สำหรับ LCBP3-DMS - ---- - -## 1. Secret Categories - -| Category | Examples | Storage Location | -| :------------------- | :----------------------------- | :----------------------- | -| Database Credentials | `MYSQL_ROOT_PASSWORD` | `.env` file (gitignored) | -| API Keys | `JWT_SECRET`, `REDIS_PASSWORD` | `.env` file (gitignored) | -| SSL Certificates | Let's Encrypt certs | NPM volume | -| SSH Keys | Backup access keys | ASUSTOR secure storage | - ---- - -## 2. Environment File Structure - -### 2.1 Main Environment File - -```bash -# File: /share/np-dms/.env (QNAP) -# ⚠️ This file MUST be in .gitignore - -# === Database === -MYSQL_ROOT_PASSWORD= -MYSQL_DATABASE=lcbp3_db -MYSQL_USER=lcbp3_user -MYSQL_PASSWORD= - -# === Redis === -REDIS_PASSWORD= - -# === Application === -JWT_SECRET= -SESSION_SECRET= - -# === Monitoring === -GRAFANA_PASSWORD= - -# === External Services === -LINE_CHANNEL_SECRET= -LINE_CHANNEL_ACCESS_TOKEN= -SMTP_PASSWORD= -``` - -### 2.2 Docker Compose Override (Optional) - -```yaml -# File: /share/np-dms/docker-compose.override.yml -# For additional local development secrets - -services: - backend: - environment: - - DEBUG_MODE=true - - LOG_LEVEL=debug -``` - ---- - -## 3. Secret Generation - -### 3.1 Generate Strong Passwords - -```bash -# Generate random 32-character password -openssl rand -base64 32 - -# Generate random hex string (for JWT) -openssl rand -hex 64 -``` - -### 3.2 Recommended Password Policy - -| Type | Length | Characters | Example Tool | -| :--------- | :----- | :--------------------- | :------------------------ | -| Database | 24+ | Alphanumeric + symbols | `openssl rand -base64 32` | -| JWT Secret | 64+ | Hex | `openssl rand -hex 64` | -| API Keys | 32+ | Alphanumeric | `openssl rand -base64 32` | - ---- - -## 4. Secret Rotation - -### 4.1 Rotation Schedule - -| Secret Type | Rotation Period | Impact on Services | -| :---------------- | :------------------- | :--------------------- | -| JWT Secret | 90 days | Users need to re-login | -| Database Password | 180 days | Requires restart | -| Redis Password | 180 days | Requires restart | -| SSL Certificates | Auto (Let's Encrypt) | None | - -### 4.2 Rotation Procedure - -```bash -# 1. Update .env file with new secret -nano /share/np-dms/.env - -# 2. Restart affected services -docker-compose up -d --force-recreate backend - -# 3. Verify services are running -docker ps -curl https://backend.np-dms.work/health -``` - ---- - -## 5. Access Control - -### 5.1 Who Has Access - -| Role | .env Access | Server SSH | Backup Access | -| :----------- | :---------- | :--------- | :------------ | -| System Admin | ✅ Full | ✅ Full | ✅ Full | -| DevOps | ✅ Read | ✅ Limited | ❌ None | -| Developer | ❌ None | ❌ None | ❌ None | - -### 5.2 Audit Logging - -```bash -# View SSH login attempts -tail -100 /var/log/auth.log - -# Monitor file access -auditctl -w /share/np-dms/.env -p rwa -k secrets_access -``` - ---- - -## 6. Emergency Procedures - -### 6.1 Secret Compromised - -1. **Immediately** rotate the compromised secret -2. **Check** access logs for unauthorized access -3. **Notify** security team -4. **Document** incident - -### 6.2 Lost Access to Secrets - -1. Contact QNAP Admin for direct access -2. Use backup `.env` from ASUSTOR (encrypted) -3. If both unavailable, regenerate all secrets and reset passwords - ---- - -## 7. Backup of Secrets - -```bash -# Encrypted backup of .env (run on QNAP) -gpg --symmetric --cipher-algo AES256 \ - /share/np-dms/.env -o /tmp/env.gpg - -# Copy to ASUSTOR -scp /tmp/env.gpg admin@192.168.10.9:/volume1/backup/secrets/ - -# Clean up -rm /tmp/env.gpg -``` - -### 7.1 Restore from Backup - -```bash -# Copy from ASUSTOR -scp admin@192.168.10.9:/volume1/backup/secrets/env.gpg /tmp/ - -# Decrypt -gpg --decrypt /tmp/env.gpg > /share/np-dms/.env - -# Clean up -rm /tmp/env.gpg -``` - ---- - -## 8. Checklist - -- [ ] `.env` file exists and is configured -- [ ] `.env` is in `.gitignore` -- [ ] All passwords are strong (24+ characters) -- [ ] JWT secret is 64+ hex characters -- [ ] Encrypted backup of secrets exists on ASUSTOR -- [ ] Access control is properly configured -- [ ] Rotation schedule is documented - ---- - -> ⚠️ **Security Warning**: ห้ามเก็บ secrets ใน version control หรือ commit ไปยัง Git repository -> -> 📝 **หมายเหตุ**: เอกสารนี้อ้างอิงจาก Architecture Document **v1.8.0** diff --git a/specs/08-infrastructure/09_app_deployment.md b/specs/08-infrastructure/09_app_deployment.md deleted file mode 100644 index 45f9d0d..0000000 --- a/specs/08-infrastructure/09_app_deployment.md +++ /dev/null @@ -1,256 +0,0 @@ -# การ Deploy Application (Backend + Frontend) บน QNAP - -> 📍 **Version:** v1.7.0 -> 🖥️ **Server:** QNAP TS-473A (Container Station) -> 🔗 **Docker Compose Path:** `/share/np-dms/app/docker-compose.yml` - ---- - -## 📋 Prerequisites - -ก่อน deploy ต้องมี services เหล่านี้รันอยู่แล้ว: - -| Service | Container Name | Docker Compose | Status | -| :------------- | :------------- | :--------------------------------- | :----- | -| MariaDB | `mariadb` | `lcbp3-db` (MariaDB_setting.md) | ✅ | -| Redis | `cache` | `services` (04_Service_setting.md) | ✅ | -| Elasticsearch | `search` | `services` (04_Service_setting.md) | ✅ | -| NPM | `npm` | `lcbp3-npm` (NPM_setting.md) | ✅ | -| Docker Network | `lcbp3` | `docker network create lcbp3` | ✅ | - ---- - -## 1. Build Docker Images - -### Option A: Build บน Dev Machine (Windows) แล้ว Transfer - -```powershell -# อยู่ที่ workspace root (nap-dms.lcbp3/) - -# Build Backend -docker build -f backend/Dockerfile -t lcbp3-backend:latest . - -# Build Frontend (NEXT_PUBLIC_API_URL bake เข้าไปตอน build) -docker build -f frontend/Dockerfile ` - --build-arg NEXT_PUBLIC_API_URL=https://backend.np-dms.work/api ` - -t lcbp3-frontend:latest . - -# Export เป็น .tar เพื่อ Transfer -docker save lcbp3-backend:latest -o lcbp3-backend.tar -docker save lcbp3-frontend:latest -o lcbp3-frontend.tar - -# Transfer ไปยัง QNAP (ผ่าน SMB Shared Folder) -# Copy lcbp3-backend.tar และ lcbp3-frontend.tar ไปที่ \\192.168.10.8\np-dms\app\ -``` - -### Option B: Build บน QNAP โดยตรง (SSH) - -```bash -# SSH เข้า QNAP -ssh admin@192.168.10.8 - -# Clone หรือ Pull code จาก Gitea -cd /share/np-dms/app/source -git pull origin main - -# Build images -docker build -f backend/Dockerfile -t lcbp3-backend:latest . -docker build -f frontend/Dockerfile \ - --build-arg NEXT_PUBLIC_API_URL=https://backend.np-dms.work/api \ - -t lcbp3-frontend:latest . -``` - ---- - -## 2. Load Images บน QNAP (เฉพาะ Option A) - -```bash -# SSH เข้า QNAP -ssh admin@192.168.10.8 - -# Load images -docker load < /share/np-dms/app/lcbp3-backend.tar -docker load < /share/np-dms/app/lcbp3-frontend.tar - -# ตรวจสอบ -docker images | grep lcbp3 -``` - ---- - -## 3. สร้าง Directories และกำหนดสิทธิ์ - -```bash -# สร้าง directories สำหรับ volumes -mkdir -p /share/np-dms/data/uploads/temp -mkdir -p /share/np-dms/data/uploads/permanent -mkdir -p /share/np-dms/data/logs/backend -mkdir -p /share/np-dms/app - -# กำหนดสิทธิ์ให้ non-root user ใน container (UID 1001) -chown -R 1001:1001 /share/np-dms/data/uploads -chown -R 1001:1001 /share/np-dms/data/logs/backend -chmod -R 750 /share/np-dms/data/uploads -``` - ---- - -## 4. Deploy ผ่าน Container Station - -### 4.1 Copy docker-compose.yml - -คัดลอกไฟล์ `specs/08-infrastructure/docker-compose-app.yml` ไปยัง QNAP: - -```bash -# วางไฟล์ที่ path -/share/np-dms/app/docker-compose.yml -``` - -### 4.2 สร้าง Application ใน Container Station - -1. เปิด **Container Station** บน QNAP Web UI -2. ไปที่ **Applications** → **Create** -3. เลือก **Create Application** -4. ตั้งชื่อ Application: `lcbp3-app` -5. วาง (Paste) เนื้อหาจาก `docker-compose-app.yml` -6. แก้ไข Environment Variables ตามต้องการ (โดยเฉพาะ Secrets) -7. กด **Create** เพื่อ deploy - -> ⚠️ **สำคัญ:** ตรวจสอบ environment variables ก่อน deploy: -> - `DB_PASSWORD` — Password ของ MariaDB -> - `REDIS_PASSWORD` — Password ของ Redis -> - `JWT_SECRET` — Secret key สำหรับ JWT Tokens -> - `AUTH_SECRET` — Secret key สำหรับ NextAuth - -### 4.3 ตรวจสอบ Container Status - -ใน Container Station → Applications → `lcbp3-app`: -- ✅ `backend` — Status: **Running** (healthy) -- ✅ `frontend` — Status: **Running** (healthy) - ---- - -## 5. Verify Deployment - -### ตรวจสอบ Health - -```bash -# Backend health (จากภายใน Docker network) -docker exec frontend wget -qO- http://backend:3000/health - -# Frontend (ผ่าน NPM) -curl -I https://lcbp3.np-dms.work - -# Backend API (ผ่าน NPM) -curl -I https://backend.np-dms.work/api -``` - -### ตรวจสอบ Logs - -```bash -# ดู logs ใน Container Station UI -# หรือผ่าน CLI: -docker logs -f backend -docker logs -f frontend -``` - ---- - -## 6. Update / Re-deploy - -เมื่อต้องการ deploy version ใหม่: - -```powershell -# 1. Build images ใหม่ (บน Dev Machine - PowerShell) -docker build -f backend/Dockerfile -t lcbp3-backend:latest . -docker build -f frontend/Dockerfile ` - --build-arg NEXT_PUBLIC_API_URL=https://backend.np-dms.work/api ` - -t lcbp3-frontend:latest . - -# 2. Export & Transfer -docker save lcbp3-backend:latest -o lcbp3-backend.tar -docker save lcbp3-frontend:latest -o lcbp3-frontend.tar -# Copy ไปที่ \\192.168.10.8\np-dms\app\ ผ่าน SMB Shared Folder - -# 3. Load บน QNAP (SSH) -ssh admin@192.168.10.8 -docker load < /share/np-dms/app/lcbp3-backend.tar -docker load < /share/np-dms/app/lcbp3-frontend.tar - -# 4. Restart ใน Container Station -# Applications → lcbp3-app → Restart -``` - ---- - -## 7. Automated Deployment via Gitea (CI/CD) - -ระบบใช้ **Gitea Actions** เพื่อทำ CI/CD โดยจะทำงานอัตโนมัติเมื่อมีการ `push` เข้าสู่สาขา `main` หรือเรียกใช้งานแบบ `manual` - -### 7.1 การตั้งค่า Gitea Secrets -เพื่อให้ Pipeline สามารถเชื่อมต่อกับ QNAP ผ่าน SSH ได้อย่างปลอดภัย ต้องตั้งค่า Secrets ที่ **Gitea Web UI**: -1. เข้าไปที่ Repository: `np-dms/lcbp3` -2. ไปที่ **Settings** → **Actions** → **Secrets** -3. กด **Add New Secret** สำหรับค่าต่อไปนี้: - -| Secret Name | Value Example | Description | -| :---------- | :------------- | :--------------------------------------- | -| `HOST` | `192.168.10.8` | IP ภายในของ QNAP (VLAN 10) | -| `PORT` | `22` | SSH Port ของ QNAP | -| `USERNAME` | `admin` | User ที่มีสิทธิ์รัน Docker | -| `PASSWORD` | `********` | รหัสผ่าน SSH (แนะนำให้ใช้ SSH Key แทนในอนาคต) | - -### 7.2 โครงสร้าง Pipeline (`deploy.yaml`) -ไฟล์ตั้งค่าอยู่ที่ [`.gitea/workflows/deploy.yaml`](file://../../.gitea/workflows/deploy.yaml) โดยมีขั้นตอนหลักดังนี้: - -```mermaid -graph TD - A[Push to main] --> B[Gitea Runner Pick up Task] - B --> C[SSH to QNAP] - C --> D[git pull latest code] - D --> E[Build Backend Image] - E --> F[Build Frontend Image] - F --> G[docker-compose up -d] - G --> H[Cleanup Unused Images] - H --> I[Finish Deploy] -``` - -### 7.3 วิธีการรันแบบ Manual (Manual Trigger) -หากต้องการ Re-deploy โดยไม่ต้องแก้โค้ด: -1. ไปที่แถบเมนู **Actions** (อยู่ข้างๆ Pull Requests ที่เมนูด้านบนสุด **ไม่ใช่ในเมนู Settings**) -2. ทางซ้ายมือจะเห็นรายการ Workflow ให้เลือก **"Build and Deploy"** -3. หากมีไฟล์ `.yaml` ถูกต้อง จะมีปุ่ม **Run workflow** ปรากฏขึ้นมา (สีฟ้า/น้ำเงิน) -4. เลือก Branch `main` -> กด **Run workflow** เพื่อเริ่มทำงาน - -### 7.4 ตัวอย่างข้อความแจ้งเตือนและการแก้ไขปัญหา (Troubleshooting) -* **❌ No matching online runner with label: ubuntu-latest:** - * **สาเหตุ:** แปลว่าในหน้า Settings -> Actions -> Runners **ไม่มี Runner ที่มีสถานะ Online และมีป้ายกำกับ (Label) ว่า `ubuntu-latest`** ครับ - * **วิธีแก้ 1:** ไปที่เมนู Settings -> Actions -> Runners กดแก้ไข Runner แล้วเพิ่ม Label `ubuntu-latest` เข้าไป (ถ้ามี Runner อยู่แล้ว) - * **วิธีแก้ 2:** แก้ไขไฟล์ `.gitea/workflows/deploy.yaml` ในบรรทัดที่ 11 จาก `runs-on: ubuntu-latest` ให้ตรงกับ **Label** ของ Runner ที่คุณมีจริง (เช่น `self-hosted` หรือชื่อที่คุณตั้งไว้) -* **📂 Paths:** ตรวจสอบว่า Code อยู่ในพาธที่ถูกต้อง (ปัจจุบันใช้ `/share/np-dms/app/source/lcbp3`) -* **SSH Timeout:** ตรวจสอบว่า QNAP เปิด SSH Service และ Firewall (ACL) อนุญาตให้เครื่องที่เป็น Runner เชื่อมต่อมายัง Port 22 ได้ -* **Disk Full:** หาก Build ไม่ผ่านบ่อยๆ ให้รัน `docker image prune -a` บน QNAP เพื่อล้าง Image เก่าออก - -### 7.5 การตรวจสอบ Gitea Runner (สำคัญมาก ⚠️) -จากรูปที่คุณส่งมา **Runners Management (Total: 0)** หมายความว่ายังไม่มีเครื่องที่จะมารันคำสั่ง Deploy ให้ครับ -* **ปัญหา:** แม้จะกด Run workflow ได้ แต่สถานะจะค้างที่ "Waiting" ตลอดไป -* **วิธีแก้:** ต้องทำการติดตั้งและ Register **Gitea Runner** (หรือ `act_runner`) ตามคู่มือนี้ครับ: [10_gitea_runner.md](10_gitea_runner.md) -* **ผลลัพธ์:** เมื่อติดตั้งสำเร็จ สถานะในหน้า Settings จะขึ้น **Total: 1** หรือมากกว่า และเป็นสีเขียวครับ - -### 7.6 ขั้นตอนการตรวจสอบผลการ Deploy -เมื่อมีการรันแล้ว สามารถดูสถานะได้ที่: -1. คลิกที่รายการในหน้า **Actions** (หน้าหลักของ Actions) -2. จะเห็น Log รายละเอียดแต่ละ Step (Build Backend, Build Frontend, etc.) หากผ่านจะเป็นสีเขียวทั้งหมด - ---- - -## 📦 Resource Summary - -| Service | Image | CPU Limit | Memory Limit | Port | -| :----------- | :---------------------- | :-------- | :----------- | :--- | -| **backend** | `lcbp3-backend:latest` | 2.0 | 1.5 GB | 3000 | -| **frontend** | `lcbp3-frontend:latest` | 2.0 | 2 GB | 3000 | - -> 📖 NPM Proxy Hosts ตั้งค่าเรียบร้อยแล้ว: -> - `lcbp3.np-dms.work` → `frontend:3000` -> - `backend.np-dms.work` → `backend:3000` diff --git a/specs/08-infrastructure/10_gitea_runner.md b/specs/08-infrastructure/10_gitea_runner.md deleted file mode 100644 index 83d5fdf..0000000 --- a/specs/08-infrastructure/10_gitea_runner.md +++ /dev/null @@ -1,74 +0,0 @@ -# การติดตั้ง Gitea Actions Runner (act_runner) บน ASUSTOR - -คู่มือนี้สำหรับติดตั้ง **act_runner** บน ASUSTOR เพื่อเชื่อมต่อกับ Gitea ที่รันอยู่บน QNAP - -> ⚠️ **Note:** Gitea อยู่บน **QNAP** แต่ Runner อยู่บน **ASUSTOR** ตามหลัก Server Role Separation -> (QNAP = Application, ASUSTOR = Infrastructure) - -## 🏗️ โครงสร้างการติดตั้ง -* **Platform:** ASUSTOR AS5403T (Infrastructure Server) -* **Method:** Portainer Stack หรือ Docker Compose -* **Path:** `/volume1/np-dms/gitea-runner/` - ---- - -## 🚀 ขั้นตอนการติดตั้ง - -### 1. รับ Registration Token -1. เข้า Gitea Web UI (`https://git.np-dms.work`) ไปที่ **Site Administration** -> **Actions** -> **Runners** -2. กดปุ่ม **Create new Runner** -3. คัดลอก **Registration Token** มาเก็บไว้ - -### 2. เตรียม Directory บน ASUSTOR -```bash -# SSH เข้า ASUSTOR -ssh admin@192.168.10.9 - -# สร้างโฟลเดอร์เก็บข้อมูล -mkdir -p /volume1/np-dms/gitea-runner/data -``` - -### 3. สร้าง Docker Compose - -สร้างไฟล์ `/volume1/np-dms/gitea-runner/docker-compose.yml` หรือใช้ Portainer Stack: - -```yaml -# File: /volume1/np-dms/gitea-runner/docker-compose.yml -# Deploy on: ASUSTOR AS5403T -# เชื่อมต่อกับ Gitea บน QNAP ผ่าน Domain URL - -version: "3.8" - -services: - runner: - image: gitea/act_runner:latest - container_name: gitea-runner - restart: always - environment: - # ใช้ Domain URL เพื่อเชื่อมต่อ Gitea ข้ามเครื่อง (QNAP) - - GITEA_INSTANCE_URL=https://git.np-dms.work - - GITEA_RUNNER_REGISTRATION_TOKEN=คัดลอก_TOKEN_มาวางที่นี่ - - GITEA_RUNNER_NAME=asustor-runner - # Label ต้องตรงกับ runs-on ใน deploy.yaml - - GITEA_RUNNER_LABELS=ubuntu-latest:docker://node:18-bullseye,self-hosted:docker://node:18-bullseye - volumes: - - /volume1/np-dms/gitea-runner/data:/data - - /var/run/docker.sock:/var/run/docker.sock -``` - -### 4. สั่งรัน Runner -```bash -cd /volume1/np-dms/gitea-runner -docker compose up -d -``` - ---- - -## 🔍 การตรวจสอบภายหลังการติดตั้ง -1. กลับไปที่หน้า **Settings -> Actions -> Runners** ใน Gitea (QNAP) -2. สถานะควรเปลี่ยนเป็น **Total: 1** และมีจุดสีเขียวหน้า `asustor-runner` -3. ลองกด **Run workflow** ในแถบ Actions เพื่อทดสอบ - -## ⚠️ ข้อควรระวัง -* **Network:** ASUSTOR ต้องเข้าถึง `https://git.np-dms.work` ได้ (ผ่าน DNS/NPM) -* **Disk Cleanup:** รัน `docker image prune -a` เป็นระยะเพื่อลบ cache images เก่า diff --git a/specs/08-infrastructure/11_Chat.md b/specs/08-infrastructure/11_Chat.md deleted file mode 100644 index f28559a..0000000 --- a/specs/08-infrastructure/11_Chat.md +++ /dev/null @@ -1,224 +0,0 @@ -# การติดตั้ง Rocket.Chat บน QNAP - -> 📍 **Version:** v1.0.0 (Chat Service) -> 🖥️ **Server:** QNAP TS-473A (Container Station) -> 🔗 **Docker Compose Path:** `/share/np-dms/rocketchat/docker-compose.yml` -> 🌐 **Domain:** `chat.np-dms.work` - ---- - -## 📋 Prerequisites - -ก่อนติดตั้ง ต้องมั่นใจว่า: -1. **Docker Network** `lcbp3` ถูกสร้างแล้ว (ตรวจสอบด้วย `docker network ls`) -2. **Nginx Proxy Manager (NPM)** รันอยู่เพื่อจัดการ SSL และ Domain - ---- - -## 1. เตรียม Directories - -สร้าง folder สำหรับเก็บข้อมูลเพื่อให้ข้อมูลไม่หายเมื่อลบ container: - -```bash -# SSH เข้า QNAP -ssh admin@192.168.10.8 - -# สร้าง directories -mkdir -p /share/np-dms/rocketchat/uploads -mkdir -p /share/np-dms/rocketchat/data/db -mkdir -p /share/np-dms/rocketchat/data/dump - -# Permissions: -# MongoDB ใน Docker ปกติใช้ uid 999 หรือ root, Rocket.Chat ใช้ uid 1000 หรือ root -# การสร้าง folder ผ่าน ssh admin ปกติจะเป็น admin:administrators -``` - ---- - -## 2. Docker Compose Configuration - -สร้างไฟล์ `docker-compose.yml` ที่ `/share/np-dms/rocketchat/docker-compose.yml`: - -```yml -x-restart: &restart_policy - restart: unless-stopped - -x-logging: &default_logging - logging: - driver: "json-file" - options: - max-size: "10m" - max-file: "5" - -services: - mongodb: - <<: [*restart_policy, *default_logging] - image: docker.io/library/mongo:7.0 - container_name: mongodb - command: mongod --oplogSize 128 --replSet rs0 --bind_ip_all - volumes: - - /share/np-dms/rocketchat/data/db:/data/db - - /share/np-dms/rocketchat/data/dump:/dump - deploy: - resources: - limits: - cpus: "1.0" - memory: 1G - networks: - - lcbp3 - - # Service สำหรับ Init Replica Set อัตโนมัติ (รันแล้วจบ) - mongo-init-replica: - image: docker.io/library/mongo:7.0 - command: > - bash -c "for i in `seq 1 30`; do - mongosh --host mongodb --eval 'rs.initiate({ _id: \"rs0\", members: [ { _id: 0, host: \"mongodb:27017\" } ] })' && break; - sleep 1; - done" - depends_on: - - mongodb - networks: - - lcbp3 - - rocketchat: - <<: [*restart_policy, *default_logging] - image: registry.rocket.chat/rocketchat/rocket.chat:latest - container_name: rocketchat - volumes: - - /share/np-dms/rocketchat/uploads:/app/uploads - environment: - - PORT=3000 - - ROOT_URL=https://chat.np-dms.work - - MONGO_URL=mongodb://mongodb:27017/rocketchat?replicaSet=rs0 - - MONGO_OPLOG_URL=mongodb://mongodb:27017/local?replicaSet=rs0 - - DEPLOY_METHOD=docker - - ACCOUNTS_AVATAR_STORE_PATH=/app/uploads - deploy: - resources: - limits: - cpus: "1.0" - memory: 1G - depends_on: - - mongodb - networks: - - lcbp3 - expose: - - "3000" - -networks: - lcbp3: - external: true -``` - -> **📝 Note:** -> - **MongoDB Replica Set (`rs0`):** จำเป็นสำหรับ Rocket.Chat เพื่อใช้ Oplog -> - **Expose:** เราเปิด port 3000 ภายใน network `lcbp3` เท่านั้น ไม่ expose ออก host โดยตรง เพื่อความปลอดภัยและให้ผ่าน NPM -> - **Resources:** กำหนด CPU/Memory Limit เพื่อป้องกันไม่ให้กินทรัพยากรเครื่อง QNAP มากเกินไป - ---- - -## 3. Deployment - -1. ไปที่ **Container Station** บน QNAP -2. เลือกเมนู **Applications** -> **Create** -3. ตั้งชื่อ Application: `lcbp3-chat` -4. วาง Code จาก `docker-compose.yml` ด้านบนลงไป -5. Check Env Variable Validations -6. กด **Create** - ---- - -## 4. Nginx Proxy Manager (NPM) Setup - -ต้องตั้งค่า Proxy Host ที่ NPM เพื่อให้เข้าใช้งานผ่าน `https://chat.np-dms.work` ได้ - -1. Login **NPM Admin** (`https://npm.np-dms.work`) -2. ไปที่ **Hosts** -> **Proxy Hosts** -> **Add Proxy Host** -3. **Details Tab:** - * **Domain Names:** `chat.np-dms.work` - * **Scheme:** `http` - * **Forward Hostname:** `rocketchat` (ชื่อ service ใน docker-compose) - * **Forward Port:** `3000` - * **Cache Assets:** ✅ - * **Block Common Exploits:** ✅ - * **Websockets Support:** ✅ (⚠️ สำคัญมากสำหรับ Real-time chat) -4. **SSL Tab:** - * **SSL Certificate:** Request a new SSL Certificate (Let's Encrypt) หรือใช้ Wildcard เดิมที่มี - * **Force SSL:** ✅ - * **HTTP/2 Support:** ✅ -5. กด **Save** - ---- - -## 5. Verification - -1. เปิด Browser เข้าไปที่ `https://chat.np-dms.work` -2. จะพบหน้า **Setup Wizard** -3. กรอกข้อมูล Admin และ Organization เพื่อเริ่มใช้งาน - ---- - -## 6. Configuration & Initial Setup - -### 6.1 Setup Wizard (First Run) -เมื่อเข้าสู่ระบบครั้งแรก จะพบกับ **Setup Wizard** ให้กรอกข้อมูลดังนี้: - -1. **Admin Info:** - * **Name:** Administrator (หรือชื่อผู้ดูแลระบบ) - * **Username:** `admin` (แนะนำให้เปลี่ยนเพื่อความปลอดภัย) - * **Email:** `admin@np-dms.work` - * **Password:** (ตั้งรหัสผ่านที่ซับซ้อนและบันทึกใน Secrets Management) - -2. **Organization Info:** - * **Organization Type:** Government / Public Sector - * **Organization Name:** Laem Chabang Port Phase 3 - * **Industry:** Construction / Infrastructure - * **Size:** 51-100 (หรือตามจริง) - * **Country:** Thailand - -3. **Server Info:** - * **Site Name:** LCBP3 DMS Chat - * **Language:** English / Thai - * **Server Type:** Private Team - * **2FA:** แนะนำให้เปิด Two Factor Authentication (Optional) - -4. **Register Server:** - * **Standalone:** เลือก "Keep standalone" หากต้องการความเป็นส่วนตัวสูงสุด (Privacy-first / Air-gapped) - * **Registered:** หากต้องการใช้ Mobile App Push Notification Gateway ของ Rocket.Chat (ฟรีจำกัดจำนวน) - -> **💡 Tip:** หากเลือก Standalone จะไม่มี Push Notification ไปยังมือถือ (iOS/Android) แต่ยังใช้งานผ่าน Browser และ Desktop App ได้ปกติ - -### 6.2 Post-Installation Settings -หลังจาก Setup เสร็จสิ้น แนะนำให้ตรวจสอบค่าเหล่านี้ใน **Administration**: - -1. **General > Site URL:** ต้องเป็น `https://chat.np-dms.work` -2. **General > Force SSL:** ต้องเป็น `True` -3. **File Upload > File Upload:** เปิดใช้งาน -4. **File Upload > Max File Size:** ปรับตามนโยบาย (Default 2MB อาจน้อยไป แนะนำ 50MB+) - * *หมายเหตุ: ต้องสัมพันธ์กับ `client_max_body_size` ใน NPM ด้วย* - ---- - -## 7. Maintenance - -### Backup Strategy -ข้อมูลสำคัญจะอยู่ที่ Path บน QNAP: -* `/share/np-dms/rocketchat/data/db` (Database) -* `/share/np-dms/rocketchat/uploads` (Files) - -ระบบ Backup (Restic บน ASUSTOR) ควรสั่ง backup folder `/share/np-dms/` ทั้งหมดอยู่แล้ว - -### Troubleshooting -หากเข้าเว็บไม่ได้ หรือขึ้น 502 Bad Gateway: -1. เช็ค Logs Rocket.Chat: `docker logs -f rocketchat` -2. เช็ค Logs MongoDB: `docker logs -f mongodb` (ดูว่า Replica Set init หรือยัง) -3. เช็ค NPM: มั่นใจว่า Forward Hostname ถูกต้อง (`rocketchat` ต้องอยู่ใน network เดียวกันคือ `lcbp3`) - ---- - -## 📦 Resource Summary - -| Service | Image | CPU Limit | Memory Limit | Port | -| :------------- | :-------------------------------------------- | :-------- | :----------- | :---- | -| **mongodb** | `mongo:7.0` | 1.0 | 1 GB | 27017 | -| **rocketchat** | `registry.rocket.chat/rocketchat/rocket.chat` | 1.0 | 1 GB | 3000 | \ No newline at end of file diff --git a/specs/08-infrastructure/Gitea_setting.md b/specs/08-infrastructure/Gitea_setting.md deleted file mode 100644 index 6a092ec..0000000 --- a/specs/08-infrastructure/Gitea_setting.md +++ /dev/null @@ -1,92 +0,0 @@ -# การติดตั้ง Gitea ใน Docker - -* user id ของ gites: - - * uid=1000(git) gid=1000(git) groups=1000(git) - -## กำหนดสิทธิ - -```bash -chown -R 1000:1000 /share/np-dms/gitea/ -[/share/Container/git] # ls -l /share/Container/gitea/etc/app.ini -[/share/Container/git] # setfacl -R -m u:1000:rwx /share/Container/gitea/ -[/share/Container/git] # setfacl -R -m u:70:rwx /share/Container/git/postgres/ -getfacl /share/np-dms/git/etc/app.ini -chown -R 1000:1000 /share/np-dms/gitea/ -ล้างสิทธิ์ -setfacl -R -b /share/np-dms/gitea/ - -chgrp -R administrators /share/np-dms/gitea/ -chown -R 1000:1000 /share/np-dms/gitea/etc /share/np-dms/gitea/lib /share/np-dms/gitea/backup -setfacl -m u:1000:rwx -m g:1000:rwx /share/np-dms/gitea/etc /share/np-dms/gitea/lib /share/np-dms/gitea/backup -``` - -## Docker file - -```yml -# File: share/np-dms/git/docker-compose.yml -# DMS Container v1_7_0 : แยก service และ folder -# Application name: git, Servive:gitea -networks: - lcbp3: - external: true - giteanet: - external: true - name: gitnet - -services: - gitea: - image: gitea/gitea:latest-rootless - container_name: gitea - restart: always - stdin_open: true - tty: true - environment: - # ---- File ownership in QNAP ---- - USER_UID: "1000" - USER_GID: "1000" - TZ: Asia/Bangkok - # ---- Server / Reverse proxy (NPM) ---- - GITEA__server__ROOT_URL: https://git.np-dms.work/ - GITEA__server__DOMAIN: git.np-dms.work - GITEA__server__SSH_DOMAIN: git.np-dms.work - GITEA__server__START_SSH_SERVER: "true" - GITEA__server__SSH_PORT: "22" - GITEA__server__SSH_LISTEN_PORT: "22" - GITEA__server__LFS_START_SERVER: "true" - GITEA__server__HTTP_ADDR: "0.0.0.0" - GITEA__server__HTTP_PORT: "3000" - GITEA__server__TRUSTED_PROXIES: "127.0.0.1/8,10.0.0.0/8,172.16.0.0/12,192.168.0.0/16" - # --- การตั้งค่าฐานข้อมูล - GITEA__database__DB_TYPE: mysql - GITEA__database__HOST: mariadb:3306 - GITEA__database__NAME: "gitea" - GITEA__database__USER: "gitea" - GITEA__database__PASSWD: "Center#2025" - # --- repos - GITEA__repository__ROOT: /var/lib/gitea/git/repositories - DISABLE_HTTP_GIT: "false" - ENABLE_BASIC_AUTHENTICATION: "true" - # --- Enable Package Registry --- - GITEA__packages__ENABLED: "true" - GITEA__packages__REGISTRY__ENABLED: "true" - GITEA__packages__REGISTRY__STORAGE_TYPE: local - GITEA__packages__REGISTRY__STORAGE_PATH: /data/registry - # Optional: lock install after setup (เปลี่ยนเป็น true เมื่อจบ onboarding) - GITEA__security__INSTALL_LOCK: "true" - volumes: - - /share/np-dms/gitea/backup:/backup - - /share/np-dms/gitea/etc:/etc/gitea - - /share/np-dms/gitea/lib:/var/lib/gitea - # ให้ repo root ใช้จาก /share/dms-data/gitea_repos - - /share/np-dms/gitea/gitea_repos:/var/lib/gitea/git/repositories - - /share/np-dms/gitea/gitea_registry:/data/registry - - /etc/timezone:/etc/timezone:ro - - /etc/localtime:/etc/localtime:ro - ports: - - "3003:3000" # HTTP (ไปหลัง NPM) - - "2222:22" # SSH สำหรับ git clone/push - networks: - - lcbp3 - - giteanet -``` diff --git a/specs/08-infrastructure/MariaDB_setting.md b/specs/08-infrastructure/MariaDB_setting.md deleted file mode 100644 index b437aee..0000000 --- a/specs/08-infrastructure/MariaDB_setting.md +++ /dev/null @@ -1,176 +0,0 @@ -# การติดตั้ง MAriaDB และ PHPMyAdmin ใน Docker - -* user id ของ mariadb: - - * uid=0(root) gid=0(root) groups=0(root) - -## กำหนดสิทธิ - -```bash - -chown -R 999:999 /share/np-dms/mariadb -chmod -R 755 /share/np-dms/mariadb -setfacl -R -m u:999:rwx /share/np-dms/mariadb -setfacl -R -d -m u:999:rwx /share/np-dms/mariadb - -chown -R 999:999 /share/np-dms/mariadb/init -chmod 755 /share/np-dms/mariadb/init -setfacl -R -m u:999:r-x /share/np-dms/mariadb/init -setfacl -R -d -m u:999:r-x /share/np-dms/mariadb/init - -chown -R 33:33 /share/np-dms/pma/tmp -chmod 755 /share/np-dms/pma/tmp -setfacl -R -m u:33:rwx /share/np-dms/pma/tmp -setfacl -R -d -m u:33:rwx /share/np-dms/pma/tmp - -chown -R 33:33 /share/dms-data/logs/pma -chmod 755 /share/dms-data/logs/pma -setfacl -R -m u:33:rwx /share/dms-data/logs/pma -setfacl -R -d -m u:33:rwx /share/dms-data/logs/pma - -setfacl -R -m u:1000:rwx /share/nap-dms/gitea -setfacl -R -m u:1000:rwx /share/nap-dms/gitea/gitea_repos -setfacl -R -m u:1000:rwx /share/nap-dms/gitea/gitea_registry -``` - -## เพิ่ม database & user สำหรับ Nginx Proxy Manager (NPM) - -```bash -docker exec -it mariadb mysql -u root -p - CREATE DATABASE npm; - CREATE USER 'npm'@'%' IDENTIFIED BY 'npm'; - GRANT ALL PRIVILEGES ON npm.* TO 'npm'@'%'; - FLUSH PRIVILEGES; -``` - -## เพิ่ม database & user สำหรับ Gitea - -```bash -docker exec -it mariadb mysql -u root -p - CREATE DATABASE gitea CHARACTER SET 'utf8mb4' COLLATE 'utf8mb4_unicode_ci'; - CREATE USER 'gitea'@'%' IDENTIFIED BY 'Center#2025'; - GRANT ALL PRIVILEGES ON gitea.* TO 'gitea'@'%'; - FLUSH PRIVILEGES; - -docker exec -it mariadb mysql -u root -p - CREATE USER 'exporter'@'%' IDENTIFIED BY 'Center#2025' WITH MAX_USER_CONNECTIONS 3; - GRANT PROCESS, REPLICATION CLIENT, SELECT ON *.* TO 'exporter'@'%'; - FLUSH PRIVILEGES; -``` - -## Docker file - -```yml -# File: share/nap-dms/mariadb/docker-compose.yml -# DMS Container v1_7_0 : ย้าย folder ไปที่ share/nap-dms/ -# Application name: lcbp3-db, Servive: mariadb, pma -x-restart: &restart_policy - restart: unless-stopped - -x-logging: &default_logging - logging: - driver: "json-file" - options: - max-size: "10m" - max-file: "5" - -services: - mariadb: - <<: [*restart_policy, *default_logging] - image: mariadb:11.8 - container_name: mariadb - stdin_open: true - tty: true - deploy: - resources: - limits: - cpus: "2.0" - memory: 4G - reservations: - cpus: "0.5" - memory: 1G - environment: - MYSQL_ROOT_PASSWORD: "Center#2025" - MYSQL_DATABASE: "lcbp3" - MYSQL_USER: "center" - MYSQL_PASSWORD: "Center#2025" - TZ: "Asia/Bangkok" - ports: - - "3306:3306" - networks: - - lcbp3 - 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" - healthcheck: - test: ["CMD", "healthcheck.sh", "--connect", "--innodb_initialized"] - interval: 10s - timeout: 5s - retries: 3 - start_period: 30s - - pma: - <<: [*restart_policy, *default_logging] - image: phpmyadmin:5-apache - container_name: pma - stdin_open: true - tty: true - deploy: - resources: - limits: - cpus: "0.25" - memory: 256M - environment: - TZ: "Asia/Bangkok" - PMA_HOST: "mariadb" - PMA_PORT: "3306" - PMA_ABSOLUTE_URI: "https://pma.np-dms.work/" - UPLOAD_LIMIT: "1G" - MEMORY_LIMIT: "512M" - ports: - - "89:80" - networks: - - lcbp3 - # expose: - # - "80" - volumes: - - "/share/np-dms/pma/config.user.inc.php:/etc/phpmyadmin/config.user.inc.php:ro" - - "/share/np-dms/pma/zzz-custom.ini:/usr/local/etc/php/conf.d/zzz-custom.ini:ro" - - "/share/np-dms/pma/tmp:/var/lib/phpmyadmin/tmp:rw" - - "/share/dms-data/logs/pma:/var/log/apache2" - depends_on: - mariadb: - condition: service_healthy - -networks: - lcbp3: - external: true - -# chown -R 999:999 /share/np-dms/mariadb/init -# chmod 755 /share/np-dms/mariadb/init -# setfacl -R -m u:999:r-x /share/np-dms/mariadb/init -# setfacl -R -d -m u:999:r-x /share/np-dms/mariadb/init - -# chown -R 33:33 /share/np-dms/pma/tmp -# chmod 755 /share/np-dms/pma/tmp -# setfacl -R -m u:33:rwx /share/np-dms/pma/tmp -# setfacl -R -d -m u:33:rwx /share/np-dms/pma/tmp - -# chown -R 33:33 /share/dms-data/logs/pma -# chmod 755 /share/dms-data/logs/pma -# setfacl -R -m u:33:rwx /share/dms-data/logs/pma -# setfacl -R -d -m u:33:rwx /share/dms-data/logs/pma - -# setfacl -R -m u:1000:rwx /share/Container/gitea -# setfacl -R -m u:1000:rwx /share/dms-data/gitea_repos -# setfacl -R -m u:1000:rwx /share/dms-data/gitea_registry - -# docker exec -it mariadb mysql -u root -p -# CREATE DATABASE npm; -# CREATE USER 'npm'@'%' IDENTIFIED BY 'npm'; -# GRANT ALL PRIVILEGES ON npm.* TO 'npm'@'%'; -# FLUSH PRIVILEGES; - -``` diff --git a/specs/08-infrastructure/NPM_setting.md b/specs/08-infrastructure/NPM_setting.md deleted file mode 100644 index c6f2905..0000000 --- a/specs/08-infrastructure/NPM_setting.md +++ /dev/null @@ -1,101 +0,0 @@ -# การติดตั้ง Nginx Proxy Manager (NPM) ใน Docker - -* ค่าเริ่มต้นคือ:Email: [admin@example.com] Password: changeme - -* user id ของ NPM: - - * uid=0(root) gid=0(root) groups=0(root) - ---- - -## กำหนดสิทธิ - -```bash -# ตรวจสอบ user id ของ NPM -docker exec -it npm id -chown -R 0:0 /share/Container/npm -setfacl -R -m u:0:rwx /share/Container/npm -``` - -## Note: Configurations - -| Domain Names | Forward Hostname | IP Forward Port | Cache Assets | Block Common Exploits | Websockets | Force SSL | HTTP/2 | SupportHSTS Enabled | -| :----------------------------- | :--------------- | :-------------- | :----------- | :-------------------- | :--------- | :-------- | :----- | :------------------ | -| backend.np-dms.work | backend | 3000 | [ ] | [x] | [ ] | [x] | [x] | [ ] | -| lcbp3.np-dms.work | frontend | 3000 | [x] | [x] | [x] | [x] | [x] | [ ] | -| db.np-dms.work | mariadb | 3306 | [x] | [x] | [x] | [x] | [x] | [ ] | -| git.np-dms.work | gitea | 3000 | [x] | [x] | [x] | [x] | [x] | [ ] | -| n8n.np-dms.work | n8n | 5678 | [x] | [x] | [x] | [x] | [x] | [ ] | -| chat.np-dms.work | rocketchat | 3000 | [x] | [x] | [x] | [x] | [x] | [ ] | -| npm.np-dms.work | npm | 81 | [ ] | [x] | [x] | [x] | [x] | [ ] | -| pma.np-dms.work | pma | 80 | [x] | [x] | [ ] | [x] | [x] | [ ] | -| np-dms.work, [www.np-dms.work] | localhost | 80 | [x] | [x] | [ ] | [x] | [x] | [ ] | - -## Docker file - -```yml -# File: share/np-dms/npm/docker-compose-npm.yml -# DMS Container v1_7_0 : ย้าย folder ไปที่ share/np-dms/ -# Application name: lcbp3-npm, Servive:npm -x-restart: &restart_policy - restart: unless-stopped - -x-logging: &default_logging - logging: - driver: "json-file" - options: - max-size: "10m" - max-file: "5" -services: - npm: - <<: [*restart_policy, *default_logging] - image: jc21/nginx-proxy-manager:latest - container_name: npm - stdin_open: true - tty: true - deploy: - resources: - limits: - cpus: "1.0" # 50% CPU - memory: 512M - ports: - - "80:80" # HTTP - - "443:443" # HTTPS - - "81:81" # NPM Admin UI - environment: - TZ: "Asia/Bangkok" - DB_MYSQL_HOST: "mariadb" - DB_MYSQL_PORT: 3306 - DB_MYSQL_USER: "npm" - DB_MYSQL_PASSWORD: "npm" - DB_MYSQL_NAME: "npm" - # Uncomment this if IPv6 is not enabled on your host - DISABLE_IPV6: "true" - networks: - - lcbp3 - - giteanet - volumes: - - "/share/np-dms/npm/data:/data" - - "/share/dms-data/logs/npm:/data/logs" # <-- เพิ่ม logging volume - - "/share/np-dms/npm/letsencrypt:/etc/letsencrypt" - - "/share/np-dms/npm/custom:/data/nginx/custom" # <-- สำคัญสำหรับ http_top.conf - # - "/share/Container/lcbp3/npm/landing:/data/landing:ro" - landing: - image: nginx:1.27-alpine - container_name: landing - restart: unless-stopped - volumes: - - "/share/np-dms/npm/landing:/usr/share/nginx/html:ro" - networks: - - lcbp3 -networks: - lcbp3: - external: true - giteanet: - external: true - name: gitnet - - - - -``` diff --git a/specs/08-infrastructure/README.md b/specs/08-infrastructure/README.md deleted file mode 100644 index 6c95013..0000000 --- a/specs/08-infrastructure/README.md +++ /dev/null @@ -1,379 +0,0 @@ -# 08-Infrastructure - -คู่มือการตั้งค่า Infrastructure สำหรับ **NAP-DMS LCBP3** (Laem Chabang Port Phase 3 - Document Management System) - -> 📍 **Platform:** QNAP (Container Station) + ASUSTOR (Portainer) -> 🌐 **Domain:** `*.np-dms.work` (IP: 159.192.126.103) -> 🔒 **Network:** `lcbp3` (Docker External Network) -> 📄 **Version:** v1.8.0 (aligned with 01-02-architecture.md) - ---- - -## 🏢 Hardware Infrastructure - -### Server Role Separation - -#### QNAP TS-473A -| (Application & Database Server) | | | -| :------------------------------ | :---------------- | :-------------------- | -| ✔ Application Runtime | ✔ API / Web | ✔ Database (Primary) | -| ✔ High CPU / RAM usage | ✔ Worker / Queue | ✖ No long-term backup | -| Container Station (UI) | 32GB RAM (Capped) | AMD Ryzen V1500B | - -#### ASUSTOR AS5403T -| (Infrastructure & Backup Server) | | | -| :------------------------------- | :---------------- | :------------------- | -| ✔ File Storage | ✔ Backup Target | ✔ Docker Infra | -| ✔ Monitoring / Registry | ✔ Log Aggregation | ✖ No heavy App logic | -| Portainer (Manage All) | 16GB RAM | Intel Celeron @2GHz | - -### Servers Specification - -| Device | Model | CPU | RAM | Resource Policy | Role | -| :---------- | :------ | :---------------------- | :--- | :------------------ | :--------------------- | -| **QNAP** | TS-473A | AMD Ryzen V1500B | 32GB | **Strict Limits** | Application, DB, Cache | -| **ASUSTOR** | AS5403T | Intel Celeron @ 2.00GHz | 16GB | **Moderate Limits** | Infra, Backup, Monitor | - -### Service Distribution by Server - -#### QNAP TS-473A (Application Stack) - -| Category | Service | Strategy | Resource Limit (Est.) | -| :-------------- | :------------------------ | :------------------------------ | :-------------------- | -| **Web App** | Next.js (Frontend) | Single Instance | 2.0 CPU / 2GB RAM | -| **Backend API** | NestJS | **2 Replicas** (Load Balanced) | 2.0 CPU / 1.5GB RAM | -| **Database** | MariaDB (Primary) | Performance Tuned (Buffer Pool) | 4.0 CPU / 5GB RAM | -| **Worker** | Redis + BullMQ Worker | **Standalone + AOF** | 2.0 CPU / 1.5GB RAM | -| **Search** | Elasticsearch | **Heap Locked (2GB)** | 2.0 CPU / 4GB RAM | -| **API Gateway** | NPM (Nginx Proxy Manager) | SSL Termination | 1.0 CPU / 512MB RAM | -| **Workflow** | n8n | Automation | 1.0 CPU / 1GB RAM | -| **Code** | Gitea | Git Repository | 1.0 CPU / 1GB RAM | -| **Chat** | Rocket.Chat | **Standalone + MongoDB RS** | 2.0 CPU / 2GB RAM | - -#### ASUSTOR AS5403T (Infrastructure Stack) -| Category | Service | Notes | -| :--------------- | :------------------ | :-------------------------------- | -| **File Storage** | NFS / SMB | Shared volumes for backup | -| **Backup** | Restic / Borg | Pull-based Backup (More Safe) | -| **Docker Infra** | Registry, Portainer | Container image registry, mgmt | -| **Monitoring** | Uptime Kuma | Service availability monitoring | -| **Metrics** | Prometheus, Grafana | Cross-Server Scraping | -| **Log** | Loki / Syslog | Centralized logging | -| **CI/CD** | Gitea Runner | Automated Deployment (act_runner) | - ---- - -## 🔄 Architecture Diagrams - -> 📊 **ดู Diagrams แบบ Interactive (Mermaid) ได้ที่:** [Network_daigram.md](Network_daigram.md) -> -> เอกสารนี้รวม Diagrams หลักไว้ได้แก่: -> - **Data Flow Diagram** - การไหลของข้อมูลระหว่าง Services -> - **Docker Management View** - การจัดการ Containers ผ่าน Portainer -> - **Security Zones Diagram** - การแบ่ง Security Zones (Public, App, Data, Infra) -> - **Network Flow Diagram** - การเชื่อมต่อ VLANs และ Firewall Rules - ---- - -## 🌐 Network Architecture (VLAN) - -### VLAN Networks - -| VLAN ID | Name | Gateway/Subnet | DHCP Range | Purpose | -| :------ | :----- | :-------------- | :----------------- | :-------------------- | -| 10 | SERVER | 192.168.10.1/24 | Static | Servers (NAS, Docker) | -| 20 | MGMT | 192.168.20.1/24 | Static | Network Management | -| 30 | USER | 192.168.30.1/24 | .10-.254 (7 days) | Staff Devices | -| 40 | CCTV | 192.168.40.1/24 | .100-.150 (7 days) | Surveillance | -| 50 | VOICE | 192.168.50.1/24 | .201-.250 (7 days) | IP Phones | -| 60 | DMZ | 192.168.60.1/24 | Static | Public Services | -| 70 | GUEST | 192.168.70.1/24 | .200-.250 (1 day) | Guest WiFi | - -### Static IP Allocation (Key Devices) - -| VLAN | Device | IP Address | Role | -| :--------- | :------- | :---------------------------- | :------------------ | -| SERVER(10) | QNAP | 192.168.10.8 | App/DB Server | -| SERVER(10) | ASUSTOR | 192.168.10.9 | Infra/Backup Server | -| 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) | OC200 | 192.168.20.250 | Omada Controller | -| USER(30) | Printer | 192.168.30.222 | Kyocera CS3554ci | -| CCTV(40) | NVR | 192.168.40.200 | HikVision NVR | -| VOICE(50) | IP Phone | 192.168.50.211-192.168.50.221 | IP Phone | - -### Network Equipment - -| Device | Model | Ports | IP Address | Role | -| :------------------ | :----------------- | :---------------------- | :------------- | :--------------- | -| **Router** | TP-LINK ER7206 | 1 SFP + WAN + 4×GbE | 192.168.20.1 | Gateway/Firewall | -| **Core Switch** | TP-LINK SG2428P | 24×GbE PoE+ + 4×SFP | 192.168.20.2 | Core/PoE Switch | -| **Server Switch** | AMPCOM | 8×2.5GbE + 1×10G SFP+ | 192.168.20.3 | Server Uplink | -| **Admin Switch** | TP-LINK ES205G | 5×GbE (Unmanaged) | N/A | Admin PC | -| **CCTV Switch** | TP-LINK TL-SL1226P | 24×PoE+ 100Mbps + 2×SFP | 192.168.20.4 | CCTV PoE | -| **IP Phone Switch** | TP-LINK SG1210P | 8×PoE+ + 1×GbE + 1×SFP | 192.168.20.5 | VoIP | -| **Controller** | TP-LINK OC200 | Omada Controller | 192.168.20.250 | AP Management | - -> 📖 Detailed port mappings and ACL rules: see [Securities.md](Securities.md) and [แผนผัง Network.md](แผนผัง%20Network.md) - ---- - -## 🔗 Network Topology - -```mermaid -graph TB - subgraph Internet - WAN[("🌐 Internet
WAN")] - end - - subgraph Router["ER7206 Router"] - R[("🔲 ER7206
192.168.20.1")] - end - - subgraph CoreSwitch["SG2428P Core Switch"] - CS[("🔲 SG2428P
192.168.20.2")] - end - - subgraph ServerSwitch["AMPCOM 2.5G Switch"] - SS[("🔲 AMPCOM
192.168.20.3")] - end - - subgraph Servers["VLAN 10 - Servers"] - QNAP[("💾 QNAP (App/DB)
192.168.10.8")] - ASUSTOR[("💾 ASUSTOR (Infra)
192.168.10.9")] - end - - subgraph AccessPoints["EAP610 x16"] - AP[("📶 WiFi APs")] - end - - subgraph OtherSwitches["Distribution"] - OC200[("🔲OC200
Omada Controller")] - CCTV_SW[("🔲 TL-SL1226P
CCTV Switch")] - PHONE_SW[("🔲 SG1210P
IP Phone Switch")] - ADMIN_SW[("🔲 ES205G
Admin Switch")] - end - - WAN --> R - R -->|Port 3 - Port 1| CS - CS -->|LAG Port 3| SS - SS -->|Port 3-4 LACP| QNAP - SS -->|Port 5-6 LACP| ASUSTOR - CS -->|Port 5-20| AP - CS -->|Port 2| OC200 - CS -->|SFP 25| CCTV_SW - CS -->|SFP 26| PHONE_SW - CS -->|Port 21| ADMIN_SW -``` - ---- - -## 📁 สารบัญเอกสาร - -| ไฟล์ | คำอธิบาย | -| :--------------------------------------------------- | :--------------------------------------------------------------------------- | -| [Infrastructure Setup.md](Infrastructure%20Setup.md) | ภาพรวมการตั้งค่าโครงสร้างพื้นฐาน (Redis, MariaDB, Backend, Monitoring, Backup, DR) | -| [แผนผัง Network.md](แผนผัง%20Network.md) | แผนผัง Network Architecture และ Container Services | -| [Securities.md](Securities.md) | VLAN Segmentation, Firewall Rules, ACL (ER7206, SG2428P, EAP) | - ---- - -## 🐳 Docker Compose Files - -### Core Services (QNAP) - -| ไฟล์ | Application | Services | Path บน QNAP | -| :----------------------------------------------- | :---------- | :---------------------------------------- | :------------------------ | -| [MariaDB_setting.md](MariaDB_setting.md) | `lcbp3-db` | `mariadb`, `pma` | `/share/np-dms/mariadb/` | -| [NPM_setting.md](NPM_setting.md) | `lcbp3-npm` | `npm`, `landing` | `/share/np-dms/npm/` | -| [Service_setting.md](Service_setting.md) | `services` | `cache` (Redis), `search` (Elasticsearch) | `/share/np-dms/services/` | -| [Gitea_setting.md](Gitea_setting.md) | `git` | `gitea` | `/share/np-dms/gitea/` | -| [n8n_setting.md](n8n_setting.md) | `n8n` | `n8n` | `/share/np-dms/n8n/` | -| [docker-compose-app.yml](docker-compose-app.yml) | `lcbp3-app` | `backend` (NestJS), `frontend` (Next.js) | `/share/np-dms/app/` | - -### Infrastructure Services (ASUSTOR) - -| ไฟล์ | Application | Services | Path บน ASUSTOR | -| :--------------------------------------------------- | :----------------- | :--------------------------------------------------- | :------------------------------ | -| [05_monitoring.md](05_monitoring.md) | `lcbp3-monitoring` | `prometheus`, `grafana`, `node-exporter`, `cadvisor` | `/volume1/np-dms/monitoring/` | -| [lcbp3-registry.yml](lcbp3-registry.yml) | `lcbp3-registry` | `registry` | `/volume1/np-dms/registry/` | -| [10_gitea_runner.md](10_gitea_runner.md) | `gitea-runner` | `act_runner` | `/volume1/np-dms/gitea-runner/` | -| [06_backup.md](06_backup.md) | `lcbp3-backup` | `restic`, Pull-based strategy | `/volume1/np-dms/backup/` | -| [07_disaster_recovery.md](07_disaster_recovery.md) | - | DR Plan, RTO/RPO Targets | - | -| [08_secrets_management.md](08_secrets_management.md) | - | Secrets & Credentials Management | - | - ---- - -## 🌐 Domain Mapping (NPM Proxy) - -### Application Domains (QNAP) - -| Domain | Service | Port | Host | Description | -| :-------------------- | :--------- | :--- | :--- | :------------------------ | -| `lcbp3.np-dms.work` | frontend | 3000 | QNAP | Frontend Next.js | -| `backend.np-dms.work` | backend | 3000 | QNAP | Backend NestJS API | -| `pma.np-dms.work` | pma | 80 | QNAP | phpMyAdmin | -| `git.np-dms.work` | gitea | 3000 | QNAP | Gitea Git Server | -| `n8n.np-dms.work` | n8n | 5678 | QNAP | n8n Workflow Automation | -| `chat.np-dms.work` | rocketchat | 3000 | QNAP | Rocket.Chat Service | -| `npm.np-dms.work` | npm | 81 | QNAP | Nginx Proxy Manager Admin | - -### Infrastructure Domains (ASUSTOR) - -| Domain | Service | Port | Host | Description | -| :----------------------- | :---------- | :--- | :------ | :----------------- | -| `grafana.np-dms.work` | grafana | 3000 | ASUSTOR | Grafana Dashboard | -| `prometheus.np-dms.work` | prometheus | 9090 | ASUSTOR | Prometheus Metrics | -| `uptime.np-dms.work` | uptime-kuma | 3001 | ASUSTOR | Uptime Monitoring | -| `portainer.np-dms.work` | portainer | 9443 | ASUSTOR | Docker Management | -| `registry.np-dms.work` | registry | 5000 | ASUSTOR | Docker Registry | - ---- - -## ⚙️ Core Services Summary - -### QNAP Services (Application) - -| Service | Technology | Port | Purpose | -| :---------------- | :----------------- | :----- | :------------------------------------------- | -| **Reverse Proxy** | NPM | 80/443 | SSL Termination, Domain Routing | -| **Backend API** | NestJS | 3000 | REST API, Business Logic, Workflow Engine | -| **Frontend** | Next.js | 3000 | Web UI (App Router, React, Tailwind, Shadcn) | -| **Database** | MariaDB 11.8 | 3306 | Primary Relational Database | -| **Cache** | Redis 7.2 | 6379 | Caching, Session, BullMQ | -| **Search** | Elasticsearch 8.11 | 9200 | Full-text Search | -| **Code Hosting** | Gitea | 3000 | Git Repository (Self-hosted) | -| **Workflow** | n8n | 5678 | Automation, Integrations (LINE, Email) | - -### ASUSTOR Services (Infrastructure) - -| Service | Technology | Port | Purpose | -| :--------------- | :-------------- | :--- | :---------------------------- | -| **Metrics** | Prometheus | 9090 | Metrics Collection | -| **Dashboard** | Grafana | 3000 | Visualization, Alerting | -| **Uptime** | Uptime Kuma | 3001 | Service Availability Monitor | -| **Registry** | Docker Registry | 5000 | Private Container Images | -| **Management** | Portainer | 9443 | Centralized Docker Management | -| **Host Metrics** | node-exporter | 9100 | CPU, Memory, Disk metrics | -| **Container** | cAdvisor | 8080 | Container resource metrics | -| **Backup** | Restic/Borg | N/A | Automated Backups | - ---- - -## 🔧 Quick Reference - -### Docker Commands (QNAP - Container Station) - -```bash -# ดู containers ทั้งหมด -docker ps -a - -# ดู logs -docker logs -f - -# เข้าไปใน container -docker exec -it sh - -# Restart service -docker restart -``` - -### Docker Commands (ASUSTOR - Portainer) - -```bash -# Remote Docker endpoint connection -# Configure via Portainer UI: Settings > Environments > Add Environment - -# Direct SSH to ASUSTOR -ssh admin@192.168.10.9 - -# Portainer API (Optional) -curl -X GET https://portainer.np-dms.work/api/endpoints \ - -H "X-API-Key: " -``` - -### Network - -```bash -# สร้าง external network (ครั้งแรก) - ต้องทำทั้ง 2 servers -# On QNAP: -docker network create lcbp3 - -# On ASUSTOR: -docker network create lcbp3 - -# ดู network -docker network ls -docker network inspect lcbp3 -``` - -### MariaDB - -```bash -# เข้า MySQL CLI (QNAP) -docker exec -it mariadb mysql -u root -p - -# Backup database (QNAP -> ASUSTOR) -docker exec mariadb mysqldump -u root -p lcbp3 > backup.sql -# Copy to ASUSTOR via NFS/SCP -``` - ---- - -## ⚙️ Environment Variables - -ตัวแปรสำคัญที่ใช้ร่วมกันทุก Service: - -| Variable | Value | Description | -| :----------------------- | :------------- | :--------------------- | -| `TZ` | `Asia/Bangkok` | Timezone | -| `MYSQL_HOST` / `DB_HOST` | `mariadb` | MariaDB hostname | -| `MYSQL_PORT` / `DB_PORT` | `3306` | MariaDB port | -| `REDIS_HOST` | `cache` | Redis hostname | -| `ELASTICSEARCH_HOST` | `search` | Elasticsearch hostname | - -> ⚠️ **Security Note:** Sensitive secrets (Password, Keys) ต้องใช้ `docker-compose.override.yml` (gitignored) หรือ Docker secrets - ห้ามระบุใน docker-compose.yml หลัก - ---- - -## 📚 เอกสารเสริม - -| ไฟล์ | คำอธิบาย | -| :------------------------------------------- | :-------------------------------------------------------- | -| [Git_command.md](Git_command.md) | คำสั่ง Git + Gitea Cheat Sheet | -| [lcbp3-db.md](lcbp3-db.md) | Docker Compose สำหรับ MariaDB (alternative version) | -| [09_app_deployment.md](09_app_deployment.md) | ขั้นตอน Deploy Backend + Frontend บน QNAP Container Station | - ---- - -## 📋 Checklist สำหรับการติดตั้งใหม่ - -### Phase 1: Network & Infrastructure -1. [ ] Configure VLANs on ER7206 Router -2. [ ] Configure Switch Profiles on SG2428P -3. [ ] Configure Static IPs (QNAP: .8, ASUSTOR: .9) - -### Phase 2: ASUSTOR Setup (Infra) -1. [ ] Create Docker Network: `docker network create lcbp3` -2. [ ] Deploy Portainer & Registry -3. [ ] Deploy Monitoring Stack (`prometheus.yml` with QNAP IP target) -4. [ ] Verify Prometheus can reach QNAP services - -### Phase 3: QNAP Setup (App) -1. [ ] Create Docker Network: `docker network create lcbp3` -2. [ ] Create `.env` file with secure passwords -3. [ ] Deploy **MariaDB** (Wait for init) -4. [ ] Deploy **Redis Standalone** (Check AOF is active) -5. [ ] Deploy **Elasticsearch** (Check Heap limit) -6. [ ] Deploy **NPM** & App Services (Backend/Frontend) -7. [ ] Verify Internal Load Balancing (Backend Replicas) - -### Phase 4: Backup & Security -1. [ ] Configure Restic on ASUSTOR to pull from QNAP -2. [ ] Set Resource Limits (Check `docker stats`) -3. [ ] Configure Firewall ACL Rules - ---- - -> 📝 **หมายเหตุ:** เอกสารทั้งหมดอ้างอิงจาก Architecture Document **v1.8.0** และ DMS Container Schema **v1.7.0** diff --git a/specs/08-infrastructure/SSH_setting.md b/specs/08-infrastructure/SSH_setting.md deleted file mode 100644 index 28e651e..0000000 --- a/specs/08-infrastructure/SSH_setting.md +++ /dev/null @@ -1,219 +0,0 @@ -# SSH Setting — QNAP & ASUSTOR - -> คู่มือการตั้งค่าและใช้งาน SSH สำหรับ NAS ทั้ง 2 เครื่องในโปรเจกต์ NAP-DMS - ---- - -## 📋 ข้อมูลการเชื่อมต่อ - -| รายการ | QNAP (TS-464) | ASUSTOR (AS5402T) | -| ------------- | ------------------ | ------------------- | -| **Role** | Application Server | Monitoring / Backup | -| **IP** | `192.168.10.8` | `192.168.10.9` | -| **SSH Port** | `22` | `22` | -| **Username** | `nattanin` | `nattanin` | -| **SSH Alias** | `qnap` | `asustor` | - ---- - -## 1. เปิดใช้งาน SSH บน NAS - -### 1.1 QNAP - -1. เข้า **QTS Web UI** → `http://192.168.10.8:8080` -2. ไปที่ **Control Panel → Network & File Services → Telnet / SSH** -3. เปิด ✅ **Allow SSH connection** -4. ตั้ง Port เป็น `22` -5. คลิก **Apply** - -### 1.2 ASUSTOR - -1. เข้า **ADM Web UI** → `http://192.168.10.9:8000` -2. ไปที่ **Settings → Terminal & SNMP** -3. เปิด ✅ **Enable SSH service** -4. ตั้ง Port เป็น `22` -5. คลิก **Apply** - ---- - -## 2. ตั้งค่า SSH Key (Client → NAS) - -### 2.1 สร้าง SSH Key (ทำครั้งเดียวบนเครื่อง Client) - -```powershell -# ตรวจสอบว่ามี key อยู่แล้วหรือไม่ -ls ~/.ssh/id_ed25519* - -# ถ้ายังไม่มี → สร้างใหม่ -ssh-keygen -t ed25519 -C "nattanin@np-dms" -``` - -> **หมายเหตุ:** กด Enter ผ่าน passphrase ได้ หรือตั้ง passphrase เพื่อความปลอดภัยเพิ่มเติม - -### 2.2 คัดลอก Public Key ไปยัง NAS - -```powershell -# QNAP -ssh-copy-id -i ~/.ssh/id_ed25519.pub nattanin@192.168.10.8 - -# ASUSTOR -ssh-copy-id -i ~/.ssh/id_ed25519.pub nattanin@192.168.10.9 -``` - -> **ถ้า `ssh-copy-id` ไม่มีบน Windows** ให้ทำ manual: - -```powershell -# อ่าน public key -cat ~/.ssh/id_ed25519.pub - -# SSH เข้า NAS ด้วย password ก่อน แล้วเพิ่ม key -ssh nattanin@192.168.10.8 -mkdir -p ~/.ssh && chmod 700 ~/.ssh -echo "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5... your-email@example.com" >> ~/.ssh/authorized_keys -chmod 600 ~/.ssh/authorized_keys -exit -``` - -### 2.3 ทดสอบการเชื่อมต่อ - -```powershell -# ต้องเข้าได้โดยไม่ต้องใส่ password -ssh nattanin@192.168.10.8 -ssh nattanin@192.168.10.9 -``` - ---- - -## 3. ตั้งค่า SSH Config (Client) - -ไฟล์: `~/.ssh/config` (Windows: `C:\Users\\.ssh\config`) - -```ssh-config -Host gitea - HostName git.np-dms.work - User git - Port 2222 - IdentityFile ~/.ssh/id_ed25519 - IdentitiesOnly yes - -Host qnap - HostName 192.168.10.8 - User nattanin - Port 22 - IdentityFile ~/.ssh/id_ed25519 - IdentitiesOnly yes - -Host asustor - HostName 192.168.10.9 - User nattanin - Port 22 - IdentityFile ~/.ssh/id_ed25519 - IdentitiesOnly yes -``` - -### การใช้งาน Alias - -```powershell -# แทนที่จะพิมพ์ ssh nattanin@192.168.10.8 -ssh qnap - -# แทนที่จะพิมพ์ ssh nattanin@192.168.10.9 -ssh asustor - -# Git push ไป Gitea (ใช้ alias gitea) -git push gitea main -``` - ---- - -## 4. คำสั่ง SSH ที่ใช้บ่อย - -### 4.1 การเชื่อมต่อ - -```powershell -# เชื่อมต่อปกติ -ssh qnap -ssh asustor - -# เชื่อมต่อพร้อมระบุ port (กรณี port ไม่ใช่ 22) -ssh -p 2222 nattanin@192.168.10.8 -``` - -### 4.2 คัดลอกไฟล์ (SCP) - -```powershell -# คัดลอกไฟล์จาก Local → NAS -scp ./myfile.txt qnap:/share/np-dms/data/ - -# คัดลอกไฟล์จาก NAS → Local -scp qnap:/share/np-dms/data/myfile.txt ./ - -# คัดลอก Folder (recursive) -scp -r ./myfolder qnap:/share/np-dms/data/ -``` - -### 4.3 รันคำสั่งบน NAS โดยไม่ต้อง Login - -```powershell -# ดู Docker containers ที่กำลังรัน -ssh qnap "docker ps" - -# ดู Disk usage -ssh qnap "df -h" - -# ดู logs ของ container -ssh qnap "docker logs --tail 50 lcbp3-backend" - -# Restart container -ssh qnap "docker restart lcbp3-backend" -``` - -### 4.4 Port Forwarding (Tunnel) - -```powershell -# Forward port 3306 ของ QNAP มาที่ localhost:3306 (MariaDB) -ssh -L 3306:localhost:3306 qnap - -# Forward port 9200 (Elasticsearch) -ssh -L 9200:localhost:9200 qnap - -# Forward port 3000 (Grafana จาก ASUSTOR) -ssh -L 3000:localhost:3000 asustor -``` - ---- - -## 5. Hardening (เพิ่มความปลอดภัย) - -> กำหนดค่าบน NAS แต่ละเครื่อง — ไฟล์: `/etc/ssh/sshd_config` - -```bash -# ปิด login ด้วย password (ใช้ key เท่านั้น) -PasswordAuthentication no - -# ปิด root login -PermitRootLogin no - -# อนุญาตเฉพาะ user ที่ต้องการ -AllowUsers nattanin - -# Restart SSH service (QNAP) -/etc/init.d/login_server.sh restart - -# Restart SSH service (ASUSTOR) -/etc/init.d/sshd restart -``` - -> ⚠️ **คำเตือน:** ก่อนปิด `PasswordAuthentication` ให้แน่ใจว่า SSH Key ใช้งานได้แล้ว มิฉะนั้นจะเข้าไม่ได้ — ต้อง login ผ่าน Web UI เพื่อแก้ไข - ---- - -## 6. Troubleshooting - -| ปัญหา | สาเหตุ | วิธีแก้ | -| ------------------------------- | -------------------------- | --------------------------------------------------------------------- | -| `Connection refused` | SSH ไม่ได้เปิดบน NAS | เปิด SSH ผ่าน Web UI (ตาม Section 1) | -| `Permission denied (publickey)` | Key ไม่ตรงหรือ permission ผิด | ตรวจ `chmod 700 ~/.ssh` และ `chmod 600 ~/.ssh/authorized_keys` บน NAS | -| `Host key verification failed` | IP เปลี่ยนแต่ key เก่ายังอยู่ | `ssh-keygen -R 192.168.10.8` แล้วเชื่อมต่อใหม่ | -| `Connection timed out` | Firewall block หรือ IP ผิด | ตรวจ ACL ใน `03_Securities.md` และ ping ทดสอบ | -| `Network is unreachable` | อยู่คนละ VLAN / subnet | ตรวจ routing ใน `02_Network_daigram.md` | diff --git a/specs/08-infrastructure/client_secret_1028957954367-93vi8kcnim3m28mnaqjbclasjvfrbbgo.apps.googleusercontent.com.json b/specs/08-infrastructure/client_secret_1028957954367-93vi8kcnim3m28mnaqjbclasjvfrbbgo.apps.googleusercontent.com.json deleted file mode 100644 index 79bf25e..0000000 --- a/specs/08-infrastructure/client_secret_1028957954367-93vi8kcnim3m28mnaqjbclasjvfrbbgo.apps.googleusercontent.com.json +++ /dev/null @@ -1 +0,0 @@ -{"web":{"client_id":"1028957954367-93vi8kcnim3m28mnaqjbclasjvfrbbgo.apps.googleusercontent.com","project_id":"gen-lang-client-0716462124","auth_uri":"https://accounts.google.com/o/oauth2/auth","token_uri":"https://oauth2.googleapis.com/token","auth_provider_x509_cert_url":"https://www.googleapis.com/oauth2/v1/certs","client_secret":"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","redirect_uris":["https://np-dms.cloudflareaccess.com/cdn-cgi/access/callback"],"javascript_origins":["https://np-dms.cloudflareaccess.com"]}} diff --git a/specs/08-infrastructure/n8n_setting.md b/specs/08-infrastructure/n8n_setting.md deleted file mode 100644 index e3a21ad..0000000 --- a/specs/08-infrastructure/n8n_setting.md +++ /dev/null @@ -1,91 +0,0 @@ -# การติดตั้ง n8n ใน Docker - -* user id ของ gites: - - * uid=1000(node) gid=1000(node) groups=1000(node) - -## กำหนดสิทธิ - -```bash -# สำหรับ n8n volumes -chown -R 1000:1000 /share/np-dms/n8n -chmod -R 755 /share/np-dms/n8n -``` - -## Docker file - -```yml -# File: share/np-dms/n8n/docker-compose.yml -# DMS Container v1_7_0 แยก service และ folder, Application name:n8n service n8n -x-restart: &restart_policy - restart: unless-stopped - -x-logging: &default_logging - logging: - driver: "json-file" - options: - max-size: "10m" - max-file: "5" -services: - n8n: - <<: [*restart_policy, *default_logging] - image: n8nio/n8n:latest - container_name: n8n - stdin_open: true - tty: true - deploy: - resources: - limits: - cpus: "1.5" - memory: 2G - reservations: - cpus: "0.25" - memory: 512M - environment: - TZ: "Asia/Bangkok" - NODE_ENV: "production" - # N8N_PATH: "/n8n/" - N8N_PUBLIC_URL: "https://n8n.np-dms.work/" - WEBHOOK_URL: "https://n8n.np-dms.work/" - N8N_EDITOR_BASE_URL: "https://n8n.np-dms.work/" - N8N_PROTOCOL: "https" - N8N_HOST: "n8n.np-dms.work" - N8N_PORT: 5678 - N8N_PROXY_HOPS: "1" - N8N_DIAGNOSTICS_ENABLED: 'false' - N8N_SECURE_COOKIE: 'true' - N8N_ENCRYPTION_KEY: "9AAIB7Da9DW1qAhJE5/Bz4SnbQjeAngI" - N8N_BASIC_AUTH_ACTIVE: 'true' - N8N_BASIC_AUTH_USER: admin - N8N_BASIC_AUTH_PASSWORD: Center#2025 - N8N_ENFORCE_SETTINGS_FILE_PERMISSIONS: 'true' - GENERIC_TIMEZONE: "Asia/Bangkok" - DB_TYPE: mysqldb - DB_MYSQLDB_DATABASE: "n8n" - DB_MYSQLDB_USER: "center" - DB_MYSQLDB_PASSWORD: "Center#2025" - DB_MYSQLDB_HOST: "mariadb" - DB_MYSQLDB_PORT: 3306 - - ports: - - "5678:5678" - networks: - lcbp3: {} - volumes: - - "/share/np-dms/n8n:/home/node/.n8n" - - "/share/np-dms/n8n/cache:/home/node/.cache" - - "/share/np-dms/n8n/scripts:/scripts" - - "/share/np-dms/n8n/data:/data" - - "/var/run/docker.sock:/var/run/docker.sock" - - healthcheck: - test: ["CMD", "wget", "-qO-", "http://127.0.0.1:5678/"] - # test: ["CMD", "curl", "-f", "http://127.0.0.1:5678/ || exit 1"] - interval: 15s - timeout: 5s - retries: 30 - -networks: - lcbp3: - external: true -``` diff --git a/specs/08-infrastructure/lcbp3-db.md b/specs/99-archives/lcbp3-db.md similarity index 100% rename from specs/08-infrastructure/lcbp3-db.md rename to specs/99-archives/lcbp3-db.md