Viết adapter cộng đồng
Trong hướng dẫn từng bước này, bạn dùng mẫu của ReputeCast để tự viết adapter email cho một nhà cung cấp gửi thư chưa có adapter có sẵn, chạy bộ kiểm thử hợp đồng với adapter đó, rồi cài nó lên một hệ thống thử nghiệm và kiểm tra rằng ReputeCast gọi được nó. Quá trình mất khoảng 30 phút.
Adapter cộng đồng là một tiến trình nhỏ nói giao thức adapter: ReputeCast gửi cho nó các yêu cầu JSON có ký (gửi những thư này, xác minh webhook này), và nó gọi API của nhà cung cấp. Mẫu là một adapter chạy được cho Example ESP, một nhà cung cấp hư cấu có API điển hình, viết bằng SDK TypeScript @reputecast/adapter-sdk.
Trước khi bắt đầu
Phần tiêu đề “Trước khi bắt đầu”- Node.js 24.11 trở lên, pnpm và Git.
- Một hệ thống ReputeCast thử nghiệm trên cùng máy, nơi bạn chạy được CLI
reputecast. Adapter đã cài áp dụng cho mọi không gian làm việc trên hệ thống, nên đừng thử trên hệ thống đang chạy thật (production). providers.allow_private_networks: truetrên hệ thống đó, cho cả máy chủ và CLI (REPUTECAST_PROVIDERS__ALLOW_PRIVATE_NETWORKS=true). Adapter lắng nghe trên127.0.0.1, và ReputeCast từ chối địa chỉ loopback nếu không bật thiết lập này.
Xây dựng và cài adapter
Phần tiêu đề “Xây dựng và cài adapter”-
Sao chép mẫu ra khỏi kho mã ReputeCast:
Terminal git clone https://github.com/hostrepute/reputecast.gitcp -r reputecast/sdk/adapter-template my-esp-adaptercd my-esp-adapterThư mụcmy-esp-adapter/
Thư mụcsrc/
- adapter.ts adapter:
defineAdapter({ id, capabilities, terms, send, … }) - main.ts khởi động máy chủ giao thức
- adapter.ts adapter:
Thư mụctest/
- adapter.test.ts kiểm thử đơn vị
- contract.test.ts
- fake-api.ts bản giả lập API của Example ESP
Thư mụcfixtures/
- webhooks.json các webhook đã ghi lại và các sự kiện chúng phải tạo ra
- manifest.json những gì ReputeCast đọc khi cài đặt
- reputecast.contract.ts thiết lập của bộ kiểm thử hợp đồng
- .env.example
- Dockerfile
- package.json
-
Cài các dependency:
Terminal pnpm installLệnh này cài
@reputecast/adapter-sdk(Node 24, TypeScript 6, không có dependency khi chạy) từ npm. -
Chạy kiểm thử:
Terminal pnpm testpnpm testchạy kiểm thử đơn vị bằng Vitest, rồi chạy bộ kiểm thử hợp đồng (reputecast-adapter test). Bộ kiểm thử hợp đồng khởi động adapter, gọi adapter qua giao thức với API giả lập trongtest/fake-api.tsvà chạy đúng các kịch bản mà bộ kiểm thử Go của ReputeCast chạy với adapter có sẵn:Kết quả Contract suite for example-esp (adapter protocol 1)✓ Registration (40 ms)✓ Health (90 ms)✓ Authentication (56 ms)✓ Send (29 ms)✓ SendRejected (17 ms)✓ SendOutage (17 ms)✓ SendAuth (33 ms)✓ SendIdempotent (36 ms)✓ Verify (81 ms)✓ Quota (8 ms)✓ SuppressionsPull (26 ms)✓ Webhook/Delivered (54 ms)✓ Webhook/HardBounce (63 ms)✓ Webhook/SoftBounce (42 ms)✓ Webhook/Batch (24 ms)15 passed, 0 failed, 0 skippedBộ kiểm thử đối chiếu manifest với mã, kiểm tra health và xác thực có ký, một lần gửi, một lần từ chối vĩnh viễn (bản giả lập từ chối
[email protected]), một sự cố ngừng hoạt động, thông tin xác thực bị từ chối, một lần thử lại idempotent, và từng webhook đã ghi lại: hợp lệ, đã được xác minh trước và bị sửa đổi. -
Đọc cách adapter được khai báo.
src/adapter.tsánh xạ API của Example ESP:src/adapter.ts (excerpt) export default defineAdapter<ExampleEvent>({id: 'example-esp',capabilities: { webhooks: true, batch: 100, suppressionSync: 'pull', quotaDiscovery: true },terms: { bulkMarketing: 'allowed' },http: {name: 'Example ESP',baseUrl: () => process.env['EXAMPLE_ESP_API_URL'] ?? API_URL,headers: (ctx) => ({ authorization: `Bearer ${String(ctx.credentials['api_key'] ?? '')}` }),},async send(msg, ctx) {const r = await ctx.http.post<{ id: string }>('/v1/messages', toPayload(msg))return { providerMessageId: r.data.id }},verifyWebhook(req, ctx) {/* checks X-Example-Signature with verifyTimestampedHmac */},normalizeEvent(raw) {/* maps one Example ESP event to a ReputeCast event */},})Mỗi khả năng đã khai báo cần hook tương ứng (
webhookscầnverifyWebhookvànormalizeEvent,quotaDiscoverycầndiscoverQuota, đồng bộpullcầnsyncSuppressions), vàdefineAdapterdừng tiến trình ngay khi khởi động nếu thiếu một hook.manifest.jsonphải khai báo cùng id, khả năng và điều khoản; máy chủ từ chối khởi động khi chúng khác nhau.ctx.httpchỉ kết nối tới các host trongpermissions.networkcủa manifest, và ánh xạ phản hồi của nhà cung cấp thành lỗi giao thức: 401 và 403 thànhprovider_auth, 429 thànhprovider_throttled, 5xx và hết thời gian chờ thànhprovider_unavailable, còn mọi 4xx khác là lời từ chối riêng cho thư đó. -
Trên máy chạy ReputeCast, cài adapter từ manifest của nó và cung cấp địa chỉ mà tiến trình sẽ lắng nghe:
Terminal reputecast adapter install ./manifest.json --url http://127.0.0.1:4600Kết quả Installed Example ESP 0.1.0 as community:example-esp.Signing secret (shown once):rcas_…Set it on the adapter process as REPUTECAST_ADAPTER_SECRET.Then check it: reputecast adapter check example-espHãy sao chép khóa bí mật để ký ngay bây giờ: ReputeCast lưu nó ở dạng niêm phong và không bao giờ hiển thị lại. Nếu làm mất, bạn chạy
reputecast adapter secret example-espđể cấp một khóa mới. -
Cấp khóa bí mật cho adapter và khởi động nó:
Terminal cp .env.example .envĐặt
REPUTECAST_ADAPTER_SECRETtrong.envbằng khóa bí mật từ bước 5 (PORT=4600vàHOST=127.0.0.1đã có sẵn), rồi khởi động tiến trình:Terminal pnpm devKết quả {"time":"2026-10-22T12:20:56.017Z","level":"info","msg":"adapter listening","adapter":"example-esp","url":"http://127.0.0.1:4600","protocol":1}pnpm devkhởi động lại tiến trình khi mã thay đổi. Nếu không có khóa bí mật, tiến trình không khởi động mà yêu cầu bạn đặtREPUTECAST_ADAPTER_SECRET.GET /healthztrả lời{"ok":true}cho health check của container, còn mọi yêu cầu khác phải được ReputeCast ký:Terminal curl -s -X POST http://127.0.0.1:4600/v1/health -d '{}'Kết quả {"protocol":1,"idempotencyKey":"","error":{"code":"unauthenticated","message":"Refused: the request is not signed, or its timestamp is more than 5 minutes off."}} -
Kiểm tra rằng ReputeCast kết nối được tới adapter bằng khóa bí mật của nó:
Terminal reputecast adapter check example-espKết quả community:example-esp: Answered as example-esp 0.1.0, protocol 1.reputecast adapter checkgọi thao táchealthcó ký và ghi lại kết quả.reputecast adapter listhiển thị từng adapter đã cài cùng địa chỉ, thời điểm cấp khóa bí mật, lần kiểm tra gần nhất và số nhà cung cấp đang dùng adapter đó. Trong vòng 15 giây, mọi tiến trình ReputeCast nhận adapter mới, và Example ESP xuất hiện trong Adapter cộng đồng trên Nhà cung cấp gửi thư, rồi Adapter và trong Thêm nhà cung cấp ở mọi không gian làm việc. -
Ánh xạ nhà cung cấp của bạn. Dừng
pnpm dev, rồi:- Đổi
idtrongsrc/adapter.tsvàmanifest.json, cùngname,maintainervà các liên kết. Id (2 đến 40 ký tự:a-z,0-9và-, bắt đầu bằng một chữ cái) trở thànhcommunity:<id>và xuất hiện trong URL webhook, nên hãy giữ id ổn định qua các phiên bản. - Viết lại
send(hoặcsendBatchcho API theo lô),verifyWebhookvànormalizeEventcho API của nhà cung cấp, với các hàm hỗ trợverifyHmac,verifyTimestampedHmac,verifyBasicAuthvàclassifyBounce. Gửi mọi header trongmsg.headersvà trảmsg.idlại trong sự kiện. - Trong
manifest.json, khai báo khả năng và điều khoản,pricingcủa nhà cung cấp (bắt buộc), biểu mẫu kết nối trongconfig(đánh dấu khóa và token là"secret": true) và các host của nhà cung cấp trongpermissions.network. Tài liệu tham chiếu giao thức liệt kê những gì ReputeCast gọi ứng với từng khả năng. - Văn bản của manifest (
name,description,terms.note,pricing,dedicatedIps, nhãn và phần trợ giúp trongconfig) hiển thị đúng như khi được viết, bằng tiếng Anh với mọi người đọc: adapter cộng đồng không có catalog thông điệp. Các huy hiệu, câu chữ của tab Adapter và lỗi lúc chạy là của ReputeCast và hiển thị bằng ngôn ngữ của từng người đọc. - Ghi lại vài webhook thật, đã xóa dữ liệu cá nhân, vào
test/fixtures/webhooks.jsoncùng các sự kiện chúng phải tạo ra, và chỉnhtest/fake-api.tscho phù hợp.
Chạy
pnpm testvàpnpm typecheckđến khi cả hai đều đạt. Sau đó cài lại manifest bằngreputecast adapter install ./manifest.json --url http://127.0.0.1:4600. Id mới là một lần cài mới: lệnh in ra khóa bí mật mới cho.env, vàreputecast adapter remove example-espgỡ adapter của mẫu. Với cùng id, lệnh cập nhật manifest đã cài và giữ nguyên khóa bí mật. Khởi động lạipnpm dev. - Đổi
-
Đóng gói thành container:
Terminal pnpm build && pnpm startdocker build -t example-esp-adapter .docker run -d --name example-esp -p 127.0.0.1:4600:4600 \-e REPUTECAST_ADAPTER_SECRET=rcas_… example-esp-adapterpnpm startchạy bản build với permission model của Node. Image của mẫu chạy dưới người dùngnodekhông phải root, chỉ đọc các tệp của chính nó, không khởi động được tiến trình con hay worker, và trả lờiHEALTHCHECKtrên/healthz. Image cài đặt bằngpnpm install --frozen-lockfile, nên hãy commitpnpm-lock.yamlđược tạo ở bước 2. Nếu nền tảng của bạn cho phép, hãy giới hạn lưu lượng mạng đi ra của container chỉ tới API của nhà cung cấp.Công bố image và
manifest.jsontại một URLhttps://. Người vận hành cài adapter bằngreputecast adapter install <manifest URL> --url <where it runs>, hoặc bằng Cài từ kho adapter trên tab Adapter; tab này không hiển thị khóa bí mật, nên họ cấp khóa bằngreputecast adapter secret <id>và đặt địa chỉ bằngreputecast adapter set-url <id> <url>.
Bạn đã làm được gì
Phần tiêu đề “Bạn đã làm được gì”Bạn có một tiến trình adapter vượt qua đúng các kịch bản hợp đồng như adapter có sẵn của ReputeCast, được cài trên hệ thống của bạn cho mọi không gian làm việc, và được ký theo cả hai chiều bằng khóa bí mật riêng. Khi adapter đã làm việc với nhà cung cấp thật của bạn, mọi quản trị viên không gian làm việc đều kết nối được nó từ Thêm nhà cung cấp như một adapter có sẵn: thông tin xác thực họ nhập được niêm phong trong ReputeCast và gửi kèm mỗi lệnh gọi, còn adapter không bao giờ lưu chúng.
reputecast adapter remove <id> gỡ cài đặt adapter; ReputeCast từ chối gỡ khi còn nhà cung cấp đã kết nối đang dùng nó. Tham chiếu CLI liệt kê mọi lệnh adapter.