# ============================================================================= # 企微IT智能服务台 — 群晖 NAS Docker Compose(含 Cloudflare Tunnel) # ============================================================================= # 适用场景:群晖 NAS + Cloudflare Tunnel + 未认证企微 # 域名:itdesk.amanzac.com(通过 Cloudflare Tunnel 暴露到公网) # # 用法: # 1. 先完成 Cloudflare Tunnel 创建,获取 token(见部署指南 §2) # 2. 复制 .env.nas 为 .env 并填入 token 和企微配置 # 3. 在 NAS SSH 或 Container Manager 中启动: # docker compose -f docker-compose.nas.yml up -d # # 架构: # 互联网 → Cloudflare Edge → Cloudflare Tunnel → nginx:80 → { /itdesk/, /itagent/, /api/, /ws/ } # ============================================================================= services: # -------------------------------------------------------------------------- # Cloudflare Tunnel — 内网穿透,替代公网 IP + HTTPS # -------------------------------------------------------------------------- # 原理:cloudflared 容器主动连接 Cloudflare Edge,建立反向隧道 # 无需开放任何端口,无需公网 IP,无需 SSL 证书 # 配置:在 Cloudflare Dashboard > Zero Trust > Networks > Tunnels 创建 # 获得 tunnel token 后填入 .env 的 CF_TUNNEL_TOKEN # -------------------------------------------------------------------------- cloudflared: image: cloudflare/cloudflared:latest container_name: wecom_it_cloudflared restart: unless-stopped # --no-autoupdate 防止容器内自动更新导致重启 # token 从 Cloudflare Dashboard 获取 command: tunnel --no-autoupdate run environment: - TUNNEL_TOKEN=${CF_TUNNEL_TOKEN} networks: - it-desk-internal depends_on: - nginx logging: driver: "json-file" options: max-size: "10m" max-file: "3" # -------------------------------------------------------------------------- # PostgreSQL 16 — 持久化数据库 # -------------------------------------------------------------------------- postgres: image: postgres:16-alpine container_name: wecom_it_postgres restart: unless-stopped environment: POSTGRES_USER: ${POSTGRES_USER:-wecom} POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:-wecom_secret} POSTGRES_DB: ${POSTGRES_DB:-wecom_it_desk} volumes: # 群晖建议使用共享文件夹存储,便于备份 # 格式:/volume1/docker/wecom-it-desk/postgres:/var/lib/postgresql/data - postgres_data:/var/lib/postgresql/data healthcheck: test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER:-wecom}"] interval: 5s timeout: 5s retries: 5 networks: - it-desk-internal logging: driver: "json-file" options: max-size: "10m" max-file: "3" # -------------------------------------------------------------------------- # Redis 7 — 缓存服务(token、会话、员工信息) # -------------------------------------------------------------------------- redis: image: redis:7-alpine container_name: wecom_it_redis restart: unless-stopped command: redis-server --appendonly yes --save 900 1 --save 300 10 volumes: - redis_data:/data healthcheck: test: ["CMD", "redis-cli", "ping"] interval: 5s timeout: 5s retries: 5 networks: - it-desk-internal logging: driver: "json-file" options: max-size: "10m" max-file: "3" # -------------------------------------------------------------------------- # FastAPI 后端 — 核心业务服务 # -------------------------------------------------------------------------- backend: build: context: ./backend dockerfile: Dockerfile image: wecom-it-desk-backend:latest container_name: wecom_it_backend restart: unless-stopped environment: # 企微凭证(从 .env 文件读取) - WECOM_CORP_ID=${WECOM_CORP_ID} - WECOM_AGENT_ID=${WECOM_AGENT_ID} - WECOM_SECRET=${WECOM_SECRET} - WECOM_TOKEN=${WECOM_TOKEN} - WECOM_ENCODING_AES_KEY=${WECOM_ENCODING_AES_KEY} # 数据库(Docker 内部网络) - DATABASE_URL=postgresql://${POSTGRES_USER:-wecom}:${POSTGRES_PASSWORD:-wecom_secret}@postgres:5432/${POSTGRES_DB:-wecom_it_desk} # Redis(Docker 内部网络) - REDIS_URL=redis://redis:6379/0 # CORS(NAS 部署用 Cloudflare Tunnel 域名) - CORS_ORIGINS=${CORS_ORIGINS:-https://itdesk.amanzac.com} # AI 服务(Dify)— NAS 部署可能无法直连内网 Dify,留空则禁用 AI 功能 - DIFY_API_URL=${DIFY_API_URL:-} - DIFY_API_KEY=${DIFY_API_KEY:-} - DIFY_TIMEOUT=${DIFY_TIMEOUT:-30} # AI Wingman(留空禁用) - DIFY_WINGMAN_API_URL=${DIFY_WINGMAN_API_URL:-} - DIFY_WINGMAN_API_KEY=${DIFY_WINGMAN_API_KEY:-} - DIFY_WINGMAN_TIMEOUT=${DIFY_WINGMAN_TIMEOUT:-30} # Mock 登录(测试阶段跳过 OAuth2) - MOCK_LOGIN_ENABLED=${MOCK_LOGIN_ENABLED:-false} # 服务配置 - BACKEND_HOST=0.0.0.0 - BACKEND_PORT=8000 depends_on: postgres: condition: service_healthy redis: condition: service_healthy command: > /bin/sh -c " echo '>>> 执行数据库迁移...' && cd /app && PYTHONPATH=/app alembic upgrade head && echo '>>> 启动 API 服务...' && uvicorn app.main:app --host 0.0.0.0 --port 8000 --workers 2 " networks: - it-desk-internal healthcheck: test: ["CMD-SHELL", "curl -f http://localhost:8000/health || exit 1"] interval: 15s timeout: 5s retries: 3 start_period: 30s logging: driver: "json-file" options: max-size: "20m" max-file: "5" # -------------------------------------------------------------------------- # Nginx — 反向代理 + 静态文件服务 # -------------------------------------------------------------------------- nginx: image: nginx:1.27-alpine container_name: wecom_it_nginx restart: unless-stopped # NAS 部署不需要映射端口到宿主机(Cloudflare Tunnel 直接连接容器网络) # 但保留映射方便内网调试 ports: - "18080:80" volumes: - ./nginx/nginx-nas.conf:/etc/nginx/nginx.conf:ro - ./frontend-h5/dist:/usr/share/nginx/html/itdesk:ro - ./frontend-agent/dist:/usr/share/nginx/html/itagent:ro depends_on: - backend networks: - it-desk-internal healthcheck: test: ["CMD-SHELL", "curl -f http://localhost:80/itdesk/health || exit 1"] interval: 15s timeout: 5s retries: 3 logging: driver: "json-file" options: max-size: "10m" max-file: "3" # ============================================================================= # 网络 # ============================================================================= networks: it-desk-internal: driver: bridge # ============================================================================= # 数据卷 — 持久化存储 # ============================================================================= volumes: postgres_data: name: wecom_it_postgres_data redis_data: name: wecom_it_redis_data