# @authhero/cloudflare-adapter 中文文档

规范地址：https://npmcn.com/package/%40authhero/cloudflare-adapter/

版本：4\.2\.0
npm 元数据更新时间：2026-11-05T14:41:56.199Z
中文译文更新时间：2026-11-09T21:36:48.120Z
软件包许可：AGPL-3\.0-only

本文由上游 README 翻译，可能存在偏差，请结合原文核对 API、命令与配置。版权与许可归原作者所有。

原始 npm 页面：https://www.npmjs.com/package/%40authhero/cloudflare-adapter
原文对照：https://npmcn.com/package/%40authhero/cloudflare-adapter/?lang=original
项目主页：https://authhero.net/
源码仓库：https://github.com/sesamyab/authhero

## 关键信息（来自 npm 元数据）

- 包体积（解压后）：549 kB，70 个文件
- 模块格式：ESM + CJS
- TypeScript 类型：包内自带
- 运行依赖：4 个
- peer 依赖：hono ^4\.12\.30，authhero ^9\.28\.0，@hono/zod-openapi ^1\.4\.0，@authhero/multi-tenancy ^15\.6\.0
- 首次发布：2025-03-04T09:28:22.252Z
- 最新版本发布：2026-11-05T14:41:55.872Z
- 近 30 天下载量：1716（2026-10-07 至 2026-11-05，npm 官方下载统计）
- 已发布版本数：255（近 12 个月 204 个）

## 中文简介

为AuthHero提供Cloudflare专用适配器,提供与Cloudflare服务的集成.

## 安装

```sh
npm install @authhero/cloudflare-adapter
```

也可以使用：`pnpm add @authhero/cloudflare-adapter`、`yarn add @authhero/cloudflare-adapter`、`bun add @authhero/cloudflare-adapter`

## README

# @authhero/cloudflare-适应器

为AuthHero提供Cloudflare专用适配器,提供与Cloudflare服务的集成.

## 特征

本包提供适配器,用于:

-   **自定义域** - 通过 Cloudflare API 管理自定义域
-   **缓存** 使用 Cloudflare 的缓存 API 进行缓存
-   **地理** (可选) - 从 Cloudflare 请求头提取地理信息
-   **日志** (可选) - 认证日志的两个选项:
    -   **分析引擎** - 低纬度写作,使用SQL查询(保留90天)
    -   **R2 SQL + 管道** - 长期储存,无限保留

## 安装

```bash
npm install @authhero/cloudflare-adapter
```

## 使用量

### 带有分析引擎日志(推荐给工人)

```typescript
import createAdapters from "@authhero/cloudflare-adapter";

// In a Cloudflare Worker
interface Env {
  AUTH_LOGS: AnalyticsEngineDataset;
  CLOUDFLARE_ACCOUNT_ID: string;
  ANALYTICS_ENGINE_API_TOKEN: string;
}

export default {
  async fetch(request: Request, env: Env) {
    const adapters = createAdapters({
      // Custom domains configuration
      zoneId: "your-cloudflare-zone-id",
      authKey: "your-cloudflare-api-key",
      authEmail: "your-cloudflare-email",
      customDomainAdapter: yourDatabaseCustomDomainsAdapter,

      // Analytics Engine logs (low latency writes)
      analyticsEngineLogs: {
        analyticsEngineBinding: env.AUTH_LOGS,
        accountId: env.CLOUDFLARE_ACCOUNT_ID,
        apiToken: env.ANALYTICS_ENGINE_API_TOKEN,
      },
    });

    const { customDomains, cache, geo, logs } = adapters;
    // ...
  },
};
```

### 带有 R2 SQL 日志( HTTP 端点模式)

```typescript
import createAdapters from "@authhero/cloudflare-adapter";

const adapters = createAdapters({
  // Custom domains configuration
  zoneId: "your-cloudflare-zone-id",
  authKey: "your-cloudflare-api-key",
  authEmail: "your-cloudflare-email",
  customDomainAdapter: yourDatabaseCustomDomainsAdapter,

  // Cache configuration (optional)
  cacheName: "default",
  defaultTtlSeconds: 3600,
  keyPrefix: "authhero:",

  // Geo adapter configuration (optional) - automatically included when getHeaders is provided
  getHeaders: () => {
    // In Cloudflare Workers, you'd typically pass request headers
    // Cloudflare automatically adds cf-ipcountry, cf-ipcity, etc.
    return request.headers;
  },

  // R2 SQL logs configuration (optional) - HTTP mode
  r2SqlLogs: {
    pipelineEndpoint: "https://your-stream-id.ingest.cloudflare.com",
    authToken: process.env.R2_SQL_AUTH_TOKEN,
    warehouseName: process.env.R2_WAREHOUSE_NAME,
    namespace: "default",
    tableName: "logs",
  },
});

// Use the adapters
const { customDomains, cache, geo, logs } = adapters;
```

### 服务约束模式( 云旗工人)

```typescript
interface Env {
  PIPELINE_SERVICE: { fetch: typeof fetch };
  R2_SQL_AUTH_TOKEN: string;
  R2_WAREHOUSE_NAME: string;
}

export default {
  async fetch(request: Request, env: Env) {
    const adapters = createAdapters({
      zoneId: "your-cloudflare-zone-id",
      authKey: "your-cloudflare-api-key",
      authEmail: "your-cloudflare-email",
      customDomainAdapter: yourDatabaseCustomDomainsAdapter,

      // Geo adapter - extract location from Cloudflare headers
      getHeaders: () => Object.fromEntries(request.headers),

      // R2 SQL logs with service binding
      r2SqlLogs: {
        pipelineBinding: env.PIPELINE_SERVICE,
        authToken: env.R2_SQL_AUTH_TOKEN,
        warehouseName: env.R2_WAREHOUSE_NAME,
      },
    });

    // Use adapters.logs and adapters.geo
  },
};
```

### 通道模式( 多重目标)

使用核心 `createPassthroughAdapter` 用于同步日志到多个目的地的功能 :

```typescript
import { createPassthroughAdapter } from "@authhero/adapter-interfaces";
import createAdapters, {
  createR2SQLLogsAdapter,
  createAnalyticsEngineLogsAdapter,
} from "@authhero/cloudflare-adapter";

// Primary adapter (e.g., existing database)
const databaseAdapter = createDatabaseLogsAdapter();

// Cloudflare logs adapters for secondary syncing
const r2SqlAdapter = createR2SQLLogsAdapter({
  pipelineEndpoint: "https://your-stream-id.ingest.cloudflare.com",
  authToken: process.env.R2_SQL_AUTH_TOKEN,
  warehouseName: process.env.R2_WAREHOUSE_NAME,
});

const analyticsEngineAdapter = createAnalyticsEngineLogsAdapter({
  analyticsEngineBinding: env.AUTH_LOGS,
  accountId: env.CLOUDFLARE_ACCOUNT_ID,
  apiToken: env.ANALYTICS_ENGINE_API_TOKEN,
});

// Create passthrough adapter - writes to primary and all secondaries
const logsAdapter = createPassthroughAdapter({
  primary: databaseAdapter,
  secondaries: [
    { adapter: { create: r2SqlAdapter.create } },
    { adapter: { create: analyticsEngineAdapter.create } },
  ],
});

// logsAdapter.create() writes to database, R2 SQL Pipeline, and Analytics Engine
// logsAdapter.get() and logsAdapter.list() read from database only
```

## 适配器

### 自定义域适应器

通过 Cloudflare API 管理自定义域.

#### 配置

```typescript
{
  zoneId: string;              // Your Cloudflare zone ID
  authKey: string;             // Your Cloudflare API key
  authEmail: string;           // Your Cloudflare account email
  enterprise?: boolean;        // Whether using Cloudflare Enterprise (default: false)
  customDomainAdapter: CustomDomainsAdapter; // Database adapter for storing domain data
}
```

#### 方法

-   `create(tenantId, domain)` - 创建自定义域
-   `get(tenantId, domainId)` - 获取自定义域
-   `getByDomain(domain)` - 按域名获取域名
-   `list(tenantId, params)` - 列出自定义域
-   `remove(tenantId, domainId)` - 删除自定义域
-   `update(tenantId, domainId, data)` - 更新自定义域

### 缓存适配器

使用 Cloudflare 的缓存 API 提供缓存 .

#### 配置

```typescript
{
  cacheName?: string;          // Cache name (default: "default")
  defaultTtlSeconds?: number;  // Default TTL in seconds (default: undefined)
  keyPrefix?: string;          // Key prefix for namespacing (default: undefined)
}
```

#### 方法

-   `get<T>(key)` - 从缓存中获取值
-   `set<T>(key, value, ttl?)` - 以可选 TTL 设置缓存值
-   `delete(key)` - 从缓存中删除一个值

#### 示例

```typescript
// Set a value with 1 hour TTL
await cache.set("user:123", userData, 3600);

// Get a value
const user = await cache.get("user:123");

// Delete a value
await cache.delete("user:123");
```

### 日志适配器(R2 SQL +管道)

将认证日志写入 Cloudflare R2 使用管道进行摄取,而R2 SQL 用于查询.

#### 建筑

这个适配器使用两个Cloudflare服务:

-   **管道**: 将日志数据输入 R2 的 HTTP 端点
-   **R2小口径**: 用于分析以 Apache Iceberg 格式存储的日志的查询接口

#### 先决条件

1.  **创建 R2 桶并启用 R2 数据目录**数字 :

```bash
npx wrangler r2 bucket create my-authhero-logs
npx wrangler r2 bucket catalog enable my-authhero-logs
```

注意输出中的"Warehouse"名称 - 你以后需要它.

2.  **创建 API 令牌**

在云花板上:

-   转到 R2 对象存储
-   选择“ 管理 API 令牌 ”
-   使用管理员读取和写入权限创建一个令牌
-   保存符号值

3.  **创建计划文件** (单位:千美元)`schema.json`:

```json
{
  "fields": [
    { "name": "tenant_id", "type": "string", "required": true },
    { "name": "type", "type": "string", "required": true },
    { "name": "date", "type": "string", "required": true },
    { "name": "description", "type": "string", "required": false },
    { "name": "ip", "type": "string", "required": false },
    { "name": "user_agent", "type": "string", "required": true },
    { "name": "details", "type": "string", "required": false },
    { "name": "isMobile", "type": "int64", "required": false },
    { "name": "user_id", "type": "string", "required": false },
    { "name": "user_name", "type": "string", "required": false },
    { "name": "connection", "type": "string", "required": false },
    { "name": "connection_id", "type": "string", "required": false },
    { "name": "client_id", "type": "string", "required": false },
    { "name": "client_name", "type": "string", "required": false },
    { "name": "audience", "type": "string", "required": false },
    { "name": "scope", "type": "string", "required": false },
    { "name": "strategy", "type": "string", "required": false },
    { "name": "strategy_type", "type": "string", "required": false },
    { "name": "hostname", "type": "string", "required": false },
    { "name": "auth0_client", "type": "string", "required": false },
    { "name": "log_id", "type": "string", "required": true },
    { "name": "country_code", "type": "string", "required": false },
    { "name": "city_name", "type": "string", "required": false },
    { "name": "latitude", "type": "string", "required": false },
    { "name": "longitude", "type": "string", "required": false },
    { "name": "time_zone", "type": "string", "required": false },
    { "name": "continent_code", "type": "string", "required": false }
  ]
}
```

4.  **创建管道**数字 :

```bash
npx wrangler pipelines setup
```

跟随提示:

-   **管道名称**数字 : `authhero-logs`
-   **启用 HTTP 端点**数字 : `yes`
-   **需要认证**数字 : `no` (或 `yes` 如果您想要额外的安全)
-   **配置自定义 CORS 来源**数字 : `no`
-   **Schema 定义**数字 : `Load from file`
-   **Schema 文件路径**数字 : `schema.json`
-   **目标类型**数字 : `Data Catalog Table`
-   **R2桶名称**数字 : `my-authhero-logs`
-   **命名空间**数字 : `default`
-   **表格名称**数字 : `logs`
-   **目录 API 令牌**: 从第 2 步输入您的令牌
-   **压缩**数字 : `zstd`
-   **大小达到时滚动文件( MB)**数字 : `100`
-   **时间到达时滚动文件( 秒)**数字 : `300` (5分钟)
-   **SQL 转换**数字 : `Use simple ingestion query`

**重要**: 从输出中保存 HTTP 端点 URL(例如, `https://abc123.ingest.cloudflare.com`(中文(简体) )

#### 配置

R2 SQL 日志适配器支持三种使用模式:

##### 1\. HTTP 端点模式(默认)

当从 Cloudflare 工作端外调用管道时使用此模式 :

```typescript
{
  pipelineEndpoint: string;    // Pipeline HTTP endpoint URL for ingesting logs
  authToken: string;           // R2 SQL API token for querying logs
  warehouseName: string;       // R2 warehouse name
  namespace?: string;          // Catalog namespace (default: "default")
  tableName?: string;          // Table name (default: "logs")
  apiBaseUrl?: string;         // R2 SQL API base URL
  timeout?: number;            // HTTP timeout in ms (default: 30000)
}
```

##### 2\. 管道捆绑方式(工人)

在有管道绑定的 Cloudflare Worker 内部运行时使用此模式 。 这是最有效的模式,因为它使用没有HTTP间接费用的直接捆绑.

**{\\fn黑体\\fs22\\bord1\\shad0\\3ahbe\\4ah00\\fscx67\\fscy66\\2chffffff\\3ch808080}我还得说**

```toml
[[pipelines]]
binding = "AUTH_LOGS_STREAM"
pipeline = "your-pipeline-id"
```

**类型脚本 :**

```typescript
interface Env {
  AUTH_LOGS_STREAM: Pipeline;
  R2_SQL_AUTH_TOKEN: string;
  R2_WAREHOUSE_NAME: string;
}

export default {
  async fetch(request: Request, env: Env) {
    const adapters = createAdapters({
      zoneId: "your-zone-id",
      authKey: "your-api-key",
      authEmail: "your-email",
      customDomainAdapter: yourDbAdapter,

      r2SqlLogs: {
        pipelineBinding: env.AUTH_LOGS_STREAM,
        authToken: env.R2_SQL_AUTH_TOKEN,
        warehouseName: env.R2_WAREHOUSE_NAME,
      },
    });

    const { logs } = adapters;
    // Use logs adapter
  },
};
```

管道绑定使用 `.send()` 直接数据摄入方法.

##### 3\. 传通模式(刷新另一个适配器)

使用核心 `createPassthroughAdapter` 用于向 R2 SQL 管道和另一个日志适配器发送日志的功能 :

```typescript
import { createPassthroughAdapter } from "@authhero/adapter-interfaces";
import { createR2SQLLogsAdapter } from "@authhero/cloudflare-adapter";

// Primary adapter (e.g., existing database)
const databaseAdapter = createDatabaseLogsAdapter();

// R2 SQL Pipeline adapter
const r2SqlAdapter = createR2SQLLogsAdapter({
  pipelineEndpoint: "https://your-stream-id.ingest.cloudflare.com",
  authToken: env.R2_SQL_AUTH_TOKEN,
  warehouseName: env.R2_WAREHOUSE_NAME,
});

// Create passthrough adapter
const logsAdapter = createPassthroughAdapter({
  primary: databaseAdapter,
  secondaries: [
    {
      adapter: { create: r2SqlAdapter.create },
      onError: (err) => console.error("R2 SQL sync failed:", err),
    },
  ],
});
```

通过模式 :

-   `create()` 首先给主适配器打电话,然后在背景中发送到第二位
-   `get()` 财务报告和审定财务报表 `list()` 仅从主适配器读取
-   二次错误被记录,但不要失败操作

#### 方法

-   `create(tenantId, log)` - 创建日志条目(发送到管道)
-   `get(tenantId, logId)` - 获取日志条目(取出R2 SQL)
-   `list(tenantId, params)` - 有页码和过滤的列表日志(取出R2 SQL)

#### 示例

```typescript
// Create a log
const log = await logs.create("tenant-123", {
  type: "s", // Successful login
  date: new Date().toISOString(),
  ip: "192.168.1.100",
  user_agent: "Mozilla/5.0...",
  isMobile: false,
  user_id: "user-456",
  client_id: "app-789",
  description: "User logged in successfully",
});

// List logs with filtering
const result = await logs.list("tenant-123", {
  page: 0,
  per_page: 50,
  include_totals: true,
  sort: {
    sort_by: "date",
    sort_order: "desc",
  },
  q: "user_id:user-456", // Lucene-style filter
});
```

#### 用 R2 SQL 查询日志

您可以直接使用 Wrangler CLI 查询日志 :

```bash
# Set up authentication
export WRANGLER_R2_SQL_AUTH_TOKEN=your_api_token

# Query logs
npx wrangler r2 sql query "your_warehouse" "
  SELECT * FROM default.logs
  WHERE tenant_id = 'tenant-123'
  AND type = 's'
  ORDER BY date DESC
  LIMIT 100
"

# Count logs by type
npx wrangler r2 sql query "your_warehouse" "
  SELECT type, COUNT(*) as count
  FROM default.logs
  WHERE tenant_id = 'tenant-123'
  GROUP BY type
"
```

### 日志适配器( 分析引擎)

将认证日志写入 Cloudflare 工人分析引擎,用于低纬度写作和基于 SQL 的查询.

#### 建筑

这个适配器使用 Cloudflare 的工人分析引擎:

-   **写入**: 使用 Fire- and-forget 写入 `writeDataPoint()` (无 HTTP 间隔)
-   **查询**: SQL API 用于分析存储在分析引擎中的日志

#### 何时使用分析引擎对 R2 SQL

| 特性 | 分析引擎 | R2 SQL + 管道 |
| --- | --- | --- |
| 写入空闲度 | ~0ms (火与遗忘) : | ~50-100毫米(HTTP) |
| 数据保留 | 90天(免费),可配置 | 无限制 |
| 查询语言 | SQL (类似 ClickHouse) | SQL (冰堡) (英语) |
| 最佳服务 | 实时分析, 最近的日志 | 长期储存、遵守 |
| 定价 | 可自由分级 | 每个存储+查询付费 |

#### 先决条件

1.  **创建分析引擎数据集**数字 :

配置在 `wrangler.toml`数字 :

```toml
[[analytics_engine_datasets]]
binding = "AUTH_LOGS"
dataset = "authhero_logs"
```

2.  **创建 API 键**数字 :

创建 Cloudflare API 令牌 `Account Analytics: Read` 允许查询日志.

#### 配置

```typescript
interface AnalyticsEngineLogsAdapterConfig {
  // Analytics Engine dataset binding (for Workers)
  analyticsEngineBinding?: AnalyticsEngineDataset;

  // Cloudflare account ID (required for SQL queries)
  accountId: string;

  // API token with Analytics Engine read permission
  apiToken: string;

  // Dataset name (default: "authhero_logs")
  dataset?: string;

  // HTTP timeout in ms (default: 30000)
  timeout?: number;
}
```

#### 使用量

```typescript
import createAdapters, {
  createAnalyticsEngineLogsAdapter,
} from "@authhero/cloudflare-adapter";

// Option 1: Use via createAdapters
const adapters = createAdapters({
  zoneId: "your-zone-id",
  authKey: "your-api-key",
  authEmail: "your-email",
  customDomainAdapter: yourDbAdapter,

  analyticsEngineLogs: {
    analyticsEngineBinding: env.AUTH_LOGS,
    accountId: env.CLOUDFLARE_ACCOUNT_ID,
    apiToken: env.ANALYTICS_ENGINE_API_TOKEN,
    dataset: "authhero_logs",
  },
});

// Option 2: Use adapter directly
const logsAdapter = createAnalyticsEngineLogsAdapter({
  analyticsEngineBinding: env.AUTH_LOGS,
  accountId: env.CLOUDFLARE_ACCOUNT_ID,
  apiToken: env.ANALYTICS_ENGINE_API_TOKEN,
});
```

#### 工人示例

```typescript
interface Env {
  AUTH_LOGS: AnalyticsEngineDataset;
  CLOUDFLARE_ACCOUNT_ID: string;
  ANALYTICS_ENGINE_API_TOKEN: string;
}

export default {
  async fetch(request: Request, env: Env) {
    const { logs } = createAdapters({
      // ... other config
      analyticsEngineLogs: {
        analyticsEngineBinding: env.AUTH_LOGS,
        accountId: env.CLOUDFLARE_ACCOUNT_ID,
        apiToken: env.ANALYTICS_ENGINE_API_TOKEN,
      },
    });

    // Write a log (fire-and-forget, no await needed)
    await logs.create("tenant-123", {
      type: "s",
      date: new Date().toISOString(),
      ip: request.headers.get("cf-connecting-ip") || "",
      user_agent: request.headers.get("user-agent") || "",
      isMobile: false,
      user_id: "user-456",
      description: "User logged in",
    });

    // Query logs
    const recentLogs = await logs.list("tenant-123", {
      per_page: 50,
      q: "type:s",
    });

    return new Response("OK");
  },
};
```

#### 通过模式

在使用核心的其他日志适配器旁使用分析引擎 `createPassthroughAdapter` 工具栏 :

```typescript
import { createPassthroughAdapter } from "@authhero/adapter-interfaces";
import { createAnalyticsEngineLogsAdapter } from "@authhero/cloudflare-adapter";

// Primary adapter (e.g., database)
const databaseAdapter = createDatabaseLogsAdapter();

// Analytics Engine adapter for write syncing
const analyticsEngineAdapter = createAnalyticsEngineLogsAdapter({
  analyticsEngineBinding: env.AUTH_LOGS,
  accountId: env.CLOUDFLARE_ACCOUNT_ID,
  apiToken: env.ANALYTICS_ENGINE_API_TOKEN,
});

// Create passthrough adapter - writes to both, reads from primary
const logsAdapter = createPassthroughAdapter({
  primary: databaseAdapter,
  secondaries: [
    {
      adapter: { create: analyticsEngineAdapter.create },
      onError: (err) => console.error("Analytics sync failed:", err),
    },
  ],
});

// logs.create() writes to both adapters (database first, then Analytics Engine)
// logs.get() and logs.list() read from database only
```

#### 数据Schema

分析引擎存储日志, 使用blob和双字段 :

| 外地 | 类型 | 说明 |
| --- | --- | --- |
| 块1 | 字符串 | 日志( d) |
| blob2 (中文(简体) ) | 字符串 | 租户( d) |
| blob3 (中文(简体) ) | 字符串 | 类型(例如“s”、“f”) |
| blob4 (中文(简体) ) | 字符串 | 日期( ISO 字符串) |
| blob5 (中文(简体) ) | 字符串 | 说明 |
| 块6 | 字符串 | 页:1 |
| blob7 (中文(简体) ) | 字符串 | 用户代理( a) |
| blob8-18 (中文(简体) ) | 字符串 | 用户 id,连接,客户端 id等. |
| blob19-20 (中文(简体) ) | 字符串 | JSON 字符串( 细节、 认证 客户端) |
| 双倍1 | 编号 | 移动 (0 或 1) |
| 双倍2 | 编号 | 时间戳( 时间戳) |
| 指数1 | 字符串 | 租户 id(用于高效过滤) |

#### 使用 SQL API 查询

```bash
# List recent logs for a tenant
curl "https://api.cloudflare.com/client/v4/accounts/{account_id}/analytics_engine/sql" \
  -H "Authorization: Bearer $API_TOKEN" \
  -d "SELECT * FROM authhero_logs WHERE index1 = 'tenant-123' ORDER BY timestamp DESC LIMIT 50"

# Count logins by type
curl "https://api.cloudflare.com/client/v4/accounts/{account_id}/analytics_engine/sql" \
  -H "Authorization: Bearer $API_TOKEN" \
  -d "SELECT blob3 as type, count() as count FROM authhero_logs WHERE index1 = 'tenant-123' GROUP BY blob3"
```

## 分析引擎)

`createAnalyticsEngineOutboxMetricsSink` 转动authhero的出箱中继参数 进入分析引擎数据点,所以死字母和重试后置是 可查询,而不是只出现在 `console` 输出.

### 设置

1.  添加装订在 `wrangler.toml`数字 :

```toml
[[analytics_engine_datasets]]
binding = "OUTBOX_METRICS"
dataset = "authhero_outbox_metrics"
```

2.  把水槽传到两个接力道上—— `init()` (按请求提供) 财务报告和审定财务报表 `runOutboxRelay()` (牛排排声):

```typescript
import { createAnalyticsEngineOutboxMetricsSink } from "@authhero/cloudflare-adapter";
import { init, runOutboxRelay } from "authhero";

const metrics = createAnalyticsEngineOutboxMetricsSink({
  analyticsEngineBinding: env.OUTBOX_METRICS,
});

const { app } = init({ dataAdapter, outbox: { enabled: true, metrics } });

// in scheduled():
await runOutboxRelay({ dataAdapter, issuer: env.ISSUER, metrics });
```

无 `analyticsEngineBinding` 水槽是无声的,所以同样的接线 从事当地的脱衣和测试工作.

### 数据Schema

| 外地 | 类型 | 说明 |
| --- | --- | --- |
| 块1 | 字符串 | 参数名称 (`outbox_events_processed_total`, (中文(简体) ), `outbox_events_dead_lettered_total`, (中文(简体) ), `outbox_retry_delay_seconds`(中文(简体) ) |
| blob2 (中文(简体) ) | 字符串 | 租户( d) |
| blob3 (中文(简体) ) | 字符串 | 事件类型 |
| blob4 (中文(简体) ) | 字符串 | 来源(`request` 或者说 `cron`(中文(简体) ) |
| blob5 (中文(简体) ) | 字符串 | 目的地(仅交付失败) |
| 块6 | 字符串 | 出错( 仅发送失败) |
| 双倍1 | 编号 | 价值 - `1` 计数器,以秒计重试延迟 `outbox_retry_delay_seconds` |
| 双倍2 | 编号 | 重新计算事件( c) |
| 双相 3 | 编号 | 时间戳( 时间戳) |
| 指数1 | 字符串 | 租户 id(用于高效过滤) |

分析引擎样本排在负载下,所以重量计数器由 `_sample_interval` 获取真实总数:

```bash
# Dead-lettered events per tenant
curl "https://api.cloudflare.com/client/v4/accounts/{account_id}/analytics_engine/sql" \
  -H "Authorization: Bearer $API_TOKEN" \
  -d "SELECT index1 as tenant, sum(double1 * _sample_interval) as dead_lettered FROM authhero_outbox_metrics WHERE blob1 = 'outbox_events_dead_lettered_total' GROUP BY index1"
```

## 地理适应器

Cloudflare Geo适配器从Cloudflare的自动请求头取出地理位置信息. 这用于用位置数据来丰富认证日志.

### 特征

-   **零纬度**: Cloudflare 已经提供的用户头
-   **没有 API 呼叫**: 不需要外部服务或数据库
-   **重大退化**: 仅具有国家代码或完整位置数据的工作
-   **免费**: Cloudflare自由计划中所有可用的功能

### Cloudflare 设置

地理适配器需要启用特定的 Cloudflare 设置:

#### 1\. 启用 IP 地理定位(需要)

这提供了 `cf-ipcountry` 上面只有国家代码.

1.  去你的云花仪表板
2.  导航到 **网络** 设置
3.  启用 **IP 地理位置**

#### 2\. 启用“ 添加访问者位置头” 管理变换( 建议)

这提供了包括城市,坐标,时区等全部位置数据.

1.  去你的云花仪表板
2.  导航到 **规则** > 翻译: **转换规则** > 翻译: **管理变换**
3.  启用 **添加访客位置头**

这是一个 **自由特性** 所有Cloudflare计划中都有.

### 配置

地球适配器在创建Cloudflare适配器时自动包含. 信头按请求时间通过 `getGeoInfo(headers)`数字 :

```typescript
import createAdapters from "@authhero/cloudflare-adapter";

// Create adapters once at startup
const adapters = createAdapters({
  // ... other config
});

// The geo adapter is always available
// Headers are passed when calling getGeoInfo
const headers = Object.fromEntries(request.headers);
const geoInfo = await adapters.geo.getGeoInfo(headers);
```

与 AuthHero 一起使用时,会自动从日志帮助器中的Hono上下文中取出标题.

### 已使用云旗信头

| 页眉 | 说明 | 示例 | 可用性 |
| --- | --- | --- | --- |
| `cf-ipcountry` | 2个字母的ISO国家代码 | `US` | 总是( 启用 IP 地理定位) |
| `cf-ipcity` | 城市名称 | `San Francisco` | 与管理变革 |
| `cf-iplatitude` | 纬度坐标 | `37.7749` | 与管理变革 |
| `cf-iplongitude` | 经度坐标 | `-122.4194` | 与管理变革 |
| `cf-timezone` | IANA 时区标识符 | `America/Los_Angeles` | 与管理变革 |
| `cf-ipcontinent` | 2个字母的大陆代码 | `NA` | 与管理变革 |

可用管理变换( 目前没有绘制) 的附加信头 :

-   `cf-region`区域名称
-   `cf-region-code`: 区域代码
-   `cf-metro-code`: 地铁代码
-   `cf-postal-code`: 邮政编码

### 响应格式

```typescript
interface GeoInfo {
  country_code: string; // "US" - always available
  city_name: string; // "San Francisco" or "" if not available
  latitude: string; // "37.7749" or "" if not available
  longitude: string; // "-122.4194" or "" if not available
  time_zone: string; // "America/Los_Angeles" or "" if not available
  continent_code: string; // "NA" or "" if not available
}
```

**只启用 IP 地理定位 :**

```json
{
  "country_code": "US",
  "city_name": "",
  "latitude": "",
  "longitude": "",
  "time_zone": "",
  "continent_code": ""
}
```

**启用“ 添加访问者位置头” 管理变换:**

```json
{
  "country_code": "US",
  "city_name": "San Francisco",
  "latitude": "37.7749",
  "longitude": "-122.4194",
  "time_zone": "America/Los_Angeles",
  "continent_code": "NA"
}
```

### 与 Auth Hero 合并

在 AuthHero 中配置时,地理适配器会自动丰富认证日志. 日志助手会自动从 Hono 上下文中提取标题 :

```typescript
import createAdapters from "@authhero/cloudflare-adapter";

const cloudflareAdapters = createAdapters({
  // ... other config
});

const dataAdapter = {
  ...yourDatabaseAdapter,
  geo: cloudflareAdapters.geo, // Add geo adapter
};
```

日志将自动包含 `location_info`数字 :

```json
{
  "type": "s",
  "date": "2025-11-28T12:00:00.000Z",
  "location_info": {
    "country_code": "US",
    "city_name": "San Francisco",
    "latitude": "37.7749",
    "longitude": "-122.4194",
    "time_zone": "America/Los_Angeles",
    "continent_code": "NA"
  }
}
```

### 替代: IP 地理位置数据库

如果您没有使用 Cloudflare 或者需要更详细的位置数据, 您可以执行自定义 `GeoAdapter` 使用 MaxMind GeoIP 等 IP 地理定位数据库 :

```typescript
import maxmind from "maxmind";
import { GeoAdapter, GeoInfo } from "@authhero/adapter-interfaces";

class MaxMindGeoAdapter implements GeoAdapter {
  private reader: maxmind.Reader<maxmind.CityResponse>;

  private constructor(reader: maxmind.Reader<maxmind.CityResponse>) {
    this.reader = reader;
  }

  static async create(databasePath: string): Promise<MaxMindGeoAdapter> {
    const reader = await maxmind.open<maxmind.CityResponse>(databasePath);
    return new MaxMindGeoAdapter(reader);
  }

  async getGeoInfo(headers: Record<string, string>): Promise<GeoInfo | null> {
    // Extract IP from headers (e.g., x-forwarded-for, cf-connecting-ip)
    const ip =
      headers["cf-connecting-ip"] ||
      headers["x-forwarded-for"]?.split(",")[0]?.trim() ||
      headers["x-real-ip"];

    if (!ip) return null;

    const lookup = this.reader.get(ip);

    if (!lookup) return null;

    return {
      country_code: lookup.country?.iso_code || "",
      city_name: lookup.city?.names?.en || "",
      latitude: lookup.location?.latitude?.toString() || "",
      longitude: lookup.location?.longitude?.toString() || "",
      time_zone: lookup.location?.time_zone || "",
      continent_code: lookup.continent?.code || "",
    };
  }
}

// Usage:
const geoAdapter = await MaxMindGeoAdapter.create(
  "/path/to/GeoLite2-City.mmdb",
);
```

**对知识产权数据库的考虑**数字 :

-   需要数据库下载和定期更新
-   检查的额外延迟(一般为1至5米)
-   可能需要许可证费
-   在任何环境中工作,而不仅仅是边缘平台

## 环境变量

建议的环境变量:

```env
# Custom Domains
CLOUDFLARE_ZONE_ID=your_zone_id
CLOUDFLARE_AUTH_KEY=your_api_key
CLOUDFLARE_AUTH_EMAIL=your_email

# R2 SQL Logs (optional)
PIPELINE_ENDPOINT=https://your-stream-id.ingest.cloudflare.com
R2_SQL_AUTH_TOKEN=your_r2_sql_token
R2_WAREHOUSE_NAME=your_warehouse_name
```

## 类型脚本

该包包括了TypeScript定义. 视需要导入类型:

```typescript
import type {
  CloudflareConfig,
  CloudflareAdapters,
  R2SQLLogsAdapterConfig,
} from "@authhero/cloudflare-adapter";
```

## 代码执行器

对于用户授权的代码钩(Actions), `@authhero/cloudflare-adapter` 2个代码执行器 根据您想要如何部署用户代码选择一个 :

### `WorkerLoaderCodeExecutor` ——有活力劳动者

根据需要从内幕密码中抽出孤立的工人 [装入器装订](https://developers.cloudflare.com/dynamic-workers/)。没有单独的部署步骤——存储在您 `actions`编号 :`hookCode` 表格被装入每个钩子引用的新鲜隔离(带有以散列为基础的缓存,因此同一代码保持温暖).

```typescript
import { WorkerLoaderCodeExecutor } from "@authhero/cloudflare-adapter";
import { init } from "authhero";

// Requires a worker_loaders binding in wrangler.toml:
// [[worker_loaders]]
// binding = "LOADER"

const codeExecutor = new WorkerLoaderCodeExecutor({
  loader: env.LOADER,
});

const { app } = init({ dataAdapter, codeExecutor });
```

### `DispatchNamespaceCodeExecutor` - 平台工人组织

Invokes 已作为单独脚本预先部署的用户员工 [发送命名空间](https://developers.cloudflare.com/cloudflare-for-platforms/workers-for-platforms/)执行者 `deploy()` 方法通过 Cloudflare API 上传脚本; `execute()` 通过命名空间绑定来引用它.

```typescript
import { DispatchNamespaceCodeExecutor } from "@authhero/cloudflare-adapter";
import { init } from "authhero";

// Requires a dispatch_namespaces binding in wrangler.toml:
// [[dispatch_namespaces]]
// binding = "DISPATCHER"
// namespace = "authhero-hooks"

const codeExecutor = new DispatchNamespaceCodeExecutor({
  accountId: env.CF_ACCOUNT_ID,
  apiToken: env.CF_API_TOKEN,
  dispatchNamespace: "authhero-hooks",
  dispatcher: env.DISPATCHER,
});

const { app } = init({ dataAdapter, codeExecutor });
```

> 以前导出为 `CloudflareCodeExecutor` 从 `authhero` 财务报告和审定财务报表 `@authhero/cloudflare-adapter` 软件包。 该名称仍作为被贬值的别名被再出口 `DispatchNamespaceCodeExecutor` 将在下一个大赛中被淘汰.

见 [钩子指南](https://www.authhero.net/features/hooks#code-executors) 详细情况.

## 相关文档

-   [云花板 API](https://developers.cloudflare.com/api/)
-   [Cloudflare 缓存 API](https://developers.cloudflare.com/workers/runtime-apis/cache/)
-   [云纹R2 SQL](https://developers.cloudflare.com/r2-sql/)
-   [R2 数据目录](https://developers.cloudflare.com/r2/data-catalog/)

## 许可证

麻省理工学院

## 常见问题

### @authhero/cloudflare-adapter 是什么？

为AuthHero提供Cloudflare专用适配器,提供与Cloudflare服务的集成\.

### 如何安装 @authhero/cloudflare-adapter？

在项目目录运行 npm install @authhero/cloudflare-adapter，或使用 pnpm add / yarn add / bun add。它还需要宿主项目提供 peer 依赖：hono（^4\.12\.30）、@hono/zod-openapi（^1\.4\.0）。npm 7 及以上与 pnpm 8 及以上默认会自动安装必需的 peer 依赖，Yarn 需要手动安装。

### @authhero/cloudflare-adapter 支持 TypeScript 吗？

支持。包内自带 TypeScript 类型声明（package\.json 声明了 types 入口），安装后即可获得类型提示，无需再安装 @types 包。

### @authhero/cloudflare-adapter 支持 ESM 还是 CommonJS？

同时提供 ES Module 与 CommonJS 入口，import 和 require\(\) 都可以使用，打包工具会自动选择合适的版本。

### @authhero/cloudflare-adapter 可以商用吗？

@authhero/cloudflare-adapter 使用 AGPL-3\.0-only 许可。最严格的 Copyleft：除分发外，通过网络向用户提供基于它的服务也需要开源对应源码。闭源 SaaS 使用前务必评估。以上为概括，不构成法律意见，请以许可原文为准。

### @authhero/cloudflare-adapter 的下载量有多少？

根据 npm 官方下载统计，@authhero/cloudflare-adapter 在 2026/10/07 – 2026/11/05 这 30 天内被下载 1,716 次，日均约 57 次。下载量会把 CI 构建、镜像同步和作为其他包依赖的安装一并计入，适合横向比较热度，不能直接等同于使用人数。

### @authhero/cloudflare-adapter 还在维护吗？

最新版本 4\.2\.0 发布于 2026/11/05（4 天前）；至今共发布 255 个版本，其中过去 12 个月发布了 204 个；首次发布于 2025/03/04。发布频率只是参考信号：功能稳定的工具可能很少需要更新，建议结合源码仓库的 issue 和提交记录判断。

