# Mobile Upgrade Admin - 部署指南

## 部署概述

本文档描述 Mobile Upgrade Admin 前端应用的部署方案，包括本地构建、Docker 容器化和生产环境部署。

## 部署架构

```
┌─────────────────────────────────────────────────────────────┐
│                        Nginx / Caddy                         │
│                      (Reverse Proxy)                         │
└─────────────────┬───────────────────────┬───────────────────┘
                  │                       │
                  ▼                       ▼
        ┌─────────────────┐     ┌─────────────────┐
        │  Mobile Upgrade │     │  Admin Service  │
        │  Frontend (3001)│     │  Backend (3000) │
        └─────────────────┘     └─────────────────┘
```

## 环境要求

| 环境 | 要求 |
|------|------|
| Node.js | >= 18.x |
| npm | >= 9.x |
| Docker | >= 20.x (可选) |
| 内存 | >= 512MB |
| 磁盘 | >= 500MB |

## 环境变量配置

### 开发环境

```bash
# .env.local
NEXT_PUBLIC_API_URL=http://localhost:3000
```

### 生产环境

```bash
# .env.production
NEXT_PUBLIC_API_URL=https://api.example.com
```

| 变量 | 说明 | 默认值 |
|------|------|--------|
| NEXT_PUBLIC_API_URL | Admin Service API 地址 | http://localhost:3000 |
| PORT | 应用监听端口 | 3000 |

## 部署方式

### 方式一：本地构建部署

#### 1. 构建生产版本

```bash
cd frontend/mobile-upgrade

# 安装依赖
npm ci

# 构建
npm run build
```

构建产物在 `.next/` 目录。

#### 2. 启动生产服务

```bash
# 使用 Node.js 直接运行
npm run start

# 或指定端口
PORT=3001 npm run start
```

#### 3. 使用 PM2 管理

```bash
# 安装 PM2
npm install -g pm2

# 启动应用
pm2 start npm --name "mobile-upgrade" -- start

# 查看状态
pm2 status

# 查看日志
pm2 logs mobile-upgrade

# 重启
pm2 restart mobile-upgrade

# 停止
pm2 stop mobile-upgrade
```

PM2 配置文件：

```javascript
// ecosystem.config.js
module.exports = {
  apps: [{
    name: 'mobile-upgrade',
    script: 'npm',
    args: 'start',
    cwd: '/path/to/mobile-upgrade',
    env: {
      NODE_ENV: 'production',
      PORT: 3001,
    },
    instances: 1,
    autorestart: true,
    watch: false,
    max_memory_restart: '500M',
  }],
}
```

### 方式二：Docker 容器部署

#### 1. Dockerfile

```dockerfile
# Dockerfile
FROM node:18-alpine AS base

# 安装依赖阶段
FROM base AS deps
WORKDIR /app
COPY package.json package-lock.json ./
RUN npm ci

# 构建阶段
FROM base AS builder
WORKDIR /app
COPY --from=deps /app/node_modules ./node_modules
COPY . .

# 设置环境变量
ARG NEXT_PUBLIC_API_URL
ENV NEXT_PUBLIC_API_URL=${NEXT_PUBLIC_API_URL}

RUN npm run build

# 运行阶段
FROM base AS runner
WORKDIR /app

ENV NODE_ENV production

RUN addgroup --system --gid 1001 nodejs
RUN adduser --system --uid 1001 nextjs

COPY --from=builder /app/public ./public

# 复制构建产物
COPY --from=builder --chown=nextjs:nodejs /app/.next/standalone ./
COPY --from=builder --chown=nextjs:nodejs /app/.next/static ./.next/static

USER nextjs

EXPOSE 3000

ENV PORT 3000
ENV HOSTNAME "0.0.0.0"

CMD ["node", "server.js"]
```

#### 2. 更新 next.config.js

```javascript
// next.config.js
/** @type {import('next').NextConfig} */
const nextConfig = {
  output: 'standalone',  // 启用独立输出模式
  // ... 其他配置
}

module.exports = nextConfig
```

#### 3. 构建 Docker 镜像

```bash
# 构建镜像
docker build \
  --build-arg NEXT_PUBLIC_API_URL=https://api.example.com \
  -t mobile-upgrade:latest \
  .

# 运行容器
docker run -d \
  --name mobile-upgrade \
  -p 3001:3000 \
  mobile-upgrade:latest
```

#### 4. Docker Compose

```yaml
# docker-compose.yml
version: '3.8'

services:
  mobile-upgrade:
    build:
      context: .
      args:
        NEXT_PUBLIC_API_URL: ${NEXT_PUBLIC_API_URL:-http://admin-service:3000}
    image: mobile-upgrade:latest
    container_name: mobile-upgrade
    ports:
      - "3001:3000"
    environment:
      - NODE_ENV=production
    restart: unless-stopped
    networks:
      - rwa-network
    depends_on:
      - admin-service

networks:
  rwa-network:
    external: true
```

启动命令：

```bash
# 启动
docker-compose up -d

# 查看日志
docker-compose logs -f mobile-upgrade

# 停止
docker-compose down
```

### 方式三：静态导出部署

适用于不需要 SSR 的场景。

#### 1. 配置静态导出

```javascript
// next.config.js
/** @type {import('next').NextConfig} */
const nextConfig = {
  output: 'export',
  trailingSlash: true,
  images: {
    unoptimized: true,
  },
}

module.exports = nextConfig
```

#### 2. 构建静态文件

```bash
npm run build
```

静态文件输出到 `out/` 目录。

#### 3. 使用 Nginx 部署

```nginx
# nginx.conf
server {
    listen 80;
    server_name upgrade.example.com;
    root /var/www/mobile-upgrade;

    location / {
        try_files $uri $uri/ /index.html;
    }

    location /api {
        proxy_pass http://admin-service:3000;
        proxy_http_version 1.1;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
    }
}
```

## 反向代理配置

### Nginx 配置

```nginx
# /etc/nginx/sites-available/mobile-upgrade
upstream mobile_upgrade {
    server 127.0.0.1:3001;
}

upstream admin_service {
    server 127.0.0.1:3000;
}

server {
    listen 80;
    server_name upgrade.example.com;

    # 前端应用
    location / {
        proxy_pass http://mobile_upgrade;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection 'upgrade';
        proxy_set_header Host $host;
        proxy_cache_bypass $http_upgrade;
        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://admin_service;
        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;

        # 大文件上传支持
        client_max_body_size 200M;
        proxy_read_timeout 300s;
        proxy_connect_timeout 75s;
    }
}
```

### Caddy 配置

```caddyfile
# Caddyfile
upgrade.example.com {
    reverse_proxy /api/* admin-service:3000
    reverse_proxy /* mobile-upgrade:3001
}
```

## 健康检查

### 应用健康检查

```typescript
// src/app/api/health/route.ts
export async function GET() {
  return Response.json({
    status: 'healthy',
    timestamp: new Date().toISOString(),
  })
}
```

### Docker 健康检查

```dockerfile
HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 \
  CMD wget --no-verbose --tries=1 --spider http://localhost:3000/api/health || exit 1
```

### Kubernetes 探针

```yaml
livenessProbe:
  httpGet:
    path: /api/health
    port: 3000
  initialDelaySeconds: 10
  periodSeconds: 30

readinessProbe:
  httpGet:
    path: /api/health
    port: 3000
  initialDelaySeconds: 5
  periodSeconds: 10
```

## CI/CD 集成

### GitHub Actions

```yaml
# .github/workflows/deploy.yml
name: Deploy

on:
  push:
    branches: [main]
    paths:
      - 'frontend/mobile-upgrade/**'

jobs:
  build-and-deploy:
    runs-on: ubuntu-latest

    steps:
      - uses: actions/checkout@v3

      - name: Setup Node.js
        uses: actions/setup-node@v3
        with:
          node-version: 18
          cache: 'npm'
          cache-dependency-path: frontend/mobile-upgrade/package-lock.json

      - name: Install dependencies
        working-directory: frontend/mobile-upgrade
        run: npm ci

      - name: Build
        working-directory: frontend/mobile-upgrade
        run: npm run build
        env:
          NEXT_PUBLIC_API_URL: ${{ secrets.API_URL }}

      - name: Build Docker image
        working-directory: frontend/mobile-upgrade
        run: |
          docker build \
            --build-arg NEXT_PUBLIC_API_URL=${{ secrets.API_URL }} \
            -t mobile-upgrade:${{ github.sha }} \
            .

      - name: Push to registry
        run: |
          docker tag mobile-upgrade:${{ github.sha }} registry.example.com/mobile-upgrade:${{ github.sha }}
          docker push registry.example.com/mobile-upgrade:${{ github.sha }}

      - name: Deploy to server
        uses: appleboy/ssh-action@master
        with:
          host: ${{ secrets.SERVER_HOST }}
          username: ${{ secrets.SERVER_USER }}
          key: ${{ secrets.SERVER_SSH_KEY }}
          script: |
            docker pull registry.example.com/mobile-upgrade:${{ github.sha }}
            docker stop mobile-upgrade || true
            docker rm mobile-upgrade || true
            docker run -d \
              --name mobile-upgrade \
              -p 3001:3000 \
              --restart unless-stopped \
              registry.example.com/mobile-upgrade:${{ github.sha }}
```

## 监控和日志

### 日志收集

```bash
# Docker 日志
docker logs -f mobile-upgrade

# PM2 日志
pm2 logs mobile-upgrade

# 日志文件位置
/var/log/mobile-upgrade/access.log
/var/log/mobile-upgrade/error.log
```

### 性能监控

建议集成：
- **Prometheus + Grafana**: 指标监控
- **Sentry**: 错误追踪
- **Datadog / New Relic**: APM

### 集成 Sentry

```bash
npm install @sentry/nextjs
```

```typescript
// sentry.client.config.ts
import * as Sentry from '@sentry/nextjs'

Sentry.init({
  dsn: process.env.NEXT_PUBLIC_SENTRY_DSN,
  tracesSampleRate: 0.1,
})
```

## 故障排查

### 常见问题

| 问题 | 可能原因 | 解决方案 |
|------|----------|----------|
| 页面空白 | 构建失败 | 检查构建日志 |
| API 请求失败 | CORS / 网络 | 检查代理配置 |
| 静态资源 404 | 路径配置 | 检查 basePath 配置 |
| 内存不足 | 并发过高 | 增加实例或内存 |

### 调试命令

```bash
# 检查进程
ps aux | grep node

# 检查端口
netstat -tlnp | grep 3001

# 检查容器
docker ps -a
docker inspect mobile-upgrade

# 检查日志
docker logs --tail 100 mobile-upgrade
```

## 回滚策略

### Docker 回滚

```bash
# 查看历史版本
docker images mobile-upgrade

# 回滚到指定版本
docker stop mobile-upgrade
docker rm mobile-upgrade
docker run -d \
  --name mobile-upgrade \
  -p 3001:3000 \
  mobile-upgrade:previous-tag
```

### PM2 回滚

```bash
# 保存当前配置
pm2 save

# 回滚
pm2 reload ecosystem.config.js --update-env
```

## 安全建议

1. **环境变量**：不要在代码中硬编码敏感信息
2. **HTTPS**：生产环境强制使用 HTTPS
3. **CSP**：配置内容安全策略
4. **更新依赖**：定期更新依赖修复安全漏洞
5. **访问控制**：配置合适的访问权限

## 相关文档

- [架构文档](./ARCHITECTURE.md)
- [API 文档](./API.md)
- [开发指南](./DEVELOPMENT.md)
- [测试指南](./TESTING.md)
