# 使用说明

## 更新记录

- 2026-07-16: 创建 Base44、Go Auth 和受控 CRUD 使用说明。
- 2026-07-16: 补充安装引入、结构化错误和 AI 扩展入口。
- 2026-07-16: 增加 Base44 访问状态、访问申请和服务端 assertion 装配说明。
- 2026-07-16: 补充 core 用户解析复用和同步/异步 Base44 Client Factory 约束。

## 安装与引入

```bat
npm install node_facade_base44_supabase
```

```js
const NodeFacadeBase44Supabase = require('node_facade_base44_supabase');
```

## 1. 包的职责

`node_facade_base44_supabase` 是 Base44 与 `go_qoder_file` 之间的门面层：

- Go 地址由 Base44 后台函数环境变量保存；
- Base44 前端只调用后台函数，不直接访问 Supabase；
- 顶层 `AccessToken` 仅用于生成 Go `Authorization: Bearer ...` 请求头，不进入 Go 业务 JSON；
- 顶层 `IdempotencyKey` 仅用于平台和管理写接口，不进入 Go 业务 JSON；
- Base44 访问申请的 `IdempotencyKey` 也只转换为 Header，不进入 Go 业务 JSON；
- 三个 Base44 用户端接口由 Backend Function 读取 `auth.me()` 并即时签发 RS256 assertion；
- 通用 CRUD 只支持 `select / insert / update / upsert`，不提供 hard delete；
- `/ts/xdata` 不在白名单中。

## 2. Base44 后台函数

在后台函数中创建一次 Handler。`CreateBase44ClientFromRequest` 应使用 Base44 官方服务端 SDK，从原始请求验证 Base44 用户。

Handler 通过 core `ResolveBase44User` 调用一次 `auth.me()`；`CreateBase44ClientFromRequest` 可以同步或异步返回 Client，但返回对象必须提供 `auth.me()`。

Base44 托管 Function 的完整装配示例：

```js
import NodeFacadeBase44Supabase from 'npm:node_facade_base44_supabase';
import { createClientFromRequest } from 'npm:@base44/sdk';

function GetRequiredEnv(NameValue) {
  const Value = String(Deno.env.get(NameValue) || '').trim();
  if (!Value) throw new Error(`Missing required environment variable: ${NameValue}`);
  return Value;
}

const Handler = NodeFacadeBase44Supabase.CreateSupabaseFunctionHandler({
  ApiBaseUrl: GetRequiredEnv('TS_GO_API_BASE_URL'),
  CreateBase44ClientFromRequest: createClientFromRequest,
  Base44AssertionAlgorithm: GetRequiredEnv('BASE44_ASSERTION_ALGORITHM'),
  Base44AssertionKeyId: GetRequiredEnv('BASE44_ASSERTION_KEY_ID'),
  Base44AssertionPrivateKeyPem: GetRequiredEnv('BASE44_ASSERTION_PRIVATE_KEY_PEM'),
  Base44AssertionIssuer: GetRequiredEnv('BASE44_ASSERTION_ISSUER'),
  Base44AssertionAudience: GetRequiredEnv('BASE44_ASSERTION_AUDIENCE'),
  Base44ProviderAppId: GetRequiredEnv('BASE44_PROVIDER_APP_ID'),
  Base44AssertionLifetimeSeconds: Number(
    GetRequiredEnv('BASE44_ASSERTION_LIFETIME_SECONDS'),
  ),
});

Deno.serve((RequestValue) => Handler.Handle(RequestValue));
```

`RequireAuth` 必须保持默认 `true`，不要启用 `ForwardUserHeaders`。三个固定路径内部始终执行 Base44 用户认证，即使测试配置误传 `RequireAuth:false` 也不会跳过。

需要配置的 Secret/环境变量：

```text
TS_GO_API_BASE_URL
BASE44_ASSERTION_ALGORITHM=RS256
BASE44_ASSERTION_KEY_ID
BASE44_ASSERTION_PRIVATE_KEY_PEM
BASE44_ASSERTION_ISSUER
BASE44_ASSERTION_AUDIENCE
BASE44_PROVIDER_APP_ID
BASE44_ASSERTION_LIFETIME_SECONDS=45
```

不需要 Supabase URL、`service_role` Key、数据库连接串、Go replay pepper 或访问申请 HMAC pepper。

## 3. Base44 前端客户端

`InvokePayload` 是你对 Base44 后台函数的调用适配器，必须返回后台函数的 JSON。

```js
const Client = NodeFacadeBase44Supabase.CreateSupabaseClient({
  InvokePayload: (PayloadValue) => InvokeBase44Function('supabaseProxy', PayloadValue),
  GetAccessToken: () => sessionStorage.getItem('ts_go_access_token') || '',
});

const LoginResult = await Client.AuthApi.Login({
  AppId: 'role-web',
  IdentityType: 'email',
  Identity: 'admin@example.com',
  Password: 'your-password',
});

const AppAdmin = Client.AdminApi.ForApp('role-web');
const Users = await AppAdmin.Users.List({ Limit: 50 });
```

访问申请流程：

```js
const Status = await Client.AuthApi.GetBase44AccessStatus();

const IdempotencyKey = crypto.randomUUID();
const RequestResult = await Client.AuthApi.CreateBase44AccessRequest(
  { RequestMessage: '申请访问业务后台' },
  { IdempotencyKey },
);

const Session = await Client.AuthApi.ExchangeCurrentBase44User();
```

同一次逻辑提交的网络重试应复用 `IdempotencyKey`；每次调用 Backend Function 都会签发新的 assertion/JTI。旧的 `ExchangeBase44(Assertion)` 仅用于兼容，传入 assertion 会被 Backend Function 忽略并安全重建，新页面不得使用。

不要记录 `PayloadValue.AccessToken`，不要把它写入 URL、查询参数或业务日志。

## 4. 受控 CRUD

```js
const Result = await Client.CrudApi.Select({
  Table: 'branch',
  Filters: [
    { column: 'brand_id', operator: 'eq', value: 'brand-1' },
  ],
  OrderBy: [
    { column: 'name', ascending: true },
  ],
  Limit: 50,
});
```

写入示例：

```js
await Client.CrudApi.Update({
  Table: 'business_table',
  Data: { state: 0 },
  Filters: [
    { column: 'id', operator: 'eq', value: 'row-1' },
  ],
});
```

删除业务数据应按 Go 约定做软删除；本包主动拒绝 `CrudApi.Request('delete', ...)`。

## 5. 幂等键

平台和管理写方法会自动生成 UUID。需要安全重试时，应显式复用同一个键：

```js
await AppAdmin.Roles.Update('role-1', InputValue, {
  IdempotencyKey: ExistingKey,
});
```

## 6. 本地开发

```bat
install.bat
run-test.bat
```

`install.bat` 会优先安装相邻目录的 `node_lib_base44_core`，方便 npm 发布前联调。

## 7. 错误处理

```js
try {
  await Client.PlatformApi.UpdateApp(AppId, InputValue);
} catch (ErrorValue) {
  console.error(
    ErrorValue.Code,
    ErrorValue.StatusCode,
    ErrorValue.RequestId,
    ErrorValue.RetryAllowedAt,
  );
}
```

Go 非 2xx JSON 的 `code`、HTTP 状态、`request_id` 和允许公开的 `retry_allowed_at` 会转换为 `SupabaseFacadeError`。SQLSTATE、SQL 文本、内部堆栈和其他未知字段不会传给前端。

## 8. assertion 与最终 Go 请求

脱敏后的 assertion 结构：

```json
{
  "header": { "alg": "RS256", "kid": "base44-key-2026-01", "typ": "JWT" },
  "claims": {
    "iss": "configured-base44-issuer",
    "aud": "configured-go-audience",
    "provider_app_id": "base44-app-id",
    "sub": "base44-user-id",
    "email": "verified@example.com",
    "email_verified": true,
    "disabled": false,
    "iat": 1760000000,
    "exp": 1760000045,
    "jti": "redacted"
  }
}
```

最终转发给 Go 的 Body 固定为：

```json
{ "assertion": "<server-signed-jwt>" }
```

```json
{
  "assertion": "<server-signed-jwt>",
  "request_message": "申请访问业务后台"
}
```

```json
{ "assertion": "<server-signed-jwt>" }
```

依次对应 `/base44/access-status`、`/base44/access-requests` 和 `/base44/exchange`。只有创建申请发送 `Idempotency-Key`；三个接口都不发送 Go `Authorization`。

扩展接口前必须阅读 [AI 快速开发指南](fun/ai_development.md)，并同时核对 Go 路由和 React 实际调用。

发布时双击 `publish-npm.bat`，`NPM_TOKEN` 必须存在于当前进程或 Windows 用户环境变量中；脚本会静默读取用户级变量，并在失败或完成后暂停。自动化调用可设置 `PUBLISH_NO_PAUSE=1`；只做预检可设置 `PUBLISH_PREFLIGHT_ONLY=1`。

首次发布后的 registry 验证会每 5 秒重试，最多 6 次。已经显示 `npm publish succeeded` 时，即使验证暂时超时也不要重复发布相同版本。
