feat: 使用 Gitea Secrets 管理全部密钥

- deploy.yml 从 secrets 动态生成 .env,部署后自动删除
- docker-compose.prod.yml 支持全部业务配置注入(短信/微信/支付宝等)
- .env.example 只保留 GITEA_RUNNER_TOKEN,其他密钥全部迁移到 Secrets
- 更新 deploy/README.md 文档,完整列出 Secrets 配置清单

服务器上不再存储任何密码文件,安全性大幅提升

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-06-01 21:01:22 +08:00
parent 501adf7819
commit b358dbdab1
6 changed files with 117 additions and 27 deletions
+44 -8
View File
@@ -66,17 +66,43 @@ jobs:
steps:
- uses: actions/checkout@v4
- name: Generate .env from secrets
run: |
cat > deploy/docker/.env.prod << 'EOF'
PROD_DB_PASSWORD=${{ secrets.PROD_DB_PASSWORD }}
PROD_JWT_SECRET=${{ secrets.PROD_JWT_SECRET }}
PROD_JWT_EXPIRES_IN=${{ secrets.PROD_JWT_EXPIRES_IN }}
PROD_JWT_REFRESH_EXPIRES_IN=${{ secrets.PROD_JWT_REFRESH_EXPIRES_IN }}
PROD_SMS_ACCESS_KEY_ID=${{ secrets.PROD_SMS_ACCESS_KEY_ID }}
PROD_SMS_ACCESS_KEY_SECRET=${{ secrets.PROD_SMS_ACCESS_KEY_SECRET }}
PROD_SMS_SIGN_NAME=${{ secrets.PROD_SMS_SIGN_NAME }}
PROD_SMS_TEMPLATE_CODE=${{ secrets.PROD_SMS_TEMPLATE_CODE }}
PROD_WECHAT_APPID=${{ secrets.PROD_WECHAT_APPID }}
PROD_WECHAT_SECRET=${{ secrets.PROD_WECHAT_SECRET }}
PROD_WECHAT_MCHID=${{ secrets.PROD_WECHAT_MCHID }}
PROD_WECHAT_SERIAL_NO=${{ secrets.PROD_WECHAT_SERIAL_NO }}
PROD_WECHAT_APIV3_KEY=${{ secrets.PROD_WECHAT_APIV3_KEY }}
PROD_WECHAT_PRIVATE_KEY=${{ secrets.PROD_WECHAT_PRIVATE_KEY }}
PROD_WECHAT_PAY_NOTIFY_URL=${{ secrets.PROD_WECHAT_PAY_NOTIFY_URL }}
PROD_WECHAT_REFUND_NOTIFY_URL=${{ secrets.PROD_WECHAT_REFUND_NOTIFY_URL }}
PROD_ALIPAY_APPID=${{ secrets.PROD_ALIPAY_APPID }}
PROD_ALIPAY_PRIVATE_KEY=${{ secrets.PROD_ALIPAY_PRIVATE_KEY }}
PROD_API_BASE_URL=${{ secrets.PROD_API_BASE_URL }}
EOF
chmod 600 deploy/docker/.env.prod
- name: Deploy to production
run: |
echo "部署到生产环境..."
cd deploy/docker
docker-compose -f docker-compose.prod.yml down --remove-orphans
docker-compose -f docker-compose.prod.yml build --parallel
docker-compose -f docker-compose.prod.yml up -d
docker-compose -f docker-compose.prod.yml --env-file .env.prod down --remove-orphans
docker-compose -f docker-compose.prod.yml --env-file .env.prod build --parallel
docker-compose -f docker-compose.prod.yml --env-file .env.prod up -d
docker image prune -f
echo "等待服务启动..."
sleep 10
docker-compose -f docker-compose.prod.yml ps
docker-compose -f docker-compose.prod.yml --env-file .env.prod ps
rm -f .env.prod
deploy-test:
runs-on: [self-hosted, rent-deploy]
@@ -85,14 +111,24 @@ jobs:
steps:
- uses: actions/checkout@v4
- name: Generate .env from secrets
run: |
cat > deploy/docker/.env.test << 'EOF'
TEST_DB_PASSWORD=${{ secrets.TEST_DB_PASSWORD }}
TEST_JWT_SECRET=${{ secrets.TEST_JWT_SECRET }}
TEST_API_BASE_URL=${{ secrets.TEST_API_BASE_URL }}
EOF
chmod 600 deploy/docker/.env.test
- name: Deploy to test
run: |
echo "部署到测试环境..."
cd deploy/docker
docker-compose -f docker-compose.test.yml down --remove-orphans
docker-compose -f docker-compose.test.yml build --parallel
docker-compose -f docker-compose.test.yml up -d
docker-compose -f docker-compose.test.yml --env-file .env.test down --remove-orphans
docker-compose -f docker-compose.test.yml --env-file .env.test build --parallel
docker-compose -f docker-compose.test.yml --env-file .env.test up -d
docker image prune -f
echo "等待服务启动..."
sleep 10
docker-compose -f docker-compose.test.yml ps
docker-compose -f docker-compose.test.yml --env-file .env.test ps
rm -f .env.test
+1 -1
View File
@@ -2,7 +2,7 @@
DB_HOST=localhost
DB_PORT=3306
DB_USERNAME=root
DB_PASSWORD=quan131735
DB_PASSWORD=xxx
DB_DATABASE=rent_platform
# Redis配置
+46 -6
View File
@@ -64,16 +64,16 @@ scripts/
```bash
# 将代码克隆到服务器
git clone <你的GitLab仓库地址> ~/rent-platform
git clone <你的Gitea仓库地址> ~/rent-platform
cd ~/rent-platform
# 运行初始化(安装 Docker、创建网络、启动网关)
# 运行初始化(安装 Docker、创建网络、启动 Gitea + 网关)
bash scripts/setup-server.sh
```
脚本会自动完成:
- 安装 Docker、Docker Compose、Git、pnpm
- 创建 `.env` 文件并提示你填写密码
- 启动 Gitea + Act Runner
- 创建 Docker 网络(rent-prod、rent-test
- 启动 Gateway Nginx
@@ -148,7 +148,47 @@ docker logs rent-act-runner
在 Gitea → **Settings → Actions → Runners** 页面应能看到 `rent-deploy-runner` 已注册并在线。
### 第 3 步:推送代码到 Gitea
### 第 3 步:配置 Secrets(密钥安全)
> 所有密钥通过 Gitea Secrets 管理,服务器上不存储任何密码文件。
Gitea → **仓库 → Settings → Actions → Secrets**,添加以下密钥:
**生产环境:**
| Secret 名 | 说明 | 示例 |
|-----------|------|------|
| `PROD_DB_PASSWORD` | 数据库密码 | 强密码 |
| `PROD_JWT_SECRET` | JWT 密钥 | 随机字符串 |
| `PROD_JWT_EXPIRES_IN` | Token 过期时间 | `2h` |
| `PROD_JWT_REFRESH_EXPIRES_IN` | 刷新 Token 过期 | `7d` |
| `PROD_SMS_ACCESS_KEY_ID` | 阿里云 AK | 短信服务 |
| `PROD_SMS_ACCESS_KEY_SECRET` | 阿里云 SK | 短信服务 |
| `PROD_SMS_SIGN_NAME` | 短信签名 | 品居会 |
| `PROD_SMS_TEMPLATE_CODE` | 短信模板编码 | SMS_xxx |
| `PROD_WECHAT_APPID` | 微信 AppID | wx1234... |
| `PROD_WECHAT_SECRET` | 微信 Secret | 微信密钥 |
| `PROD_WECHAT_MCHID` | 微信商户号 | 1234567890 |
| `PROD_WECHAT_SERIAL_NO` | 证书序列号 | 微信支付 |
| `PROD_WECHAT_APIV3_KEY` | APIv3 密钥 | 32 位字符串 |
| `PROD_WECHAT_PRIVATE_KEY` | 商户私钥 | PEM 内容 |
| `PROD_WECHAT_PAY_NOTIFY_URL` | 支付回调 | `https://api.pinzhuhui.com/...` |
| `PROD_WECHAT_REFUND_NOTIFY_URL` | 退款回调 | `https://api.pinzhuhui.com/...` |
| `PROD_ALIPAY_APPID` | 支付宝 AppID | 2021xxx |
| `PROD_ALIPAY_PRIVATE_KEY` | 支付宝私钥 | 密钥内容 |
| `PROD_API_BASE_URL` | API 地址 | `https://api.pinzhuhui.com` |
**测试环境:**
| Secret 名 | 说明 |
|-----------|------|
| `TEST_DB_PASSWORD` | 测试数据库密码 |
| `TEST_JWT_SECRET` | 测试 JWT 密钥 |
| `TEST_API_BASE_URL` | `https://api-test.pinzhuhui.com` |
> 暂时没有的配置(如微信支付、支付宝)可以先不填,流水线会使用空值默认。
### 第 4 步:推送代码到 Gitea
```bash
# 添加 Gitea 远程仓库
@@ -160,7 +200,7 @@ git push gitea test
git push gitea prod
```
### 第 4 步:自动部署触发
### 第 5 步:自动部署触发
```bash
# 推送 test 分支 → 自动部署测试环境
@@ -170,7 +210,7 @@ git push gitea test
git push gitea prod
```
流水线:install → build → deploy
流水线:install → build → generate .env from secrets → deploy
在 Gitea → **仓库 → Actions** 页面查看实时日志。
+2 -8
View File
@@ -1,10 +1,4 @@
# ===== 生产环境 =====
PROD_DB_PASSWORD=change_me_in_production
PROD_JWT_SECRET=change_me_in_production
# ===== 测试环境 =====
TEST_DB_PASSWORD=change_me_in_test
TEST_JWT_SECRET=change_me_in_test
# ===== Gitea Act Runner =====
# 注册 Runner 时从 Gitea Web 界面获取 token 填入这里
# 其他所有密钥通过 Gitea → Settings → Actions → Secrets 管理
GITEA_RUNNER_TOKEN=change_me
+21 -2
View File
@@ -52,14 +52,33 @@ services:
environment:
NODE_ENV: production
PORT: 3000
# Docker 内部网络
DB_HOST: mysql
DB_PORT: 3306
DB_USERNAME: root
DB_PASSWORD: ${PROD_DB_PASSWORD:-rent123456}
DB_DATABASE: rent_platform
REDIS_HOST: redis
REDIS_PORT: 6379
JWT_SECRET: ${PROD_JWT_SECRET:-change_this_in_production}
# 从 .env.prod 注入的密钥
DB_PASSWORD: ${PROD_DB_PASSWORD}
JWT_SECRET: ${PROD_JWT_SECRET}
JWT_EXPIRES_IN: ${PROD_JWT_EXPIRES_IN:-2h}
JWT_REFRESH_EXPIRES_IN: ${PROD_JWT_REFRESH_EXPIRES_IN:-7d}
SMS_ACCESS_KEY_ID: ${PROD_SMS_ACCESS_KEY_ID:-}
SMS_ACCESS_KEY_SECRET: ${PROD_SMS_ACCESS_KEY_SECRET:-}
SMS_SIGN_NAME: ${PROD_SMS_SIGN_NAME:-}
SMS_TEMPLATE_CODE: ${PROD_SMS_TEMPLATE_CODE:-}
WECHAT_APPID: ${PROD_WECHAT_APPID:-}
WECHAT_SECRET: ${PROD_WECHAT_SECRET:-}
WECHAT_MCHID: ${PROD_WECHAT_MCHID:-}
WECHAT_SERIAL_NO: ${PROD_WECHAT_SERIAL_NO:-}
WECHAT_APIV3_KEY: ${PROD_WECHAT_APIV3_KEY:-}
WECHAT_PRIVATE_KEY: ${PROD_WECHAT_PRIVATE_KEY:-}
WECHAT_PAY_NOTIFY_URL: ${PROD_WECHAT_PAY_NOTIFY_URL:-}
WECHAT_REFUND_NOTIFY_URL: ${PROD_WECHAT_REFUND_NOTIFY_URL:-}
ALIPAY_APPID: ${PROD_ALIPAY_APPID:-}
ALIPAY_PRIVATE_KEY: ${PROD_ALIPAY_PRIVATE_KEY:-}
API_BASE_URL: ${PROD_API_BASE_URL:-http://localhost:3000}
depends_on:
mysql:
condition: service_healthy
+3 -2
View File
@@ -55,11 +55,12 @@ services:
DB_HOST: mysql
DB_PORT: 3306
DB_USERNAME: root
DB_PASSWORD: ${TEST_DB_PASSWORD:-rent123456}
DB_DATABASE: rent_platform
REDIS_HOST: redis
REDIS_PORT: 6379
JWT_SECRET: ${TEST_JWT_SECRET:-change_this_in_test}
DB_PASSWORD: ${TEST_DB_PASSWORD}
JWT_SECRET: ${TEST_JWT_SECRET}
API_BASE_URL: ${TEST_API_BASE_URL:-http://localhost:3001}
depends_on:
mysql:
condition: service_healthy