使用 @corsair-dev/brandfetch:在 Corsair 中集成 Brandfetch 品牌数据插件
2026/9/16 19:01:42 网站建设 项目流程

使用 @corsair-dev/brandfetch:在 Corsair 中集成 Brandfetch 品牌数据插件

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

@corsair-dev/brandfetch是 Corsair 生态中用于对接 Brandfetch 与源码实现,系统讲解其安装方式、9 个端点操作、API Key/Client ID 认证模型、无入站 Webhook 的设计取舍、内置错误重试策略与本地缓存 schema,帮助你直接在 Corsair 应用中快速接入 Logo、配色、字体等品牌视觉资产数据。

安装与包结构

该插件与核心运行时corsair解耦发布,通过 pnpm 安装:

pnpm add @corsair-dev/brandfetch

安装后即可从包入口导入插件工厂函数brandfetch,它返回一个符合 CorsairCorsairPlugin契约的插件对象(插件 id 为brandfetch)。从 index.ts 的导出可以看到,包同时对外提供:

  • 插件工厂:brandfetch(options)brandfetchAuthConfig
  • 端点类型:BrandfetchEndpointsBrandfetchBoundEndpoints及全部输入/输出类型
  • 上下文类型:BrandfetchContextBrandfetchKeyBuilderContext
  • 运行时 schema 与配置:brandfetchEndpointSchemasbrandfetchEndpointMeta

插件的 peer 依赖为corsair >= 0.1.0zod ^4.1.13(见 package.json),所有端点出入参均由 zod schema 在运行时校验,保证传输数据的类型安全。

端点总览:9 个操作、全部为 read 风险

README 中给出了插件的完整端点清单,全部操作的风险等级均为read(只读),不会产生写入副作用:

OperationOperation IDRiskDescription
brands.getbrandfetch.api.brands.getreadGet brand logos, colors, fonts, and company details by domain, ticker, ISIN, crypto symbol, or Brand ID
brands.getCompanybrandfetch.api.brands.getCompanyreadGet firmographic company data for a brand identifier
brands.searchbrandfetch.api.brands.searchreadSearch brands by name for autocomplete (requires client ID)
graphql.getVersionbrandfetch.api.graphql.getVersionreadGet the Brandfetch GraphQL API version
logos.getbrandfetch.api.logos.getreadBuild a Brandfetch Logo CDN URL (requires client ID)
taxonomy.getbrandfetch.api.taxonomy.getreadGet Brandfetch industries, countries, and geographic regions
transactions.getbrandfetch.api.transactions.getreadMatch a payment descriptor to merchant brand data
webhooks.listbrandfetch.api.webhooks.listreadList registered Brandfetch webhooks
webhooks.listEventsbrandfetch.api.webhooks.listEventsreadList webhook event types that can be subscribed to

这些操作在源码中被组织为按资源分组的嵌套端点对象,再由插件统一扁平化为brandfetch.api.*的 Operation ID。从 endpoints/index.ts 可以清晰看到分组结构:brands(get / search / getCompany)、logos(get)、transactions(get)、taxonomy(get)、graphql(getVersion)、webhooks(list / listEvents),对应的CorsairEndpoint实现分别位于 endpoints/rest.ts(REST 类)与 endpoints/graphql.ts(GraphQL 类)。

值得注意的设计细节是:虽然插件的 Operation ID 里带webhooks,但按 index.ts 中的注释,webhooks.listwebhooks.listEvents本质是GraphQL 只读查询,用于枚举用户在 Brandfetch 平台配置的 webhook 与可订阅事件类型,并非 Corsair 侧的入站 webhook 投递。

认证模型:API Key 为主,Client ID 为辅

README 明确指出本插件的认证方式是API key,并且「Corsair prompts your tenant for credentials on first use」——即多租户场景下,租户首次使用时 Corsair 会引导其录入凭据。源码 index.ts 中的认证配置进一步说明:

export const brandfetchAuthConfig = { api_key: { account: ['client_id'] as const, }, } as const satisfies PluginAuthConfig;

即插件的账号维度凭据包含api_key(API Key)与client_id(Client ID)两类。实际运行时的取值优先级由keyBuilder决定(index.ts):

  1. 插件选项中的key
  2. 租户已存储的 API Key(通过ctx.keys?.get_api_key()获取,封装在 client.ts 的tryGetStoredKey中,该函数会吞掉「no dek found」这类密钥未解密的错误并返回空值);
  3. 若仍为空,则返回空字符串,交由端点层抛出AuthMissingError(见 rest.ts 的requireApiKey)。

Client ID 的解析顺序则相反(resolveClientId,见 client.ts):请求入参 > 插件选项clientId> 存储的client_id,三者皆无时抛出BrandfetchAPIError,提示「Brandfetch clientId is required for Brand Search and Logo CDN」。这意味着brands.searchlogos.get两个操作必须提供 Client ID,其余操作仅需 API Key。

插件配置选项

BrandfetchPluginOptions(index.ts)支持以下字段:

  • authType:固定为'api_key',默认值即api_key
  • key:直接注入 API Key,跳过租户凭据存储;
  • clientId:为 Brand Search 与 Logo CDN 提供 Client ID(对应 URL 上的?c=参数);
  • hooks:Corsair 生命周期钩子;
  • errorHandlers:自定义错误处理,会与内置errorHandlers浅合并(自定义项覆盖同名项);
  • permissions:基于嵌套端点对象的权限配置。

端点详解:入参、底层调用与缓存落库

brands.get:按标识符获取品牌完整档案

这是插件的核心操作,底层请求为GET https://api.brandfetch.io/v2/brands/{type}/{identifier}BRANDFETCH_API_BASE定义在 client.ts)。标识符支持domaintickerisincrypto四种显式类型(IdentifierTypeSchema,见 endpoints/types.ts),省略identifierType时按domain → ticker → ISIN → crypto的顺序自动探测。入参还包括可选的allowNsfw:为true时返回 NSFW 品牌,为false时 NSFW 品牌返回 404,缺省时采用 Brandfetch 默认行为。

响应(GetBrandInfoResponseSchema)包含idnamedomainclaimeddescriptionlongDescriptionlinkslogoscolorsfontsimagesqualityScorecompanyisNsfwurn等字段,其中 logo 对象携带theme(dark/light)、formats(svg/webp/png/jpeg)与type(icon/logo/symbol/other),颜色对象携带hextype(accent/dark/light/brand),字体对象携带nametype(title/body)与origin(google/custom/system)。这些结构在 types.ts 中均有对应 zod schema,任何字段缺失或类型不符都会在校验阶段被拒绝(测试output schemas reject malformed payloads验证了这一点)。

调用成功后,getBrandInfo还会尝试将品牌核心字段upsertByEntityId写入本地缓存表ctx.db.brands(best-effort,写入失败不影响返回),并记录事件日志brandfetch.brands.get

brands.getCompany:工商信息(firmographic)数据

复用brands.get的请求链路,但只返回响应中的company对象(CompanySchema,可为null)。CompanySchema包含employees(员工数分桶)、financialIdentifiers(isin/ticker 数组)、foundedYearindustrieskind(组织形式)与location(city/country/countryCode/region/state/subregion)。成功后同样 best-effort 写入ctx.db.companies

brands.search:品牌名自动补全搜索

底层请求为GET https://api.brandfetch.io/v2/search/{name}?c={clientId}不使用 Bearer 认证bearer: false,见 rest.ts),而是靠 URL 上的c参数传递 Client ID。入参name必填(最少 1 字符),clientId可选(缺省时按 选项 > 存储 的顺序回退)。返回结果为品牌候选数组,每项含iconnamedomainclaimedbrandId,适合做搜索下拉框的自动补全数据源。

logos.get:构建 Logo CDN URL

该操作不直接请求 CDN 下载图片,而是返回一个可直接用于<img>标签的 Logo CDN URL。URL 由buildCdnLogoUrl(rest.ts)拼接,格式为https://cdn.brandfetch.io/{type}/{identifier}[/w/{w}][/h/{h}][/theme/{theme}][/fallback/{fallback}][/type/{logoType}]?c={clientId}

  • w/h:正整数,指定像素宽高;
  • themelightdark
  • fallbackbrandfetch|transparent|lettermark|404,指定缺图时的兜底行为;
  • logoTypeicon|logo|symbol,默认icon

ops.test.ts 中的断言给出了两个典型输出:

https://cdn.brandfetch.io/domain/nike.com/w/400/h/400/theme/dark/fallback/lettermark/type/icon?c=test-client-id https://cdn.brandfetch.io/nike.com?c=abc # 省略 identifierType 时走自动探测

transactions.get:支付描述符 → 商户品牌匹配

底层为POST https://api.brandfetch.io/v2/brands/transaction,请求体携带transactionLabel(信用卡账单上的原始交易文本,必填)与countryCode(ISO 3166-1 alpha-2 国家码,必填)。countryCode在 schema 层做了.trim().toUpperCase()规范化并用正则/^[A-Z]{2}$/校验——测试用例验证了'us'会被自动转为'US',而'USA'会被直接拒绝(见 ops.test.ts)。返回结构等同于品牌完整档案,可用于把STARBUCKS 1523 OMAHA NE这类账单文本映射为结构化商户品牌数据。

taxonomy.get / graphql.getVersion / webhooks.*:GraphQL 只读查询

这四个操作统一经由makeBrandfetchGraphqlRequest(client.ts)发往https://graphql.brandfetch.io,以 POST + JSON body({ query, variables })的形式执行固定查询,并对errors数组与空data做显式错误抛出:

  • taxonomy.get:一次性拉取taxonomy { industries, countries, geographicRegions },返回带层级(parent/children/depth)、emoji 与坐标的行业、国家、地理区域分类数据,可作为品牌筛选器或行业目录的数据源;
  • graphql.getVersion:查询{ version },返回 Brandfetch GraphQL API 版本号;
  • webhooks.listEvents:查询subscribableEvents,列出可订阅的事件类型(如测试中的brand.updated,含namespacenamedescriptionsubscriptionScope);
  • webhooks.list:分页查询webhooks(first, after),入参first(1–100,GraphQL 默认 10)与after(上一页pageInfo.endCursor游标),返回nodespageInfo { hasNextPage, endCursor },并 best-effort 将 webhook 节点写入ctx.db.webhooks

错误处理与重试策略

所有上游请求都统一由makeBrandfetchRequest包装,任何ApiError都会转换为带statusstatusTextbodyretryAfterBrandfetchAPIError。内置的错误处理策略定义在 error-handlers.ts:

错误类型匹配条件重试策略
RATE_LIMIT_ERRORHTTP 429,或消息含too many requests/quota exceeded指数退避,最多 5 次;命中quota(配额耗尽)则不重试;支持Retry-After
AUTH_ERRORHTTP 401/403,或消息含unauthorized/forbidden不重试
NOT_FOUND_ERRORHTTP 404,或消息含not found不重试
BAD_REQUEST_ERRORHTTP 400,或消息含failed to enrich transaction不重试
SERVER_ERRORHTTP ≥ 500,或消息含internal server error指数退避,最多 2 次
DEFAULT兜底不重试

此外,makeBrandfetchRequest在发送前会拒绝路径中包含{/}的请求(防止 Corsair HTTP 层占位符正则的潜在 ReDoS 风险),并自动压缩掉undefined的查询参数(compactQuery)。

Webhooks:无入站投递

README 明确「No webhooks」。从源码看,brandfetchWebhooksNested被定义为空对象(index.ts),webhookHooksundefined,且pluginWebhookMatcher恒定返回false,即插件主动拒绝一切 Brandfetch 入站投递(0 个 trigger)。前面提到的webhooks.list/webhooks.listEvents只是用于读取 Brandfetch 平台上已配置的 webhook 及事件类型,帮助你在 Corsair 外部(如 Brandfetch 控制台)编排投递逻辑。

本地缓存:brands / companies / webhooks 三张表

插件声明了版本号为1.0.0的数据库 schema(schema/index.ts),包含三个实体(详见 schema/database.ts):

  • BrandfetchBrand:品牌档案的扁平化字段(idnamedomainclaimeddescriptionlongDescriptionqualityScoreisNsfwurn)加checkedAt时间戳;
  • BrandfetchCompany:工商信息行,employees采用官方分桶(1/2/11/51/201/501/1001/5001/10001),kind取值包括EDUCATIONALGOVERNMENT_AGENCYNON_PROFITPARTNERSHIPPRIVATELY_HELDPUBLIC_COMPANYSELF_EMPLOYEDSELF_OWNED,外加总部location各维度;
  • BrandfetchWebhook:webhook 端点记录(urnurldescriptionenabledevents),刻意不存储 secret

设计取舍在注释中有明确说明:嵌套的 logo/image 数组体积大且 CDN URL 会过期,因此不做镜像,本地只保留用于检索的工商与品牌行记录。所有写入均为 best-effort(失败被捕获且不影响主流程)。

验证与测试

插件的核心行为有完整的 Jest 测试保障(endpoints/ops.test.ts,配套 jest.config.cjs),覆盖:

  • brands.get/getCompanyInfo的请求路径(含 identifierType 前缀)与响应 schema 解析;
  • buildCdnLogoUrl的完整 URL 拼装与自动探测分支;
  • transactions.get的请求体与国家码规范化/校验;
  • GraphQL 四个操作(taxonomy、version、subscribableEvents、webhooks 分页)的查询与响应映射;
  • 输出 schema 对畸形负载的拒绝行为。

运行时可通过pnpm test(Jest)与pnpm typecheck(tsc --noEmit)在包内自行验证,pnpm build则通过 tsc + tsup 产出 dist 产物(见 package.json)。

小结

@corsair-dev/brandfetch以「9 个 read 端点 + API Key/Client ID 双凭据 + 内置限流重试 + 本地实体缓存」的完整形态,把 Brandfetch 的品牌资产能力无缝纳入 Corsair 的权限、日志、错误处理与数据库抽象体系。无论是做品牌 Logo 展示、账单文本归类、工商信息查询,还是构建品牌选择器,都可以用这一插件快速落地,且无需自己维护 Brandfetch 的 HTTP 细节与凭据生命周期。插件遵循 Apache-2.0 协议发布,可放心在商业化项目中集成使用。

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

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

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

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

立即咨询