From cb483298d597d89c5fcacacdab35c51658e04b84 Mon Sep 17 00:00:00 2001 From: moira Date: Wed, 13 May 2026 15:31:23 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E6=96=B0=E5=A2=9Edeploy.sh=20--init-en?= =?UTF-8?q?v=E4=B8=80=E9=94=AE=E5=88=9D=E5=A7=8B=E5=8C=96.env+=E5=AE=8C?= =?UTF-8?q?=E5=96=84JWT=5FSECRET=E7=AE=A1=E7=90=86=E6=96=87=E6=A1=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 新增 --init-env 功能: - 自动生成 256 位随机 JWT_SECRET (openssl rand -base64 32) - 推送完整.env模板到服务器,设置权限600 - 文件已存在时先备份再覆盖,有确认提示 - dry-run 模式下安全预览 JWT_SECRET 管理方案: - dev/prod 使用不同密钥 - 仅在服务器.env中存储(不进入Git/TOML) - deploy.sh check_env_file() 部署前自动验证 - AGENTS.md 新增完整管理文档(生成/存储/验证/轮换) deploy.sh 帮助文档同步更新 --init-env 说明 --- AGENTS.md | 47 +++++++++++++++++++++-- deploy.sh | 111 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 154 insertions(+), 4 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index 1edc4ed..fc212fb 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -581,6 +581,7 @@ let new_expires = base_time + days(pkg_days); | `--skip-tests` | 跳过部署后测试 | | `--rollback` | 回滚到上一个备份版本 | | `--backup-list` | 列出可用备份 | +| `--init-env` | 初始化远程 .env(生成随机 JWT_SECRET + 推送模板到服务器) | | `--help, -h` | 显示帮助信息 | **示例:** @@ -588,6 +589,7 @@ let new_expires = base_time + days(pkg_days); ./deploy.sh production --dry-run # 预览生产部署 ./deploy.sh production --yes # 无需确认直接部署 ./deploy.sh development --skip-tests # 跳过测试 +./deploy.sh production --init-env # 初始化生产 .env ./deploy.sh production --rollback # 回滚到上一个版本 ./deploy.sh production --backup-list # 列出可用备份 ``` @@ -660,12 +662,49 @@ WEBHOOK_URL="https://example.com/webhook" ./deploy.sh production 参考本地文件:`rust-backend.service`(生产)、`rust-backend-dev.service`(开发) -**2. 创建 .env 环境配置:** +**2. 初始化 .env 环境配置(推荐使用 deploy.sh):** -开发环境:`/root/rust/rust_backend_dev/.env` -生产环境:`/root/rust/rust_backend/.env` +```bash +# 生产环境 — 自动生成随机 JWT_SECRET,推送 .env 模板到服务器 +./deploy.sh production --init-env -参考 `.env.example` 或询问运维获取。 +# 开发环境 +./deploy.sh development --init-env +``` + +`--init-env` 会: +- 生成 256 位随机 `JWT_SECRET`(`openssl rand -base64 32`) +- 推送 `.env` 文件到服务器 `${REMOTE_DIR}/.env` +- 设置文件权限 `600`(仅 root 可读) +- 如文件已存在,先备份再覆盖 + +执行后仍需 SSH 到服务器填写以下手动变量: + +```bash +ssh root@1panel-server +vim /root/rust/rust_backend/.env + +# 必须手动填写: +DATABASE_URL=postgres://user:pass@host:5432/dbname +WECHAT_APPID=wx... +WECHAT_SECRET=xxx +``` + +> **不建议**手动创建 `.env`——因为 `deploy.sh --init-env` 会自动生成强随机 `JWT_SECRET`,手动创建容易忘记生成或使用弱密钥。 + +### JWT_SECRET 管理 + +| 生命周期 | 操作 | 说明 | +|----------|------|------| +| **生成** | `openssl rand -base64 32` | 首次 `deploy.sh --init-env` 自动完成 | +| **存储** | 服务器 `.env`,权限 600 | 不进入 Git,不进入 TOML 配置 | +| **验证** | `deploy.sh check_env_file` | 部署前自动检查是否缺失 | +| **轮换** | 手动替换 → 重启服务 | 旧 token 失效,所有用户需重新登录 | + +**原则:** +- Dev 和 Prod 使用**不同**的 JWT_SECRET +- JWT_SECRET **永不出现在** `config/*.toml` 或任何 Git 管理的文件中 +- 服务器 `.env` 文件**不通过 `deploy.sh` 同步**(不会上传到服务器),仅手动或通过 `--init-env` 创建 **3. 启用服务:** diff --git a/deploy.sh b/deploy.sh index fe4e08f..7335ffc 100755 --- a/deploy.sh +++ b/deploy.sh @@ -31,6 +31,7 @@ TAIL_LOGS=false LOG_LINES=50 VIEW_STATUS=false POST_DEPLOY_LOGS=false +INIT_ENV=false # 全局变量(部署流程中使用) BACKUP_PATH="" @@ -73,6 +74,9 @@ while [[ $# -gt 0 ]]; do --post-logs) POST_DEPLOY_LOGS=true ;; + --init-env) + INIT_ENV=true + ;; --help|-h) echo "用法: $0 [development|production] [options]" echo "" @@ -86,6 +90,7 @@ while [[ $# -gt 0 ]]; do echo " --tail [N] 实时跟踪后端日志(Ctrl+C 退出)" echo " --status 查看服务状态" echo " --post-logs 部署后自动显示日志" + echo " --init-env 初始化远程 .env 文件(生成随机 JWT_SECRET,推送模板到服务器)" exit 0 ;; *) @@ -571,6 +576,106 @@ check_env_file() { log_info ".env 文件检查通过(${#required_vars[@]} 个必需变量均已配置)" fi } + +# 初始化远程 .env 文件(生成随机 JWT_SECRET,推送模板到服务器) +init_env() { + log_step "初始化远程 .env 文件..." + + if [ "$DRY_RUN" = true ]; then + log_dry "在 ${REMOTE_HOST}:${REMOTE_DIR}/.env 生成带随机 JWT_SECRET 的配置文件" + return 0 + fi + + # 1. 检查服务器 .env 是否已存在 + local env_exists=false + if ssh "${REMOTE_USER}@${REMOTE_HOST}" "test -f '${REMOTE_DIR}/.env'" 2>/dev/null; then + env_exists=true + log_warn ".env 文件已存在: ${REMOTE_DIR}/.env" + echo "" + echo -e " ${YELLOW}文件已存在,继续将覆盖现有配置!${NC}" + echo "" + read -p "是否覆盖? [y/N] " -n 1 -r + echo + if [[ ! $REPLY =~ ^[Yy]$ ]]; then + log_warn "已取消" + return 0 + fi + # 备份旧文件 + local backup_file=".env.backup.$(date +%Y%m%d_%H%M%S)" + ssh "${REMOTE_USER}@${REMOTE_HOST}" "cp '${REMOTE_DIR}/.env' '${REMOTE_DIR}/${backup_file}'" 2>/dev/null || true + log_info "旧文件已备份: ${backup_file}" + fi + + # 2. 生成随机 JWT_SECRET + local jwt_secret + if command -v openssl &>/dev/null; then + jwt_secret=$(openssl rand -base64 32 | tr -d '\n') + else + jwt_secret="change-me-$(date +%s)-$$" + log_warn "openssl 未找到,使用临时密钥(请手动替换)" + fi + + # 3. 构建 .env 内容 + local env_content + env_content=$(cat < '${REMOTE_DIR}/.env'" + ssh "${REMOTE_USER}@${REMOTE_HOST}" "chmod 600 '${REMOTE_DIR}/.env'" + + log_info ".env 文件已生成: ${REMOTE_DIR}/.env" + log_warn "请 SSH 到服务器手动填写以下变量:" + echo "" + echo -e " ${YELLOW}DATABASE_URL${NC} — 数据库连接字符串" + echo -e " ${YELLOW}WECHAT_APPID${NC} — 微信小程序 AppID" + echo -e " ${YELLOW}WECHAT_SECRET${NC} — 微信小程序 Secret" + echo "" + log_info "JWT_SECRET 已自动生成(无需修改)" + log_info "文件权限已设为 600(仅所有者可读)" + echo "" + + # 5. 确认是否需要重启服务 + if [ "$env_exists" = true ]; then + log_info "如果已部署的服务正在运行,建议重启:" + echo -e " ${GREEN}ssh ${REMOTE_USER}@${REMOTE_HOST} 'systemctl restart ${SERVICE_NAME}'${NC}" + fi +} + check_dependencies() { log_step "检查依赖..." local deps=("cargo" "rsync" "ssh") @@ -1376,6 +1481,12 @@ main() { exit 0 fi + if [ "$INIT_ENV" = true ]; then + ssh_connect + init_env + exit 0 + fi + if [ "$VIEW_LOGS" = true ]; then ssh_connect view_logs