feat: 添加 miniapp-api_test 测试环境自动化部署配置

- 新增 docker-compose.test.yml 测试环境配置
  - API 端口: 3001
  - MongoDB 端口: 27019
  - 独立数据卷隔离
  - SERVER_URL: https://miniapp-api-test.dxz99wyr.cn
- 新增 deploy/webhook-server.js Webhook 自动部署服务
- 新增 deploy/webhook.service systemd 服务配置
- 新增 deploy/setup.sh 一键初始化脚本
- Webhook Secret 与正式版保持一致: miniapp-api-deploy-secret
This commit is contained in:
Developer
2026-05-18 20:42:40 +08:00
parent e2dae5942d
commit e73149f91d
5 changed files with 351 additions and 0 deletions
+120
View File
@@ -0,0 +1,120 @@
# MiniApp API Test 自动化部署配置
## 文件说明
| 文件 | 说明 |
|------|------|
| `docker-compose.test.yml` | 测试环境 Docker Compose 配置 |
| `deploy/webhook-server.js` | Webhook 服务器,接收 Git 推送并自动部署 |
| `deploy/webhook.service` | systemd 服务配置,保持 webhook 常驻运行 |
| `deploy/setup.sh` | 一键初始化脚本 |
## 云服务器部署步骤
### 1. 修改配置
编辑以下文件,替换为你的实际信息:
- **`deploy/setup.sh`**:
```bash
GIT_REPO="git@github.com:your-username/miniapp-api_test.git"
```
- **`deploy/webhook.service`**:
```ini
Environment="WEBHOOK_SECRET=你的webhook密钥"
```
- **`deploy/webhook-server.js`** (可选):
```javascript
const SECRET = process.env.WEBHOOK_SECRET || '你的webhook密钥';
```
### 2. 上传代码到服务器
```bash
# 方式1: 直接上传
scp -r ./* root@your-server-ip:/opt/miniapp-api_test/
# 方式2: 先推送到 git,再在服务器克隆
```
### 3. 运行初始化脚本
```bash
ssh root@your-server-ip
cd /opt/miniapp-api_test
chmod +x deploy/setup.sh
./deploy/setup.sh
```
### 4. 配置 Git Webhook
在 Git 仓库设置中添加 Webhook:
- **Payload URL**: `http://your-server-ip:9001/webhook`
- **Content type**: `application/json`
- **Secret**: 你设置的 `WEBHOOK_SECRET`
- **触发事件**: Push events (main 分支)
### 5. 开放防火墙端口
```bash
# 开放 9001 端口(webhook)和 3001 端口(API)
ufw allow 9001/tcp
ufw allow 3001/tcp
```
### 6. 配置 Nginx 反向代理 (推荐)
```nginx
server {
listen 80;
server_name miniapp-api-test.dxz99wyr.cn;
location / {
proxy_pass http://localhost:3001;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
}
```
## 常用命令
```bash
# 查看 webhook 日志
journalctl -u miniapp-api_test-webhook -f
# 查看容器状态
docker-compose -f docker-compose.test.yml ps
# 查看容器日志
docker logs -f miniapp-api_test
# 手动重启容器
docker-compose -f docker-compose.test.yml restart
# 手动触发部署
cd /opt/miniapp-api_test && docker-compose -f docker-compose.test.yml up -d --build
```
## 升级正式版本
当测试版本稳定后,执行以下操作升级到正式版:
```bash
# 1. 推送测试版本代码到正式仓库
cd /opt/miniapp-api_test
git push 正式仓库地址 main
# 2. 在正式环境重新构建
# (正式环境的部署方式保持不变)
```
或者由开发者手动推送:
```bash
git remote add production <正式仓库地址>
git push production main
```
+52
View File
@@ -0,0 +1,52 @@
#!/bin/bash
set -e
echo "=========================================="
echo " MiniApp API Test 自动化部署环境配置"
echo "=========================================="
DEPLOY_DIR="/opt/miniapp-api_test"
GIT_REPO="ssh://git@8.136.137.59:2222/Superuser/miniapp-api_test.git"
echo ""
echo "[1/6] 创建部署目录..."
mkdir -p "$DEPLOY_DIR"
cd "$DEPLOY_DIR"
echo ""
echo "[2/6] 克隆 miniapp-api_test 仓库..."
if [ ! -d ".git" ]; then
git clone "$GIT_REPO" .
else
echo "仓库已存在, 跳过克隆"
fi
echo ""
echo "[3/6] 创建必要的目录..."
mkdir -p public/uploads public/avatars public/admin
echo ""
echo "[4/6] 配置 systemd 服务..."
cp deploy/webhook.service /etc/systemd/system/miniapp-api_test-webhook.service
systemctl daemon-reload
systemctl enable miniapp-api_test-webhook.service
echo ""
echo "[5/6] 启动 webhook 服务..."
systemctl start miniapp-api_test-webhook.service
echo ""
echo "[6/6] 启动 Docker 容器..."
docker-compose -f docker-compose.test.yml up -d --build
echo ""
echo "=========================================="
echo " 配置完成!"
echo "=========================================="
echo ""
echo "Webhook 接收地址: http://$(curl -s ifconfig.me):9001/webhook"
echo "API 测试地址: https://miniapp-api-test.dxz99wyr.cn"
echo ""
echo "查看 webhook 日志: journalctl -u miniapp-api_test-webhook -f"
echo "查看容器状态: docker-compose -f docker-compose.test.yml ps"
echo ""
+105
View File
@@ -0,0 +1,105 @@
const http = require('http');
const { exec } = require('child_process');
const path = require('path');
const crypto = require('crypto');
const PORT = 9001;
const DEPLOY_DIR = '/opt/miniapp-api_test';
const COMPOSE_FILE = 'docker-compose.test.yml';
const SECRET = process.env.WEBHOOK_SECRET || 'miniapp-api-deploy-secret';
function verifySignature(payload, signature) {
const hmac = crypto.createHmac('sha256', SECRET);
const digest = 'sha256=' + hmac.update(payload).digest('hex');
return crypto.timingSafeEqual(Buffer.from(digest), Buffer.from(signature));
}
function runCommand(command, cwd) {
return new Promise((resolve, reject) => {
console.log(`[${new Date().toISOString()}] 执行命令: ${command}`);
const child = exec(command, { cwd, maxBuffer: 1024 * 1024 }, (error, stdout, stderr) => {
if (error) {
console.error(`[${new Date().toISOString()}] 命令执行失败:`, error.message);
console.error('stderr:', stderr);
reject(error);
return;
}
console.log(`[${new Date().toISOString()}] 命令输出:\n${stdout}`);
if (stderr) console.error(`stderr: ${stderr}`);
resolve(stdout);
});
});
}
async function deploy() {
const timestamp = new Date().toISOString();
console.log(`\n========== 开始部署 miniapp-api_test [${timestamp}] ==========`);
try {
await runCommand('git fetch origin', DEPLOY_DIR);
await runCommand('git reset --hard origin/main', DEPLOY_DIR);
await runCommand(`docker-compose -f ${COMPOSE_FILE} build --no-cache`, DEPLOY_DIR);
await runCommand(`docker-compose -f ${COMPOSE_FILE} up -d`, DEPLOY_DIR);
await runCommand('docker image prune -f', DEPLOY_DIR);
console.log(`[${new Date().toISOString()}] 部署成功完成`);
return { success: true, message: '部署成功' };
} catch (error) {
console.error(`[${new Date().toISOString()}] 部署失败:`, error.message);
return { success: false, message: error.message };
}
}
const server = http.createServer(async (req, res) => {
if (req.method !== 'POST' || req.url !== '/webhook') {
res.writeHead(404);
res.end('Not Found');
return;
}
let body = '';
req.on('data', chunk => { body += chunk; });
req.on('end', async () => {
const signature = req.headers['x-hub-signature-256'] || req.headers['x-gitlab-token'];
if (SECRET !== 'your_webhook_secret_here' && signature) {
const isValid = verifySignature(body, signature);
if (!isValid) {
console.warn(`[${new Date().toISOString()}] Webhook 签名验证失败`);
res.writeHead(401);
res.end('Unauthorized');
return;
}
}
let payload;
try {
payload = JSON.parse(body);
} catch (e) {
payload = {};
}
const ref = payload.ref || payload.object_attributes?.ref || 'unknown';
console.log(`[${new Date().toISOString()}] 收到 Webhook 请求, ref: ${ref}`);
if (ref === 'refs/heads/main' || ref === 'main' || !ref.includes('refs/')) {
res.writeHead(202);
res.end(JSON.stringify({ status: 'accepted', message: '部署任务已启动' }));
const result = await deploy();
console.log(`[${new Date().toISOString()}] 部署结果:`, result);
} else {
res.writeHead(200);
res.end(JSON.stringify({ status: 'ignored', message: '非 main 分支推送, 忽略' }));
}
});
});
server.listen(PORT, '0.0.0.0', () => {
console.log(`Webhook 服务器已启动, 监听端口 ${PORT}`);
console.log(`部署目录: ${DEPLOY_DIR}`);
console.log(`接收地址: http://your-server-ip:${PORT}/webhook`);
});
+18
View File
@@ -0,0 +1,18 @@
[Unit]
Description=MiniApp API Test Webhook Auto Deploy Service
After=network.target docker.service
Requires=docker.service
[Service]
Type=simple
User=root
WorkingDirectory=/opt/miniapp-api_test/deploy
Environment="WEBHOOK_SECRET=miniapp-api-deploy-secret"
ExecStart=/usr/bin/node /opt/miniapp-api_test/deploy/webhook-server.js
Restart=always
RestartSec=5
StandardOutput=journal
StandardError=journal
[Install]
WantedBy=multi-user.target
+56
View File
@@ -0,0 +1,56 @@
services:
app-test:
build:
context: .
dockerfile: Dockerfile
container_name: miniapp-api_test
restart: unless-stopped
ports:
- "3001:3001"
environment:
- PORT=3001
- NODE_ENV=production
- WECHAT_APPID=wxa83262674846ca1a
- WECHAT_APPSECRET=365653aa1214a5523a6a0e7d793eec6a
- MONGODB_URI=mongodb://mongo-test:27017/quanyixiaozhushou_test
- JWT_SECRET=your_jwt_secret_key_here_change_in_production
- JWT_EXPIRES_IN=7d
- LOG_LEVEL=info
- BAIDU_OCR_API_KEY=IfYLOLzL6X60h5UOdnkX6OmT
- BAIDU_OCR_SECRET_KEY=wGXbp6DwazDghJ1EXtjAT7XAFwJLqVD4
- SERVER_URL=https://miniapp-api-test.dxz99wyr.cn
- EXPORT_ENCRYPT_KEY=QuanYiXiaoZhuShou_2026_Secret_Key
- ADMIN_KEY=quanyiAdmin2026
volumes:
- uploads_data_test:/app/public/uploads
- ./public/avatars:/app/public/avatars
depends_on:
mongo-test:
condition: service_healthy
healthcheck:
test: ["CMD", "node", "-e", "require('http').get('http://localhost:3001/health', (r) => {process.exit(r.statusCode === 200 ? 0 : 1)})"]
interval: 30s
timeout: 10s
retries: 3
start_period: 30s
mongo-test:
image: mongo:7.0
container_name: miniapp-api_test-mongo
restart: unless-stopped
ports:
- "27019:27017"
volumes:
- mongo_data_test:/data/db
environment:
- MONGO_INITDB_DATABASE=quanyixiaozhushou_test
healthcheck:
test: echo 'db.runCommand("ping").ok' | mongosh --quiet
interval: 10s
timeout: 5s
retries: 5
start_period: 20s
volumes:
uploads_data_test:
mongo_data_test: