File size: 8,782 Bytes
07f6b3b
a431c63
 
07f6b3b
a431c63
07f6b3b
a431c63
07f6b3b
 
 
52dad07
a431c63
52dad07
a431c63
8b4dc02
a431c63
8b4dc02
52dad07
8b4dc02
e45e71c
8b4dc02
e45e71c
8b4dc02
e45e71c
8b4dc02
 
 
 
 
e45e71c
8b4dc02
 
 
 
 
a431c63
52dad07
a431c63
52dad07
 
 
 
8b4dc02
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
a431c63
52dad07
a431c63
52dad07
e45e71c
52dad07
a431c63
52dad07
 
a431c63
8b4dc02
a431c63
52dad07
a431c63
52dad07
 
 
 
 
 
a431c63
 
8b4dc02
e45e71c
52dad07
a431c63
52dad07
a431c63
52dad07
 
 
a431c63
 
52dad07
a431c63
8b4dc02
 
 
a431c63
52dad07
a431c63
52dad07
a431c63
52dad07
 
 
 
 
a431c63
52dad07
a431c63
52dad07
 
 
a431c63
8b4dc02
a431c63
52dad07
a431c63
52dad07
a431c63
8b4dc02
a431c63
52dad07
a431c63
8b4dc02
 
 
a431c63
8b4dc02
 
 
a431c63
8b4dc02
 
a431c63
8b4dc02
a431c63
8b4dc02
a431c63
8b4dc02
a431c63
8b4dc02
 
 
a431c63
8b4dc02
a431c63
8b4dc02
 
a431c63
52dad07
a431c63
8b4dc02
a431c63
52dad07
a431c63
52dad07
 
 
a431c63
52dad07
a431c63
52dad07
 
 
a431c63
 
8b4dc02
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
a431c63
8b4dc02
 
 
a431c63
8b4dc02
a431c63
8b4dc02
 
 
 
 
 
 
a431c63
8b4dc02
 
 
 
 
a431c63
52dad07
a431c63
52dad07
 
 
 
 
 
 
a431c63
 
52dad07
 
8b4dc02
 
 
52dad07
 
a431c63
 
 
 
52dad07
 
a431c63
 
 
8b4dc02
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
---
title: Claude2API
emoji: 🔁
colorFrom: purple
colorTo: indigo
sdk: docker
app_port: 7860
pinned: false
---

# Claude2API — Hugging Face Space 说明

当前 Space:<https://ohmyapi-c2api.hf.space>

这是一个围绕 `pushzx/claude2api:latest` 的 Hugging Face Docker Space 包装层。目标很克制:

- 让根路径 `/` 直接进入真实可用的管理登录页 `/admin/login`
- 保持 `/admin``/health``/v1/*` 等能力继续由上游 `claude2api` 提供
- wrapper 只处理入口体验与反代,不在外层伪造持久化配置语义

## 当前部署状态

当前线上状态已经收敛为 **public Space + simple mode**- Space 类型:**public Space**
- 运行模式:**simple mode**
- 当前不启用 DB-backed 持久化
- 当前已从 Space Secrets 中移除:`DB_HOST``DB_PORT``DB_USER``DB_PASS``DB_NAME``PGSSLMODE`
- 当前根路径 `/` 会直接 `302``/admin/login`

这样收敛的原因很明确:simple mode 已经稳定可用,而当前上游镜像的 DB-only 路径对 Neon / Supabase 这类强制 TLS 的托管 PostgreSQL 仍未验证通过,不能在这里把它写成现成功能。

## 当前访问行为

public Space 的含义是:**入口默认公开**,不是 **Secrets 自动公开**。

当前线上已验证:

- `/``302 /admin/login`
- `/admin/login` 可正常打开
- `/health` 返回 `200`
- `/v1/messages` 未带正确 token 时返回 `401 Invalid API key`
- 带正确 app token 时,`claude-sonnet-4-6` 已成功完成实际调用
- `/v1/models` 可在带 token 的情况下返回模型列表

## public Space 的风险边界

public Space 不会因为是 public 就把 Hugging Face Secrets 直接暴露出来,但它会让你的入口地址处于公网可探测状态。

这意味着:

- `https://ohmyapi-c2api.hf.space/` 这类地址任何人都能访问与探测
- Hugging Face Space Secrets **不会**被页面或公开 API 直接回显
- 真正更常见的泄露面是:把 token / session 写进仓库文件、前端页面、日志、导出的配置或本机历史文件
- public Space 是否安全,关键不在于“是否 public”,而在于:
  - app 自身接口是否强制鉴权
  - `CLAUDE_API_KEY` / `CLAUDE_SESSION_KEYS` 是否只保留在服务端运行时环境
  - 仓库与页面输出里是否完全不出现真实 secret

换句话说,public Space **不会自动泄露 session**,但如果 app token 泄露、接口未加保护,别人就能直接消费你的服务。

## 为什么 Hugging Face 环境变量能直接生效

因为当前 Space 使用的是 **Docker SDK**。

Hugging Face 对 Docker Space 的处理方式不是“把变量写进仓库文件”,而是在容器启动时把 Space Settings 里的:

- **Variables**
- **Secrets**

直接注入成容器进程的真实运行时环境变量。

对这个仓库来说,等价于容器启动时直接拿到了例如:

```env
ADMIN_USER=admin
LISTEN_ADDR=:7860
CLAUDE_API_KEY=...
ADMIN_PASS=...
CLAUDE_SESSION_KEYS=...
```

因此,只要上游 `claude2api` 在启动时通过 `os.Getenv(...)` 或等价方式读取配置,它就会直接生效,不需要额外写入 `.env``config.yaml` 或其它文件。

### 当前已确认的 Space Variables

当前公开可读到的 Variables 是:

```env
LISTEN_ADDR=:7860
ADMIN_USER=admin
```

Secrets 不会被 Hugging Face API 直接回显,但从线上行为可以确认至少存在并已生效:

- `CLAUDE_API_KEY`
- `CLAUDE_SESSION_KEYS`
- `ADMIN_PASS`

## 当前 wrapper 做了什么

当前 `Dockerfile` 不是直接把上游镜像原样暴露到 `:7860`,而是:

- 前置一个很薄的 nginx
- nginx 监听 `:7860`
- 上游 `claude2api` 在容器内监听 `127.0.0.1:8080`
- `/``/index.html` 直接跳转到 `/admin/login`
- 其余路径继续透传到上游

这样做的原因很简单:

- 旧版 root path 会显示 `404 page not found`
- 现在用户访问根路径时,会直接进入真实可用的登录入口
- 同时不需要去改上游镜像源码

## 当前为什么不启用 PostgreSQL 持久化

### 结论

**按当前已验证状态,不能认为这个 Space 已经配置并启用了 PostgreSQL 持久化存储。**

更准确地说:当前线上已经明确收敛为 **simple mode only**,不是“正在尝试 DB-only 但尚未成功”。

### 原因

1. 只要存在 `CLAUDE_SESSION_KEYS`,上游就可以走 simple mode,并且当前已验证可稳定提供 API 能力。
2. 当前 Hugging Face Space runtime 已确认 `storage.current = None``storage.requested = None`,因此容器内本地 PostgreSQL 不能视为正式持久化层。
3. 对当前上游镜像的排查显示,DB-only 路径很可能会把 PostgreSQL DSN 拼成:

   ```text
   host=%s port=%s user=%s password=%s dbname=%s sslmode=disable
   ```

   这会与 Neon / Supabase 这类强制 TLS 的托管 PostgreSQL 冲突。
4. 因此,即使文档里存在“可连接外部数据库”的描述,也不能把它等同于“当前镜像已支持外部托管 PG 持久化”。

### 为什么 local PostgreSQL 不能当成 HF 持久化

如果你打算在 Hugging Face Space 容器里直接放一个“本地 PostgreSQL”,要先明确一件事:**默认情况下,这不等于可靠持久化。**

原因是:

1. Docker Space 默认使用的是容器自身的可写层,而不是天然持久卷。
2. 如果没有单独申请 Hugging Face persistent storage,并把数据库目录明确挂到持久路径,本地 PG 数据在 restart / rebuild / redeploy 后都可能丢失。
3. 当前这个 Space 没有启用 persistent storage,因此“容器内本地 PG”不能当成正式配置持久化方案。

因此:

- 本地 PG 更适合临时调试,不适合承担当前 Space 的正式配置持久化
- 真正需要稳定持久化时,应优先使用外部 PostgreSQL,或者先补齐 HF persistent storage,再验证上游是否支持对应的数据目录语义

## 当前推荐的线上配置思路

如果你只是想把 Space 稳定跑起来,当前推荐就是保持 simple mode:

### 必要 Secrets

- `CLAUDE_SESSION_KEYS`
- `CLAUDE_API_KEY`
- `ADMIN_PASS`

### 当前 Variables

```env
LISTEN_ADDR=:7860
ADMIN_USER=admin
```

### 当前不启用的配置

以下 DB 相关项当前视为**暂不启用**,不要再把它们当成线上必需配置:

- `DB_HOST`
- `DB_PORT`
- `DB_USER`
- `DB_PASS`
- `DB_NAME`
- `PGSSLMODE`

## 当前已验证可调用的模型

带正确 app token 时,以下模型已通过真实 `/v1/messages` 请求验证,可以正常返回结果:

- `claude-sonnet-4-5`
- `claude-haiku-4-5`
- `claude-sonnet-4-6`
- `claude-haiku-4-6`
- `claude-3-5-sonnet-20241022`
- `claude-3-5-haiku-20241022`
- `claude-3-sonnet-20240229`
- `claude-3-haiku-20240307`

以下模型虽然会出现在 `/v1/models` 列表里,但当前实测返回的是 `403 model_not_available`- `claude-opus-4-5`
- `claude-opus-4-6`
- `claude-3-opus-20240229`

这也说明一件事:**`/v1/models` 返回的是“当前镜像声明支持的模型列表”,真正能否调用仍然取决于当前 session / account 的实际可用性。**

## 关于 admin password / auth token 持久化的当前结论

目前已做过的验证结论是:

- `api_key` 可以在当前运行期通过管理接口更新,并立即影响鉴权结果
- 但容器重启后,会回退到启动时环境变量 / HF Secrets 的值
- `admin_pass` 目前看**不能**通过现有 settings 路径真正更新并持久生效

所以当前更准确的理解是:

- Hugging Face Secrets / Variables = **启动配置源**
- 上游 admin UI 的部分设置 = **运行期状态修改**
- 两者目前**不是**一个可自动回写、可稳定持久继承的统一配置层

## 当前仓库文件职责

- `Dockerfile`:构建 HF Space 容器
- `nginx.conf`:把根路径重定向到 `/admin/login`,并透传其他请求
- `entrypoint.sh`:同时启动前置 nginx 和上游 `claude2api`
- `.env.example`:本地 / VPS 运行示例,不代表 HF Secrets 会被运行时回写
- `docker-compose.yml`:本地 / VPS 参考,不会直接在 HF Space 中运行

## 最小验收命令

```bash
curl -I https://ohmyapi-c2api.hf.space/
curl https://ohmyapi-c2api.hf.space/health

curl https://ohmyapi-c2api.hf.space/v1/models \
  -H "Authorization: Bearer $CLAUDE_API_KEY"

curl https://ohmyapi-c2api.hf.space/v1/messages \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer $CLAUDE_API_KEY" \
  -d '{
    "model": "claude-sonnet-4-6",
    "max_tokens": 32,
    "messages": [{"role": "user", "content": "Reply with exactly: ok"}]
  }'
```

如果最后一条返回正常消息体,就说明当前公开 Space、app token、以及 Claude session 这三层链路都已经打通。