diff --git a/deploy.sh b/deploy.sh new file mode 100644 index 0000000..58fd7f6 --- /dev/null +++ b/deploy.sh @@ -0,0 +1,78 @@ +#!/bin/bash +# ============================================================ +# Track 生产流转系统 — 增量部署脚本 +# 服务器: 172.16.0.198 +# 路径: /opt/Track +# 仅更新代码,不覆盖数据库 +# ============================================================ +set -e + +SERVER="dxc@172.16.0.198" +REMOTE_DIR="/opt/Track" +TIMESTAMP=$(date +%Y%m%d_%H%M) +REMOTE_BACKUP_DIR="$REMOTE_DIR/backups/$TIMESTAMP" + +echo "===================================================" +echo "🚀 Track 增量部署 — 仅更新代码" +echo "===================================================" + +# 1. 远端备份 +echo "[1/4] 服务器备份旧代码..." +ssh -t $SERVER "sudo mkdir -p $REMOTE_BACKUP_DIR && \ + cd $REMOTE_DIR && \ + echo '>> 备份当前代码...' && \ + sudo tar -czf $REMOTE_BACKUP_DIR/code_backup.tar.gz \ + backend frontend docker-compose.prod.yml 2>/dev/null || true && \ + echo '>> 保留最近 3 个备份...' && \ + cd $REMOTE_DIR/backups && \ + sudo sh -c 'ls -dt */ 2>/dev/null | tail -n +4 | xargs -I {} rm -rf {} || true'" + +if [ $? -ne 0 ]; then echo "❌ 备份失败,终止!"; exit 1; fi + +# 2. 本地打包 +echo "[2/4] 本地打包代码..." +tar -czf deploy.tar.gz \ + --exclude="backend/venv" \ + --exclude="backend/.venv" \ + --exclude="backend/__pycache__" \ + --exclude="backend/*.pyc" \ + --exclude="backend/.env" \ + --exclude="backend/uploads" \ + --exclude="frontend/node_modules" \ + --exclude="frontend/dist" \ + --exclude=".git" \ + --exclude=".idea" \ + --exclude=".vscode" \ + --exclude="track-uniapp/node_modules" \ + --exclude="track-uniapp/dist" \ + --exclude="track-uniapp/unpackage" \ + backend frontend docker-compose.prod.yml + +FILESIZE=$(stat -c%s "deploy.tar.gz" 2>/dev/null || stat -f%z "deploy.tar.gz") +echo ">> 打包完成: $((FILESIZE / 1024 / 1024)) MB" + +# 3. 上传 +echo "[3/4] 上传到服务器..." +scp deploy.tar.gz $SERVER:/tmp/deploy.tar.gz +if [ $? -ne 0 ]; then echo "❌ 上传失败!"; rm -f deploy.tar.gz; exit 1; fi + +# 4. 替换重启 +echo "[4/4] 服务器替换并重启..." +ssh -t $SERVER "cd $REMOTE_DIR && \ + echo '>> 移除旧备份...' && \ + sudo rm -rf backend_old frontend_old && \ + echo '>> 保留当前为 old...' && \ + (sudo mv backend backend_old 2>/dev/null || true) && \ + (sudo mv frontend frontend_old 2>/dev/null || true) && \ + echo '>> 解压新代码...' && \ + sudo mv /tmp/deploy.tar.gz . && \ + sudo tar -xzf deploy.tar.gz && \ + echo '>> 重启 Docker...' && \ + sudo docker compose -f docker-compose.prod.yml up -d --build && \ + sudo rm deploy.tar.gz && \ + echo '>> ✅ 部署完成!'" + +rm -f deploy.tar.gz +echo "===================================================" +echo "✅ 增量部署完成!访问 http://172.16.0.198:8010" +echo "===================================================" diff --git a/deploy_full.sh b/deploy_full.sh new file mode 100644 index 0000000..70c5eac --- /dev/null +++ b/deploy_full.sh @@ -0,0 +1,91 @@ +#!/bin/bash +# ============================================================ +# Track 生产流转系统 — 全量部署脚本(含数据库同步) +# 服务器: 172.16.0.198 +# 路径: /opt/Track +# ⚠️ 会覆盖生产数据库!请确认后再执行! +# ============================================================ +set -e + +SERVER="dxc@172.16.0.198" +REMOTE_DIR="/opt/Track" +TIMESTAMP=$(date +%Y%m%d_%H%M) +REMOTE_BACKUP_DIR="$REMOTE_DIR/backups/$TIMESTAMP" + +echo "⚠️⚠️⚠️ 警告:此操作将覆盖生产数据库!⚠️⚠️⚠️" +read -p "确认继续?(y/n) " -n 1 -r +echo +if [[ ! $REPLY =~ ^[Yy]$ ]]; then + exit 1 +fi + +# 1. 远端全量备份 +echo "[1/5] 服务器全量备份..." +ssh -t $SERVER "sudo mkdir -p $REMOTE_BACKUP_DIR && \ + cd $REMOTE_DIR && \ + echo '>> 导出线上数据库...' && \ + sudo sh -c 'docker exec track_db_prod pg_dumpall -c -U track | gzip > $REMOTE_BACKUP_DIR/db_backup.sql.gz' && \ + echo '>> 备份代码...' && \ + sudo tar -czf $REMOTE_BACKUP_DIR/code_backup.tar.gz \ + backend frontend docker-compose.prod.yml 2>/dev/null || true && \ + echo '>> 保留最近 3 个备份...' && \ + cd $REMOTE_DIR/backups && \ + sudo sh -c 'ls -dt */ 2>/dev/null | tail -n +4 | xargs -I {} rm -rf {} || true'" + +if [ $? -ne 0 ]; then echo "❌ 备份失败,终止!"; exit 1; fi + +# 2. 导出本地数据库 +echo "[2/5] 导出本地数据库..." +docker exec track_db pg_dumpall -c -U track | gzip > db_sync.sql.gz +echo ">> 数据库导出完成" + +# 3. 本地打包 +echo "[3/5] 本地打包代码+数据库..." +tar -czf deploy_full.tar.gz \ + --exclude="backend/venv" \ + --exclude="backend/.venv" \ + --exclude="backend/__pycache__" \ + --exclude="backend/*.pyc" \ + --exclude="backend/.env" \ + --exclude="backend/uploads" \ + --exclude="frontend/node_modules" \ + --exclude="frontend/dist" \ + --exclude=".git" \ + --exclude=".idea" \ + --exclude=".vscode" \ + --exclude="track-uniapp/node_modules" \ + --exclude="track-uniapp/dist" \ + --exclude="track-uniapp/unpackage" \ + --exclude="backups" \ + backend frontend docker-compose.prod.yml db_sync.sql.gz + +FILESIZE=$(stat -c%s "deploy_full.tar.gz" 2>/dev/null || stat -f%z "deploy_full.tar.gz") +echo ">> 打包完成: $((FILESIZE / 1024 / 1024)) MB" + +# 4. 上传 +echo "[4/5] 上传到服务器..." +scp deploy_full.tar.gz $SERVER:/tmp/deploy_full.tar.gz +if [ $? -ne 0 ]; then echo "❌ 上传失败!"; rm -f deploy_full.tar.gz db_sync.sql.gz; exit 1; fi + +# 5. 替换+导入+重启 +echo "[5/5] 服务器部署..." +ssh -t $SERVER "cd $REMOTE_DIR && \ + echo '>> 移除旧备份...' && \ + sudo rm -rf backend_old frontend_old && \ + echo '>> 保留当前为 old...' && \ + (sudo mv backend backend_old 2>/dev/null || true) && \ + (sudo mv frontend frontend_old 2>/dev/null || true) && \ + echo '>> 解压新代码...' && \ + sudo mv /tmp/deploy_full.tar.gz . && \ + sudo tar -xzf deploy_full.tar.gz && \ + echo '>> 重启 Docker...' && \ + sudo docker compose -f docker-compose.prod.yml up -d --build && \ + echo '>> 导入数据库...' && \ + sudo sh -c 'gunzip -c db_sync.sql.gz | docker exec -i track_db_prod psql -U track -d track_production' && \ + sudo rm deploy_full.tar.gz db_sync.sql.gz && \ + echo '>> ✅ 全量部署完成!'" + +rm -f deploy_full.tar.gz db_sync.sql.gz +echo "===================================================" +echo "✅ 全量部署完成!访问 http://172.16.0.198:8010" +echo "===================================================" diff --git a/docker-compose.prod.yml b/docker-compose.prod.yml new file mode 100644 index 0000000..fb03997 --- /dev/null +++ b/docker-compose.prod.yml @@ -0,0 +1,81 @@ +# ============================================================ +# 生产流转管理系统 — 生产环境 Docker Compose +# 服务器: 172.16.0.198 +# 部署路径: /opt/Track +# 启动: docker compose -f docker-compose.prod.yml up -d --build +# ============================================================ + +services: + # ============================================================ + # PostgreSQL 15 + pgvector + # ============================================================ + db: + image: pgvector/pgvector:pg15 + container_name: track_db_prod + restart: always + environment: + POSTGRES_USER: track + POSTGRES_PASSWORD: track_prod_2026 + POSTGRES_DB: track_production + ports: + - "8012:5432" + volumes: + - track_pgdata_prod:/var/lib/postgresql/data + healthcheck: + test: ["CMD-SHELL", "pg_isready -U track -d track_production"] + interval: 10s + timeout: 5s + retries: 5 + + # ============================================================ + # FastAPI 后端 — gunicorn + uvicorn (生产模式,无热更新) + # ============================================================ + backend: + build: + context: ./backend + dockerfile: Dockerfile.dev + container_name: track_backend_prod + restart: always + environment: + DATABASE_URL: postgresql+asyncpg://track:track_prod_2026@track_db_prod:5432/track_production + SECRET_KEY: change-me-in-production-please + DEBUG: "false" + CORS_ORIGINS: '["http://172.16.0.198:8010","https://172.16.0.198:8010","http://172.16.0.198","tauri://localhost"]' + # 🚀 MOM 老系统数据库 — 用户登录验证 + 用户名单来源 + MOM_DB_HOST: inventory_db_prod + MOM_DB_PORT: "5432" + ports: + - "8011:8000" + volumes: + - ./backend:/app + command: > + sh -c "uvicorn app.main:app --host 0.0.0.0 --port 8000 --workers 4 --log-level info" + depends_on: + db: + condition: service_healthy + networks: + - default + - mom_net + + # ============================================================ + # React 前端 — Nginx 静态文件服务 (生产模式) + # ============================================================ + frontend: + build: + context: ./frontend + dockerfile: Dockerfile.prod + container_name: track_frontend_prod + restart: always + ports: + - "8010:80" + depends_on: + - backend + +networks: + mom_net: + external: true + name: inventory-app_default + +volumes: + track_pgdata_prod: + name: track_pgdata_prod diff --git a/frontend/Dockerfile.prod b/frontend/Dockerfile.prod new file mode 100644 index 0000000..b1c7e32 --- /dev/null +++ b/frontend/Dockerfile.prod @@ -0,0 +1,18 @@ +# ============================================================ +# 前端生产 Dockerfile — 多阶段构建:Vite 打包 → Nginx 托管 +# ============================================================ + +# --- 阶段 1:编译 React --- +FROM node:20-alpine AS builder +WORKDIR /app +COPY package.json package-lock.json ./ +RUN npm ci --production=false +COPY . . +RUN npx vite build + +# --- 阶段 2:Nginx 托管静态文件 --- +FROM nginx:alpine +COPY nginx.conf /etc/nginx/conf.d/default.conf +COPY --from=builder /app/dist /usr/share/nginx/html +EXPOSE 80 +CMD ["nginx", "-g", "daemon off;"] diff --git a/frontend/nginx.conf b/frontend/nginx.conf new file mode 100644 index 0000000..9da175f --- /dev/null +++ b/frontend/nginx.conf @@ -0,0 +1,32 @@ +server { + listen 80; + server_name _; + + root /usr/share/nginx/html; + index index.html; + + # Gzip 压缩 + gzip on; + gzip_types text/css application/javascript application/json image/svg+xml; + gzip_min_length 256; + + # 静态文件缓存 + location /assets/ { + expires 1y; + add_header Cache-Control "public, immutable"; + } + + # API 代理到后端 + location /api/ { + proxy_pass http://backend:8000; + 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; + } + + # SPA 路由回退 + location / { + try_files $uri $uri/ /index.html; + } +}