name: Grant Super Admin (设超管 · 可带外建号) # 超管不是"某个预置账号+密码"——代码里没有种子管理员、密码全是 salt+hash 不可反推。 # 超管 = .env 的 SUPER_ADMIN_EMAILS 白名单:【白名单里的邮箱,谁拥有这个账号,谁就是超管】。 # # ⚠ 重要(安全策略变更后的正确顺序): # 为堵住"管理员邮箱可被任何人抢注→拿全部后台权限"的漏洞,后端【已禁止用白名单邮箱自助注册】。 # 所以【先加白名单、再去注册】这条老路已经走不通(注册会被 403 挡掉)。 # 现在有两条路: # A) 已有账号:你的邮箱【已经】在平台注册过 → 直接跑本工作流加白名单 → 重新登录即为超管。 # B) 没有账号:勾选 create_or_reset_account → 本工作流【带外】在库里建号/重置密码(密码取自 # 仓库 Secret: ADMIN_INIT_PASSWORD)→ 加白名单 → 用该邮箱+该密码登录即为超管。 # # 走 B 之前请先在 GitHub 仓库 Settings → Secrets and variables → Actions 添加: # ADMIN_INIT_PASSWORD = 你的强密码(≥8位)。用 Secret 而不是工作流输入框——输入框的值会明文 # 留在 Actions 运行记录里,Secret 会在日志中被自动打码。 # # 前置:后端需已部署过(含 scripts/create-admin.js)。若提示脚本不存在,先跑一次后端部署工作流。 on: workflow_dispatch: inputs: admin_email: description: '要设为超管的邮箱(用你自己的)' required: true type: string create_or_reset_account: description: '带外建号/重置密码(账号还不存在、或忘了密码时勾选;密码取自 Secret ADMIN_INIT_PASSWORD)' required: false default: false type: boolean delete_orphan_admin: description: '同时删除误建的孤儿账号 admin@test.com(密码随机无人知道,建议删)' required: false default: true type: boolean jobs: grant: runs-on: ubuntu-latest steps: - name: Set SUPER_ADMIN_EMAILS (+ optional out-of-band account provisioning) uses: appleboy/ssh-action@v1 env: ADMIN_EMAIL: ${{ inputs.admin_email }} ADMIN_PASSWORD: ${{ secrets.ADMIN_INIT_PASSWORD }} DO_CREATE: ${{ inputs.create_or_reset_account }} DO_DELETE_ORPHAN: ${{ inputs.delete_orphan_admin }} with: host: ${{ secrets.ECS_HOST }} username: ${{ secrets.ECS_USER }} password: ${{ secrets.ECS_PASSWORD }} command_timeout: 5m envs: ADMIN_EMAIL,ADMIN_PASSWORD,DO_CREATE,DO_DELETE_ORPHAN script: | set -e cd /var/www/digitalme-api.invoratec.cn [ -f .env ] || { echo "❌ 生产 .env 缺失,中止"; exit 1; } echo "──── 当前 SUPER_ADMIN_EMAILS ────" grep '^SUPER_ADMIN_EMAILS=' .env || echo "(未设置 → 现在没有任何人是超管)" EMAIL="$(echo "$ADMIN_EMAIL" | tr '[:upper:]' '[:lower:]' | tr -d ' ')" if [ -z "$EMAIL" ]; then echo "❌ 邮箱为空"; exit 1; fi # ── 带外建号/重置密码(账号不存在时的唯一入口,因为白名单邮箱已禁止自助注册) ── if [ "$DO_CREATE" = "true" ]; then if [ -z "$ADMIN_PASSWORD" ]; then echo "❌ 勾了「带外建号」但仓库 Secret ADMIN_INIT_PASSWORD 未设置。" echo " 去 GitHub → Settings → Secrets and variables → Actions 添加 ADMIN_INIT_PASSWORD(≥8位) 再重跑。" exit 1 fi [ -f scripts/create-admin.js ] || { echo "❌ 服务器上没有 scripts/create-admin.js —— 请先跑一次【后端部署】工作流再来。"; exit 1; } echo "──── 带外建号/重置密码 ────" ADMIN_EMAIL="$EMAIL" ADMIN_PASSWORD="$ADMIN_PASSWORD" node scripts/create-admin.js fi # ── 写入/覆盖超管白名单(只放这一个邮箱,最小权限) ── if grep -q '^SUPER_ADMIN_EMAILS=' .env; then sed -i "s|^SUPER_ADMIN_EMAILS=.*|SUPER_ADMIN_EMAILS=$EMAIL|" .env else echo "SUPER_ADMIN_EMAILS=$EMAIL" >> .env fi echo "──── 写入后 ────" grep '^SUPER_ADMIN_EMAILS=' .env # 删掉被误建的孤儿账号 admin@test.com(随机密码、无人知道、白占邮箱) if [ "$DO_DELETE_ORPHAN" = "true" ] && [ "$EMAIL" != "admin@test.com" ]; then MONGO_URI="$(grep '^MONGO_URI=' .env | cut -d= -f2- || echo 'mongodb://localhost:27017')" MONGO_DB="$(grep '^MONGO_DB=' .env | cut -d= -f2- || echo 'creator_cn')" echo "──── 删除孤儿账号 admin@test.com ────" mongosh "$MONGO_URI/$MONGO_DB" --quiet --eval ' const u = db.users.findOne({ email: "admin@test.com" }); if (!u) { print("admin@test.com 不存在,无需清理"); } else { const r = db.users.deleteOne({ _id: u._id }); print("已删除 admin@test.com,deletedCount=" + r.deletedCount); } ' || echo "⚠ mongosh 执行失败(可手动清理),不影响超管设置" fi # 重启后端让新的 .env 生效 pm2 restart digitalme-api --update-env pm2 describe digitalme-api | grep -E 'status|restarts' || true echo "✅ 完成。用 $EMAIL 登录 → 即为超管(右上角出现盾牌图标 / 可进 /admin)。" echo " 若刚带外建号:用你在 Secret ADMIN_INIT_PASSWORD 里设的密码登录,登录后建议立刻改密。"