From 0dd8ae21e9b31994e6e3e8aa3f107f75cb51568b Mon Sep 17 00:00:00 2001 From: porsche5130 Date: Wed, 4 Mar 2026 01:19:46 +0800 Subject: [PATCH] Add development environment setup and deployment guides --- .env.development | 16 +++ DEPLOYMENT.md | 212 ++++++++++++++++++++++++++++++++ QUICK_START.md | 92 ++++++++++++++ START_DEVELOPMENT.bat | 24 ++++ traefik-dev-route.yml | 19 +++ update_keycloak_redirect_uri.py | 90 ++++++++++++++ 6 files changed, 453 insertions(+) create mode 100644 .env.development create mode 100644 DEPLOYMENT.md create mode 100644 QUICK_START.md create mode 100644 START_DEVELOPMENT.bat create mode 100644 traefik-dev-route.yml create mode 100644 update_keycloak_redirect_uri.py diff --git a/.env.development b/.env.development new file mode 100644 index 0000000..c74365a --- /dev/null +++ b/.env.development @@ -0,0 +1,16 @@ +# WebMail Gateway 開發環境配置 + +# Redis 配置 +REDIS_HOST=10.1.0.20 +REDIS_PORT=6379 +REDIS_PASSWORD=DC1qaz2wsx +REDIS_DB=2 + +# Virtual MIS 資料庫 +DATABASE_URL=postgresql://admin:DC1qaz2wsx@10.1.0.20:5433/virtual_mis + +# Keycloak 配置 +KEYCLOAK_SERVER_URL=https://auth.lab.taipei + +# 開發環境 Port +PORT=8100 diff --git a/DEPLOYMENT.md b/DEPLOYMENT.md new file mode 100644 index 0000000..412d142 --- /dev/null +++ b/DEPLOYMENT.md @@ -0,0 +1,212 @@ +# WebMail Gateway 部署指南 + +## 環境架構 + +### 開發環境 (Development) +- **主機**: 10.1.0.245 (Windows 桌機) +- **目錄**: `D:\_Develop\porscheworld_develop\webmail-gateway\` +- **Port**: 8100 +- **啟動**: `START_DEVELOPMENT.bat` +- **用途**: 開發、測試、功能迭代 + +### 正式環境 (Production) +- **主機**: 10.1.0.254 (Ubuntu Server - home) +- **目錄**: `/home/porsche/services/webmail-gateway/` +- **Port**: 8000 (容器內) +- **啟動**: `docker compose up -d` +- **用途**: 對外服務、穩定運行 + +--- + +## 開發流程 + +### 1. 在開發環境啟動服務 + +```batch +cd D:\_Develop\porscheworld_develop\webmail-gateway +START_DEVELOPMENT.bat +``` + +### 2. 切換 Traefik 路由到開發機 + +在伺服器上執行: + +```bash +ssh porsche@10.1.0.254 + +# 停止正式環境容器 +cd /home/porsche/services/webmail-gateway +docker compose down + +# 建立 Traefik dynamic 配置目錄 (如果不存在) +mkdir -p /home/porsche/traefik/dynamic + +# 上傳開發路由配置 +# (從開發機執行) +scp traefik-dev-route.yml porsche@10.1.0.254:/home/porsche/traefik/dynamic/webmail-dev.yml + +# Traefik 會自動重新載入配置 +``` + +### 3. 開發與測試 + +現在訪問 https://webmail.lab.taipei 會自動路由到開發機 (10.1.0.245:8100) + +- ✅ 支援熱重載 (`--reload`) +- ✅ 可以即時修改程式碼 +- ✅ 可以使用 debugger + +### 4. 切換回正式環境 + +```bash +ssh porsche@10.1.0.254 + +# 刪除開發路由配置 +rm /home/porsche/traefik/dynamic/webmail-dev.yml + +# 啟動正式環境容器 +cd /home/porsche/services/webmail-gateway +docker compose up -d +``` + +--- + +## 部署到正式環境 + +### 方案 A: 直接複製 (快速) + +```bash +# 1. 停止開發伺服器 (Ctrl+C) + +# 2. 從開發機上傳到伺服器 +scp app.py porsche@10.1.0.254:/home/porsche/services/webmail-gateway/ + +# 3. 重建容器 +ssh porsche@10.1.0.254 'cd /home/porsche/services/webmail-gateway && docker compose down && docker compose build && docker compose up -d' +``` + +### 方案 B: 透過 Git (推薦) + +```bash +# 1. 在開發機提交變更 +cd D:\_Develop\porscheworld_develop\webmail-gateway +git add . +git commit -m "Feature: 完整 Gmail 風格 UI" +git push gitea main + +# 2. 在伺服器拉取 +ssh porsche@10.1.0.254 +cd /home/porsche/services/webmail-gateway +git pull +docker compose down && docker compose build && docker compose up -d +``` + +--- + +## 多租戶服務部署架構 + +當需要為每個租戶部署獨立服務時 (例如 10 個租戶 = 10 個服務): + +### 架構設計 + +``` +Traefik (Reverse Proxy) + │ + ├─── webmail.lab.taipei/vmis-admin → webmail-vmis-admin:8000 + ├─── webmail.lab.taipei/porsche1 → webmail-porsche1:8000 + ├─── webmail.lab.taipei/porsche2 → webmail-porsche2:8000 + ├─── ... + └─── webmail.lab.taipei/tenant10 → webmail-tenant10:8000 +``` + +### 部署腳本範例 + +```bash +#!/bin/bash +# deploy_tenant_service.sh + +TENANT_CODE=$1 + +if [ -z "$TENANT_CODE" ]; then + echo "Usage: ./deploy_tenant_service.sh " + exit 1 +fi + +# 建立租戶專屬目錄 +mkdir -p /home/porsche/services/webmail-${TENANT_CODE} + +# 複製基礎配置 +cp -r /home/porsche/services/webmail-gateway/Dockerfile /home/porsche/services/webmail-${TENANT_CODE}/ +cp -r /home/porsche/services/webmail-gateway/app.py /home/porsche/services/webmail-${TENANT_CODE}/ +cp -r /home/porsche/services/webmail-gateway/requirements.txt /home/porsche/services/webmail-${TENANT_CODE}/ + +# 建立專屬 docker-compose.yml +cat > /home/porsche/services/webmail-${TENANT_CODE}/docker-compose.yml << EOF +services: + webmail-${TENANT_CODE}: + build: . + container_name: webmail-${TENANT_CODE} + environment: + - REDIS_HOST=10.1.0.20 + - REDIS_PORT=6379 + - REDIS_PASSWORD=DC1qaz2wsx + - REDIS_DB=2 + - DATABASE_URL=postgresql://admin:DC1qaz2wsx@10.1.0.20:5433/virtual_mis + - KEYCLOAK_SERVER_URL=https://auth.lab.taipei + - TENANT_CODE=${TENANT_CODE} + networks: + - traefik-network + - mailserver_mailserver-internal + labels: + - "traefik.enable=true" + - "traefik.docker.network=traefik-network" + - "traefik.http.routers.webmail-${TENANT_CODE}.rule=Host(\`webmail.lab.taipei\`) && PathPrefix(\`/${TENANT_CODE}\`)" + - "traefik.http.routers.webmail-${TENANT_CODE}.entrypoints=web,websecure" + - "traefik.http.routers.webmail-${TENANT_CODE}.tls.certresolver=letsencrypt" + - "traefik.http.services.webmail-${TENANT_CODE}.loadbalancer.server.port=8000" + restart: always + +networks: + traefik-network: + external: true + mailserver_mailserver-internal: + external: true +EOF + +# 啟動容器 +cd /home/porsche/services/webmail-${TENANT_CODE} +docker compose up -d + +echo "Tenant ${TENANT_CODE} service deployed!" +echo "Access: https://webmail.lab.taipei/${TENANT_CODE}" +``` + +### 使用方式 + +```bash +# 部署 porsche1 租戶服務 +./deploy_tenant_service.sh porsche1 + +# 部署 porsche2 租戶服務 +./deploy_tenant_service.sh porsche2 + +# ... 依此類推 +``` + +--- + +## 注意事項 + +### 開發環境切換 +- ⚠️ 開發時請確保正式環境容器已停止 (`docker compose down`) +- ⚠️ 開發完成後記得切換回正式環境 +- ⚠️ 不要在開發環境直接操作正式資料庫 + +### 多租戶部署 +- 🔄 每個租戶獨立容器,互不影響 +- 📊 可以針對不同租戶調整資源限制 +- 🔍 容易追蹤各租戶的日誌和效能 +- 💰 資源消耗較高 (10 個租戶 = 10 個容器) + +### 替代方案:單一服務多租戶 +目前的設計 (路徑參數路由) 已經支援多租戶,不需要為每個租戶部署獨立服務。除非有特殊需求 (例如:租戶要求獨立部署、資源隔離等)。 diff --git a/QUICK_START.md b/QUICK_START.md new file mode 100644 index 0000000..5ad9b07 --- /dev/null +++ b/QUICK_START.md @@ -0,0 +1,92 @@ +# WebMail Gateway 快速開發指南 + +## 開發環境設定 (最簡單方式) + +### 第一步:停止正式環境 + +```bash +ssh porsche@10.1.0.254 'cd /home/porsche/services/webmail-gateway && docker compose down' +``` + +### 第二步:在開發機啟動服務 + +```batch +cd D:\_Develop\porscheworld_develop\webmail-gateway +START_DEVELOPMENT.bat +``` + +服務會在 `http://10.1.0.245:8100` 啟動 + +### 第三步:測試訪問 + +**方式 A: 直接訪問開發機 (推薦)** +- ✅ http://10.1.0.245:8100/vmis-admin +- ✅ http://10.1.0.245:8100/porsche1 + +**方式 B: 透過 SSH 隧道** +```bash +# 在本機執行 +ssh -L 8100:10.1.0.245:8100 porsche@10.1.0.254 + +# 然後訪問 +# http://localhost:8100/vmis-admin +``` + +**方式 C: 修改 hosts 檔案 (如果需要 HTTPS)** +``` +# Windows: C:\Windows\System32\drivers\etc\hosts +10.1.0.245 webmail.lab.taipei +``` +然後訪問: http://webmail.lab.taipei:8100/vmis-admin + +--- + +## 開發完成後:部署到正式環境 + +### 快速部署 + +```bash +# 1. 上傳修改的檔案 +scp app.py porsche@10.1.0.254:/home/porsche/services/webmail-gateway/ + +# 2. 重啟正式環境 +ssh porsche@10.1.0.254 'cd /home/porsche/services/webmail-gateway && docker compose down && docker compose build && docker compose up -d' +``` + +### 透過 Git 部署 (建立 Gitea 倉庫後) + +```bash +# 1. 提交變更 +git add . +git commit -m "Feature: 完整 UI 功能" +git push gitea main + +# 2. 伺服器拉取 +ssh porsche@10.1.0.254 +cd /home/porsche/services/webmail-gateway +git pull +docker compose down && docker compose build && docker compose up -d +``` + +--- + +## 重要注意事項 + +⚠️ **Keycloak Redirect URI** + +開發時如果使用不同的 URL,需要更新 Keycloak client 的 Redirect URI: + +``` +正式環境: https://webmail.lab.taipei/{tenant_code}/callback +開發環境: http://10.1.0.245:8100/{tenant_code}/callback +``` + +可以同時設定兩個 URI,這樣開發和正式環境都能用。 + +⚠️ **Redis 和 Database** + +開發環境會直接連接正式環境的 Redis (10.1.0.20) 和 PostgreSQL (10.1.0.20),請小心操作。 + +⚠️ **Session 衝突** + +如果同時有人在使用正式環境,Session 可能會衝突。建議開發時使用測試帳號。 diff --git a/START_DEVELOPMENT.bat b/START_DEVELOPMENT.bat new file mode 100644 index 0000000..4413847 --- /dev/null +++ b/START_DEVELOPMENT.bat @@ -0,0 +1,24 @@ +@echo off +REM WebMail Gateway 開發環境啟動腳本 + +echo ================================================ +echo WebMail Gateway - Development Server +echo ================================================ +echo. + +REM 載入環境變數 +set REDIS_HOST=10.1.0.20 +set REDIS_PORT=6379 +set REDIS_PASSWORD=DC1qaz2wsx +set REDIS_DB=2 +set DATABASE_URL=postgresql://admin:DC1qaz2wsx@10.1.0.20:5433/virtual_mis +set KEYCLOAK_SERVER_URL=https://auth.lab.taipei + +echo Starting Uvicorn on port 8100... +echo. +echo Access URL: http://10.1.0.245:8100 +echo. +echo Press Ctrl+C to stop the server +echo. + +uvicorn app:app --host 0.0.0.0 --port 8100 --reload diff --git a/traefik-dev-route.yml b/traefik-dev-route.yml new file mode 100644 index 0000000..7922ec1 --- /dev/null +++ b/traefik-dev-route.yml @@ -0,0 +1,19 @@ +## Traefik 開發路由配置 +## 將此檔案放到伺服器的 Traefik dynamic 配置目錄 + +http: + routers: + webmail-dev: + rule: "Host(`webmail.lab.taipei`)" + entryPoints: + - web + - websecure + service: webmail-dev-service + tls: + certResolver: letsencrypt + + services: + webmail-dev-service: + loadBalancer: + servers: + - url: "http://10.1.0.245:8100" diff --git a/update_keycloak_redirect_uri.py b/update_keycloak_redirect_uri.py new file mode 100644 index 0000000..0c82d59 --- /dev/null +++ b/update_keycloak_redirect_uri.py @@ -0,0 +1,90 @@ +"""更新 Keycloak webmail client 的 Redirect URI 以支援開發環境""" + +import requests +from urllib3.exceptions import InsecureRequestWarning +requests.packages.urllib3.disable_warnings(InsecureRequestWarning) + +KEYCLOAK_URL = "https://auth.lab.taipei" +REALMS = ["vmis-admin", "porsche1"] + +# 使用 admin-cli 直接取得 access token +token_url = f"{KEYCLOAK_URL}/realms/master/protocol/openid-connect/token" +token_data = { + "client_id": "admin-cli", + "username": "admin", + "password": "admin", + "grant_type": "password" +} + +print("Getting Admin Access Token...") +response = requests.post(token_url, data=token_data, verify=False) +if response.status_code != 200: + print(f"[ERROR] Cannot get token: {response.text}") + exit(1) + +access_token = response.json()["access_token"] +print("[OK] Access Token retrieved\n") + +headers = { + "Authorization": f"Bearer {access_token}", + "Content-Type": "application/json" +} + +for realm in REALMS: + print(f"=" * 60) + print(f"Processing Realm: {realm}") + print(f"=" * 60) + + # 取得 webmail client + clients_url = f"{KEYCLOAK_URL}/admin/realms/{realm}/clients" + response = requests.get(clients_url, headers=headers, verify=False) + + if response.status_code != 200: + print(f"[ERROR] 無法取得 clients: {response.text}") + continue + + clients = response.json() + webmail_client = None + + for client in clients: + if client["clientId"] == "webmail": + webmail_client = client + break + + if not webmail_client: + print(f"[WARNING] webmail client 不存在於 {realm} realm\n") + continue + + print(f"[OK] 找到 webmail client (ID: {webmail_client['id']})") + + # 更新 Redirect URIs (支援開發環境 + 正式環境) + webmail_client["redirectUris"] = [ + # 正式環境 + f"https://webmail.lab.taipei/{realm}/callback", + "https://webmail.lab.taipei/*", + # 開發環境 + f"http://10.1.0.245:8100/{realm}/callback", + "http://10.1.0.245:8100/*" + ] + + webmail_client["webOrigins"] = [ + "https://webmail.lab.taipei", + "http://10.1.0.245:8100" + ] + + print(f"更新 Redirect URIs...") + update_url = f"{KEYCLOAK_URL}/admin/realms/{realm}/clients/{webmail_client['id']}" + response = requests.put(update_url, json=webmail_client, headers=headers, verify=False) + + if response.status_code in (200, 204): + print(f"[OK] Redirect URIs 已更新") + print("\n新的 Redirect URIs:") + for uri in webmail_client["redirectUris"]: + print(f" - {uri}") + print() + else: + print(f"[ERROR] 更新失敗: {response.status_code} - {response.text}\n") + +print("=" * 60) +print("完成!現在可以在開發環境測試了") +print("=" * 60)