Teaching an Old Dog New Tricks: Forgejo + XMPP

Marius

教老狗新把戲:Forgejo + XMPP

原文由 Marius 發布,訂閱此部落格

教老狗新把戲:Forgejo + XMPP

我最近決定離開 Codeberg,改為自行架設 Forgejo 實例。完成初步設定後,我才意識到,自己架設的程式碼託管平台,就能由自己來形塑,不必再將就別人覺得合理的功能組合。現在我可以真正把它打造成屬於我自己的樣子,開啟那些我一直希望 Git 託管服務能做到的功能。

首先想動的就是註冊機制。目前的 Forgejo 跟幾乎所有其他程式碼託管平台一樣,要求提供電子郵件地址並寄送驗證連結,因為顯然點擊連結至今仍被視為對面是真人的證明。不過,我更希望讓大家直接用 XMPPJID 來註冊。一方面,能收到驗證連結的電子郵件地址早就不再是阻擋自動垃圾訊息的真正門檻,所以整套電子郵件驗證的好處,並不如大家想像的那麼大;另一方面,XMPP 本來就是更優秀的協定,我本來就希望使用者一開始就是透過它、而非電子郵件來加入。

可惜的是,Forgejo 並沒有原生的 XMPP 支援。此外,它也沒有利用 Goshared objects,那本來可以讓擴充功能在很大程度上獨立於核心之外建構,對於這種規模的單體式架構來說其實非常合理。想不動到核心就乾淨地加入一種協定,根本沒有合適的切入點。

不過,我本來就沒打算在 Forgejo 裡實作真正的 JID 註冊與驗證。那根本是唐吉訶德式的風車之戰,這話我可是有經驗才敢說的。過去我嘗試過遠比這輕量得多的改動,都被諸如設計討論蒐集使用案例之類的官僚要求給扼殺了,搞不好中途還會冒出一張laissez-passer A38。所以我做了任何理性但又有點瘋狂的人都會做的事:決定把 Forgejo 現有的電子郵件整合橋接到 XMPP 上。

介紹 Switchyard

Switchyard 是一個小型的常駐程式,一邊說 SMTP,一邊說 XMPP。電子郵件和 XMPP 剛好共用相同的地址格式 [email protected],所以收件者可以直接對應,寄給 [email protected] 的郵件,就會以聊天訊息的形式發送到 JID [email protected]。它透過 SMTP 接收服務提交的郵件,將每一封轉成佇列中的一項工作,再由 worker 透過一條常駐的 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 用的帳號密碼、提交埠(submission port)用的憑證,以及用來發送所有訊息的單一 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 原本會用電子郵件寄出的每一封信——像是註冊確認、重設密碼,或是有新 issue 的通知——都會改為透過 XMPP,發送到使用者在電子郵件欄位中填入的 JIDSwitchyard 也會處理 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)。輕輕鬆鬆,簡簡單單。

這算是乾淨優雅的工程設計嗎?絕對不是。它就是把一個 SMTP 伺服器硬接到一個 XMPP 客戶端上(中間夾著一個工作佇列),它能動,也確實把工作完成了。透過 Switchyard,我得到了那個本該屬於上游、卻幾乎永遠不會被上游接受的功能,對我來說,這比一個或許能在選美比賽中勝出、卻可能一輩子都只是個補丁的乾淨實作更有價值。一個我得在每次 Forgejo 更新時不斷合併、維護的補丁。

註:我目前仍在自己的 Git 平台 tty.fail 上徹底測試這套實作,還沒有開放給其他人註冊。如果你想為任何專案貢獻、因而希望提早取得使用權限,歡迎加入社群頻道告訴我!

本文章由 muse-spark-1.2-contributor 進行翻譯

留言