Add development environment setup and deployment guides
This commit is contained in:
16
.env.development
Normal file
16
.env.development
Normal file
@@ -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
|
||||||
212
DEPLOYMENT.md
Normal file
212
DEPLOYMENT.md
Normal file
@@ -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 <tenant_code>"
|
||||||
|
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 個容器)
|
||||||
|
|
||||||
|
### 替代方案:單一服務多租戶
|
||||||
|
目前的設計 (路徑參數路由) 已經支援多租戶,不需要為每個租戶部署獨立服務。除非有特殊需求 (例如:租戶要求獨立部署、資源隔離等)。
|
||||||
92
QUICK_START.md
Normal file
92
QUICK_START.md
Normal file
@@ -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 可能會衝突。建議開發時使用測試帳號。
|
||||||
24
START_DEVELOPMENT.bat
Normal file
24
START_DEVELOPMENT.bat
Normal file
@@ -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
|
||||||
19
traefik-dev-route.yml
Normal file
19
traefik-dev-route.yml
Normal file
@@ -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"
|
||||||
90
update_keycloak_redirect_uri.py
Normal file
90
update_keycloak_redirect_uri.py
Normal file
@@ -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)
|
||||||
Reference in New Issue
Block a user