diff --git a/deploy/deploy-1panel.sh b/deploy/deploy-1panel.sh new file mode 100755 index 0000000..dcbf68d --- /dev/null +++ b/deploy/deploy-1panel.sh @@ -0,0 +1,170 @@ +#!/bin/bash +# X-Server Mobile 1Panel 静态站点部署脚本 +# 使用方法: ./deploy-1panel.sh [server] + +set -e + +# ============================================ +# 配置 +# ============================================ +SERVER=${1:-"your-server-ip"} +DEPLOY_USER="root" +FRONTEND_BUILD_DIR="./frontend/dist" +BACKEND_SOURCE_DIR="./backend" + +# 1Panel 网站目录 +REMOTE_ROOT="/www/sites/uniservice-mobile.hnxinyouhe.com/index" + +# 后端部署目录 +REMOTE_BACKEND_DIR="/opt/x-server-mobile/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 "✓ 环境检查通过" +} + +# ============================================ +# 构建前端 +# ============================================ +build_frontend() { + log_info "构建前端..." + cd frontend + npm run build + cd .. + log_info "✓ 前端构建完成" +} + +# ============================================ +# 部署前端(上传静态文件) +# ============================================ +deploy_frontend() { + log_info "部署前端到 1Panel 静态站点..." + + rsync -avz --delete \ + --exclude='*.map' \ + ${FRONTEND_BUILD_DIR}/ \ + ${DEPLOY_USER}@${SERVER}:${REMOTE_ROOT}/ + + log_info "✓ 前端部署完成" +} + +# ============================================ +# 部署后端 +# ============================================ +deploy_backend() { + log_info "部署后端..." + + # 创建后端目录(如果不存在) + ssh ${DEPLOY_USER}@${SERVER} "mkdir -p ${REMOTE_BACKEND_DIR}" + + rsync -avz \ + --exclude='vendor' \ + --exclude='runtime' \ + --exclude='.git' \ + ${BACKEND_SOURCE_DIR}/ \ + ${DEPLOY_USER}@${SERVER}:${REMOTE_BACKEND_DIR}/ + + log_info "✓ 后端部署完成" +} + +# ============================================ +# 重启后端容器 +# ============================================ +restart_backend() { + log_info "重启后端容器..." + + ssh ${DEPLOY_USER}@${SERVER} << 'EOF' + cd /opt/x-server-mobile + + # 停止旧容器 + docker-compose -f deploy/docker-compose.prod.yml down + + # 启动新容器 + docker-compose -f deploy/docker-compose.prod.yml up -d + + # 查看容器状态 + docker ps | grep x-server + + echo "✓ 后端容器重启完成" +EOF +} + +# ============================================ +# 主流程 +# ============================================ +main() { + echo "==========================================" + echo " X-Server Mobile 1Panel 部署脚本" + echo "==========================================" + echo "" + echo "目标服务器: ${SERVER}" + echo "" + + # 检查环境 + check_env + + # 询问是否构建前端 + read -p "是否需要构建前端? (y/N): " build_frontend_choice + if [[ $build_frontend_choice =~ ^[Yy]$ ]]; then + build_frontend + fi + + # 部署前端 + deploy_frontend + + # 询问是否部署后端 + read -p "是否需要部署后端? (y/N): " deploy_backend_choice + if [[ $deploy_backend_choice =~ ^[Yy]$ ]]; then + deploy_backend + restart_backend + fi + + echo "" + log_info "==========================================" + log_info "部署完成!" + log_info "==========================================" + echo "" + echo "访问地址: https://uniservice-mobile.hnxinyouhe.com" + echo "" + echo "后续步骤:" + echo "1. 在 1Panel 面板中找到网站配置" + echo "2. 将 deploy/nginx-1panel.conf 的内容添加到 Nginx 配置中" + echo "3. 重载 Nginx 配置" + echo "" +} + +# 执行主流程 +main "$@" diff --git a/deploy/docker-compose.prod.yml b/deploy/docker-compose.prod.yml index 794e8ab..f0883a5 100644 --- a/deploy/docker-compose.prod.yml +++ b/deploy/docker-compose.prod.yml @@ -1,5 +1,5 @@ # X-Server Mobile 生产环境 Docker Compose -# 使用方法: docker-compose -f docker-compose.prod.yml up -d +# 使用方法: docker-compose -f deploy/docker-compose.prod.yml up -d services: # PHP 后端服务 @@ -16,19 +16,17 @@ services: - APP_ENV=production - APP_DEBUG=false - # 数据库配置(使用 1Panel 的 MySQL) - # 注意:请替换为实际的容器名和密码 - - DB_HOST=1Panel-mysql-409s + # 数据库配置(1Panel MySQL) + - DB_HOST=1Panel-mysql-XyxS - DB_PORT=3306 - DB_DATABASE=xserver - DB_USERNAME=xserver - - DB_PASSWORD=${DB_PASSWORD:-change_me_in_production} + - DB_PASSWORD=NsNBSQm4b2b7SzhR - # Redis 配置(使用 1Panel 的 Redis) - # 注意:请替换为实际的容器名和密码 - - REDIS_HOST=1Panel-redis-xxxx + # Redis 配置(1Panel Redis) + - REDIS_HOST=1Panel-redis-pxdb - REDIS_PORT=6379 - - REDIS_PASSWORD=${REDIS_PASSWORD:-change_me_in_production} + - REDIS_PASSWORD=redis_FTzdM6 # 飞书配置(请替换为实际值) - FEISHU_APP_ID=${FEISHU_APP_ID} diff --git a/deploy/nginx-1panel.conf b/deploy/nginx-1panel.conf new file mode 100644 index 0000000..ebf364d --- /dev/null +++ b/deploy/nginx-1panel.conf @@ -0,0 +1,150 @@ +# uniservice-mobile.hnxinyouhe.com Nginx 配置 +# 1Panel 静态站点配置 +# 文件位置: 1Panel 网站设置 -> 配置文件 + +server { + listen 80; + listen 443 ssl; + server_name uniservice-mobile.hnxinyouhe.com; + + index index.php index.html index.htm default.php default.htm default.html; + + access_log /www/sites/uniservice-mobile.hnxinyouhe.com/log/access.log main; + error_log /www/sites/uniservice-mobile.hnxinyouhe.com/log/error.log; + + # ============================================ + # 安全配置 + # ============================================ + + location ~ ^/(\.user.ini|\.htaccess|\.git|\.env|\.svn|\.project|LICENSE|README.md) { + return 404; + } + + # Let's Encrypt ACME 挑战 + location ^~ /.well-known/acme-challenge { + allow all; + root /usr/share/nginx/html; + } + + # 禁止访问敏感文件扩展 + if ($uri ~ "^/\.well-known/.*\.(php|jsp|py|js|css|lua|ts|go|zip|tar\.gz|rar|7z|sql|bak)$") { + return 403; + } + + # ============================================ + # SSL 证书配置 + # ============================================ + + http2 on; + + if ($scheme = http) { + return 301 https://$host$request_uri; + } + + ssl_certificate /www/sites/uniservice-mobile.hnxinyouhe.com/ssl/fullchain.pem; + ssl_certificate_key /www/sites/uniservice-mobile.hnxinyouhe.com/ssl/privkey.pem; + ssl_protocols TLSv1.3 TLSv1.2; + ssl_ciphers ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-SHA384:DHE-RSA-AES128-SHA256:!aNULL:!eNULL:!EXPORT:!DSS:!DES:!RC4:!3DES:!MD5:!PSK:!KRB5:!SRP:!CAMELLIA:!SEED; + ssl_prefer_server_ciphers off; + ssl_session_cache shared:SSL:10m; + ssl_session_timeout 10m; + error_page 497 https://$host$request_uri; + + # HSTS 安全头 + add_header Strict-Transport-Security "max-age=31536000" always; + proxy_set_header X-Forwarded-Proto https; + + # ============================================ + # 后端 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; + } + + # ============================================ + # 飞书登录回调(必须在 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; + } + + # ============================================ + # 静态资源缓存 + # ============================================ + + location ~* \.(js|css|png|jpg|jpeg|gif|webp|svg|ico|woff|woff2|ttf|eot)$ { + root /www/sites/uniservice-mobile.hnxinyouhe.com/index; + expires 7d; + add_header Cache-Control "public, immutable"; + access_log off; + } + + # ============================================ + # 前端 SPA(所有其他请求) + # ============================================ + + root /www/sites/uniservice-mobile.hnxinyouhe.com/index; + + location / { + try_files $uri $uri/ /index.html; + + # SPA HTML 不缓存 + add_header Cache-Control "no-cache, no-store, must-revalidate"; + add_header Pragma "no-cache"; + add_header Expires "0"; + } + + # 404 页面 + error_page 404 /404.html; +} diff --git a/docs/DEPLOY_ONLINE_GUIDE.md b/docs/DEPLOY_ONLINE_GUIDE.md new file mode 100644 index 0000000..1534f60 --- /dev/null +++ b/docs/DEPLOY_ONLINE_GUIDE.md @@ -0,0 +1,262 @@ +# uniservice-mobile.hnxinyouhe.com 线上部署说明 + +## 线上环境信息 + +| 项目 | 值 | +|------|-----| +| **域名** | uniservice-mobile.hnxinyouhe.com | +| **网站根目录** | /www/sites/uniservice-mobile.hnxinyouhe.com/index | +| **后端端口** | 8787 | +| **MySQL 容器** | 1Panel-mysql-XyxS | +| **Redis 容器** | 1Panel-redis-pxdb | +| **Docker 网络** | 1panel-network | + +--- + +## 部署目录结构 + +``` +线上服务器: +/www/sites/uniservice-mobile.hnxinyouhe.com/ +├── index/ ← 前端静态文件 +│ ├── index.html +│ ├── assets/ +│ └── ... +├── log/ ← 访问日志 +│ ├── access.log +│ └── error.log +└── ssl/ ← SSL 证书 + ├── fullchain.pem + └── privkey.pem + +/opt/x-server-mobile/ ← 后端部署目录 +├── backend/ ← 后端代码 +│ ├── app/ +│ ├── vendor/ +│ └── ... +├── deploy/ ← 部署配置文件 +│ ├── docker-compose.prod.yml +│ ├── .env.production +│ ├── nginx-1panel.conf +│ └── deploy-1panel.sh +└── logs/ ← 后端日志 + └── backend/ +``` + +--- + +## 快速部署步骤 + +### 方式一:使用部署脚本(推荐) + +```bash +# 1. 构建前端 +cd /www/x-server-mobile/frontend +npm run build + +# 2. 执行部署脚本 +./deploy/deploy-1panel.sh your-server-ip + +# 脚本会提示: +# - 是否需要构建前端(已构建则选 N) +# - 是否需要部署后端 +# - 自动重启后端容器 +``` + +### 方式二:手动部署 + +#### 1. 前端部署 + +```bash +# 本地构建 +cd /www/x-server-mobile/frontend +npm run build + +# 上传静态文件到服务器 +rsync -avz --delete frontend/dist/* root@your-server:/www/sites/uniservice-mobile.hnxinyouhe.com/index/ +``` + +#### 2. 后端部署 + +```bash +# 上传后端代码 +rsync -avz --exclude='vendor' --exclude='runtime' backend/ root@your-server:/opt/x-server-mobile/backend/ + +# 在服务器上启动容器 +ssh root@your-server +cd /opt/x-server-mobile + +# 上传 docker-compose 和环境变量文件 +# (从本地复制这些文件) +``` + +--- + +## 1Panel Nginx 配置 + +### 配置位置 +1Panel 面板 → 网站 → 找到 `uniservice-mobile.hnxinyouhe.com` → 配置文件 + +### 需要添加的反向代理规则 + +将以下内容添加到现有的 `server {}` 配置块中(在 `root` 指令之前): + +```nginx +# ============================================ +# 后端 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; +} + +# ============================================ +# 飞书登录回调 +# ============================================ + +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; +} + +# ============================================ +# 静态资源缓存 +# ============================================ + +location ~* \.(js|css|png|jpg|jpeg|gif|webp|svg|ico|woff|woff2|ttf|eot)$ { + root /www/sites/uniservice-mobile.hnxinyouhe.com/index; + expires 7d; + add_header Cache-Control "public, immutable"; + access_log off; +} +``` + +### 配置说明 + +| 配置项 | 说明 | +|--------|------| +| `/api/*` | 后端 API 接口,代理到 8787 端口 | +| `/login/*` | 飞书登录回调,代理到后端 | +| `/feishu/card/callback` | 飞书卡片回调,代理到后端 | +| 静态资源 | js/css/images 等,缓存 7 天 | +| 根路径 `/` | SPA 路由,返回 index.html | + +--- + +## 环境变量配置 + +已创建的文件:`deploy/.env.production` + +需要填写的配置: + +```env +# 飞书配置(必须配置) +FEISHU_APP_ID=cli_axxxxxxxxxxxx +FEISHU_APP_SECRET=xxxxxxxxxxxxxxxxxxxx +FEISHU_ENCRYPT_KEY=xxxxxxxxxxxxxxxxxxxx +FEISHU_VERIFICATION_TOKEN=xxxxxxxxxxxxxxxxxxxx +``` + +--- + +## 部署文件清单 + +| 文件 | 说明 | 用途 | +|------|------|------| +| `deploy/docker-compose.prod.yml` | 后端容器编排 | **线上使用** | +| `deploy/.env.production` | 线上环境变量 | **线上使用** | +| `deploy/nginx-1panel.conf` | Nginx 配置参考 | 1Panel 配置参考 | +| `deploy/deploy-1panel.sh` | 自动部署脚本 | 简化部署流程 | + +**注意**:开发和线上配置完全分离,不会互相影响: +- 开发环境使用 `docker-compose.yml` +- 线上环境使用 `deploy/docker-compose.prod.yml` + +--- + +## 验证部署 + +```bash +# 1. 检查前端 +curl -I https://uniservice-mobile.hnxinyouhe.com + +# 2. 检查 API +curl https://uniservice-mobile.hnxinyouhe.com/api/v1/user/info \ + -H "Authorization: Bearer YOUR_TOKEN" + +# 3. 检查后端容器 +ssh root@server +docker ps | grep x-server +docker logs -f x-server-backend-prod +``` + +--- + +## 常见问题 + +### Q: 前端显示但 API 调用失败? +A: 检查 1Panel Nginx 配置是否添加了 `/api/` 的反向代理规则。 + +### Q: 后端容器启动失败? +A: 检查 `deploy/docker-compose.prod.yml` 中的数据库和 Redis 容器名称是否正确。 + +### Q: 飞书登录失败? +A: 检查 `/login/*` 的反向代理规则是否已添加。 + +--- + +## 回滚操作 + +```bash +# 使用备份回滚 +ssh root@server +cd /www/sites/uniservice-mobile.hnxinyouhe.com +cp -r backup/YYYYMMDD_HHMMSS/* index/ + +# 重启后端容器到指定版本 +cd /opt/x-server-mobile +git log # 查看提交历史 +git checkout # 切换到指定版本 +docker-compose -f deploy/docker-compose.prod.yml up -d --build +```