8000 feat(server): 内置 GitHub 登录 by InfinityPacer · Pull Request #23 · InfinityPacer/meterleaf · GitHub
[go: up one dir, main page]

Skip to content
8000
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,14 @@ METERLEAF_USD_BASIS=subscription
# 本机采集器写入密钥:来源标识:SHA-256 摘要,多台电脑用逗号分隔;留空不开放写入接口。
# METERLEAF_INGEST_KEYS=claude-code-macbook:0000000000000000000000000000000000000000000000000000000000000000

# GitHub 登录:四项都填写时开启,只允许名单内的 GitHub 账号访问;回调地址为 METERLEAF_PUBLIC_URL/auth/github/callback。
# METERLEAF_GITHUB_CLIENT_ID=
# METERLEAF_GITHUB_CLIENT_SECRET=
# METERLEAF_GITHUB_USERS=your-github-login
# METERLEAF_PUBLIC_URL=https://meterleaf.example.com
# 登录有效天数,使用中自动顺延,范围 1 至 365。
# METERLEAF_SESSION_DAYS=30

# 演示模式必须显式开启,不连接真实数据源。
METERLEAF_DEMO=false

Expand Down
2 changes: 1 addition & 1 deletion AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,6 @@ Meterleaf 是独立 AI 用量账本,只读采集上游用量并在本地计价

## 配置与协作边界

- 当前按 `.env.example` 通过环境变量配置来源,可连接一个 Sub2API 实例、通过 `METERLEAF_INGEST_KEYS` 接收本机采集器推送,两者至少一项;来源管理、内置认证和用户角色系统不在当前产品范围。
- 当前按 `.env.example` 通过环境变量配置来源,可连接一个 Sub2API 实例、通过 `METERLEAF_INGEST_KEYS` 接收本机采集器推送,两者至少一项;可选的 GitHub 登录按允许名单放行账号;来源管理和用户角色系统不在当前产品范围。
- 项目采用 Apache-2.0。凭据、真实账户数据、运行数据库、机器路径及构建产物不得进入公开交付内容;真实数据失败不得静默替换为演示数据。
- 工作台不属于产品交付内容。排查报表或同步失败先区分读取和采集阶段;清库、修改同步设置不作为默认恢复动作。
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@

## [未发布]

- 新增内置 GitHub 登录。填写 OAuth 应用和允许的 GitHub 用户名后,页面和读取接口只对这些账号开放,不再需要在反向代理前另配认证服务。登录默认保持 30 天,使用中自动顺延,采集器写入、健康检查和安装用的静态文件不受影响。配置见[部署指南](docs/deployment.md#github-登录)。
- 开启 GitHub 登录后,「关于」页显示当前登录的账号,并提供退出登录入口。

## [0.6.4] - 2026-09-30

- 大幅减少空闲时的磁盘写入:报表缓存、账户与额度记录以及同步状态只在内容变化时写入。
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ docker compose up -d --pull never

访问 `http://127.0.0.1:4318`。连接了 Sub2API 时,从右上角「数据同步」启动首次采集,默认不自动采集,可自行开启自动同步。只用 Claude Code 时没有这个入口,采集器推送后数据自动出现。

已发布镜像的拉取方式、跨主机访问、反向代理、配置项及备份恢复见[部署指南](docs/deployment.md)。应用不内置认证,可在反向代理层接入 OAuth/OIDC 认证。
已发布镜像的拉取方式、跨主机访问、反向代理、配置项及备份恢复见[部署指南](docs/deployment.md)。对外开放前可开启 [GitHub 登录](docs/deployment.md#github-登录),只允许指定账号访问。

### 接入 Claude Code

Expand Down
5 changes: 5 additions & 0 deletions compose.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,11 @@ services:
METERLEAF_USD_BASIS: ${METERLEAF_USD_BASIS:-subscription}
METERLEAF_PRICE_BOOK: ${METERLEAF_PRICE_BOOK:-}
METERLEAF_INGEST_KEYS: ${METERLEAF_INGEST_KEYS:-}
METERLEAF_GITHUB_CLIENT_ID: ${METERLEAF_GITHUB_CLIENT_ID:-}
METERLEAF_GITHUB_CLIENT_SECRET: ${METERLEAF_GITHUB_CLIENT_SECRET:-}
METERLEAF_GITHUB_USERS: ${METERLEAF_GITHUB_USERS:-}
METERLEAF_PUBLIC_URL: ${METERLEAF_PUBLIC_URL:-}
METERLEAF_SESSION_DAYS: ${METERLEAF_SESSION_DAYS:-30}
METERLEAF_DATA_DIR: /app/app_data
METERLEAF_HOST: 0.0.0.0
METERLEAF_PORT: 4318
Expand Down
36 changes: 34 additions & 2 deletions docs/deployment.md
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ docker compose up -d --no-build --pull never

生产构建包含 PWA 清单、图标和离线页,浏览器可将应用安装为独立窗口,无需额外安装插件。首页在脚本加载期间显示启动提示;构建会为文本静态资源生成 Brotli/Gzip 变体,服务器按浏览器能力返回。除本机地址外,需要 HTTPS;反向代理须允许访问 `/manifest.webmanifest`、`/sw.js`、`/offline.html` 和 `/icons/*`。离线资源不缓存 API 或账户数据,断网后重新打开不会展示旧账本。

随附 Compose 仅绑定宿主机回环地址。如果反向代理在其他容器中,应加入共享 Docker 网络并访问 `meterleaf:4318`,或显式调整宿主机绑定地址。应用不内置认证,可在代理层接入 OAuth/OIDC 认证。
随附 Compose 仅绑定宿主机回环地址。如果反向代理在其他容器中,应加入共享 Docker 网络并访问 `meterleaf:4318`,或显式调整宿主机绑定地址。对外开放前按[GitHub 登录](#github-登录)开启登录保护。

## 配置

Expand All @@ -63,6 +63,7 @@ docker compose up -d --no-build --pull never
| `METERLEAF_PRICE_BOOK` | 内置价格表 | 自定义完整价格 JSON 路径 |
| `METERLEAF_LOG_LEVEL` | `info` | `debug`、`info`、`warn`、`error`、`silent` |
| `METERLEAF_INGEST_KEYS` | 未启用 | 本机采集器写入密钥,见下文 |
| `METERLEAF_GITHUB_CLIENT_ID` 等 | 未启用 | GitHub 登录,见下文 |

`SUB2API_DATABASE_URL` 与 `METERLEAF_INGEST_KEYS` 至少填写一项,未填写的来源不启用。

Expand Down Expand Up @@ -99,7 +100,7 @@ docker compose up -d --no-build --pull never # 已发布镜像

采集器需要能访问 Meterleaf。采集器与服务在同一台电脑时使用 `http://127.0.0.1:4318`。服务在 NAS 等其他主机时,随附 Compose 默认只监听回环地址,局域网访问需把 `METERLEAF_BIND_ADDRESS` 改为 `0.0.0.0` 或宿主机局域网地址,或者经由已有的反向代理访问。HTTP 下写入密钥明文传输,只在可信局域网中使用。

若要让外网的电脑推送,只在反向代理上为 `POST /api/ingest/v1/batches` 绕过原有登录保护,其余页面照旧。这个地址用写入密钥鉴权,必须走 HTTPS。反向代理的请求体上限至少设为 8 MB,Nginx 默认 1 MB 会拒绝大批次。以 Nginx 为例:
若要让外网的电脑推送,地址必须走 HTTPS。开启了 [GitHub 登录](#github-登录)时采集器写入已绕过登录;如果改由反向代理做登录保护,只为 `POST /api/ingest/v1/batches` 绕过它,其余页面照旧。这个地址用写入密钥鉴权。反向代理的请求体上限至少设为 8 MB,Nginx 默认 1 MB 会拒绝大批次。以 Nginx 为例:

```nginx
location = /api/ingest/v1/batches {
Expand All @@ -111,6 +112,37 @@ location = /api/ingest/v1/batches {
}
```

## GitHub 登录

Meterleaf 可以要求访问者先用 GitHub 账号登录,只有允许名单里的账号能查看页面和读取接口。不开启时,任何能访问到服务的人都能看到账本,只适合本机或可信局域网。

1. 在 GitHub 的 Settings → Developer settings → OAuth Apps 新建应用。Homepage URL 填浏览器访问 Meterleaf 的地址,Authorization callback URL 填同一地址加 `/auth/github/callback`,例如 `https://meterleaf.example.com/auth/github/callback`。
2. 生成 Client Secret,把下面几项写入 `.env`:

```dotenv
METERLEAF_GITHUB_CLIENT_ID=<Client ID>
METERLEAF_GITHUB_CLIENT_SECRET=<Client Secret>
METERLEAF_GITHUB_USERS=your-github-login
METERLEAF_PUBLIC_URL=https://meterleaf.example.com
# METERLEAF_SESSION_DAYS=30
```

3. 执行 `docker compose up -d`(按实际来源加上前文的参数)让容器按新配置重建。

| 变量 | 说明 |
| -------------------------------- | ---------------------------------------------------------- |
| `METERLEAF_GITHUB_CLIENT_ID` | OAuth 应用的 Client ID |
| `METERLEAF_GITHUB_CLIENT_SECRET` | OAuth 应用的 Client Secret |
| `METERLEAF_GITHUB_USERS` | 允许登录的 GitHub 用户名,多个用英文逗号分隔,不区分大小写 |
| `METERLEAF_PUBLIC_URL` | 浏览器访问 Meterleaf 的地址,须与 OAuth 应用的回调地址一致 |
| `METERLEAF_SESSION_DAYS` | 登录有效天数,默认 `30`,范围 1 至 365 |

前四项要么都填,要么都留空;只填一部分时服务不会启动。地址是 HTTPS 时登录 Cookie 只经 HTTPS 发送。

登录保持在浏览器里,经常使用时自动顺延,闲置超过有效天数才需要重新登录。在「关于」页点击「退出登录」即可退出。从 `METERLEAF_GITHUB_USERS` 删除某个账号并重建容器后,该账号已有的登录立即失效;更换 Client Secret 会让所有人重新登录。

开启后,健康检查 `/api/health`、采集器写入地址和图标等安装用的静态文件仍可直接访问,采集器继续用写入密钥推送,不需要登录。

## 首次采集与自动同步

本节适用于 Sub2API 来源。首次启动默认不采集。在「数据同步」中点击「立即同步」启动历史补采;服务保持在线,重复点击不会建立并发任务。自动同步默认关闭,开关会保存在本地账本并在重启后恢复。
Expand Down
20 changes: 19 additions & 1 deletion src/server/app.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,11 @@ import { encodeLedger } from "../shared/ledger-wire";
import type { SyncStatus } from "./sync";
import { registerResponseCompression } from "./response-compression";
import { registerIngestRoutes, type IngestTarget } from "./ingest";
import {
registerGithubAuth,
type GithubAuthConfig,
type GithubClient,
} from "./auth";
import { z } from "zod";
import {
createLedgerView,
Expand Down Expand Up @@ -41,6 +46,10 @@ interface AppOptions {
diagnostics?: DiagnosticsLogger;
/** 本机采集器的推送入口;未配置写入密钥时不注册。 */
ingest?: IngestTarget;
/** 未配置时不做认证,由部署方在反向代理层负责。 */
githubAuth?: GithubAuthConfig;
githubClient?: GithubClient;
now?: () => number;
sync?: {
status(): SyncStatus;
requestSync(): unknown;
Expand All @@ -59,7 +68,7 @@ function isReportBuilding(
);
}

/** 服务读写本地账本状态;上游保持只读,认证由外部反代负责。 */
/** 服务读写本地账本状态;上游保持只读。配置 GitHub 登录时由服务自身认证,否则交给外部反代。 */
export function createApp({
snapshot,
view,
Expand All @@ -68,6 +77,9 @@ export function createApp({
sync,
accountArchive,
ingest,
githubAuth,
githubClient,
now,
}: AppOptions) {
const app = Fastify({ logger: false });
registerResponseCompression(app);
Expand Down Expand Up @@ -99,6 +111,12 @@ export function createApp({
},
);
});
if (githubAuth)
registerGithubAuth(app, githubAuth, {
client: githubClient,
now,
diagnostics,
});
app.get("/api/health", () => ({ status: "ok" }));
if (ingest) registerIngestRoutes(app, ingest, diagnostics);
const displayState = () => ({
Expand Down
Loading
Loading
0