Teaching an Old Dog New Tricks: Forgejo + XMPP

Marius

老狗学新把戏:Forgejo + XMPP

老狗学新把戏:Forgejo + XMPP

我最近决定离开 Codeberg,改为运行自己的 Forgejo 实例。完成初始设置后,我意识到自己托管的代码托管平台(forge)是可以由我亲手塑造的,我再也不必迁就别人认为合理的功能集了。现在我可以把它真正变成自己的东西,开启我一直希望 Git 托管平台具备的功能。

其中第一件事与注册有关。目前 Forgejo 和几乎所有其他代码托管平台一样,要求提供电子邮箱地址并向其发送验证链接,因为显然点击一个链接仍被视为对面是真人的一种证明。然而,我更希望让人们改用 XMPPJID(Jabber 标识符) 来注册。部分原因是,能接收链接的邮箱地址早已不再是自动化垃圾信息的真正障碍,所以整个邮箱验证的实际收益比人们想象的要低;另一部分原因则是 XMPP 是更优秀的协议,我希望我的用户从一开始就通过它而不是通过电子邮件到来。

遗憾的是,Forgejo 没有原生的 XMPP 支持。此外,它也没有利用 Go共享对象(shared objects),这本可以让扩展在很大程度上独立于核心进行构建,对于这种规模的单体应用来说会非常合理。如果不深入核心本身,就没有一个干净的地方可以添加新协议。

不过,我并不打算把真正的 JID 注册和认证实现进 Forgejo。那将是一场与风车作战的徒劳之举——这话是我凭经验说的。我过去尝试更轻量的改动时,都被诸如设计讨论收集用例之类的官僚流程扼杀了,大概中途还得再交一份A38 通行证(《高卢英雄历险记》里的梗)。于是我做了任何一个理智但略微疯狂的人都会做的事:决定把 Forgejo 现有的电子邮件集成桥接到 XMPP 上。

介绍 Switchyard

Switchyard 是一个小型守护进程,一端说 SMTP,另一端说 XMPP。电子邮件和 XMPP 恰好共用同一种地址形式 [email protected],因此收件人可以直接映射过去,发给 [email protected] 的消息就会作为聊天消息发送给 JID [email protected]。它接受服务通过 SMTP 提交的邮件,把每封邮件转化为队列中的一个任务,然后由工作进程通过一条常驻的 XMPP 连接投递出去;当服务器断开连接时,该连接会自动重连。

由于它像任何邮件服务器一样监听 SMTPForgejo 无需打补丁就能把邮件交给它。只需把它配置为 mailer 端点,使用你本来指向真实邮件服务器的同一套配置块即可:

[mailer]
ENABLED        = true
FROM           = [email protected]
PROTOCOL       = smtp
SMTP_ADDR      = 127.0.0.1
SMTP_PORT      = 587
USER           = forgejo
PASSWD         = `change-me`

在另一端,一个简短的 switchyard.toml 为它提供了用于校验 Forgejo 的凭据、提交端口所需的证书,以及用于发送所有消息的唯一 XMPP 账户:

[redis]
addrs = ["127.0.0.1:6379"]

[smtp]
allowed_ips = ["127.0.0.1/32"]
username = "forgejo"
password = "change-me"

[smtp.tls]
enable = true
cert = "/etc/switchyard/tls/cert.pem"
key = "/etc/switchyard/tls/key.pem"

[xmpp]
server = "xmpp.myhost.tld:5222"
username = "[email protected]"
password = "change-me"

配置就绪后,Forgejo 原本要通过电子邮件发送的每条消息——比如注册确认、密码重置或新工单通知——都会改经 XMPP 发送到用户在邮箱字段里填写的任何 JIDSwitchyard 还支持国际化域名(IDN),毕竟,这算是我的本行。:-)

SMTP 始终以 punycode 形式携带域名,而 XMPP 使用原生 IDN,所以它会在传输途中解码收件人域名:在 Forgejo 中以 [email protected] 注册的用户,在 XMPP 上可以通过 user@マリウス.com 联系到。

不止于 Forgejo

这个轻量级服务并非 Forgejo 专属。凡是能指向某个 SMTP 服务器的软件,Switchyard 都能让它开口说 XMPP,而软件本身完全察觉不到任何变化。唯一需要在另一端做的调整,是把 HTML 模板中的“Email address”字段标签改成“JID”,好让人们知道该填什么。就 Forgejo 而言,这只是一个小小的模板补丁,你自己就能轻松维护。这与维护一个上游几乎永远不会接受的完整 XMPP 实现相比,完全是两码事。

缺点是这会让你失去电子邮件功能(暂时如此!)。一旦 Forgejo 开始与 Switchyard 通信,它发出的所有内容都会走 XMPP,而填写了邮箱地址的用户将什么都收不到。我已经在着手解决这个问题:让 Switchyard 先检查目标主机是否真的是一台 XMPP 服务器,如果不是,则回退到你配置的普通邮件服务器转发该消息。这样一来,一种协议(SMTP) 进来,然后分发到首选协议(XMPP)(如果可用),否则转发到后备协议(SMTP)。小菜一碟。

这些算得上干净优雅的工程设计吗?绝对不算。它是一个接在 XMPP 客户端上的 SMTP 服务器(中间夹着一个任务队列),它能运行,也能完成任务。有了 Switchyard,我得到了一个本应属于上游、却几乎肯定永远进不了上游的功能;对我来说,这比一个或许能在选美比赛中获胜、却可能终生都只是一个补丁的干净实现更重要——那个补丁我还得在 Forgejo 的各个版本之间持续合并和维护。

注:我仍在 tty.fail(我自己的 Git 平台)上对这套实现进行全面测试,尚未开放注册供他人加入。如果你想为其中任何项目做贡献并因此希望获得早期访问权限,请加入社区频道并告诉我!

原文由 Marius 发布

本文章由 stealth/ox-alpha 进行翻译