最近,Fediverse 一直是個熱門的討論話題,即使沒有數百萬,也有成千上萬的新使用者在 Mastodon 這樣的平台上建立帳戶,以完全移動到「另一端」或實驗並瞭解這個新的社交網路。
今天,我們隆重推出 Wildebeest,這是一個開放原始碼、易於部署且與 ActivityPub 和 Mastodon 相容的伺服器,完全基於 Cloudflare 的 Supercloud 構建。如果您想在 Fediverse 中經營自己的網站,現在可以完全在 Cloudflare 上完成了。
基於 Cloudflare 構建的 Fediverse
現在,如果您想加入 Mastodon 聯盟網路,有兩個選項:加入其中一個現有伺服器(伺服器也稱為社群,每個伺服器都有自己的基礎結構和規則),或者執行自託管伺服器。
您希望執行自己的伺服器可能是出於下列幾種原因:
您希望建立一個新社群,並透過共同的主題和使用規則吸引其他使用者。
您不希望被迫信任協力廠商伺服器或遵守其原則,並希望您的伺服器在您的網域下,為您的個人帳戶服務。
您希望完全掌控您的資料、個人資訊和內容,並完全瞭解執行個體上發生的一切。
Mastodon gGmbH 非營利組織使用 Ruby、Node.js、PostgreSQL 和 Redis 提供伺服器實作。不過,執行官方伺服器可能很有挑戰性。您需要在某處擁有或租用伺服器或 VPS;您必須安裝和設定軟體、設定資料庫和面向公眾的 Web 伺服器,以及設定和保護您的網路免受攻擊或濫用。然後,您必須對所有這些進行維護並處理不斷的更新。您需要完成大量的指令碼處理和技術工作,才能啟動並執行它;因此,它絕對不適合那些不太專精於技術的愛好者。
Wildebeest 有兩種用途:您可以基於 Cloudflare 快速部署與 Mastodon 相容的伺服器,並在幾分鐘內將其連線到 Fediverse,而且無需為維護或保護它免受濫用或攻擊的事情擔心;Cloudflare 會自動為您完成這些工作。
Wildebeest 不是受管理的服務。它是在您的 Cloudflare 帳戶下在我們的雲端執行的執行個體、資料和程式碼。此外,它也是開放原始碼的,這意味著它可以不斷地發展以提供更多功能,而且任何人都可以擴充和改進它。
目前,我們提供下列支援:
ActivityPub、WebFinger、NodeInfo、WebPush 和 Mastodon 相容的 API。Wildebeest 可以與其他 Fediverse 伺服器連線。
與最熱門的 Mastodon Web(如 Pinafore)、桌面和行動用戶端相容。我們還提供一個簡單的唯讀 Web 介面,來探索時間表和使用者個人資料。
您可以發佈、編輯、推廣或刪除貼文、道歉、發出嘟嘟聲提示。我們支援文字、影像以及(很快提供)視訊。
任何人都可以關注您;您可以關注任何人。
您可以搜尋內容。
您可以在執行個體下註冊一或多個帳戶。驗證可以基於電子郵件,也可以使用任何與 Cloudflare Access 相容的 IdP(如 GitHub 或 Google)。
您可以編輯個人資料、頭像和頁首影像。
如何構建
我們的實作完全基於我們的產品和 API 構建。構建 Wildebeest 是又一個展示我們技術堆疊功能和多功能性的絕佳機會,並證明任何人都可以使用 Cloudflare 構建涉及多個系統和複雜要求的大型應用程式。
下面是 Wildebeest 架構的鳥瞰圖:
現在,我們來瞭解一下詳細資料和技術支援。
Cloudflare Pages
究其核心,Wildebeest 就是一個 Cloudflare Pages 專案並使用 Pages Functions 來執行程式碼。Cloudflare Pages 為構建和部署應用程式以及服務配套資產提供了絕佳的基礎,而 Functions 則為您提供了 Workers 生態系統的完整存取權,您可以在其中執行任何程式碼。
Functions 有一個內建的基於檔案的路由器。透過 Wildebeest 的持續部署組建上傳的 /functions 目錄結構定義了您的應用程式路由,以及處理每個 HTTP 端點要求的檔案和程式碼。這種路由技術類似於 Next.js 等其他架構所使用的技術。
例如,Mastodon 的 /api/v1/timelines/public API 端點由 /functions/api/v1/timelines/public.ts 使用 onRequest 方法進行處理。
對這些端點進行單元測試也變得更容易了,因為我們只需從測試架構呼叫 handleRequest() 函數即可。請查看我們的 Jest 測試之一,mastodon.spec.ts:
export onRequest = async ({ request, env }) => {
const { searchParams } = new URL(request.url)
const domain = new URL(request.url).hostname
...
return handleRequest(domain, env.DATABASE, {})
}
export async function handleRequest(
…
): Promise<Response> {
…
}
與任何其他標準 Worker 一樣,Functions 也可讓您設定繫結以與其他 Cloudflare 產品和功能(如 KV、R2、D1、Durable Objects)互動。該清單一直在不斷地增長。
import * as v1_instance from 'wildebeest/functions/api/v1/instance'
describe('Mastodon APIs', () => {
describe('instance', () => {
test('return the instance infos v1', async () => {
const res = await v1_instance.handleRequest(domain, env)
assert.equal(res.status, 200)
assertCORS(res)
const data = await res.json<Data>()
assert.equal(data.rules.length, 0)
assert(data.version.includes('Wildebeest'))
})
})
})
我們使用 Functions 來實作大部分的官方 Mastodon API 規格,讓 Wildebeest 與其他伺服器和用戶端應用程式的現有生態系統相容,並且還可以在同一個專案程式碼庫下執行我們自己的唯讀 Web 前端。
Wildebeest 的 Web 前端使用 Qwik,這是一個針對速度進行了最佳化的通用 Web 架構,它使用 JSX JavaScript 語法擴充等新式概念,並支援伺服器端轉譯 (SSR) 和靜態網站生成 (SSG)。
Qwik 提供了一個現成可用的 Cloudflare Pages Adaptor,因此,我們就使用它了(請查看我們的架構指南,深入瞭解如何在 Cloudflare Pages 上部署 Qwik 網站)。對於樣式,我們使用由 Qwik 提供原生支援的 Tailwind CSS 架構。
我們的前端網站程式碼和靜態資產可以在 /frontend 目錄下找到。該應用程式由 /functions/[[path]].js 動態路由處理,該路由基本上會擷取所有非 API 要求,然後叫用 Qwik 自己的內部路由器 Qwik City,它會接管此後的一切。
由於 Pages 和 Functions 路由具有強大的功能和多功能性,可以在同一個專案下同時執行後端 API 和伺服器端轉譯的動態用戶端,實際上是一個完整堆疊應用程式。
現在,我們來更深入地瞭解伺服器如何與我們架構中的其他元件互動。
D1
Wildebeest 使用 D1,這是 Cloudflare 的第一個用於 Workers 平台的 SQL 資料庫並基於 SQLite 構建,現在以 alpha 版向所有人開放,用於儲存和查詢資料。我們的結構描述如下:
隨著我們新增的功能越來越多,該結構描述將來可能會發生變更。沒問題,因為 D1 支援遷移,非常適合幫您更新資料庫結構描述而不會遺失資料。對於每個新的 Wildebeest 版本,如果需要資料庫結構描述變更,我們都可以建立一個新的遷移檔案。
D1 公開了一個功能強大的用戶端 API,開發人員可以利用它來操作並查詢 Worker 指令碼中的資料,在我們的例子中就是 Pages Functions。
-- Migration number: 0001 2023-01-16T13:09:04.033Z
CREATE UNIQUE INDEX unique_actor_following ON actor_following (actor_id, target_actor_id);
下面是一個經過簡化的例子,它展示了當您開始在 Fediverse 上關注某人時,我們如何與 D1 互動:
Cloudflare 的內部測試和基於我們自己的產品構建的文化,意味著我們有時會先於使用者體驗到種種缺陷。我們在使用基於 SQLite 構建的 D1 儲存資料時,確實面臨一些挑戰。下面是兩個例子。
export async function addFollowing(db, actor, target, targetAcct): Promise<UUID> {
const query = `INSERT OR IGNORE INTO actor_following (id, actor_id, target_actor_id, state, target_actor_acct) VALUES (?, ?, ?, ?, ?)`
const out = await db
.prepare(query)
.bind(id, actor.id.toString(), target.id.toString(), STATE_PENDING, targetAcct)
.run()
return id
}
ActivityPub 使用 UUID 來標識物件並在 URI 中廣泛地參照它們。這些物件需要儲存在資料庫中。其他資料庫(如 PostgreSQL)會提供內建函數來產生唯一識別碼。SQLite 和 D1 還未提供,但我們已經進行規劃了。
不過,不用擔心,Workers 執行階段支援 Web Crypto,因此,我們使用 crypto.randomUUID() 來取得唯一識別碼。請查看 /backend/src/activitypub/actors/inbox.ts:
問題解決了。
export async function addObjectInInbox(db, actor, obj) {
const id = crypto.randomUUID()
const out = await db
.prepare('INSERT INTO inbox_objects(id, actor_id, object_id) VALUES(?, ?, ?)')
.bind(id, actor.id.toString(), obj.id.toString())
.run()
}
另一個例子是我們需要以亞秒級解析度儲存日期。同樣,PostgreSQL 這樣的資料庫可以滿足:
而 SQLite 未能滿足,如下所示:
psql> select now();
2023-02-01 11:45:17.425563+00
我們使用 strftime() 藉助一個小型駭客解決了這個問題:
sqlite> select datetime();
2023-02-01 11:44:02
請參閱我們的初始 SQL 結構描述,查找 cdate 預設值。
sqlite> select strftime('%Y-%m-%d %H:%M:%f', 'NOW');
2023-02-01 11:49:35.624
Images
Mastodon 包含大量的多媒體內容。我們不必浪費時間重新構建影像管線;Cloudflare Images 提供的 API 可以從我們的全球 CDN 上傳、轉換和提供最佳化的影像,因此它能夠很好地滿足 Wildebeest 的要求。
諸如發佈內容影像、個人資料頭像或標題之類的事情,全都使用 Images API。請參閱 /backend/src/media/image.ts,以瞭解我們如何與 Images 連接配合。
如果您想瞭解 Images 以在下一個專案中使用,請透過教學課程,學習如何將 Cloudflare Images 整合到您的網站上。
async function upload(file: File, config: Config): Promise<UploadResult> {
const formData = new FormData()
const url = `https://api.cloudflare.com/client/v4/accounts/${config.accountId}/images/v1`
formData.set('file', file)
const res = await fetch(url, {
method: 'POST',
body: formData,
headers: {
authorization: 'Bearer ' + config.apiToken,
},
})
const data = await res.json()
return data.result
}
您也可以從儀表板存取 Cloudflare Images。您可以使用它快速瀏覽或管理目錄。
Queues
ActivityPub 通訊協定在設計上比較隨意。根據社交圖表的大小,可能會有很多往返的 HTTP 流量。我們不能在每次有人發佈內容時,讓用戶端為了等待數百次 Fediverse 訊息傳遞而封鎖。
我們需要採取非同步工作的方式並啟動背景工作,以便從主應用程式移除資料處理並保持用戶端簡潔快速。官方 Mastodon 伺服器採用了類似的策略,使用 Sidekiq 來進行背景處理。
所幸我們也無需為這種複雜性擔心。Cloudflare Queues 可讓開發人員傳送和接收有送達保證的郵件,並從 Workers 要求中移除工作,從而有效地為您提供非同步批次工作功能。
簡而言之,您有一個佇列主題識別碼,它基本上是一個可自動擴展的緩衝清單,然後有一個或多個產生者可以產生結構化郵件(在我們的例子中是 JSON 物件),並把它們放入佇列中(您來定義它們的結構描述),最後會有一個或多個訂閱該佇列的消費者接收郵件,並以自己的速度處理它們。
請造訪 Queues 運作方式頁面以瞭解詳細資訊。
在我們的例子中,只要任何傳入的 API 呼叫需要冗長而昂貴的操作,主應用程式就會產生佇列工作。例如,當有人發佈內容、道歉、_發出嘟嘟聲提示_時,我們需要將其廣播到其粉絲的收件匣中,可能會對遠端伺服器觸發多個要求。在這裡,我們正在將工作列入佇列,從而釋放 API 以保持回應:
同樣,當遠端伺服器向我們的執行個體收件匣傳送郵件時,我們也不希望停止主 API。下面顯示了 Wildebeest 在收件匣中收到郵件時建立非同步工作:
export async function deliverFollowers(
db: D1Database,
from: Actor,
activity: Activity,
queue: Queue
) {
const followers = await getFollowers(db, from)
const messages = followers.map((id) => {
const body = {
activity: JSON.parse(JSON.stringify(activity)),
actorId: from.id.toString(),
toActorId: id,
}
return { body }
})
await queue.sendBatch(messages)
}
最後一步,我們的佇列消費者在單獨的 Worker 中執行,獨立於 Pages 專案。消費者會接聽新郵件並按照自己的節奏依順序處理它們,從而使其他所有人都無法阻止。當事情變得繁忙時,佇列會增加緩衝區。儘管如此,事情仍在繼續,並且最終會將工作分派出去,釋放主 API 來完成關鍵工作:盡快回應遠端伺服器和用戶端。
export async function handleRequest(
domain: string,
db: D1Database,
id: string,
activity: Activity,
queue: Queue,
): Promise<Response> {
const handle = parseHandle(id)
const actorId = actorURL(domain, handle.localPart)
const actor = await actors.getPersonById(db, actorId)
// creates job
await queue.send({
type: MessageType.Inbox,
actorId: actor.id.toString(),
activity,
})
// frees the API
return new Response('', { status: 200 })
}
如果您想親自體驗一下 Queues,下面的範例簡單地展示了如何使用 Queues 在 R2 中儲存資料。
export default {
async queue(batch, env, ctx) {
for (const message of batch.messages) {
…
switch (message.body.type) {
case MessageType.Inbox: {
await handleInboxMessage(...)
break
}
case MessageType.Deliver: {
await handleDeliverMessage(...)
break
}
}
}
},
}
Caching 和 Durable Objects
在需要處理資料的複雜應用程式中,快取重複性操作是另一種提升效能的策略。一位著名的 Netscape 開發人員 Phil Karlton 曾經說過:「電腦科學中只有兩件難事:快取無效判定和命名。」
Cloudflare 顯然對快取了如指掌,因為它是我們全球 CDN 的核心功能。我們還為客戶提供 Workers KV,這是一個全球性的低延遲索引鍵值資料存放區,任何人都可以使用它來快取資料中心中的資料物件並構建快速的網站和應用程式。
但是,KV 透過最終保持一致性來實現其效能。雖然這對於許多應用程式和使用案例來說都沒問題,但對其他應用程式並不適合。
ActivityPub 通訊協定具有高度交易性,無法承受最終一致性。下面有一個例子:產生完整的時間表價格昂貴,所以我們快取該操作。但是,當您發佈內容時,我們需要在回覆用戶端之前使該快取無效。否則,新貼文不會出現在時間表中,並且用戶端可能會因為沒有看到它而失敗並發生錯誤。實際上,這發生在最熱門的一個用戶端中。
我們需要變得更聰明。團隊討論了幾個選項。所幸我們的 API 目錄有很多選項。滿足 Durable Objects。
Durable Objects 是提供交易式儲存 API 的單一執行個體 Workers。當您需要中央協調、強大的一致性和狀態持續性時,它們是理想的選擇。您可以在處理多個 WebSocket 連線的狀態、協調和路由聊天室中的訊息,甚至執行多人遊戲(如 Doom)等情況下使用 Durable Objects。
您已經知道現在會發生什麼了。是的,我們基於 Durable Object 針對 Wildebeest 實作了索引鍵值快取子系統。透過利用 DO 的原生交易式儲存 API,我們可以提供強大的保證,每當我們建立或變更索引鍵時,下一次讀取將始終傳回最新版本。
這個想法如此簡單有效,以至於我們僅使用幾行程式碼就可以透過以下兩個原始物件實作索引鍵值快取:HTTP PUT 和 GET。
這就是強式一致性。現在,我們開始進行使用者註冊和驗證。
export class WildebeestCache {
async fetch(request: Request) {
if (request.method === 'GET') {
const { pathname } = new URL(request.url)
const key = pathname.slice(1)
const value = await this.storage.get(key)
return new Response(JSON.stringify(value))
}
if (request.method === 'PUT') {
const { key, value } = await request.json()
await this.storage.put(key, value)
return new Response('', { status: 201 })
}
}
}
零信任存取
官方 Mastodon 伺服器通常會使用電子郵件來處理使用者註冊,然後您可以選擇本機使用者名稱並開始使用該服務。但是,如果我們要從頭開始構建,處理使用者註冊和驗證可能會令人怯步,並且非常耗時。
此外,人們並不希望為他們想要使用的每項新服務都建立新的認證,而是希望使用更方便的類似 OAuth 的授權和驗證方法,以便重複使用現有的 Apple,Google 或 GitHub 帳戶。
我們希望使用 Cloudflare 的內建功能來簡化操作。當然,我們有一個產品可以處理 Cloudflare 背後任何應用程式的使用者上線、驗證和存取原則;這就是 Zero Trust。所以我們將 Wildebeest 置於其後。
Zero Trust 存取可以使用電子郵件進行一次性 PIN 碼 (OTP) 驗證,也可以進行單一登入 (SSO),這要藉助多個識別提供者(例如:Google、Facebook、GitHub、LinkedIn),包括任何支援 SAML 2.0 的一般識別提供者。
當您開始在一個用戶端使用 Wildebeest 時,根本不需要註冊。相反,您可以直接登入,然後會將您重新導向至 Access 頁面,並根據您(即執行個體擁有者)所設定的原則來處理驗證。
該原則定義可以進行驗證的人員以及驗證的方式。
通過驗證後,Access 會將您重新導向回 Wildebeest。第一次使用時,我們會偵測到我們沒有使用者的相關資訊,並要求您提供使用者名稱和顯示名稱。我們只會要求提供一次,這些資訊將用於創建您的公開的 Mastodon 個人資料。
技術上而言,Wildebeest 實作 OAuth 2 規格。Zero Trust 將保護 /oauth/authorize 端點,並在使用者通過驗證後在要求標頭中發出有效的 JWT 權杖。然後 Wildebeest 會讀取並驗證 JWT,並在 URL 重新導向中傳回授權碼。
用戶端有了授權碼之後,就可以使用 /oauth/token 端點來獲取 API 存取權杖。後續的 API 呼叫會在授權標頭中插入一個持有人權杖:
Authorization: Bearer access_token
部署與持續整合
我們並不希望為 Mastodon 執行受管理服務,因為這會稍微減弱同盟和資料擁有權的概念。此外,我們還認識到,ActivityPub 和 Mastodon 是快節奏的新興技術,它們將以目前很難預測的方式快速發展。
出於上述種種原因,我們認為,現在幫助生態系統的最佳方法是提供開放原始碼的軟體套件,任何人都可以基於我們的雲端使用、自訂,改進和部署該套件。Cloudflare 顯然將不斷改進 Wildebeest 並為社群提供支援,但我們希望為 Fediverse 維護人員提供對執行個體和資料的完全控制權和擁有權。
剩下的問題就是,當需要設定如此多種 Cloudflare 功能時,我們如何發佈 Wildebeest 搭售方案並使其易於部署到某人的帳戶中,以及我們如何隨著時間的推移促進軟體更新?
解決方案最終將搭配使用 GitHub 與 GitHub Actions、使用 Workers 部署和 Terraform 巧妙地融於一體。
「使用 Workers 部署」按鈕是一個特製的連結,可自動產生向使用者提問的工作流程頁面,而 Cloudflare 會處理授權 GitHub 部署至 Workers、將 Wildebeest 存放庫自動分叉到使用者的帳戶中,然後使用 GitHub Actions 工作流程設定和部署專案。
GitHub Actions 工作流程是一個 YAML 檔案,它宣告了要在每個步驟中執行的動作。下面是一個 Wildebeest 工作流程(簡化版):
更新 Wildebeest
name: Deploy
on:
push:
branches:
- main
repository_dispatch:
jobs:
deploy:
runs-on: ubuntu-latest
timeout-minutes: 60
steps:
- name: Ensure CF_DEPLOY_DOMAIN and CF_ZONE_ID are defined
...
- name: Create D1 database
uses: cloudflare/wrangler-action@2.0.0
with:
command: d1 create wildebeest-${{ env.OWNER_LOWER }}
...
- name: retrieve Zero Trust organization
...
- name: retrieve Terraform state KV namespace
...
- name: download VAPID keys
...
- name: Publish DO
- name: Configure
run: terraform plan && terraform apply -auto-approve
- name: Create Queue
...
- name: Publish consumer
...
- name: Publish
uses: cloudflare/wrangler-action@2.0.0
with:
command: pages publish --project-name=wildebeest-${{ env.OWNER_LOWER }} .
此工作流程會在每次主分支發生變更時自動執行,因此更新 Wildebeest 就像將上游官方存放庫與分支同步處理一樣簡單。您甚至不需要為此使用 git 命令;GitHub 在 UI 中提供了一個便於使用的「同步」按鈕,您只需按一下即可。
更重要的是什麼呢?我們採用累加式更新,不會造成破壞。當 GitHub Actions 工作流程重新部署 Wildebeest 時,我們只對設定進行必要的變更,而不進行其他任何變更。您不會遺失資料;我們無需刪除您現有的設定。下文闡述了我們是如何做到這一點的:
我們使用 Terraform,這是一種宣告式設定語言和工具,可與我們的 API 互動,並可以查詢和設定您的 Cloudflare 功能。我們的秘訣就是,每次套用新設定時,都在 Cloudflare KV 索引鍵中保留 Wildebeest 的 Terraform 狀態複本。當觸發新部署時,我們會從 KV 複本取得該狀態,計算差異,然後只變更必要的項目。
資料遺失也不是問題,因為正如上文所述,D1 支援遷移。如果我們需要將欄新增至資料表或新資料表中,無需銷毀資料庫並重新建立;只需將必要的 SQL 套用至該變更即可。
與生俱來的保護、最佳化和可觀察性
在 Wildebeest 啟動並執行后,您可以保護它免受不良流量和惡意執行者的侵害。Cloudflare 為您提供現成可用的 DDoS、WAF 和機器人管理保護,只需按一下滑鼠即可獲得。
同樣,您將從我們的產品中獲得即時的網路和內容傳遞最佳化,並獲得有關 Wildebeest 執行個體的執行和使用方式的分析。
ActivityPub、WebFinger、NodeInfo 和 Mastodon API
Mastodon 讓 Fediverse 概念得以普及,但它使用的很多基礎技術已經存在相當長的一段時間了。這是一個難得的時刻,所有的技術終於結合在一起,建立起一個初步的平台,可以幫助網際網路使用者解決實際的使用案例。我們來快速瀏覽一下 Wildebeest 必須實作的通訊協定:
ActivityPub
ActivityPub 是一個非集中化的社交網路通訊協定,W3C 至少從 2018 年就一直推薦使用。它定義了用於建立和操作內容的用戶端 API,以及用於內容交換和通知的伺服器對伺服器 API,也稱為同盟。ActivityPub 使用 ActivityStreams(這是一種更舊的 W3C 通訊協定)中的術語。
其他很多動作和活動的執行者(個人資料)、郵件或物件(嘟嘟聲提示)、收件匣(您從關注的人那裡收到嘟嘟聲提示)和寄件匣(您將嘟嘟聲提示傳送給您關注的人)等概念,都在 ActivityPub 規格中定義。
下面是包含 ActivityPub 實作的資料夾。
WebFinger
import type { APObject } from 'wildebeest/backend/src/activitypub/objects'
import type { Actor } from 'wildebeest/backend/src/activitypub/actors'
export async function addObjectInInbox(db, actor, obj) {
const id = crypto.randomUUID()
const out = await db
.prepare('INSERT INTO inbox_objects(id, actor_id, object_id) VALUES(?, ?, ?)')
.bind(id, actor.id.toString(), obj.id.toString())
.run()
}
WebFinger 是一個簡單的 HTTP 通訊協定,用於探索有關任何實體(如個人資料、伺服器或特定功能)的資訊。它將 URI 解析為資源物件。
Mastodon 使用 WebFinger 查閱來探索有關遠端使用者的資訊。例如,假設您想要與 @user@example.com 進行互動。您的本機伺服器會要求 https://example.com/.well-known/webfinger?resource=acct:user@example.com(使用 acct 配置)並獲得如下所示的內容:
現在我們知道如何與 @user@example.com
互動了,使用 https://example.com/ap/users/user endpoint
。
{
"subject": "acct:user@example.com",
"aliases": [
"https://example.com/ap/users/user"
],
"links": [
{
"rel": "self",
"type": "application/activity+json",
"href": "https://example.com/ap/users/user"
}
]
}
下面是 WebFinger 回應:
Mastodon API
export async function handleRequest(request, db): Promise<Response> {
…
const jsonLink = /* … link to actor */
const res: WebFingerResponse = {
subject: `acct:...`,
aliases: [jsonLink],
links: [
{
rel: 'self',
type: 'application/activity+json',
href: jsonLink,
},
],
}
return new Response(JSON.stringify(res), { headers })
}
最後,諸如設定伺服器資訊、個人資料資訊、產生時間表、通知和搜索之類的事情都是 Mastodon 特定的 API。Mastodon 開放原始碼專案定義了一個 REST API 目錄,您可以在他們的網站上找到所有相關文件。
我們的 Mastodon API 實作可以在這裡(REST 端點)和這裡(後端原始物件)找到。下面是一個有關 Mastodon 的伺服器資訊 /api/v2/instance 的例子,由 Wildebeest 實作:
Wildebeest 還實作了 WebPush(用於用戶端通知)和 NodeInfo(用於伺服器資訊)。
export async function handleRequest(domain, db, env) {
const res: InstanceConfigV2 = {
domain,
title: env.INSTANCE_TITLE,
version: getVersion(),
source_url: 'https://github.com/cloudflare/wildebeest',
description: env.INSTANCE_DESCR,
thumbnail: {
url: DEFAULT_THUMBNAIL,
},
languages: ['en'],
registrations: {
enabled: false,
},
contact: {
email: env.ADMIN_EMAIL,
},
rules: [],
}
return new Response(JSON.stringify(res), { headers })
}
其他與 Mastodon 相容的伺服器也必須實作上述所有的通訊協定;Wildebeest 就是其中之一。社群在討論未來增強功能方面表現得非常積極活躍;隨著時間的推移,我們將不斷改進相容性並增加對更多功能的支援,以確保 Wildebeest 隨著 Fediverse 伺服器和用戶端生態系統的出現發揮良好的作用。
立即開始使用
關於技術,我們不再贅言;讓我們帶您進入 Fediverse 吧。我們嘗試詳細說明了部署伺服器的所有步驟。若要開始使用 Wildebeest,請前往公用 GitHub 存放庫,查看我們的入門教學課程。
Wildebeest 的相依項大多提供一個慷慨的免費方案,可讓您嘗試使用它們來處理並非關鍵業務的個人或業餘愛好專案,但是您需要訂閱 Images 方案(最低層應該足以滿足大多數需求),並且根據您的伺服器負載訂閱 Workers Unbound(同樣,對於大多數使用案例來說,最低成本應該足夠用了)。
遵循我們的內部測試原則,Cloudflare 今天也正式加入了 Fediverse。您可以開始關注我們的 Mastodon 帳戶,並使用最喜歡的 Mastodon 應用程式,從 Cloudflare 獲得與您在其他社交平台上從我們這裡獲得定期更新同樣的體驗。這些帳戶完全基於 Wildebeest 伺服器執行:
@cloudflare@cloudflare.social - 我們的主要帳戶
@radar@cloudflare.social - Cloudflare Radar
Wildebeest 與大多數用戶端應用程式相容;我們已確認可以使用官方 Mastodon Android 和 iOS 應用程式,Pinafore、Mammoth 和 tooot,並正在研究其他應用程式(如 Ivory)。如果您最喜歡的應用程式不好用,請在這裡提交問題,我們會竭盡所能地提供幫助和支援。
結束語
Wildebeest 完全基於我們的 Supercloud 堆疊構建。這是我們使用各種 Cloudflare 產品和功能建立的最完整也最複雜的專案之一。
希望這篇報道不僅能促使您嘗試部署 Wildebeest 和加入 Fediverse,還可以嘗試基於 Cloudflare 構建您的下一個應用程式,無論要求有多高。
Wildebeest 現在是一個符合最低可用限度的與 Mastodon 相容的伺服器,但是隨著時間的推移,我們將不斷提供更多功能進行改進,並對其提供支援;畢竟,我們正在用它作為我們的官方帳戶。它也是開放原始碼的,這意味著非常歡迎您透過提取要求或意見反應來發表意見。
與此同時,我們開啟了一個 Wildebeest 聊天室(在開發人員 Discord 伺服器上),並密切關注 GitHub 存放庫的問題索引標籤。請隨時與我們交流;團隊渴望知道您如何使用 Wildebeest 並回答您的問題。
又及:本篇部落格中的程式碼片段經過了簡化,這是為了提高可讀性和節省空間(比如,已刪除 TypeScript 類型和錯誤處理程式碼)。如需完整版本,請參閱 GitHub 存放庫連結。