Teaching an Old Dog New Tricks: Forgejo + XMPP

Marius

教老狗学新招:Forgejo + XMPP

原文由 Marius 发布,订阅该博客

教老狗学新招:Forgejo + XMPP

不久前,我决定离开 Codeberg,转而自己搭建了一个 Forgejo 实例。完成初始设置后,我意识到,自己托管的代码托管平台是可以由我来塑造的,不必再迁就别人眼中“合理”的那套功能。我终于可以让它真正属于自己,开启那些我一直希望 Git 托管平台能拥有的功能。

首先要改的就是注册。现在的 Forgejo 和几乎所有其他代码托管平台一样,要求用户提供电子邮件地址并向其发送验证链接,仿佛点击一下链接仍能证明对面是个真人。而我更希望让人们直接用 XMPPJID 来注册。一方面,能接收验证链接的邮箱早已拦不住自动化垃圾注册,邮件验证的好处远没有人们想象的那么大;另一方面,XMPP 本就是更优秀的协议,我也更希望用户一开始就通过它、而不是通过电子邮件接入。

遗憾的是,Forgejo 并没有原生的 XMPP 支持。此外,它也没有利用 Go共享对象,而这本可以让扩展在很大程度上独立于核心来构建,对于这样庞大的单体项目来说,这其实非常有意义。没有一处干净的切入点能在不动核心的情况下为它添加一种新协议。

不过,我也并不打算在 Forgejo 内部真正去实现 JID 的注册与认证。那无异于与风车作战,这话是有经验之谈。我过去一些轻量得多的改动,都因为官僚化的要求而被扼杀,比如设计讨论用例收集,再往后大概还得要一张A38 通行证。所以我做了任何一个理智但又有点出格的人都会做的事,决定把 Forgejo 现有的邮件集成桥接到 XMPP 上。

介绍 Switchyard

Switchyard 是一个小型的守护进程,一边说 SMTP,一边说 XMPP。电子邮件和 XMPP 恰好使用相同的地址形式 [email protected],因此收件人可以直接映射,发送到 [email protected] 的邮件会作为聊天消息发往 JID [email protected]。它通过 SMTP 接收服务提交的邮件,将每封邮件转为队列中的一个任务,再由工作进程通过一条常驻的 XMPP 连接投递出去,连接断开时会自动重连。

因为它的监听方式和普通邮件服务器一样,Forgejo 无需任何补丁就能把邮件交过去。只需像指向真实邮件服务器那样,把它配置为 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 发送到用户在邮箱一栏中填入的任意 JID 上。Switchyard 也能处理国际化域名(IDN),毕竟,你知道的,这算是我的老本行了。:-)

SMTP 始终以 punycode 形式携带域名,而 XMPP 则使用原生的 IDN,因此它会在转发过程中对收件人域名进行解码,于是在 Forgejo 中注册为 [email protected] 的用户,在 XMPP 上会以 user@マリウス.com 的形式收到消息。

不止于 Forgejo

这个轻量服务并非 Forgejo 专用。Switchyard 适用于任何可以指向 SMTP 服务器的软件,能让这些软件在自身毫无感知的情况下开口说 XMPP。另一端唯一需要做的调整,是把 HTML 模板中的“Email address”字段重命名为“JID”,让人知道该填什么。在 Forgejo 里,这只是一个很小的模板补丁,你完全可以自行维护。这与维护一个上游很可能永远不会接受的完整 XMPP 实现,完全是两码事。

缺点是,这么做会让你暂时失去电子邮件功能!一旦 Forgejo 开始与 Switchyard 对接,它发出的所有内容都会走向 XMPP,填了真实邮箱地址的用户反而什么也收不到。我已经在着手修复这个问题,让 Switchyard 先检查目标主机是否真的是 XMPP 服务器,如果不是,就将消息转发到你配置的普通邮件服务器上。这样一来,一种协议(SMTP)进来,再按需分发——能走首选协议(XMPP)就走它,否则就回落到备用协议(SMTP)。小菜一碟。

这算是干净优雅的工程吗?显然不是。它就是一个连着 XMPP 客户端的 SMTP 服务器(中间夹着一个任务队列),但它能跑,也能把活干完。有了 Switchyard,我得到了一个本该属于上游、却几乎永远不可能被合入的功能,对我而言,这比一个或许能在选美比赛中获胜、却可能一辈子都只是个补丁的“优雅实现”更有价值。一个需要我在每次 Forgejo 发布时不断合并、持续维护的补丁。

注:我仍在 tty.fail(我自己的 Git 平台)上对这套实现进行全面测试,尚未对他人开放注册。如果你想为相关项目做贡献、因而希望提前体验,请加入社区频道并告诉我!

本文章由 muse-spark-1.2-contributor 进行翻译

评论