有声书项目 - 生产环境部署文档
环境信息
| 项目 |
值 |
| 服务器 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/
服务地址
快速部署
# 一键部署(本地执行)
./deploy-prod.sh
服务器目录结构
/data/ai/audio/
├── server/ # 后端服务
│ ├── dist/ # 编译后的 JS 文件
│ ├── prisma/ # 数据库 schema
│ ├── uploads/ # 上传文件
│ ├── public/videos/ # 视频文件
│ └── .env # 环境变量
├── frontend/
│ └── build/h5/ # 前端构建产物
└── backup_YYYYMMDD_HHMMSS/ # 备份
服务管理命令
# 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. 构建本地项目
# 构建前端
cd my-uniapp-vue3
npm run build:h5
# 构建后端
cd ../server
npm run build
2. 同步文件到服务器
# 同步后端(排除 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 安装依赖
cd /data/ai/audio/server
npm install --production
npx prisma generate
3.2 环境变量 (.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
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
nginx -t && nginx -s reload
5. 启动后端服务
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 命令
# 进入 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 配置文件 |
故障排查
后端服务无法启动
# 查看错误日志
pm2 logs server --err --lines 50
# 手动测试运行
cd /data/ai/audio/server
export SERVER_PORT=3100
node dist/app.js
数据库连接失败
# 检查 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 错误
# 检查后端是否运行
pm2 status
# 检查端口监听
ss -tlnp | grep 3100
# 测试直接访问
curl http://localhost:3100/health
# 检查 Nginx 配置
nginx -t
重启所有服务
# 重启后端
pm2 restart server
# 重载 Nginx
nginx -s reload
# 重启 MySQL(如需要)
docker restart single-mysql
重新部署完整流程
# 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