# 有声书项目 - 生产环境部署文档

## 环境信息

| 项目 | 值 |
|------|-----|
| 服务器 IP | 8.159.134.106 |
| 部署路径 | /data/ai/audio |
| 后端端口 | 3100 |
| 前端路径 | /data/ai/audio/frontend/build/h5 |
| 域名 | book.rrbrr.com |
| 后端 API 域名 | book.rrbrr.com/api |
| SSH 端口 | 22622 |
| SSH Key | ~/.ssh/id_rsa |

**Nginx 配置由宝塔面板管理**，配置文件位于 `/www/server/panel/vhost/nginx/`

## 服务地址

| 服务 | 地址 |
|------|------|
| 前端 | http://book.rrbrr.com |
| 后端 API | http://book.rrbrr.com/api |
| 健康检查 | http://book.rrbrr.com/api/health |

## 快速部署

```bash
# 一键部署（本地执行）
./deploy-prod.sh
```

## 服务器目录结构

```
/data/ai/audio/
├── server/              # 后端服务
│   ├── dist/            # 编译后的 JS 文件
│   ├── prisma/         # 数据库 schema
│   ├── uploads/         # 上传文件
│   ├── public/videos/  # 视频文件
│   └── .env            # 环境变量
├── frontend/
│   └── build/h5/       # 前端构建产物
└── backup_YYYYMMDD_HHMMSS/  # 备份
```

## 服务管理命令

```bash
# SSH 连接
ssh -i ~/.ssh/id_rsa -p 22622 root@8.159.134.106

# 查看后端服务状态
pm2 status

# 查看后端日志
pm2 logs server --lines 100

# 重启后端服务
pm2 restart server

# 重载 Nginx
nginx -s reload
```

## 手动部署步骤

### 1. 构建本地项目

```bash
# 构建前端
cd my-uniapp-vue3
npm run build:h5

# 构建后端
cd ../server
npm run build
```

### 2. 同步文件到服务器

```bash
# 同步后端（排除 node_modules, .ts 等）
rsync -azP -e "ssh -i ~/.ssh/id_rsa -p 22622" \
  --exclude='node_modules' --exclude='.git' --exclude='*.ts' \
  --exclude='test-*.js' --exclude='*.log' \
  server/ root@8.159.134.106:/data/ai/audio/server/

# 同步前端
rsync -azP -e "ssh -i ~/.ssh/id_rsa -p 22622" \
  my-uniapp-vue3/dist/ root@8.159.134.106:/data/ai/audio/frontend/
```

### 3. 服务器端配置

#### 3.1 安装依赖

```bash
cd /data/ai/audio/server
npm install --production
npx prisma generate
```

#### 3.2 环境变量 (.env)

```env
NODE_ENV=production
SERVER_PORT=3100
AUTH_ENABLED=false

JWT_SECRET=my-jwt-secret-key-2024
JWT_EXPIRES_IN=7d

# 阿里云百炼 TTS
DASHSCOPE_API_KEY=sk-c25679401ba24c749f53be86b0c9a7a6
DASHSCOPE_MODEL=qwen3-tts-instruct-flash
DASHSCOPE_TTS_MODELS=qwen3-tts-flash
DASHSCOPE_VOICE=Cherry
DASHSCOPE_USE_REALTIME=true
DASHSCOPE_REALTIME_MODEL=qwen3-tts-instruct-flash-realtime

# MySQL (Docker MySQL 8.0)
DATABASE_URL="mysql://root:password@localhost:3306/audio_book"

# 存储
STORAGE_TYPE=oss

# Redis
REDIS_HOST=localhost
REDIS_PORT=6379
REDIS_PASSWORD=
REDIS_DB=0

# 阿里云 OSS
OSS_ENDPOINT=oss-cn-hangzhou.aliyuncs.com
OSS_ACCESS_KEY_ID=LTAI5tBn4G5HMo8PqMdNsqHd
OSS_ACCESS_KEY_SECRET=f0R5nOQf6E1lIGESENKiK8cNzFl8wd
OSS_BUCKET_NAME=aaaa33dfsf32rfsf
OSS_REGION=cn-hangzhou

# Minimax
MINIMAX_API_KEY=sk-cp-s8hGJr4ACdlZ4GM0LjZ1l2ZNBUAMGwd2ahl0c3W2g6C0EiNH0UvwOBx2q0oJOYO6ca5HqJccP_KRUPucoBizavlwDSZp-ohOGeUw2ymAdXPuM6r3CIDmmlY
```

### 4. 配置 Nginx

Nginx 配置文件位置：`/www/server/panel/vhost/nginx/`

#### 4.1 前端 - html_book.rrbrr.com.conf

```nginx
server
{
    listen 80;
    server_name book.rrbrr.com;
    return 301 https://$server_name$request_uri;
}

server
{
    listen 443 ssl;
    http2 on;
    server_name book.rrbrr.com;
    index index.html index.htm default.htm default.html;
    root /data/ai/audio/frontend/build/h5;
    include /www/server/panel/vhost/nginx/extension/book.rrbrr.com/*.conf;
    #CERT-APPLY-CHECK--START
    # 用于SSL证书申请时的文件验证相关配置 -- 请勿删除并保持这段设置在优先级高的位置
    include /www/server/panel/vhost/nginx/well-known/book.rrbrr.com.conf;
    #CERT-APPLY-CHECK--END

    #SSL-START SSL相关配置，请勿删除或修改下一行带注释的404规则
    #error_page 404/404.html;
    ssl_certificate /www/server/panel/vhost/cert/book.rrbrr.com/fullchain.pem;
    ssl_certificate_key /www/server/panel/vhost/cert/book.rrbrr.com/privkey.pem;
    ssl_protocols TLSv1.1 TLSv1.2 TLSv1.3;
    ssl_ciphers EECDH+CHACHA20:EECDH+CHACHA20-draft:EECDH+AES128:RSA+AES128:EECDH+AES256:RSA+AES256:EECDH+3DES:RSA+3DES:!MD5;
    ssl_prefer_server_ciphers on;
    ssl_session_tickets on;
    ssl_session_cache shared:SSL:10m;
    ssl_session_timeout 10m;
    add_header Strict-Transport-Security "max-age=31536000";
    add_header Alt-Svc 'quic=":443"; h3=":443"; h3-29=":443"; h3-27=":443";h3-25=":443"; h3-T050=":443"; h3-Q050=":443";h3-Q049=":443";h3-Q048=":443"; h3-Q046=":443"; h3-Q043=":443"';
    error_page 497 https://$host$request_uri;
    #SSL-END

    #ERROR-PAGE-START  错误页配置，可以注释、删除或修改
    #error_page 404 /404.html;
    #error_page 502 /502.html;
    #ERROR-PAGE-END

    #API-PROXY-START
    location /api/ {
        proxy_pass http://127.0.0.1:3100;
        proxy_set_header Host $http_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;
    }
    #API-PROXY-END

    #REWRITE-START URL重写规则引用,修改后将导致面板设置的伪静态规则失效
    include /www/server/panel/vhost/rewrite/html_book.rrbrr.com.conf;
    #REWRITE-END

    #禁止访问的文件或目录
    location ~ ^/(\.user.ini|\.htaccess|\.git|\.env|\.svn|\.project|LICENSE|README.md)
    {
        return 404;
    }

    #一键申请SSL证书验证目录相关设置
    location ~ \.well-known{
        allow all;
    }

    #禁止在证书验证目录放入敏感文件
    if ( $uri ~ "^/\.well-known/.*\.(php|jsp|py|js|css|lua|ts|go|zip|tar\.gz|rar|7z|sql|bak)$" ) {
        return 403;
    }

    location ~ .*\.(gif|jpg|jpeg|png|bmp|swf)$
    {
        expires      30d;
        error_log /dev/null;
        access_log /dev/null;
    }

    location ~ .*\.(js|css)?$
    {
        expires      12h;
        error_log /dev/null;
        access_log /dev/null;
    }
    access_log  /www/wwwlogs/book.rrbrr.com.log;
    error_log  /www/wwwlogs/book.rrbrr.com.error.log;
}
```

#### 4.3 重载 Nginx

```bash
nginx -t && nginx -s reload
```

### 5. 启动后端服务

```bash
cd /data/ai/audio/server
export SERVER_PORT=3100

# 停止旧服务
pm2 delete server 2>/dev/null || true

# 启动服务
pm2 start dist/app.js --name server --env production

# 保存并设置开机启动
pm2 save
pm2 startup
```

### 6. 数据库操作

#### MySQL Docker 信息

| 项目 | 值 |
|------|-----|
| 容器名 | single-mysql |
| 版本 | MySQL 8.0 |
| 端口 | 3306 (Docker 映射到主机) |
| 密码 | password |
| 数据库 | audio_book |

#### 常用 MySQL 命令

```bash
# 进入 MySQL 容器
docker exec -it single-mysql mysql -u root -ppassword

# 查看数据库
docker exec single-mysql mysql -u root -ppassword -e 'SHOW DATABASES;'

# 执行 SQL
docker exec single-mysql mysql -u root -ppassword audio_book -e 'SELECT 1;'

# 备份数据库
docker exec single-mysql mysqldump -u root -ppassword audio_book > audio_book_$(date +%Y%m%d).sql

# 数据库迁移
cd /data/ai/audio/server
npx prisma migrate deploy
```

## 数据库信息

| 项目 | 值 |
|------|-----|
| 类型 | MySQL 8.0 (Docker) |
| 容器 | single-mysql |
| 主机连接 | localhost:3306 |
| 容器内连接 | 3306 |
| 数据库名 | audio_book |
| 用户名 | root |
| 密码 | password |

## 文件说明

| 文件/目录 | 说明 |
|------|------|
| `/data/ai/audio/server/` | 后端 Node.js 服务 |
| `/data/ai/audio/frontend/build/h5/` | 前端 H5 构建产物 |
| `/data/ai/audio/server/uploads/` | 用户上传文件 |
| `/data/ai/audio/server/public/videos/` | 生成的视频文件 |
| `/www/server/panel/vhost/nginx/` | Nginx 配置文件 |

## 故障排查

### 后端服务无法启动

```bash
# 查看错误日志
pm2 logs server --err --lines 50

# 手动测试运行
cd /data/ai/audio/server
export SERVER_PORT=3100
node dist/app.js
```

### 数据库连接失败

```bash
# 检查 MySQL 容器状态
docker ps | grep mysql

# 重启 MySQL 容器
docker restart single-mysql

# 测试连接
docker exec single-mysql mysql -u root -ppassword -e 'SELECT 1;'

# 检查 .env 数据库配置
cat /data/ai/audio/server/.env | grep DATABASE
```

### Nginx 502 错误

```bash
# 检查后端是否运行
pm2 status

# 检查端口监听
ss -tlnp | grep 3100

# 测试直接访问
curl http://localhost:3100/health

# 检查 Nginx 配置
nginx -t
```

### 重启所有服务

```bash
# 重启后端
pm2 restart server

# 重载 Nginx
nginx -s reload

# 重启 MySQL（如需要）
docker restart single-mysql
```

### 重新部署完整流程

```bash
# 1. 同步文件
rsync -azP -e "ssh -i ~/.ssh/id_rsa -p 22622" --exclude='node_modules' --exclude='.git' --exclude='*.ts' server/ root@8.159.134.106:/data/ai/audio/server/
rsync -azP -e "ssh -i ~/.ssh/id_rsa -p 22622" my-uniapp-vue3/dist/ root@8.159.134.106:/data/ai/audio/frontend/

# 2. 服务器端
ssh -i ~/.ssh/id_rsa -p 22622 root@8.159.134.106
cd /data/ai/audio/server
npm install --production
npx prisma generate
export SERVER_PORT=3100
pm2 restart server
nginx -s reload
```
