Zulip 多组织(Realm)架构解析:从创建链接到子域名托管
【免费下载链接】zulipZulip server and web application. Open-source team chat that helps teams stay productive and focused.项目地址: https://gitcode.com/GitHub_Trending/zu/zulip
Zulip 允许单台服务器托管多个realm(代码库内部对"组织"的称呼),本文围绕 docs/subsystems/realms.md 展开,系统讲解 realm 的两种创建方式、子域名解析原理、开发环境的多组织测试方案,并结合源码与测试说明底层实现机制。读完本文,你将掌握如何在生产与开发环境中创建、托管和调试多个 Zulip 组织。
什么是 Realm
Realm 是 Zulip 代码库内部的术语,指用户文档中所说的"组织"(Organization)。该命名源自 Kerberos 的概念。Zulip 在用户可见的字符串与文档中尽量避免使用realm一词,统一使用"Organization",并且有 linter 在可翻译字符串中强制这一规则;未来 Zulip 可能将内部实现也统一改为organization。
从源码结构看,realm 对应的核心模型是 zerver/models/init.py 中的Realm类,每个 realm 拥有独立的子域名(subdomain)、用户、频道(stream)与配置。服务器的系统级配置中SYSTEM_BOT_REALM = "zulipinternal"(见 zproject/default_settings.py),用于承载通知机器人、欢迎机器人等系统 bot。
相关延伸阅读:多组织生产部署指南。
创建 Realm 的两种方式
Zulip 提供两种创建新组织的方式:唯一链接生成器与开放创建(open realm creation)。
方式一:唯一链接生成器
在服务器上运行:
./manage.py generate_realm_creation_link该命令会输出一个 URL,任何人访问该链接即可创建一个新组织及其管理员账号。该链接具有两个过期特性:
- 使用一次即失效:组织创建完成后链接立即失效;
- 7 天未使用自动过期:过期天数由
CAN_CREATE_REALM_LINK_VALIDITY_DAYS控制,默认值为7(见 zproject/default_settings.py)。
若需调整过期时间,修改zproject/default_settings.py中的CAN_CREATE_REALM_LINK_VALIDITY_DAYS即可。
源码级实现细节
命令的实现位于 zerver/management/commands/generate_realm_creation_link.py:
- 命令首先通过
Realm.objects.first()检查数据库是否已初始化,若未初始化则抛出CommandError并提示运行initialize-database; - 随后调用
generate_realm_creation_url(by_admin=True)生成一次性链接,并打印提示信息。
链接的生成链路如下:
- confirmation/models.py 中的
generate_realm_creation_url()调用prepare_realm_creation_url(presume_email_valid=by_admin); - zerver/views/registration.py 中的
prepare_realm_creation_url()创建一条RealmCreationStatus记录,再通过create_confirmation_link()生成确认链接; - 该链接属于
Confirmation.CAN_CREATE_REALM类型,其有效期绑定CAN_CREATE_REALM_LINK_VALIDITY_DAYS(见 confirmation/models.py)。
by_admin=True意味着管理员生成链接时presume_email_valid为真,创建流程会跳过邮箱验证环节,直接进入组织创建表单。
测试验证
zerver/tests/test_management_commands.py 中的TestGenerateRealmCreationLink覆盖了完整流程:
- 使用链接打开创建页会返回 "Create a new Zulip organization";
- 提交组织信息(组织名、类型、子域名等)后 302 跳转至确认页;
- 无论链接使用一次,还是将
expiry_date手动前移超过CAN_CREATE_REALM_LINK_VALIDITY_DAYS + 1天,再次访问原链接都会提示 "Organization creation link expired or invalid"。
方式二:开放 Realm 创建
希望允许互联网上的任何人自行创建新组织(如 Zulip Cloud)时,可在/etc/zulip/settings.py(生产环境)中设置:
OPEN_REALM_CREATION = True该配置项的默认值为False(见 zproject/default_settings.py)。需要特别注意的是,向公众开放组织创建意味着服务器需要承担安全、垃圾邮件/滥用治理、GDPR/CCPA 等法律合规责任,运维方应充分评估风险。
此外,zproject/default_settings.py 提供了与开放创建配套的反垃圾配置:
| 配置项 | 默认值 | 作用 |
|---|---|---|
INVITES_MIN_USER_AGE_DAYS | 3 | 加入开放组织不足该天数的非管理员不能发送邀请 |
INVITES_DEFAULT_REALM_DAILY_MAX | 100 | 组织每日最大邀请数(仅当OPEN_REALM_CREATION为真时生效) |
INVITES_NEW_REALM_LIMIT_DAYS | [(1, 100)] | 对新组织全局邀请速率的限制(天, 上限)列表 |
INVITES_NEW_REALM_DAYS | 7 | 界定"新组织"的天数 |
子域名:多组织的承载方式
单台 Zulip 服务器托管多个组织的方式,是给每个组织分配主域名下的唯一子域名。例如实例托管于zulip.example.com,某组织子域名为acme,则该组织通过acme.zulip.example.com访问。
DNS 配置
要让子域名生效,需将 DNS 记录指向 Zulip 安装服务器的 IP。最简单的方式是添加一条 host 值为*的 A 记录指向服务器 IP,使所有子域名统一解析到该 IP。
根域名与系统 bot realm
- 根域名组织:大多数 Zulip 服务器在根域名(如
zulip.example.com)上托管一个组织,其内部实现是子域名使用空字符串''。混合部署(根域名 + 子域名组织)时,由于根域名的 auth cookie 对子域名可见,同一浏览器无法同时登录两个组织,因此不推荐该组合。 - 系统 bot realm:每个 Zulip 服务器都存在一个非用户创建的
zulipinternalrealm(SYSTEM_BOT_REALM = "zulipinternal",见 zproject/default_settings.py),默认只包含系统 bot。可通过./scripts/get-django-setting INTERNAL_BOTS查看机器人列表。
子域名变更与迁移
- 可通过管理命令变更已有组织的子域名(变更会中断用户访问,需谨慎操作);
- 从根域名配置迁移到子域名配置时,务必清除之前在根域名登录过的浏览器 cookie,否则会出现奇怪的跳转问题。
使用非子域名的主机名
若希望组织使用互不构成子域关系的独立主机名,可在/etc/zulip/settings.py中配置REALM_HOSTS:
REALM_HOSTS = { "mysubdomain": "hostname.example.com", }该配置使hostname.example.com成为本应位于mysubdomain.zulip.example.com组织的访问主机名。创建新组织时,需要在"subdomain"字段填写mysubdomain。mysubdomain的值不会展示给用户,唯一限制是不能在mysubdomain.zulip.example.com上再创建另一个组织。REALM_HOSTS的默认值为空字典(见 zproject/default_settings.py),其值也会被自动加入ALLOWED_HOSTS。
认证回调子域名
Google、GitHub、SAML 等第三方认证通常要求向认证提供商提供回调 URL 白名单。更简洁的方案是注册一个专用子域名(如auth.zulip.example.com),然后在/etc/zulip/settings.py中设置:
SOCIAL_AUTH_SUBDOMAIN = "auth"默认值为None(见 zproject/default_settings.py),开发环境的默认值则是"auth"(见 zproject/dev_settings.py)。
开发环境中的子域名测试
Zulip 的开发环境专门为测试不同子域名配置而设计,核心机制如下:
- 各组织位于
*.zulipdev.com下,正如生产环境的*.zulipchat.com; - 根域
zulipdev.com本身对应根域组织; - 默认组织(含 Shakespeare 测试用户)托管在
localhost:9991,而不是zulip.zulipdev.com,这正利用了上文介绍的REALM_HOSTS特性。
域名解析原理
Linux 默认没有便捷的方式在本地使用子域名,因此 Zulip 借助zulipdev.com域——它在公网 DNS 上有通配 A 记录指向127.0.0.1,本地开发时可借此访问开发服务器。默认组织子域名为zulip,访问zulip.zulipdev.com即可进入。
从 zproject/dev_settings.py 可以看到开发环境的默认配置逻辑:
- 未设置
EXTERNAL_HOST环境变量时,EXTERNAL_HOST = "zulipdev.com:9991",并将zulip子域映射到localhost:9991,保证离线也能直接访问默认组织; - 设置
EXTERNAL_HOST时则使用该值,并把zulip映射到它; - 开发环境同时开启
ROOT_DOMAIN_LANDING_PAGE = True(见 zproject/dev_settings.py),根域作为落地页而非组织。
代理服务器与 hosts 文件
如果开发机位于代理服务器之后,浏览器请求zulipdev.com时代理会代为获取页面,而zulipdev.com指向127.0.0.1,代理很可能返回 503。解决办法是为*.zulipdev.com禁用代理;若禁用代理后 DNS 解析仍失败,可在/etc/hosts中手动添加记录:
127.0.0.1 localhost 127.0.0.1 zulipdev.com 127.0.0.1 zulip.zulipdev.com 127.0.0.1 testsubdomain.zulipdev.com这些记录在断网环境下运行 Puppeteer 测试等场景中同样非常有用。
生产环境多组织部署清单
结合 docs/production/multiple-organizations.md 与本文,生产环境新增一个组织的完整步骤为:
- 为所有计划使用的子域名准备 SSL 证书(可使用 Let's Encrypt 工具一次性签发多域名证书,组织数量大时考虑通配符证书);
- 如有必要,修改 nginx 配置以使用新证书;
- 再次运行
./manage.py generate_realm_creation_link创建新组织; - 若使用 GitHub 等社交认证,配置
SOCIAL_AUTH_SUBDOMAIN; - 使用
REALM_HOSTS支持非子域名主机名,并注意根域名与子域名组织混布时 cookie 冲突的限制。
小结
Realm 机制是 Zulip 支撑多组织托管的核心抽象。本文覆盖了两条组织创建路径(一次性链接与开放创建)、子域名解析与 DNS 配置、REALM_HOSTS主机名映射、SOCIAL_AUTH_SUBDOMAIN认证回调,以及开发环境基于zulipdev.com的完整测试方案;所有关键行为均有对应源码(generate_realm_creation_link.py、confirmation/models.py、zproject/default_settings.py、zproject/dev_settings.py)与测试(test_management_commands.py)佐证,便于读者在仓库中进一步追踪验证。
【免费下载链接】zulipZulip server and web application. Open-source team chat that helps teams stay productive and focused.项目地址: https://gitcode.com/GitHub_Trending/zu/zulip
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考