diff --git a/CLAUDE.md b/CLAUDE.md index cc23bca..294f0ae 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -68,8 +68,9 @@ Lang::setLocale('en'); ├── docker-compose.yml # Docker 编排 ├── nginx/ # OpenResty 配置 ├── frpc/ # frpc 配置 -├── PROJECT.md # 项目详细文档 -└── IMPLEMENTATION_PLAN.md # 实现计划 +├── docs/ # 项目文档 +│ ├── PROJECT.md # 项目详细文档 +│ └── IMPLEMENTATION_PLAN.md # 实现计划 ``` ## 环境配置 @@ -269,11 +270,11 @@ composer require symfony/var-dumper:^7.0 ## 快速参考 ### 查看问题汇总 -所有历史问题已整理在项目根目录的 `PROJECT_ISSUES.md` 文件中。 +所有历史问题已整理在 `docs/PROJECT_ISSUES.md` 文件中。 ### 让 CLAUDE 加载问题文档 ``` -请参考 /www/x-server-mobile/PROJECT_ISSUES.md 中的问题汇总 +请参考 docs/PROJECT_ISSUES.md 中的问题汇总 ``` ### 调试后端 diff --git a/deploy/.env.production.template b/deploy/.env.production.template new file mode 100644 index 0000000..f6ef367 --- /dev/null +++ b/deploy/.env.production.template @@ -0,0 +1,51 @@ +# X-Server Mobile 生产环境变量模板 +# 使用方法: cp .env.production.template .env.production +# 然后填写实际值,不要将 .env.production 提交到代码仓库 + +# ============================================ +# 数据库配置 +# ============================================ +DB_HOST=1Panel-mysql-xxxx +DB_PORT=3306 +DB_DATABASE=xserver +DB_USERNAME=xserver +DB_PASSWORD=your_strong_password_here + +# ============================================ +# Redis 配置 +# ============================================ +REDIS_HOST=1Panel-redis-xxxx +REDIS_PORT=6379 +REDIS_PASSWORD=your_redis_password_here +REDIS_DATABASE=0 + +# ============================================ +# 飞书配置 +# ============================================ +FEISHU_APP_ID=cli_xxxxxxxxxxxxx +FEISHU_APP_SECRET=xxxxxxxxxxxxxxxxxxxx +FEISHU_ENCRYPT_KEY=xxxxxxxxxxxxxxxxxxxx +FEISHU_VERIFICATION_TOKEN=xxxxxxxxxxxxxxxxxxxx +FEISHU_TENANT_KEY=14e7bc0fe5985740 + +# 飞书应用 URL(获取方式:飞书开放平台 -> 应用详情) +FEISHU_APP_URL=https://open.feishu.cn + +# ============================================ +# 应用配置 +# ============================================ +APP_ENV=production +APP_DEBUG=false +APP_URL=https://xsm.hnxinyouhe.com + +# ============================================ +# 日志配置 +# ============================================ +LOG_LEVEL=info + +# ============================================ +# 部署配置 +# ============================================ +DEPLOY_USER=root +DEPLOY_SERVER=your-server-ip +DEPLOY_PATH=/opt/x-server-mobile diff --git a/deploy/Dockerfile.prod b/deploy/Dockerfile.prod new file mode 100644 index 0000000..1ec798c --- /dev/null +++ b/deploy/Dockerfile.prod @@ -0,0 +1,61 @@ +# X-Server Mobile 生产环境 Dockerfile +# 基于官方 PHP 8.5 CLI 镜像 + +FROM php:8.5-cli + +# 安装系统依赖 +RUN apt-get update && apt-get install -y \ + unzip \ + git \ + libzip-dev \ + libpng-dev \ + libonig-dev \ + curl \ + && rm -rf /var/lib/apt/lists/* + +# 安装 PHP 扩展 +RUN docker-php-ext-install \ + zip \ + pdo \ + pdo_mysql \ + bcmath \ + pcntl \ + opcache \ + mbstring + +# 安装 Composer +COPY --from=composer:latest /usr/bin/composer /usr/local/bin/composer + +# 设置工作目录 +WORKDIR /app + +# 复制 composer 文件 +COPY composer.json composer.lock* ./ + +# 安装依赖(生产环境) +RUN composer install \ + --no-dev \ + --optimize-autoloader \ + --no-interaction \ + --no-progress + +# 复制应用文件 +COPY . . + +# 复制 PHP 生产环境配置 +COPY php-prod.ini /usr/local/etc/php/conf.d/99-production.ini + +# 创建运行时目录 +RUN mkdir -p /app/runtime/logs \ + && mkdir -p /app/runtime/cache \ + && chmod -R 755 /app/runtime + +# 暴露端口 +EXPOSE 8787 + +# 健康检查 +HEALTHCHECK --interval=30s --timeout=10s --start-period=40s --retries=3 \ + CMD php -r "exit(0);" || exit 1 + +# 启动 Webman(前台运行) +CMD ["php", "start.php", "start"] diff --git a/deploy/deploy.sh b/deploy/deploy.sh new file mode 100755 index 0000000..f43f623 --- /dev/null +++ b/deploy/deploy.sh @@ -0,0 +1,169 @@ +#!/bin/bash +# X-Server Mobile 生产部署脚本 +# 使用方法: ./deploy.sh [server] + +set -e + +# 配置 +SERVER=${1:-"your-server.com"} # 默认服务器,可以通过参数指定 +DEPLOY_USER="root" +DEPLOY_PATH="/opt/x-server-mobile" +FRONTEND_BUILD_DIR="./frontend/dist" +BACKEND_SOURCE_DIR="./backend" + +# 颜色输出 +RED='\033[0;31m' +GREEN='\033[0;32m' +YELLOW='\033[1;33m' +NC='\033[0m' # No Color + +log_info() { + echo -e "${GREEN}[INFO]${NC} $1" +} + +log_warn() { + echo -e "${YELLOW}[WARN]${NC} $1" +} + +log_error() { + echo -e "${RED}[ERROR]${NC} $1" +} + +# 检查环境 +check_env() { + log_info "检查部署环境..." + + if ! command -v rsync &> /dev/null; then + log_error "rsync 未安装,请先安装: apt-get install rsync" + exit 1 + fi + + if [ ! -d "$FRONTEND_BUILD_DIR" ]; then + log_error "前端构建目录不存在: $FRONTEND_BUILD_DIR" + log_info "请先运行: cd frontend && npm install && npm run build" + exit 1 + fi + + log_info "环境检查通过" +} + +# 备份远程服务器 +backup_remote() { + log_info "备份远程服务器..." + + BACKUP_NAME="$(date +%Y%m%d_%H%M%S)" + + ssh ${DEPLOY_USER}@${SERVER} << EOF + mkdir -p ${DEPLOY_PATH}/backup/${BACKUP_NAME} + cp -r ${DEPLOY_PATH}/frontend ${DEPLOY_PATH}/backup/${BACKUP_NAME}/ 2>/dev/null || true + cp -r ${DEPLOY_PATH}/backend ${DEPLOY_PATH}/backup/${BACKUP_NAME}/ 2>/dev/null || true + echo "备份完成: ${BACKUP_NAME}" +EOF + + log_info "备份完成: ${BACKUP_NAME}" +} + +# 部署前端 +deploy_frontend() { + log_info "部署前端..." + + rsync -avz --delete \ + --exclude='*.map' \ + ${FRONTEND_BUILD_DIR}/ \ + ${DEPLOY_USER}@${SERVER}:${DEPLOY_PATH}/frontend/ + + log_info "前端部署完成" +} + +# 部署后端 +deploy_backend() { + log_info "部署后端..." + + rsync -avz \ + --exclude='vendor/' \ + --exclude='runtime/' \ + --exclude='.git/' \ + --exclude='*.log' \ + ${BACKEND_SOURCE_DIR}/ \ + ${DEPLOY_USER}@${SERVER}:${DEPLOY_PATH}/backend/ + + log_info "后端部署完成" +} + +# 重启服务 +restart_services() { + log_info "重启服务..." + + ssh ${DEPLOY_USER}@${SERVER} << 'EOF' + cd /opt/x-server-mobile + + # 重启后端容器 + docker-compose -f docker-compose.prod.yml restart backend + + # 等待后端启动 + sleep 5 + + # 检查容器状态 + docker ps | grep x-server-backend-prod + + # 重载 OpenResty + openresty -s reload || nginx -s reload + + echo "服务重启完成" +EOF + + log_info "服务重启完成" +} + +# 健康检查 +health_check() { + log_info "健康检查..." + + sleep 3 + + # 检查前端 + if curl -s -o /dev/null -w "%{http_code}" http://${SERVER}/ | grep -q "200\|301\|302"; then + log_info "前端检查通过" + else + log_warn "前端检查失败" + fi + + # 检查 API + if curl -s http://${SERVER}/api/v1/health 2>/dev/null | grep -q "ok\|success"; then + log_info "API 检查通过" + else + log_warn "API 检查失败(可能需要登录)" + fi +} + +# 主流程 +main() { + echo "=========================================" + echo " X-Server Mobile 生产部署脚本" + echo "=========================================" + echo "服务器: ${SERVER}" + echo "部署路径: ${DEPLOY_PATH}" + echo "" + + # 确认部署 + read -p "确认部署到 ${SERVER}? (y/N) " -n 1 -r + echo + if [[ ! $REPLY =~ ^[Yy]$ ]]; then + log_info "部署已取消" + exit 0 + fi + + check_env + backup_remote + deploy_frontend + deploy_backend + restart_services + health_check + + echo "" + log_info "部署完成!" + echo "查看日志: ssh ${DEPLOY_USER}@${SERVER} 'docker logs -f x-server-backend-prod'" +} + +# 执行主流程 +main diff --git a/deploy/docker-compose.prod.yml b/deploy/docker-compose.prod.yml new file mode 100644 index 0000000..794e8ab --- /dev/null +++ b/deploy/docker-compose.prod.yml @@ -0,0 +1,58 @@ +# X-Server Mobile 生产环境 Docker Compose +# 使用方法: docker-compose -f docker-compose.prod.yml up -d + +services: + # PHP 后端服务 + backend: + build: + context: ./backend + dockerfile: Dockerfile.prod + container_name: x-server-backend-prod + restart: unless-stopped + ports: + - "8787:8787" + environment: + # 应用环境 + - APP_ENV=production + - APP_DEBUG=false + + # 数据库配置(使用 1Panel 的 MySQL) + # 注意:请替换为实际的容器名和密码 + - DB_HOST=1Panel-mysql-409s + - DB_PORT=3306 + - DB_DATABASE=xserver + - DB_USERNAME=xserver + - DB_PASSWORD=${DB_PASSWORD:-change_me_in_production} + + # Redis 配置(使用 1Panel 的 Redis) + # 注意:请替换为实际的容器名和密码 + - REDIS_HOST=1Panel-redis-xxxx + - REDIS_PORT=6379 + - REDIS_PASSWORD=${REDIS_PASSWORD:-change_me_in_production} + + # 飞书配置(请替换为实际值) + - FEISHU_APP_ID=${FEISHU_APP_ID} + - FEISHU_APP_SECRET=${FEISHU_APP_SECRET} + - FEISHU_ENCRYPT_KEY=${FEISHU_ENCRYPT_KEY} + - FEISHU_VERIFICATION_TOKEN=${FEISHU_VERIFICATION_TOKEN} + - FEISHU_TENANT_KEY=${FEISHU_TENANT_KEY:-14e7bc0fe5985740} + volumes: + - ./backend:/app + - ./logs/backend:/app/runtime/logs + networks: + - 1panel-network + healthcheck: + test: ["CMD", "php", "-r", "exit(0);"] + interval: 30s + timeout: 10s + retries: 3 + start_period: 40s + logging: + driver: "json-file" + options: + max-size: "10m" + max-file: "3" + +networks: + 1panel-network: + external: true diff --git a/deploy/php-prod.ini b/deploy/php-prod.ini new file mode 100644 index 0000000..cdc59b7 --- /dev/null +++ b/deploy/php-prod.ini @@ -0,0 +1,58 @@ +; X-Server Mobile 生产环境 PHP 配置 +; 文件位置: backend/php-prod.ini +; 用途: Docker 容器内的 PHP 生产环境配置 + +; ============================================ +; OPcache 配置 +; ============================================ +opcache.enable=1 +opcache.enable_cli=1 +opcache.memory_consumption=128 +opcache.interned_strings_buffer=8 +opcache.max_accelerated_files=10000 +opcache.revalidate_freq=60 +opcache.fast_shutdown=1 +opcache.save_comments=1 + +; ============================================ +; 错误处理 +; ============================================ +display_errors=0 +display_startup_errors=0 +log_errors=1 +error_log=/app/runtime/logs/php_errors.log +error_reporting=E_ALL & ~E_DEPRECATED & ~E_STRICT + +; ============================================ +; 安全配置 +; ============================================ +expose_php=Off +allow_url_fopen=On +allow_url_include=Off + +; 禁用危险函数(根据实际需求调整) +; disable_functions=exec,passthru,shell_exec,system,proc_open,popen,curl_exec,curl_multi_exec,parse_ini_file,show_source + +; ============================================ +; 性能配置 +; ============================================ +max_execution_time=60 +max_input_time=60 +memory_limit=256M +post_max_size=50M +upload_max_filesize=50M +max_file_uploads=20 + +; ============================================ +; 会话配置 +; ============================================ +session.cookie_httponly=1 +session.cookie_secure=1 +session.use_strict_mode=1 +session.cookie_samesite="Lax" + +; ============================================ +; 其他配置 +; ============================================ +date.timezone=Asia/Shanghai +default_charset="UTF-8" diff --git a/deploy/rollback.sh b/deploy/rollback.sh new file mode 100755 index 0000000..8c14448 --- /dev/null +++ b/deploy/rollback.sh @@ -0,0 +1,159 @@ +#!/bin/bash +# X-Server Mobile 回滚脚本 +# 使用方法: ./rollback.sh [backup_name] + +set -e + +# 配置 +SERVER="your-server.com" +DEPLOY_USER="root" +DEPLOY_PATH="/opt/x-server-mobile" +BACKUP_NAME=$1 + +# 颜色输出 +RED='\033[0;31m' +GREEN='\033[0;32m' +YELLOW='\033[1;33m' +NC='\033[0m' + +log_info() { + echo -e "${GREEN}[INFO]${NC} $1" +} + +log_warn() { + echo -e "${YELLOW}[WARN]${NC} $1" +} + +log_error() { + echo -e "${RED}[ERROR]${NC} $1" +} + +# 列出可用备份 +list_backups() { + log_info "可用备份列表:" + + ssh ${DEPLOY_USER}@${SERVER} << 'EOF' + cd /opt/x-server-mobile/backup + ls -1t | head -10 +EOF +} + +# 确认备份存在 +check_backup() { + log_info "检查备份 ${BACKUP_NAME}..." + + EXISTS=$(ssh ${DEPLOY_USER}@${SERVER} " + if [ -d ${DEPLOY_PATH}/backup/${BACKUP_NAME} ]; then + echo 'exists' + else + echo 'not_found' + fi + ") + + if [ "$EXISTS" != "exists" ]; then + log_error "备份不存在: ${BACKUP_NAME}" + list_backups + exit 1 + fi + + log_info "备份检查通过" +} + +# 回滚前端 +rollback_frontend() { + log_info "回滚前端..." + + ssh ${DEPLOY_USER}@${SERVER} << EOF + rm -rf ${DEPLOY_PATH}/frontend/* + cp -r ${DEPLOY_PATH}/backup/${BACKUP_NAME}/frontend/* ${DEPLOY_PATH}/frontend/ +EOF + + log_info "前端回滚完成" +} + +# 回滚后端(可选) +rollback_backend() { + read -p "是否回滚后端代码? (y/N) " -n 1 -r + echo + + if [[ ! $REPLY =~ ^[Yy]$ ]]; then + log_info "跳过后端回滚" + return + fi + + log_info "回滚后端..." + + ssh ${DEPLOY_USER}@${SERVER} << EOF + # 保留 vendor 目录 + mv ${DEPLOY_PATH}/backend/vendor /tmp/backend_vendor_backup 2>/dev/null || true + + rm -rf ${DEPLOY_PATH}/backend/* + cp -r ${DEPLOY_PATH}/backup/${BACKUP_NAME}/backend/* ${DEPLOY_PATH}/backend/ + + # 恢复 vendor 目录 + mv /tmp/backend_vendor_backup ${DEPLOY_PATH}/backend/vendor 2>/dev/null || true +EOF + + log_info "后端回滚完成" +} + +# 重启服务 +restart_services() { + log_info "重启服务..." + + ssh ${DEPLOY_USER}@${SERVER} << 'EOF' + cd /opt/x-server-mobile + + # 重启后端容器 + docker-compose -f docker-compose.prod.yml restart backend + + # 等待后端启动 + sleep 5 + + # 重载 OpenResty + openresty -s reload || nginx -s reload + + echo "服务重启完成" +EOF + + log_info "服务重启完成" +} + +# 主流程 +main() { + echo "=========================================" + echo " X-Server Mobile 回滚脚本" + echo "=========================================" + echo "服务器: ${SERVER}" + + if [ -z "$BACKUP_NAME" ]; then + log_error "请指定备份名称" + echo "" + list_backups + echo "" + echo "使用方法: ./rollback.sh " + exit 1 + fi + + echo "备份名称: ${BACKUP_NAME}" + echo "" + + # 确认回滚 + read -p "确认回滚到 ${BACKUP_NAME}? (y/N) " -n 1 -r + echo + if [[ ! $REPLY =~ ^[Yy]$ ]]; then + log_info "回滚已取消" + exit 0 + fi + + check_backup + rollback_frontend + rollback_backend + restart_services + + echo "" + log_info "回滚完成!" +} + +# 执行主流程 +main diff --git a/deploy/x-server-mobile-prod.conf b/deploy/x-server-mobile-prod.conf new file mode 100644 index 0000000..1a93b43 --- /dev/null +++ b/deploy/x-server-mobile-prod.conf @@ -0,0 +1,183 @@ +# X-Server Mobile 线上环境 OpenResty 配置 +# 文件位置: /usr/local/openresty/nginx/conf/conf.d/x-server-mobile.conf + +# HTTP 重定向到 HTTPS +server { + listen 80; + server_name xsm.hnxinyouhe.com; + + # Let's Encrypt ACME 挑战 + location /.well-known/acme-challenge/ { + root /var/www/certbot; + } + + # 强制 HTTPS + location / { + return 301 https://$server_name$request_uri; + } +} + +# HTTPS 主服务 +server { + listen 443 ssl http2; + server_name xsm.hnxinyouhe.com; + + # ============================================ + # SSL 证书配置 + # ============================================ + + # Let's Encrypt 证书路径 + ssl_certificate /etc/letsencrypt/live/xsm.hnxinyouhe.com/fullchain.pem; + ssl_certificate_key /etc/letsencrypt/live/xsm.hnxinyouhe.com/privkey.pem; + + # 如果使用自签名证书,取消下面的注释并注释上面的 Let's Encrypt 配置 + # ssl_certificate /etc/nginx/ssl/xsm.hnxinyouhe.com.crt; + # ssl_certificate_key /etc/nginx/ssl/xsm.hnxinyouhe.com.key; + + # SSL 协议和加密套件 + ssl_protocols TLSv1.2 TLSv1.3; + ssl_ciphers 'ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384'; + ssl_prefer_server_ciphers off; + + # SSL 会话缓存 + ssl_session_cache shared:SSL:10m; + ssl_session_timeout 10m; + ssl_session_tickets off; + + # OCSP Stapling + ssl_stapling on; + ssl_stapling_verify on; + ssl_trusted_certificate /etc/letsencrypt/live/xsm.hnxinyouhe.com/chain.pem; + resolver 8.8.8.8 8.8.4.4 valid=300s; + resolver_timeout 5s; + + # ============================================ + # 日志配置 + # ============================================ + + access_log /opt/x-server-mobile/logs/nginx_access.log combined; + error_log /opt/x-server-mobile/logs/nginx_error.log warn; + + # ============================================ + # 安全头 + # ============================================ + + add_header X-Frame-Options "SAMEORIGIN" always; + add_header X-Content-Type-Options "nosniff" always; + add_header X-XSS-Protection "1; mode=block" always; + add_header Referrer-Policy "no-referrer-when-downgrade" always; + + # ============================================ + # Gzip 压缩 + # ============================================ + + gzip on; + gzip_vary on; + gzip_min_length 1024; + gzip_comp_level 6; + gzip_types + text/plain + text/css + text/xml + text/javascript + application/json + application/javascript + application/xml+rss + application/rss+xml + font/truetype + font/opentype + application/vnd.ms-fontobject + image/svg+xml; + + # ============================================ + # 静态资源(长期缓存) + # ============================================ + + location ~* \.(js|css|png|jpg|jpeg|gif|webp|svg|ico|woff|woff2|ttf|eot)$ { + root /opt/x-server-mobile/frontend; + expires 7d; + add_header Cache-Control "public, immutable"; + access_log off; + } + + # ============================================ + # 飞书回调路由(必须在 API 之前) + # ============================================ + + location /login { + proxy_pass http://127.0.0.1:8787; + proxy_http_version 1.1; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + proxy_set_header Host $host; + } + + location /login/callback { + proxy_pass http://127.0.0.1:8787; + proxy_http_version 1.1; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + proxy_set_header Host $host; + } + + location /login/qrcode { + proxy_pass http://127.0.0.1:8787; + proxy_http_version 1.1; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + proxy_set_header Host $host; + } + + # 飞书卡片回调 + location /feishu/card/callback { + proxy_pass http://127.0.0.1:8787; + proxy_http_version 1.1; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + proxy_set_header Host $host; + } + + # ============================================ + # API 代理 + # ============================================ + + location /api/ { + proxy_pass http://127.0.0.1:8787; + proxy_http_version 1.1; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + proxy_set_header Host $host; + + # 超时配置 + proxy_connect_timeout 60s; + proxy_send_timeout 60s; + proxy_read_timeout 60s; + + # 缓冲配置 + proxy_buffering on; + proxy_buffer_size 4k; + proxy_buffers 8 4k; + proxy_busy_buffers_size 8k; + } + + # ============================================ + # 前端 SPA(所有其他请求返回 index.html) + # ============================================ + + location / { + root /opt/x-server-mobile/frontend; + + # SPA 路由支持 + try_files $uri $uri/ /index.html; + + # 禁用 HTML 缓存 + add_header Cache-Control "no-cache, no-store, must-revalidate"; + add_header Pragma "no-cache"; + add_header Expires "0"; + } +} diff --git a/docs/DEPLOYMENT.md b/docs/DEPLOYMENT.md new file mode 100644 index 0000000..5f98b82 --- /dev/null +++ b/docs/DEPLOYMENT.md @@ -0,0 +1,598 @@ +# X-Server Mobile 线上部署方案 + +## 线上环境 + +- **面板**: 1Panel (Docker 管理) +- **Web 服务器**: OpenResty +- **数据库**: MySQL (容器) +- **缓存**: Redis (容器) +- **域名**: `xsm.hnxinyouhe.com` (待配置) + +--- + +## 架构设计 + +``` + ┌─────────────────────────────────────┐ + │ OpenResty (80/443) │ + │ ┌─────────────────────────────┐ │ + │ │ 静态资源托管 │ │ + │ │ /frontend/dist/* │ │ + │ │ SPA Router fallback │ │ + │ └─────────────────────────────┘ │ + │ ┌─────────────────────────────┐ │ + │ │ API 反向代理 │ │ + │ │ /api/* → localhost:8787 │ │ + │ └─────────────────────────────┘ │ + │ ┌─────────────────────────────┐ │ + │ │ 飞书回调代理 │ │ + │ │ /login/* → localhost:8787 │ │ + │ └─────────────────────────────┘ │ + └─────────────────────────────────────┘ + │ + ┌───────────────┼───────────────┐ + ▼ ▼ ▼ + ┌───────────┐ ┌───────────┐ ┌───────────┐ + │ 后端 │ │ MySQL │ │ Redis │ + │ :8787 │ │ :3306 │ │ :6379 │ + │ Docker │ │ 1Panel │ │ 1Panel │ + └───────────┘ └───────────┘ └───────────┘ +``` + +--- + +## 部署步骤 + +### 第一步:准备线上目录结构 + +```bash +# 在服务器上创建部署目录 +mkdir -p /opt/x-server-mobile +mkdir -p /opt/x-server-mobile/frontend +mkdir -p /opt/x-server-mobile/backend +mkdir -p /opt/x-server-mobile/logs +mkdir -p /opt/x-server-mobile/backup +``` + +--- + +### 第二步:前端部署 + +#### 2.1 本地构建 + +```bash +# 在开发环境 +cd /www/x-server-mobile/frontend + +# 安装依赖(如果需要) +npm install + +# 构建生产版本 +npm run build + +# 生成的文件在 frontend/dist/ 目录 +``` + +#### 2.2 上传静态文件 + +```bash +# 方式1:使用 scp 上传 +scp -r frontend/dist/* root@your-server:/opt/x-server-mobile/frontend/ + +# 方式2:使用 rsync 同步 +rsync -avz --delete frontend/dist/ root@your-server:/opt/x-server-mobile/frontend/ +``` + +#### 2.3 构建环境配置 + +创建 `frontend/.env.production`: + +```env +# API 地址(使用相对路径,由 OpenResty 代理) +VITE_API_BASE=/api/v1 + +# 或使用绝对地址(如果前后端分离部署) +# VITE_API_BASE=https://api.xsm.hnxinyouhe.com +``` + +--- + +### 第三步:后端部署 + +#### 3.1 创建生产环境 Docker Compose + +**文件**: `/opt/x-server-mobile/deploy/docker-compose.prod.yml` + +```yaml +services: + backend: + build: + context: ./backend + dockerfile: Dockerfile + container_name: x-server-backend-prod + restart: unless-stopped + ports: + - "8787:8787" + environment: + # 生产环境配置 + - APP_ENV=production + - APP_DEBUG=false + + # 数据库配置(使用 1Panel 的 MySQL) + - DB_HOST=1Panel-mysql-xxxx # 替换为实际容器名 + - DB_PORT=3306 + - DB_DATABASE=xserver + - DB_USERNAME=xserver # 建议创建专用数据库用户 + - DB_PASSWORD=your_password # 强密码 + + # Redis 配置(使用 1Panel 的 Redis) + - REDIS_HOST=1Panel-redis-xxxx # 替换为实际容器名 + - REDIS_PORT=6379 + - REDIS_PASSWORD=your_redis_password + + # 飞书配置 + - FEISHU_APP_ID=cli_xxxxxxxxxxxxx + - FEISHU_APP_SECRET=xxxxxxxxxxxxxxxxxxxx + - FEISHU_ENCRYPT_KEY=xxxxxxxxxxxxxxxxxxxx + - FEISHU_VERIFICATION_TOKEN=xxxxxxxxxxxxxxxxxxxx + - FEISHU_TENANT_KEY=xxxxxxxxxxxxxxxxxxxx + volumes: + - ./backend:/app + - ./logs/app:/app/runtime/logs + networks: + - 1panel-network + healthcheck: + test: ["CMD", "php", "-r", "exit(0);"] + interval: 30s + timeout: 10s + retries: 3 + start_period: 40s + +networks: + 1panel-network: + external: true +``` + +#### 3.2 创建生产环境后端配置 + +**后端 OPcache 配置** (`deploy/php-prod.ini`): + +```ini +# 生产环境 OPcache 配置 +opcache.enable=1 +opcache.memory_consumption=128 +opcache.interned_strings_buffer=8 +opcache.max_accelerated_files=10000 +opcache.revalidate_freq=60 +opcache.fast_shutdown=1 +opcache.enable_cli=1 + +# 禁用错误显示 +display_errors=0 +display_startup_errors=0 +log_errors=1 +error_log=/app/runtime/logs/php_errors.log +``` + +#### 3.3 更新 Dockerfile + +```dockerfile +FROM php:8.5-cli + +# Install system dependencies +RUN apt-get update && apt-get install -y \ + unzip \ + git \ + libzip-dev \ + libpng-dev \ + curl \ + && rm -rf /var/lib/apt/lists/* + +# Install PHP extensions +RUN docker-php-ext-install zip pdo pdo_mysql bcmath pcntl opcache + +# Install Composer +COPY --from=composer:latest /usr/bin/composer /usr/local/bin/composer + +# Set working directory +WORKDIR /app + +# Copy composer files +COPY composer.json composer.lock* ./ + +# Install dependencies +RUN composer install --no-dev --optimize-autoloader --no-interaction + +# Copy application files +COPY . . + +# Copy PHP configuration +COPY php-prod.ini /usr/local/etc/php/conf.d/99-production.ini + +# Create runtime directories +RUN mkdir -p /app/runtime/logs + +# Expose port +EXPOSE 8787 + +# Start webman in foreground mode +CMD ["php", "start.php", "start"] +``` + +--- + +### 第四步:OpenResty 配置 + +#### 4.1 站点配置 + +**文件**: `/usr/local/openresty/nginx/conf/conf.d/x-server-mobile.conf` + +```nginx +# X-Server Mobile 线上环境配置 +server { + listen 80; + server_name xsm.hnxinyouhe.com; + + # 强制 HTTPS + return 301 https://$server_name$request_uri; +} + +server { + listen 443 ssl http2; + server_name xsm.hnxinyouhe.com; + + # SSL 证书配置(使用 Let's Encrypt 或自签名) + ssl_certificate /etc/nginx/ssl/xsm.hnxinyouhe.com.crt; + ssl_certificate_key /etc/nginx/ssl/xsm.hnxinyouhe.com.key; + + # SSL 安全配置 + ssl_protocols TLSv1.2 TLSv1.3; + ssl_ciphers HIGH:!aNULL:!MD5; + ssl_prefer_server_ciphers on; + ssl_session_cache shared:SSL:10m; + ssl_session_timeout 10m; + + # 日志 + access_log /opt/x-server-mobile/logs/nginx_access.log; + error_log /opt/x-server-mobile/logs/nginx_error.log; + + # Gzip 压缩 + gzip on; + gzip_vary on; + gzip_min_length 1024; + gzip_types text/plain text/css text/xml text/javascript + application/json application/javascript application/xml+rss + application/rss+xml font/truetype font/opentype + application/vnd.ms-fontobject image/svg+xml; + + # 静态资源缓存 + location ~* \.(js|css|png|jpg|jpeg|gif|webp|svg|ico|woff|woff2|ttf|eot)$ { + root /opt/x-server-mobile/frontend; + expires 7d; + add_header Cache-Control "public, immutable"; + } + + # 飞书回调(必须在 API 之前配置) + location /login { + proxy_pass http://127.0.0.1:8787; + proxy_http_version 1.1; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + } + + location /login/callback { + proxy_pass http://127.0.0.1:8787; + proxy_http_version 1.1; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + } + + location /login/qrcode { + proxy_pass http://127.0.0.1:8787; + proxy_http_version 1.1; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + } + + # 飞书卡片回调 + location /feishu/card/callback { + proxy_pass http://127.0.0.1:8787; + proxy_http_version 1.1; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + } + + # API 代理 + location /api/ { + proxy_pass http://127.0.0.1:8787; + proxy_http_version 1.1; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + proxy_set_header Host $host; + + # 超时配置 + proxy_connect_timeout 60s; + proxy_send_timeout 60s; + proxy_read_timeout 60s; + } + + # 前端 SPA(所有其他请求返回 index.html) + location / { + root /opt/x-server-mobile/frontend; + try_files $uri $uri/ /index.html; + add_header Cache-Control "no-cache, no-store, must-revalidate"; + } +} +``` + +#### 4.2 配置说明 + +1. **静态资源**: 直接由 OpenResty 托管,7天缓存 +2. **API 请求**: 代理到后端 8787 端口 +3. **飞书回调**: 特殊路径直接代理到后端 +4. **SPA 路由**: 所有其他请求返回 index.html + +--- + +### 第五步:数据库准备 + +#### 5.1 创建数据库和用户 + +```sql +-- 创建专用数据库用户 +CREATE USER 'xserver'@'%' IDENTIFIED BY 'your_strong_password'; + +-- 授予权限 +GRANT ALL PRIVILEGES ON xserver.* TO 'xserver'@'%'; +FLUSH PRIVILEGES; + +-- 如果数据库不存在,创建并导入结构 +CREATE DATABASE IF NOT EXISTS xserver CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; +``` + +#### 5.2 数据库迁移 + +```bash +# 从开发环境导出数据 +docker exec 1Panel-mysql-409s mysqldump -uroot -pmysql_xEsdPa xserver > xserver_backup.sql + +# 导入到线上数据库 +docker exec -i 1Panel-mysql-xxxx mysql -uxserver -pyour_password xserver < xserver_backup.sql +``` + +--- + +### 第六步:SSL 证书配置 + +#### 6.1 使用 Let's Encrypt(推荐) + +```bash +# 安装 certbot +apt-get install certbot + +# 获取证书 +certbot certonly --standalone -d xsm.hnxinyouhe.com + +# 证书路径 +# /etc/letsencrypt/live/xsm.hnxinyouhe.com/fullchain.pem +# /etc/letsencrypt/live/xsm.hnxinyouhe.com/privkey.pem +``` + +#### 6.2 自动续期 + +```bash +# 添加 crontab +0 3 * * * certbot renew --quiet && nginx -s reload +``` + +--- + +### 第七步:部署流程 + +#### 开发到生产部署 + +```bash +#!/bin/bash +# deploy.sh - 部署脚本 + +set -e + +echo "=========================================" +echo " X-Server Mobile 生产部署脚本" +echo "=========================================" + +# 1. 备份当前版本 +echo "📦 备份当前版本..." +ssh root@your-server "mkdir -p /opt/x-server-mobile/backup/\$(date +%Y%m%d_%H%M%S)" +ssh root@your-server "cp -r /opt/x-server-mobile/frontend /opt/x-server-mobile/backup/\$(date +%Y%m%d_%H%M%S)/" + +# 2. 构建前端 +echo "🔨 构建前端..." +cd frontend +npm install +npm run build + +# 3. 上传前端 +echo "📤 上传前端..." +rsync -avz --delete dist/ root@your-server:/opt/x-server-mobile/frontend/ + +# 4. 上传后端 +echo "📤 上传后端..." +rsync -avz --exclude='vendor' --exclude='runtime' \ + ../backend/ root@your-server:/opt/x-server-mobile/backend/ + +# 5. 重启后端容器 +echo "🔄 重启后端..." +ssh root@your-server "cd /opt/x-server-mobile && docker-compose -f docker-compose.prod.yml restart backend" + +# 6. 重载 OpenResty +echo "🔄 重载 OpenResty..." +ssh root@your-server "openresty -s reload" + +echo "✅ 部署完成!" +``` + +--- + +### 第八步:监控和日志 + +#### 8.1 日志位置 + +```bash +# Nginx 访问日志 +/opt/x-server-mobile/logs/nginx_access.log + +# Nginx 错误日志 +/opt/x-server-mobile/logs/nginx_error.log + +# 后端应用日志 +docker logs x-server-backend-prod -f + +# 后端运行时日志 +/opt/x-server-mobile/logs/app/ +``` + +#### 8.2 监控命令 + +```bash +# 检查后端容器状态 +docker ps | grep x-server-backend-prod + +# 检查后端健康 +curl http://127.0.0.1:8787/ + +# 检查数据库连接 +docker exec x-server-backend-prod php -r "var_dump(getenv('DB_HOST'));" + +# 查看 PHP OPcache 状态 +# 需要在后端添加 OPcache 状态页面 +``` + +--- + +### 第九步:安全加固 + +#### 9.1 防火墙配置 + +```bash +# 只开放必要端口 +ufw allow 22 # SSH +ufw allow 80 # HTTP +ufw allow 443 # HTTPS +ufw enable +``` + +#### 9.2 PHP 安全配置 + +```ini +# php-prod.ini +expose_php = Off +allow_url_fopen = Off +allow_url_include = Off +disable_functions = exec,passthru,shell_exec,system,proc_open,popen +``` + +#### 9.3 环境变量保护 + +- 不要将敏感信息提交到代码仓库 +- 使用 `.env` 文件或 Docker secrets 管理敏感信息 +- 定期轮换密码和密钥 + +--- + +### 第十步:回滚方案 + +#### 10.1 快速回滚 + +```bash +#!/bin/bash +# rollback.sh - 回滚脚本 + +BACKUP_DATE=$1 # 格式: 20240227_150000 + +if [ -z "$BACKUP_DATE" ]; then + echo "请指定备份日期,格式: 20240227_150000" + exit 1 +fi + +echo "🔄 回滚到 $BACKUP_DATE ..." + +# 恢复前端 +cp -r /opt/x-server-mobile/backup/$BACKUP_DATE/frontend/* /opt/x-server-mobile/frontend/ + +# 恢复后端(可选) +# cd /opt/x-server-mobile/backend +# git checkout +# docker-compose -f docker-compose.prod.yml restart backend + +# 重载 OpenResty +openresty -s reload + +echo "✅ 回滚完成" +``` + +--- + +## 检查清单 + +部署前检查: + +- [ ] 域名 DNS 已配置指向服务器 IP +- [ ] SSL 证书已配置 +- [ ] MySQL 数据库已创建 +- [ ] Redis 可正常连接 +- [ ] 飞书应用回调地址已更新 +- [ ] 环境变量已正确配置 +- [ ] 防火墙规则已设置 +- [ ] 日志目录已创建 + +部署后验证: + +- [ ] 前端页面可正常访问 +- [ ] API 接口返回正常 +- [ ] 飞书登录功能正常 +- [ ] 审批流程功能正常 +- [ ] 日志输出正常 +- [ ] 容器健康检查通过 + +--- + +## 常见问题 + +### Q1: 前端刷新 404 + +**原因**: OpenResty 配置没有正确处理 SPA 路由 + +**解决**: 确保 `try_files $uri $uri/ /index.html;` 配置正确 + +### Q2: API 请求跨域 + +**原因**: 前端和后端域名不同 + +**解决**: 在后端添加 CORS 头,或使用 OpenResty 代理统一域名 + +### Q3: 飞书回调失败 + +**原因**: 回调地址配置错误或 SSL 证书问题 + +**解决**: +- 确认飞书后台配置的回调地址为 `https://xsm.hnxinyouhe.com/login/callback` +- 确保服务器可公网访问 +- 检查 SSL 证书是否有效 + +### Q4: 容器无法连接数据库 + +**原因**: 容器网络不在同一个网络 + +**解决**: 确保后端容器和 MySQL/Redis 容器在同一个 Docker 网络(如 1panel-network) + +--- + +## 联系方式 + +如有问题,请联系开发团队。 diff --git a/docs/DEPLOYMENT_CHECKLIST.md b/docs/DEPLOYMENT_CHECKLIST.md new file mode 100644 index 0000000..2bf44ff --- /dev/null +++ b/docs/DEPLOYMENT_CHECKLIST.md @@ -0,0 +1,287 @@ +# X-Server Mobile 部署检查清单 + +## 部署前检查 + +### 服务器环境 +- [ ] 服务器已安装 1Panel 面板 +- [ ] 已安装 OpenResty 并正常运行 +- [ ] 已安装 MySQL 容器(1Panel 管理) +- [ ] 已安装 Redis 容器(1Panel 管理) +- [ ] Docker 网络配置正确(1panel-network 存在) + +### 网络配置 +- [ ] 域名 DNS 已解析到服务器 IP +- [ ] 服务器防火墙已开放 80、443 端口 +- [ ] 服务器可从外网访问 + +### 数据库准备 +- [ ] 创建生产数据库 `xserver` +- [ ] 创建专用数据库用户 `xserver` +- [ ] 授予用户权限 +- [ ] 导入数据库结构和初始数据 + +### SSL 证书 +- [ ] 安装 certbot: `apt-get install certbot` +- [ ] 获取证书: `certbot certonly --standalone -d xsm.hnxinyouhe.com` +- [ ] 验证证书路径正确 + +### 环境变量 +- [ ] 复制 `.env.production.template` 为 `.env.production` +- [ ] 填写数据库配置 +- [ ] 填写 Redis 配置 +- [ ] 填写飞书配置 +- [ ] 更新飞书回调地址为 `https://xsm.hnxinyouhe.com/login/callback` + +### 目录结构 +- [ ] 创建部署目录: `/opt/x-server-mobile` +- [ ] 创建子目录: frontend, backend, logs, backup +- [ ] 设置正确的目录权限 + +--- + +## 部署步骤 + +### 1. 准备服务器环境 + +```bash +# SSH 登录服务器 +ssh root@your-server + +# 创建目录结构 +mkdir -p /opt/x-server-mobile/{frontend,backend,logs,backup} +mkdir -p /opt/x-server-mobile/logs/backend +mkdir -p /opt/x-server-mobile/logs/nginx + +# 创建日志文件 +touch /opt/x-server-mobile/logs/nginx_access.log +touch /opt/x-server-mobile/logs/nginx_error.log +``` + +### 2. 配置 OpenResty + +```bash +# 复制配置文件到 OpenResty 配置目录 +cp deploy/nginx/x-server-mobile-prod.conf /usr/local/openresty/nginx/conf/conf.d/x-server-mobile.conf + +# 测试配置 +openresty -t + +# 如果测试通过,重载配置 +openresty -s reload +``` + +### 3. 构建前端 + +```bash +# 在开发环境 +cd /www/x-server-mobile/frontend +npm install +npm run build + +# 验证构建产物 +ls -la dist/ +``` + +### 4. 部署 + +```bash +# 方式1: 使用部署脚本 +./deploy.sh your-server-ip + +# 方式2: 手动部署 +# 上传前端 +scp -r frontend/dist/* root@server:/opt/x-server-mobile/frontend/ + +# 上传后端 +scp -r backend/ root@server:/opt/x-server-mobile/backend/ + +# 上传 docker-compose +scp docker-compose.prod.yml root@server:/opt/x-server-mobile/ +``` + +### 5. 启动后端 + +```bash +# SSH 登录服务器 +ssh root@server + +# 进入部署目录 +cd /opt/x-server-mobile + +# 加载环境变量 +source .env.production + +# 启动容器 +docker-compose -f docker-compose.prod.yml up -d + +# 查看容器状态 +docker ps | grep x-server + +# 查看日志 +docker logs -f x-server-backend-prod +``` + +--- + +## 部署后验证 + +### 前端验证 +- [ ] 访问 `https://xsm.hnxinyouhe.com` 显示登录页面 +- [ ] 页面样式加载正常 +- [ ] 浏览器控制台无错误 + +### 后端验证 +- [ ] 容器运行正常: `docker ps | grep x-server` +- [ ] 健康检查通过 +- [ ] 日志无错误信息 + +### 功能验证 +- [ ] 邮箱密码登录功能正常 +- [ ] 飞书客户端内免登正常 +- [ ] 飞书扫码登录正常 +- [ ] 审批列表正常显示 +- [ ] 出差申请功能正常 +- [ ] 飞书卡片通知正常 + +### 飞书回调验证 +- [ ] 飞书后台配置回调地址: `https://xsm.hnxinyouhe.com/login/callback` +- [ ] 点击飞书卡片"查看详情"跳转正确 +- [ ] 快速审批按钮功能正常 + +--- + +## 监控配置 + +### 日志监控 + +```bash +# Nginx 访问日志 +tail -f /opt/x-server-mobile/logs/nginx_access.log + +# Nginx 错误日志 +tail -f /opt/x-server-mobile/logs/nginx_error.log + +# 后端容器日志 +docker logs -f x-server-backend-prod + +# 后端应用日志 +tail -f /opt/x-server-mobile/logs/backend/*.log +``` + +### 性能监控 + +```bash +# 容器资源使用 +docker stats x-server-backend-prod + +# 磁盘使用 +du -sh /opt/x-server-mobile/* + +# 内存使用 +free -h +``` + +### 数据库监控 + +```bash +# 检查 MySQL 连接 +docker exec x-server-backend-prod php -r " + try { + new PDO('mysql:host=DB_HOST;dbname=xserver', 'xserver', 'password'); + echo 'Database connection OK\n'; + } catch (Exception \$e) { + echo 'Database connection FAILED: ' . \$e->getMessage() . '\n'; + } +" +``` + +--- + +## 回滚准备 + +### 备份检查 + +```bash +# 检查备份目录 +ls -la /opt/x-server-mobile/backup/ + +# 查看最新备份 +ls -lt /opt/x-server-mobile/backup/ | head -5 +``` + +### 回滚测试 + +```bash +# 测试回滚脚本 +./rollback.sh +``` + +--- + +## 定期维护 + +### 每周 +- [ ] 检查容器运行状态 +- [ ] 检查日志错误 +- [ ] 检查磁盘空间 + +### 每月 +- [ ] 更新 SSL 证书(自动续期) +- [ ] 数据库备份 +- [ ] 日志归档和清理 + +### 每季度 +- [ ] 安全更新检查 +- [ ] 性能优化评估 +- [ ] 灾难恢复演练 + +--- + +## 应急处理 + +### 服务异常 + +```bash +# 查看容器状态 +docker ps -a | grep x-server + +# 重启后端容器 +docker restart x-server-backend-prod + +# 查看详细日志 +docker logs --tail 100 x-server-backend-prod +``` + +### 数据库异常 + +```bash +# 检查 MySQL 容器 +docker ps | grep mysql + +# 进入 MySQL +docker exec -it mysql -uxserver -p + +# 检查连接数 +SHOW PROCESSLIST; +``` + +### 网络异常 + +```bash +# 检查端口监听 +netstat -tlnp | grep -E '80|443|8787' + +# 检查防火墙 +ufw status + +# 测试外网访问 +curl -I https://xsm.hnxinyouhe.com +``` + +--- + +## 联系信息 + +- 开发负责人: ___________ +- 运维负责人: ___________ +- 紧急联系电话: ___________ diff --git a/docs/DEV_REVERSE_PROXY_SETUP.md b/docs/DEV_REVERSE_PROXY_SETUP.md new file mode 100644 index 0000000..4761143 --- /dev/null +++ b/docs/DEV_REVERSE_PROXY_SETUP.md @@ -0,0 +1,219 @@ +# 开发环境反向代理配置指南 + +本指南用于配置开发环境的 OpenResty 和 frpc,以便将本地 API 暴露到公网调试飞书登录。 + +## 前提条件 + +- 已安装并运行 OpenResty +- 已安装并运行 frpc +- Docker 中运行的后端服务 (localhost:8787) + +## 配置步骤 + +### 1. OpenResty 配置 + +在你的 OpenResty 配置目录中创建以下配置文件: + +```nginx +# /usr/local/openresty/nginx/conf/conf.d/x-server-api.conf + +server { + listen 8080; # 本地开发端口,避免与 80 端口冲突 + server_name api.x-server.local; + + # API 反向代理 + location /api/ { + proxy_pass http://127.0.0.1:8787/api/; + proxy_http_version 1.1; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + + # CORS 配置(飞书请求需要) + add_header Access-Control-Allow-Origin *; + add_header Access-Control-Allow-Methods 'GET, POST, PUT, DELETE, OPTIONS'; + add_header Access-Control-Allow-Headers 'DNT,X-Mx-ReqToken,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Authorization,X-Tenant-Key'; + + if ($request_method = 'OPTIONS') { + return 204; + } + + proxy_connect_timeout 60s; + proxy_send_timeout 60s; + proxy_read_timeout 60s; + } + + # 健康检查 + location /health { + return 200 "OK\n"; + add_header Content-Type text/plain; + } +} +``` + +重载配置: +```bash +sudo openresty -s reload +# 或 +sudo systemctl reload openresty +``` + +### 2. frpc 配置 + +配置 frpc 将本地 OpenResty 服务暴露到公网: + +```ini +# ~/.frpc.ini 或 /etc/frp/frpc.ini + +[common] +server_addr = your-frp-server.com # 你的 frp 服务器地址 +server_port = 7000 +token = your-token + +[x-server-api] +type = http +local_ip = 127.0.0.1 +local_port = 8080 # OpenResty 监听端口 +custom_domains = api-dev.x-server.your-domain.com # 开发环境域名 + +# 可选:启用加密和压缩 +use_encryption = true +use_compression = true +``` + +启动 frpc: +```bash +frpc -c ~/.frpc.ini +``` + +### 3. 本地 hosts 配置 + +在本地开发时配置 hosts: + +```bash +# /etc/hosts +127.0.0.1 api.x-server.local +``` + +### 4. 飞书应用配置 + +在飞书开放平台配置开发环境回调 URL: + +``` +https://api-dev.x-server.your-domain.com/api/v1/auth/feishu-login +``` + +## 验证配置 + +### 1. 本地测试 +```bash +# 直接访问后端 +curl http://localhost:8787/api/v1/user/info + +# 通过 OpenResty 访问 +curl http://localhost:8080/api/v1/user/info + +# 通过本地域名访问 +curl http://api.x-server.local/api/v1/user/info +``` + +### 2. 公网测试 +```bash +# 通过 frps 域名访问 +curl https://api-dev.x-server.your-domain.com/api/v1/user/info +``` + +### 3. 飞书调试 +1. 在飞书开放平台创建测试应用 +2. 配置回调 URL 为开发环境域名 +3. 使用飞书 APP ID 和 APP SECRET 进行测试 +4. 检查后端日志查看请求详情 + +## 开发与生产环境区分 + +### 开发环境(使用 frpc) +- 域名:`api-dev.x-server.your-domain.com` +- 使用 frpc 暴露本地服务 +- CORS 允许所有来源 +- 详细日志输出 + +### 生产环境(不使用 frpc) +- 域名:`api.x-server.com` +- 直接部署到服务器 +- CORS 严格限制 +- 日志级别调整 + +## 故障排查 + +### OpenResty 无法访问后端 +1. 检查 Docker 容器是否运行:`docker ps` +2. 检查端口是否正确:`docker ps | grep 8787` +3. 检查网络连通性:`curl http://127.0.0.1:8787` + +### frpc 连接失败 +1. 检查 frp 服务器是否可访问 +2. 检查 token 是否正确 +3. 查看 frpc 日志:`frpc -c ~/.frpc.ini` + +### 飞书回调失败 +1. 检查域名 DNS 解析是否指向 frp 服务器 +2. 检查 frp 服务器是否配置了域名白名单 +3. 检查后端日志是否收到请求 +4. 验证 CORS 配置是否正确 + +## 安全建议 + +1. **开发环境使用独立的域名和 token** +2. **定期更新 frp token** +3. **不要在代码中硬编码生产环境配置** +4. **使用环境变量管理敏感信息** +5. **生产环境部署前移除 frpc 配置** + +## 常用命令 + +```bash +# 查看 Docker 容器日志 +docker logs x-server-backend -f + +# 重启后端服务 +docker compose restart backend + +# 重载 OpenResty 配置 +sudo openresty -s reload + +# 查看 frpc 状态 +frpc status -c ~/.frpc.ini + +# 测试 API 连通性 +curl -v http://localhost:8787/api/v1/user/info +``` + +## 环境变量配置 + +创建 `.env.development` 文件用于开发环境: + +```env +# 后端配置 +DB_HOST=mysql +DB_PORT=3306 +DB_DATABASE=x_server_mobile +DB_USERNAME=root +DB_PASSWORD=mysql_xEsdPa + +# Redis 配置 +REDIS_HOST=redis +REDIS_PORT=6379 + +# 飞书配置(开发环境) +FEISHU_APP_ID=cli_xxxxxxxxxxxxx +FEISHU_APP_SECRET=xxxxxxxxxxxxxxxxxxxx +FEISHU_ENCRYPT_KEY=xxxxxxxxxxxxxxxxxxxx +FEISHU_VERIFICATION_TOKEN=xxxxxxxxxxxxxxxxxxxx + +# API 配置 +API_BASE_URL=http://localhost:8787 +PUBLIC_API_URL=https://api-dev.x-server.your-domain.com +``` + +生产环境使用 `.env.production` 文件,移除 frpc 相关配置。 diff --git a/docs/GIT_WORKFLOW.md b/docs/GIT_WORKFLOW.md new file mode 100644 index 0000000..e36db9a --- /dev/null +++ b/docs/GIT_WORKFLOW.md @@ -0,0 +1,171 @@ +# Git 工作流 + +## 分支策略 + +采用 Git Flow 简化版工作流: + +### 主要分支 + +| 分支 | 用途 | 说明 | +|------|------|------| +| `master` | 生产环境 | 保持稳定,只接受从 develop 合并的代码 | +| `develop` | 开发环境 | 主开发分支,包含最新的开发功能 | +| `feature/*` | 功能分支 | 从 develop 分支,开发完成后合并回 develop | +| `hotfix/*` | 热修复分支 | 从 master 分支,修复后合并到 master 和 develop | + +## 分支命名规范 + +### 功能分支 +``` +feature/功能名称 +例如:feature/i18n-support +``` + +### 热修复分支 +``` +hotfix/问题描述 +例如:hotfix/login-error +``` + +## 工作流程 + +### 1. 开发新功能 + +```bash +# 从 develop 创建功能分支 +git checkout develop +git pull origin develop +git checkout -b feature/your-feature-name + +# 开发并提交 +git add . +git commit -m "feat: add your feature" + +# 推送到远程 +git push -u origin feature/your-feature-name + +# 完成后合并到 develop +git checkout develop +git merge feature/your-feature-name +git push origin develop + +# 删除功能分支(可选) +git branch -d feature/your-feature-name +``` + +### 2. 修复紧急问题 + +```bash +# 从 master 创建热修复分支 +git checkout master +git pull origin master +git checkout -b hotfix/your-fix-name + +# 修复并提交 +git add . +git commit -m "fix: describe the fix" + +# 推送到远程 +git push -u origin hotfix/your-fix-name + +# 合并到 master 和 develop +git checkout master +git merge hotfix/your-fix-name +git push origin master + +git checkout develop +git merge hotfix/your-fix-name +git push origin develop +``` + +### 3. 发布到生产 + +```bash +# 将 develop 合并到 master +git checkout master +git merge develop +git push origin master + +# 打标签 +git tag -a v1.0.0 -m "Release version 1.0.0" +git push origin v1.0.0 +``` + +## 提交信息规范 + +使用 Conventional Commits 规范: + +| 类型 | 说明 | 示例 | +|------|------|------| +| `feat` | 新功能 | `feat: add user login` | +| `fix` | 修复bug | `fix: correct i18n loading` | +| `docs` | 文档变更 | `docs: update README` | +| `style` | 代码格式 | `style: format code` | +| `refactor` | 重构 | `refactor: simplify API` | +| `test` | 测试 | `test: add unit tests` | +| `chore` | 构建/工具 | `chore: update dependencies` | + +格式: +``` +(): + + + +