Corsair AMcards 插件接入指南:用 10 个类型安全的 API 调用打通自动化实体贺卡邮寄
2026/9/16 1:16:18 网站建设 项目流程

Corsair AMcards 插件接入指南:用 10 个类型安全的 API 调用打通自动化实体贺卡邮寄

【免费下载链接】corsairConnect your users to their apps项目地址: https://gitcode.com/GitHub_Trending/corsa/corsair

Corsair 是一个面向多租户场景的第三方应用连接框架,而@corsair-dev/amcards是它官方提供的 AMcards 集成插件。AMcards 是自动化贺卡平台,支持向真实地址邮寄个性化实体贺卡。本文将围绕该插件的完整接入流程展开:从安装、租户授权,到 10 个类型安全的amcards.api.*操作、5 个本地同步实体与检索过滤器,再到底层的请求封装、限流与错误处理实现,帮助你在自己的 Agent 或后端服务中快速落地"替用户发送实体贺卡"的能力。

插件概览:一个插件,两套能力

@corsair-dev/amcards插件围绕 AMcards 官方 API v1(Django REST / Tastypie 风格)封装出两层能力:

  • 10 个类型安全的 API 操作cards.listcategories.get/listcontacts.listgifts.get/listschema.getApi/getCategorytemplates.get/list,全部标注为read风险级别;
  • 5 个本地同步实体cardscategoriescontactsgiftstemplates,数据同步到本地数据库后可通过.search()/.list()快速检索。

在插件文档(overview.mdx)中,它被定义为"Automated greeting card platform for sending personalized physical mail campaigns"(自动化实体贺卡邮寄平台)。

安装与初始化

安装依赖

使用pnpm安装插件(Corsair 插件采用 workspace 级 peerDependencies 设计,需要corsair >= 0.1.0zod ^4.1.13,见 package.json):

pnpm add corsair @corsair-dev/amcards

仓库示例与插件本身均使用pnpm工作区(pnpm-workspace.yaml),若使用 npm/yarn/bun 同样支持:

npm install corsair @corsair-dev/amcards # 或 yarn add corsair @corsair-dev/amcards # 或 bun add corsair @corsair-dev/amcards

注册插件

在创建 Corsair 实例时传入amcards()工厂函数:

import Database from 'better-sqlite3'; import { createCorsair } from 'corsair'; import { amcards } from '@corsair-dev/amcards'; export const corsair = createCorsair({ plugins: [ amcards(), ], database: new Database('corsair.db'), kek: process.env.CORSAIR_KEK!, hub: { projectApiKey: process.env.CORSAIR_API_KEY!, signingSecret: process.env.CORSAIR_SIGNING_SECRET!, }, });

要点说明:

  • database为本地数据库实例,AMcards 同步数据会持久化到其中;
  • kek(Key Encryption Key)与hub配置用于凭证加密与连接流程,可参考 quick-start.mdx 与 multi-tenancy.mdx;
  • 多租户是默认行为,后续所有调用需通过corsair.withTenant(id)划定租户作用域。

从源码看,amcards()工厂(index.ts)默认将authType收敛为'api_key',并返回一个满足CorsairPlugin契约的插件对象:包含id: 'amcards'authConfigschema、10 个 endpoint、Zod 输入输出 schema、endpointMeta(每个操作的风险级别与描述)以及自定义的keyBuilder

认证方式:API Key(推荐)

AMcards 插件只支持API Key(即 AMcards 的 API Access Token)这一种认证方式,不支持 OAuth

  • 首次以某个租户身份发起请求时,Corsair 会提示为该租户录入 API Key(见 api-key.mdx);
  • 凭证由租户维度的密钥管理器托管,调用时无需在业务代码中显式携带 Token。

底层认证实现

keyBuilder(index.ts)的逻辑是:

  1. 仅接受source === 'endpoint'的调用场景,否则抛出AuthMissingError('amcards', 'api_key')
  2. 若插件选项里显式传入了key,直接使用;
  3. 否则从ctx.keys.get_api_key()解析租户的 API Key,取不到同样抛出AuthMissingError

认证配置amcardsAuthConfig(index.ts)将api_key的账户作用域声明为['tenant_external_id'],即密钥按租户外部 ID 隔离存储。

Token 的发送方式

底层 HTTP 层(client.ts)使用 Django REST 风格的 TokenAuthentication:

Authorization: Token <api_access_token>

源码注释明确说明:OpenAPIConfig.TOKEN有意保持未设置,以避免共享传输层自动发出Authorization: Bearer头。每次请求还会固定携带Accept: application/jsonContent-Type: application/json

全部端点一览

插件 README(README.md)给出了 10 个端点的官方总览,全部为read风险级别:

操作操作 ID风险描述
cards.listamcards.api.cards.listread列出当前账户的贺卡
categories.getamcards.api.categories.getread按 id 获取卡片模板分类
categories.listamcards.api.categories.listread按优先级顺序列出卡片模板分类
contacts.listamcards.api.contacts.listread列出联系人,可按姓名或邮箱过滤
gifts.getamcards.api.gifts.getread按 id 获取礼物
gifts.listamcards.api.gifts.listread列出可用礼物
schema.getApiamcards.api.schema.getApiread获取 AMcards API v1 schema(资源映射)
schema.getCategoryamcards.api.schema.getCategoryread获取只读的 Category 资源 schema
templates.getamcards.api.templates.getread按 id 获取公开卡片模板
templates.listamcards.api.templates.listread列出公开卡片模板

这 10 个端点与 index.ts 中的amcardsEndpointsNested一一对应,在运行时以嵌套命名空间暴露为tenant.amcards.api.<resource>.<action>()

端点详解与调用示例

以下所有调用均基于const tenant = corsair.withTenant('acme')获取的租户作用域对象。每个操作的输入输出 Schema 定义在 endpoints/types.ts,底层 HTTP 映射在 endpoints/handlers.ts。

cards.list — 列出账户贺卡

await tenant.amcards.api.cards.list({});

输入参数(均可选):

名称类型描述
skipnumber跳过的行数,对应 Tastypie/DRF 的offset
limitnumber每页大小

输出{ id: number | string }数组,或带count/next/previous/results/meta/objects的分页包装结构。

categories.get / categories.list — 模板分类

按 id 获取单个分类:

await tenant.amcards.api.categories.get({ category_id: 9 });

category_id为必填的正整数。分类字段包括idtitlepriority(1 为最高优先级)、parenthierarchy

列出分类(支持多级筛选):

await tenant.amcards.api.categories.list({ parent__id: 3, // 按父分类 id 筛子分类 title__icontains: 'birthday', // 标题大小写不敏感搜索 parent__title__icontains: 'holiday', // 父标题大小写不敏感搜索 });

contacts.list — 联系人列表

await tenant.amcards.api.contacts.list({ email: 'ada@example.com', first_name: 'Ada', last_name: 'Lovelace', skip: 0, limit: 50, });

联系人字段:idfirst_namelast_nameemailcreated_atupdated_at。handler 会把这些参数原样映射为 AMcards 的emailfirst_namelast_name查询参数(见 handlers.ts)。

gifts.get / gifts.list — 礼物

无需认证即可访问的公开资源。handler 通过auth: false省略 Token 头(handlers.ts)。

await tenant.amcards.api.gifts.get({ id: 3 }); await tenant.amcards.api.gifts.list({});

礼物字段:idnamedescriptionpriceshipping_costavailableavailability,其中price/shipping_cost兼容字符串或数字两种返回形式。

templates.get / templates.list — 公开卡片模板

同为公开资源(auth: false),可按分类或名称模糊搜索:

await tenant.amcards.api.templates.get({ id: 4 }); await tenant.amcards.api.templates.list({ category__id: 9, // 按分类 id 过滤 name__icontains: 'thanks', // 模板名大小写不敏感搜索 });

模板字段:idnamecategoryconfigurationpanelsmetadata(后四者为任意结构)。

schema.getApi / schema.getCategory — 获取 API schema

两个 schema 端点返回的是描述性元数据,常用于动态发现 API 结构:

await tenant.amcards.api.schema.getApi({}); // API v1 资源映射 await tenant.amcards.api.schema.getCategory({}); // Category 资源 schema

getApi对应GET /api/v1/(DRF/Tastypie API root),getCategory对应GET /api/v1/categories/schema/

租户连接流程

插件 README 明确标注"Auth: API key. Corsair prompts your tenant for credentials on first use",具体落地为:

  1. 业务后端调用corsair.manage.connect.createLink生成连接链接(参考 connect.mdx):
const { connectUrl } = await corsair.manage.connect.createLink({ plugin: 'amcards', tenantId: 'acme', }); // 将用户浏览器重定向到 connectUrl
  1. 用户在 Corsair Hub 托管的页面上录入 AMcards API Access Token;
  2. Hub 将结果投递回你的应用,此后该租户的所有amcards.*调用自动携带凭证,无需再次录入。

Webhooks:当前无支持

插件 README 明确说明"No webhooks"。源码层面也印证了这一点:index.ts 中webhooks: {}为空对象、webhookHooks: undefinedpluginWebhookMatcher: undefined。因此该插件目前是纯拉取模型(API 调用 + 本地同步),不提供任何入站事件推送。

本地同步数据与检索

插件将cardscategoriescontactsgiftstemplates五个实体同步到本地数据库,实体字段定义在 schema/database.ts,字段命名遵循 AMcards API v1 的 snake_case 约定,并使用.loose()保留响应中的额外键。

每个实体都支持tenant.amcards.db.<entity>.search().list()(完整过滤器见 database.mdx):

const rows = await tenant.amcards.db.contacts.search({ data: { first_name: { equals: 'Ada' } }, limit: 100, offset: 0, });

各实体的可检索字段与操作符汇总:

实体可检索字段支持操作符
cardsentity_idequals, contains, startsWith, endsWith, in
categoriesentity_id,title,priority字符串类:equals, contains, startsWith, endsWith, in;priority(number):equals, gt, gte, lt, lte, in
contactsentity_id,first_name,last_name,email,created_at,updated_atequals, contains, startsWith, endsWith, in
giftsentity_id,name,description,available字符串类同上;available(boolean):equals
templatesentity_id,nameequals, contains, startsWith, endsWith, in

所有.search()均支持limitoffset分页;.list().search()位于同一路径(省略.search后缀),用于直接列举。这些本地检索能力让 Agent 可以在不频繁打 AMcards 远程 API 的情况下快速完成"查找联系人 / 匹配模板 / 选礼物"等决策逻辑。

底层实现:请求封装、限流与错误处理

请求封装与分页兼容

makeAmcardsRequest(client.ts)统一处理:

  • GET/POST/PUT/PATCH/DELETE 方法分发,写请求自动携带 JSON mediaType;
  • compactQuery剔除值为undefined的查询参数,避免发出?foo=undefined
  • 路径 ID 通过encodeAmcardsPathId做 URI 编码(client.ts)。

值得注意的分页兼容设计:AMcards 的列表响应可能是 Django REST 的results结构、Tastypie 的objects结构,甚至裸数组。listResponse(endpoints/types.ts)用z.union同时接受这三种形态,保证任何历史版本的响应都不会导致解析失败。

内置限流

client.ts 为共享传输层配置了限流策略:

const AMCARDS_RATE_LIMIT_CONFIG: RateLimitConfig = { enabled: true, maxRetries: 3, initialRetryDelay: 1000, backoffMultiplier: 2, headerNames: { retryAfter: 'Retry-After' }, };

即最多重试 3 次、初始退避 1 秒、退避倍数 2,并读取Retry-After响应头。

错误类型与错误处理器

所有请求失败都会被包装为AmcardsAPIError(client.ts),并透传statusstatusTextbodyretryAfterrateLimitResetrateLimitRemainingrateLimitLimit等字段。

error-handlers.ts 定义了六类错误匹配与重试策略:

分类匹配依据(状态码优先)重试策略
RATE_LIMIT_ERROR429不重试
AUTH_ERROR401/403不重试
NOT_FOUND_ERROR404不重试
VALIDATION_ERROR400/422不重试
SERVER_ERROR>= 500最多重试 2 次,指数退避
DEFAULT兜底不重试

注释明确指出:AMcards 是 Django REST API,HTTP 状态码本身就是契约,消息启发式匹配(如 message 中包含 "rate limit"、"invalid token" 等)只在拿不到状态码时兜底使用。

测试覆盖

插件自带完整测试:handlers.test.ts 通过 mockmakeAmcardsRequest验证 10 个 handler 的请求参数映射与输出 Schema 解析,例如联系人(first_name/last_name/email/created_at/updated_at)、分类(title/priority)、礼物(price/shipping_cost/available)、模板(name/panels)等典型负载均被覆盖;另有 client.test.ts 与 schema.test.ts 分别覆盖请求封装与 Schema 校验。

与 Agent / MCP 集成

插件能力可以通过 Corsair 的 MCP 适配层暴露为 MCP 工具,使 Claude、Cursor 等编码 Agent 直接调用 AMcards 的 10 个操作(参考 mcp-adapters.mdx)。由于全部操作均为read风险级别,将其暴露给 Agent 时权限模型相对简单清晰;同时借助本地同步实体的.search(),Agent 可以先在本地完成联系人匹配与模板选择,再按需调用远程 API,降低延迟与限流压力。

版本与许可

  • 插件包名为@corsair-dev/amcards,peerDependencies 要求corsair >= 0.1.0zod ^4.1.13(见 package.json);
  • 许可协议为Apache-2.0(README 与 package.json 一致);
  • 插件类型定义:amcardsAmcardsPluginOptionsAmcardsContextAmcardsEndpoints等均从 index.ts 导出;输入输出类型与 Schema 从 endpoints/types.ts 导出。

小结

@corsair-dev/amcards插件把 AMcards 的 Django REST / Tastypie 风格 API 收敛为 10 个类型安全的只读操作,配合本地 5 实体同步、内置限流与按状态码分类的错误处理,为"替用户发送个性化实体贺卡"的 Agent 场景提供了开箱即用的连接能力。核心使用路径可概括为:安装插件 → 注册到 Corsair → 通过 connect link 引导租户录入 API Key →withTenant(id)作用域内调用amcards.api.*或检索amcards.db.*。更完整的输入输出类型可继续查阅 api.mdx 与 database.mdx。

【免费下载链接】corsairConnect your users to their apps项目地址: https://gitcode.com/GitHub_Trending/corsa/corsair

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询