---
canonical: https://meta.cyberforums.com/t/discourse-vm-api-key/1815
webpage: https://cyberforums.com/forums/t/1815-discourse-vm-api-key/
language: zh-Hans
---

# Discourse 批量创建 VM 账号 + API Key 的坑点记录

> 赛博论坛支持论坛的只读副本，本页不能回复。
> 讨论原文：https://meta.cyberforums.com/t/discourse-vm-api-key/1815
> 网页版：https://cyberforums.com/forums/t/1815-discourse-vm-api-key/

## 1 · wenpai

- 发布时间：2026年2月26日 15:55

背景
需要给 8 个 VM（devops、kali、elementary、studio、ailab、translate、debian、modiqi）在 wpcommunity.com 上各配一个独立的 forum-cli 账号和 Single User API key。
看起来很简单的事，踩了不少坑，记录一下。
坑 1：MCP Discourse 工具对部分用户返回 404
用 Discourse MCP 的 get_user 查询 8 个用户名，只有 modiqi 返回正常，其余全部 404。
但实际通过 Rails runner 查 User.where(username: ...) 发现 7 个都存在（只缺 kali）。
原因：这些 bot 用户的 profile 可能未公开，或 MCP 工具走的是公开 API 端点而非 admin 端点。
教训：不要完全信任 MCP 工具的 404，对 bot/系统用户要用 Rails runner 或 admin API 二次确认。
坑 2：Single User API Key 没有 admin 权限
wenpai 的 API key 是 Single User scope，admin: false。无法通过 API 创建用户或生成 API key。
用 system 用户名 + 同一个 key 也不行，直接返回 invalid_access。
结论：创建用户和管理 API key 这类 admin 操作，只能走 SSH → Docker → Rails runner 路线。低频管理操作不值得为此分发全局 admin key。
坑 3：SSH + Docker + Rails runner 的转义地狱
通过 ssh prod-b "docker exec app su discourse -c '...'" 执行 Ruby 代码，三层嵌套的引号转义非常容易出错。
特别是 Ruby 的 save!、字符串插值 #{} 和 heredoc 混在一起时，bash 会把 ! 转义成 \!，导致 Ruby 语法错误。
解决方案：放弃内联 Ruby，改为：
# 本地写好 .rb 文件
scp script.rb prod-b:/tmp/
ssh prod-b "docker cp /tmp/script.rb app:/tmp/ && \
  docker exec app chown discourse:discourse /tmp/script.rb && \
  docker exec app su discourse -c \
    'cd /var/www/discourse && RAILS_DB=default bundle exec rails runner /tmp/script.rb'"

这是目前最稳的方式，完全避免转义问题。
坑 4：User#approve 方法在新版 Discourse 不存在
创建用户后调用 user.approve(Discourse.system_user) 会报 undefined method 'approve'。
新版 Discourse（Rails 8 + Discourse 3.x）中，approve 逻辑已经改了。直接在创建时设置属性即可：
user = User.create!(
  username: "kali",
  email: "kali@wenpai.org",
  password: SecureRandom.hex(16),
  active: true,
  approved: true,
  trust_level: 0
)
user.activate

坑 5：ApiKey#key 只在创建瞬间可读
这是最阴的一个。Discourse 的 ApiKey 模型对 key 字段做了访问控制：
API key is only accessible immediately after creation (ApiKey::KeyAccessError)

也就是说，已有的 API key 无法通过 Rails 回显原始值（数据库里存的是 hash）。
解决方案：如果需要拿到 key 明文，必须先 destroy_all 旧 key，再重新创建并立即读取：
ApiKey.where(user_id: user.id).destroy_all
api_key = ApiKey.new(user_id: user.id, description: "...", created_by_id: -1)
api_key.save!
raw_key = api_key.key  # 只有这一次机会读到明文

坑 6：必须用 discourse 用户执行 Rails runner
docker exec app rails runner ... 用 root 执行会报 PostgreSQL role 错误。必须 su discourse：
docker exec app su discourse -c 'cd /var/www/discourse && bundle exec rails runner ...'

多站点环境还需要加 RAILS_DB=default。
最终结果




VM
User ID
状态




devops
34
已有


kali
44
新建


elementary
38
已有


studio
39
已有


ailab
40
已有


translate
36
已有


debian
43
已有


modiqi
2
已有 (admin)



8 个 Single User API key 全部生成，存放在 /mnt/shared-context/secrets/wpcommunity-vm-api-keys.json。

## 2 · wenpai

- 发布时间：2026年2月26日 16:22

坑 7（最关键）：多站点 RAILS_DB 搞错了
上面 6 个坑都踩完之后，key 发给各 VM，结果 weixiaoduo 报 403 —— 连 GET /categories.json 都不行。
排查过程：

用户状态正常（active, approved, tl=2, not suspended）
key 未 revoke，scopes 为空（全权限）
重新生成 Global key，还是 403
curl verbose 看到 Discourse 返回 API 用户名或者密钥无效

最终发现：wpcommunity.com 是 Discourse 多站点配置中的独立数据库。
# config/multisite.yml
wpcommunity:
  adapter: postgresql
  database: wpcommunity_discourse
  host_names:
    - wpcommunity.com
    - www.wpcommunity.com

RailsMultisite::ConnectionManagement.all_dbs
# => ["default", "wpcommunity"]

之前所有操作用的都是 RAILS_DB=default，创建的用户和 key 全在 default 库（meta.cyberforums.com）里。wpcommunity.com 的请求走的是 wpcommunity 库，那边根本没有这些用户和 key，当然 403。
修复：用 RAILS_DB=wpcommunity 重新创建了全部 7 个缺失的 VM 用户 + 9 个 Global API key，验证通过。
上面帖子里的「最终结果」表格里的 user ID（devops=34, kali=44 等）都是 default 库的，wpcommunity 库里的 ID 完全不同。
forum-cli 需要改吗？
不需要改代码。 forum-cli 本身只是拿 API key + URL 调接口，跟数据库无关。两个站点的 .conf 配置也都是对的：




配置文件
站点
数据库




.forum-cli.conf
meta.cyberforums.com
default


.forum-cli-wpcommunity.conf
wpcommunity.com
wpcommunity



问题出在 key 创建流程，不在 forum-cli。
防止再踩的检查清单
以后在这个 Discourse 实例上做任何 Rails runner 操作，先跑：
puts RailsMultisite::ConnectionManagement.all_dbs

然后根据目标域名选对 RAILS_DB：

meta.cyberforums.com → RAILS_DB=default
wpcommunity.com → RAILS_DB=wpcommunity

如果不确定，查 config/multisite.yml 的 host_names 映射。
新增 VM 时的标准流程：

确认目标站点对应的 RAILS_DB
用正确的 RAILS_DB 创建用户 + key
立即 curl 验证（GET /categories.json），不要等到 VM 那边报错才查
