Base Account / Integration

账号系统接入文档

按能力模块拆分的接入规范,供产品工程和 AI Agent 按需获取。

Issuerhttps://user.stringzhao.lifeAudiencebase-account-clientJWKShttps://user.stringzhao.life/.well-known/jwks.jsonDocv2026-03-06.3

数据统计接入

复制统计接入文档

基于「自托管 Umami + @stringzhao/analytics-sdk」,为下游应用提供 PV/UV 与自定义事件统计。 装一个包、配两个环境变量、加一行 <Analytics /> 即可接入,服务端事件容错不阻塞主流程。

概述

统计接入基于「自托管 Umami + @stringzhao/analytics-sdk」:

- 浏览器端:<Analytics/> 自动采集 PV/UV,track() 上报自定义事件
- 服务端:trackServerEvent() 上报转化事件(登录/注册等),容错不阻塞主流程
- 数据落自托管 Umami(独立 database,与业务库物理隔离)

下游应用只需:装一个包 + 配环境变量 + 加一行 <Analytics/>。

安装

```bash
npm install @stringzhao/analytics-sdk
```

peerDependencies: next >= 15, react >= 19(未安装时组件渲染 null,不影响应用运行)。

环境变量

浏览器端(会暴露到客户端,用于 script 注入):
```
NEXT_PUBLIC_UMAMI_HOST=<Umami 根域名,如 https://umami.stringzhao.life>
NEXT_PUBLIC_UMAMI_WEBSITE_ID=<Umami 后台 Add Website 拿到的 Website ID>
```

服务端(不暴露到客户端,trackServerEvent 用):
```
UMAMI_HOST=<Umami 域名>/api/send
UMAMI_WEBSITE_ID=<同上 Website ID>
```

注意:
- NEXT_PUBLIC_UMAMI_HOST 必须是根域名(script 注入为 ${host}/script.js)
- UMAMI_HOST 必须含 /api/send(@umami/node 的 hostUrl 约定)
- 同一站点两个 WEBSITE_ID 通常相同;多站点各自配各自的 ID

浏览器接入(PV/UV + 自定义事件)

在根 layout 注入组件:
```tsx
// app/layout.tsx
import { Analytics } from "@stringzhao/analytics-sdk";

export default function RootLayout({ children }) {
  return (
    <html>
      <body>
        {children}
        {/* 放 <body> 内任意位置;未配置 env 时渲染 null */}
        <Analytics />
      </body>
    </html>
  );
}
```

<Analytics/> 通过 next/script(strategy afterInteractive)注入 Umami 脚本,PV/UV 自动采集。

自定义事件(SSR 安全:服务端调用 no-op):
```ts
import { track } from "@stringzhao/analytics-sdk";

track("signup_click", { source: "hero" });
```

服务端事件(容错,永不阻塞主流程)

```ts
import { trackServerEvent } from "@stringzhao/analytics-sdk";

// 在登录/注册等转化成功后上报
await trackServerEvent("register_success", { via_linked_email: 0 });
```

trackServerEvent 内部委托官方 @umami/node 客户端,catch 所有错误
(网络/配置/客户端 reject),Promise 永远 resolve,
绝不影响调用方主流程——即使 Umami 不可达,登录/注册仍照常成功。

验证

- 浏览器 DevTools Network:应见对 <umami 域名>/api/send 的 POST,以及 script.js 已加载(<script data-website-id="...">)
- Umami 后台 Events:触发登录/注册后,应出现 login_success / register_success 等事件
- 多站点隔离:不同应用配不同 Website ID,事件只计入对应站点

CLI 自助管理 Website(list + create)

不登 Umami 后台,用 ba CLI 查询/建站:

```bash
ba admin umami list                          # 查询所有 website(websiteId 即填入 NEXT_PUBLIC_UMAMI_WEBSITE_ID)
ba admin umami create --name X --domain Y    # 建站(幂等:已存在返回 existing)
```

返回示例(create):
```json
{ "website": { "websiteId": "c4a8...", "name": "ai-todo", "domain": "ai-todo.stringzhao.life", "existed": false } }
```

建站走 umami 官方 REST API(POST /api/websites),不直接写 umami DB。

前置条件:
- admin 鉴权:BA_API_KEY(admin API key)或 ba login 的账号在 ADMIN_EMAILS 中
- list 需 UMAMI_DATABASE_URL(Umami 独立 database 只读连接串)
- create 需 UMAMI_ADMIN_USERNAME / UMAMI_ADMIN_PASSWORD + NEXT_PUBLIC_UMAMI_HOST(umami 根域名)
- 缺失/异常 → 503 umami_db_unavailable(list)/ umami_api_unavailable(create),不泄露内部细节

完整部署指南

本章节是面向下游应用接入者的精简版。

完整部署指南见仓库文档(覆盖:Umami 自托管到 Vercel、独立 database 与业务库隔离、
故障降级预案、auth-service 自身接入范例):

```
docs/analytics.md
```