Commit Graph
5411 Commits
Author SHA1 Message Date
wucm667 7cd3dc85c3 fix(channel-monitor): require request samples before health scoring 2026-10-08 10:41:20 +08:00
wucm667 07d51a6aef chore: sync upstream security updates 2026-10-08 08:46:24 +08:00
lyy0709 1817a8428a feat(ops): show per-request output TPS percentiles 2026-10-07 13:06:19 -07:00
liuyh06 61e1afdd49 perf(openai): lazily rebuild input when stripping namespaces 2026-10-08 01:11:23 +08:00
shaw 6e315616ed fix(setup): stop shipping guessable admin credentials on fresh installs (fixes #7850)
Fresh Docker installs created the admin as admin@sub2api.local and accepted
any ADMIN_PASSWORD (e.g. "123456"), making them easy brute-force targets.

- Generate a random admin-<12 hex>@sub2api.local login email when
  ADMIN_EMAIL is empty; drop the fixed default from .env.example and the
  compose files, and have docker-deploy.sh write a random ADMIN_EMAIL.
- Validate user-supplied ADMIN_PASSWORD (8-72 bytes) and ADMIN_EMAIL on the
  auto-setup path, only when an admin is actually about to be created, so
  existing deployments are never blocked at startup.
- Align validateEmail with the login endpoint's email binding so setup can no
  longer create an admin that can never log in (e.g. "a@b", "Name <a@b.com>").
- Cap validatePassword at bcrypt's 72-byte limit; the CLI reuses it and now
  defaults to a random admin email instead of admin@example.com.
- Update READMEs/deploy docs to stop suggesting guessable credentials.
2026-10-07 09:27:36 +08:00
wucm667 63800c50d4 fix(gateway): guard structured-input fallback against unsafe retries 2026-10-07 08:50:13 +08:00
ASWJ df89228c2d fix(opencode): report 403 as neutral forbidden in usage snapshot 2026-10-06 23:57:15 +08:00
ASWJ 480f668ba9 fix(opencode): cap Retry-After hint for billing probe schedule 2026-10-06 23:56:27 +08:00
ASWJ aa3b049000 fix(opencode): cap Retry-After hint at 24h for usage refresh backoff 2026-10-06 23:55:46 +08:00
ASWJ 0dccd7b6f5 fix(opencode): correct Zen qwen3.8-max endpoint and reject unsupported models
- Route qwen3.8-max to Chat Completions on OpenCode Zen mode instead of Messages
- Fast-reject unsupported model families (gemini-*, jev-*) with HTTP 400 on OpenCode gateway to avoid upstream 404 failovers and strike accumulation
- Sync frontend DEFAULT_OPENCODE_ZEN_PROTOCOL_RULES with backend
2026-10-06 23:18:57 +08:00
honest.tai d1aac6b989 fix(payment): close EasyPay forged-callback sign reuse (fixes #7881)
An order-creation signature (visible to the payer in popup submit.php
URLs) could be replayed as a payment-success notification, with no
merchant key required. Two compounding defects:

- CanonicalizeReturnURL kept client-supplied query parameters, so a
  return_url ending in '&trade_status=TRADE_SUCCESS' entered the signed
  order string verbatim;
- the sign base string concatenates values unescaped, so a callback that
  decodes return_url only up to the smuggled pair re-sorts to a
  byte-identical sign base and passes verification.

Fixes:

- CanonicalizeReturnURL now drops client query parameters entirely; the
  server builds its own signed set and the client query has no
  legitimate use;
- VerifyNotification rejects any parameter outside the genuine async
  notify set (pid/trade_no/out_trade_no/type/name/money/trade_status/
  param/sign/sign_type), failing closed against the whole smuggling
  class regardless of the injection point.

Regression tests cover the PoC payload, full order-URL replay, genuine
callback acceptance, unknown-parameter rejection (including empty
values), and the return_url smuggling strip. Genuinely paid orders that
an exotic upstream variant might reject are still recovered through the
existing upstream QueryOrder reconcile path.
2026-10-06 23:14:56 +08:00
whhphd 54810f8c82 fix(grok): include consent token and origin in SSO device approval 2026-10-06 22:53:10 +08:00
Achord Chan 5ef517d0fc fix(accounts): attribute connectivity test failure logs 2026-10-06 13:59:37 +08:00
hezhaoqianandClaude Opus 5.5 0173861a5a fix(channel-monitor): build zhipu probe URL from the configured endpoint
The zhipu adapter always appended the official /api/paas/v4/chat/completions
path, so probes broke whenever the endpoint was not the official root:

- OpenAI-compatible relay (e.g. https://relay.example.com): requested
  /api/paas/v4/chat/completions, which relays do not serve -> 404
- Coding Plan base (https://open.bigmodel.cn/api/coding/paas/v4): produced
  .../coding/paas/v4/api/paas/v4/chat/completions -> 404

Resolve the path from the endpoint instead: append /chat/completions when it
already carries /paas/v4, keep the official path for bigmodel.cn / z.ai
roots, and fall back to /v1/chat/completions (same as kimi/deepseek/minimax)
for everything else.

Fixes #7067

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-06 13:09:51 +08:00
wucm667 c15d858eca fix(gateway): preserve Responses semantics in chat fallback 2026-10-06 10:49:15 +08:00
风起andClaude Opus 5.5 412718edf5 fix(apicompat): always include signature on Anthropic thinking blocks
AnthropicContentBlock / antigravity ClaudeContentItem serialized `signature`
with omitempty, so thinking blocks without an upstream signature (Chat
Completions reasoning_content, Responses reasoning without encrypted_content,
every streamed thinking content_block_start) went out with no `signature` key.

Anthropic always sends the key (`""` on content_block_start). Strict clients
such as Grok Build deserialize it as a required field and abort the response
or SSE stream with "missing field `signature`".

- apicompat: thinking MarshalJSON always emits `thinking` and `signature`
- antigravity: ClaudeContentItem MarshalJSON does the same; streamed thinking
  content_block_start carries `signature: ""`
- tests: wire-shape regression tests; update TestStreamingReasoning expectation

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-05 08:41:15 +00:00
wucm667 9f84204c21 fix(streaming): preserve chat stream heartbeat comments 2026-10-04 09:13:04 +08:00
cyh da4419e626 fix(anthropic): keep cache TTL order valid on the mimic path
Anthropic processes cache breakpoints in tools, system, messages order and
rejects a ttl=1h breakpoint after a 5m one. The OAuth mimic path adds 5m
breakpoints to the injected system block and the last tool, so a third-party
client that marks a single message block with ttl=1h received a 400.

After the block limit, raise ephemeral breakpoints that precede the last 1h
breakpoint to 1h. Valid requests are unchanged.
2026-10-03 22:38:26 +08:00
yJader 856d7ea905 fix: expose mapped antigravity test models 2026-10-03 20:56:49 +08:00
feeeei a46249aac5 test(account): 补充临时不可调度作用范围单元测试
- 后端:scope 解析归一化、缺省/显式账号级/显式模型级/模型未知兜底的分流判定,
  以及显式账号级、显式模型级 401、模型未知三种命中的落库位置与状态 JSON
- 前端:未设置 scope 的存量规则按 401 账号级、其余模型级回显并原样保存,
  显式 scope 回显与切换后提交,新增规则默认模型级
2026-10-03 12:08:05 +08:00
feeeei 5a25507dcb feat(account): 临时不可调度规则新增作用范围 scope 选项
- 规则新增 scope 字段:account 命中后暂停整个账号,model 只冷却本次请求的模型;
  字段缺省时保持现有语义(模型已知且非 401 走模型级,否则账号级),显式取值由运营人员决定,401 也不例外
- 模型未知的路径按账号级兜底;状态 JSON 记录 scope/model 便于排查
- 创建/编辑账号弹窗的规则编辑器新增作用范围单选卡片,新规则默认模型级;
  未设置 scope 的存量规则按后端缺省语义回显(401 账号级、其余模型级),打开再保存不改变行为
2026-10-03 12:08:05 +08:00
Achord Chan 852f0f824f fix(anthropic): preserve requested tool change beta headers 2026-10-03 10:34:06 +08:00
wucm667 5800abf1cb fix(models): preserve composite catalog isolation after base update 2026-10-03 09:11:53 +08:00
Neo Tan abdfbb704c fix(web): bypass bare API aliases from embedded frontend fallback
The embedded frontend middleware runs before the gateway routes and only
let a few root aliases through. Bare aliases such as /chat/completions,
/embeddings, /messages/count_tokens and /models/:model were answered with
index.html and HTTP 200 instead of reaching the API handlers.

Add every root alias registered in routes/gateway.go to the bypass list.
This also covers POST /videos, which the /videos/ prefix did not match.

Fixes #7670
2026-10-02 11:40:14 -04:00
Wesley Liddick 3040209f20 Merge pull request #7813 from kingsleydon/fix/typesafe-upstream-billing-probe
fix(billing): allow upstream billing probe for TypeSafe API-key accounts
2026-10-02 20:02:28 +08:00
shaw 432a6a461f chore(billing): simplify comment 2026-10-02 19:52:00 +08:00
shaw c2d5bbd93e fix(billing): settle usage when the API key is deleted before billing completes
When an API key with quota or rate limits was deleted while a request was
still in flight, the key counter update matched no rows and returned
ErrAPIKeyNotFound, rolling back the whole billing transaction including the
balance/subscription charge. Skip the key-scoped counters in that case and
keep settling the user and account side as usual.
2026-10-02 19:51:06 +08:00
KingsleyandClaude Opus 5.5 18380398d5 chore(codex): drop added comments
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-02 10:48:40 +00:00
KingsleyandClaude Opus 5.5 8b9788eeb4 test(billing): drop added comments
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-02 10:48:37 +00:00
KingsleyandClaude Opus 5.5 66a9e2e6bb fix(billing): allow upstream billing probe for TypeSafe API-key accounts
The create form shows the upstream billing auto-probe toggle for every
API-key platform and enables it by default. TypeSafe was added as an
API-key platform but not to IsUpstreamBillingProbeIdentity, so creating a
TypeSafe account with the default form state fails with
400 UPSTREAM_BILLING_PROBE_ACCOUNT_INVALID ("account is not an API key
account").

TypeSafe accounts store credentials.api_key/base_url like every other
API-key platform, which is all the probe reads. Add TypeSafe to the probe
identity set, and add typesafe.ai to the official API domains so accounts
on the default https://api.typesafe.ai base URL record "unsupported"
without sending the key to a path that cannot exist, matching the other
official providers.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-02 10:45:53 +00:00
KingsleyandClaude Opus 5.5 dd7ee21703 style(codex): use tagged switch for capability conflict fallback
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-02 09:47:26 +00:00
KingsleyandClaude Opus 5.5 c0e5a4670e fix(codex): never advertise null service_tiers in models manifest
intersectUpstreamModelMetadata falls back to null for every Codex tool
capability field whose value differs across peer accounts. Since
e6d191a83 added service_tiers to codexToolCapabilityFields, a model served
by accounts with different (or partially declared) tier lists ends up with
"service_tiers": null in the generated Codex manifest. Codex decodes
service_tiers as a required array and rejects the whole manifest, so the
client loses model metadata for every model.

Fall back to [] for conflicting service_tiers (do not advertise any tier
that is not shared by all peers), and normalize an explicit null to []
when applying capabilities.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-02 09:38:08 +00:00
wucm667 983cf297da fix(gateway): retry rejected structured input through chat endpoint 2026-10-02 17:16:37 +08:00
Wesley Liddick 2529371bab Merge pull request #7676 from william-drakemond/fix/email-code-token-atomic
fix(email): 验证码尝试次数原子化 + 重置密码 token 哈希存储且原子单次消费
2026-10-02 14:32:42 +08:00
Wesley Liddick 43c4c882fd Merge pull request #7674 from william-drakemond/fix/antigravity-sanitize-upstream-error
fix(antigravity): 返回客户端前脱敏上游错误体,避免泄露账号池身份
2026-10-02 14:32:12 +08:00
Wesley Liddick 719013053a Merge pull request #7803 from william-drakemond/feat/api-key-group-sort
feat(keys): 支持按分组名称排序 API 密钥
2026-10-02 14:31:49 +08:00
Wesley Liddick f3dc0835df Merge pull request #7673 from william-drakemond/fix/public-order-verify-hardening
fix(payment): 为匿名订单查询接口 /payment/public/orders/verify 增加 IP 限流
2026-10-02 14:31:17 +08:00
Wesley Liddick 5637f469fa Merge pull request #7802 from feeeei/feat/recharge-promotion-tiers
feat(payment): 充值优惠阶梯,支持赠金与折扣两种模式
2026-10-02 14:30:29 +08:00
shaw fa49647c6d test(model): 错误透传平台列表守卫纳入 typesafe 2026-10-02 14:00:12 +08:00
shaw 9eb394e3e9 fix(typesafe): 堵住校验解析差异与审计盲区,收紧模型列表口径
- System One 校验拒绝重复键与大小写/Unicode 折叠变体键(请求顶层、
  questions、问题对象):encoding/json 大小写不敏感且取最后一个重复键,
  而原始 body 原样转发,此前 {"model":"x","MODEL":"jev-latest"} 可绕过
  jev-latest 限制、模型白名单与流式限制。
- Prompt 审计与旧审核同时收集对象键名、问题 ID 与未知扩展字段(不含规范
  字段名与已校验的 type),此前把内容放进键名即可完全绕过审计。
- 上游成功响应的 usage/model 宽松解码(浮点、数字字符串),避免上游已
  计费而网关整体 502 不记账;解码失败记录 response_error ops 事件。
- 上游 413 与 400/422 同视为请求错误:透传状态码、不切换、不改账号状态。
- TypeSafe base_url 末尾的 /v1 自动剥离,避免拼出 /v1/v1/systemone;
  /v1/systemone 改挂文本请求体上限。
- Composite 静态兜底模型列表、管理端候选列表恢复为不含 jev-latest;
  Codex 清单永不列出 TypeSafe 模型;TypeSafe 分组候选默认 jev-latest。
- 创建账号从 Grok 切到 TypeSafe 时重置为白名单模式;错误透传平台列表补 typesafe。
2026-10-02 13:51:58 +08:00
shaw 2d8a386811 fix(typesafe): 补齐平台迁移、审计覆盖、端点隔离与错误策略
- 新增 241 迁移:user_platform_quotas / composite_model_routes 的 CHECK 约束
  加入 typesafe。此前设置 typesafe 默认配额会让注册时的多行配额快照整体
  违约(fail-open 后新用户所有平台配额丢失),单用户配额与 Composite 路由保存 500。
- Prompt 审计新增 typesafe_systemone 协议提取(state + 各问题 instructions/
  criteria/选项标签,键排序保证哈希稳定);此前提取为空,阻断与异步审计均被放行。
  旧内容审核同样覆盖问题文本,且不再丢弃含 <system-reminder> 的 System One 文本。
- TypeSafe 分组及路由到 TypeSafe 的 Composite 请求访问 Messages / count_tokens /
  Chat Completions / Responses 时返回 404,避免以 x-api-key 打到错误上游路径并
  污染账号状态;TypeSafe 账号 base_url 为空时不再回退 api.anthropic.com。
- SystemOne 补在途余额预留、利润控制准入终检、wrapReleaseOnDone,失败切换改用
  共享 FailoverState(同账号重试 / 池模式 / 临时封禁)。
- 上游错误沿用共享账号错误策略:402/403、自定义错误码、临时不可调度规则生效,
  记录 ops 上游错误;400/422 仍不切换且永不改变账号状态。
- 账号测试改走原生 System One 探测,可用模型只返回 jev-latest;
  响应超限显式报错,响应 Content-Type 仅透传 JSON 类型。
2026-10-02 13:08:40 +08:00
aofee bb97ab364a fix(typesafe): align System One validation with upstream schema 2026-10-02 09:44:50 +08:00
aofee 7225538358 merge: sync Jev support with latest upstream main 2026-10-02 09:37:30 +08:00
William 0cecf3c367 feat(keys): support sorting API keys by group name 2026-10-01 13:01:01 -04:00
feeeei 1aa34d478d feat(payment): 充值优惠阶梯支持折扣模式,快捷金额角标改为促销价签
- 新增全局模式开关 RECHARGE_BONUS_MODE(bonus / discount),接入支付配置、后台设置接口与 checkout-info
- 折扣模式:到账不变,实付基数按百分比打折(按币种精度取整),手续费 / 每日限额 / 渠道选实例沿用折后基数;
  百分比须 < 100,运行时 ≥ 100 按无优惠处理
- 充值页角标改为单行红色价签(赠金「+20%」/ 折扣「20% OFF」),第二行按模式显示到账或折后实付;
  摘要卡折扣模式显示优惠行,渠道限额校验改用折后实付
- 后台「充值优惠阶梯」拆为独立卡片:赠金 / 折扣切换、紧凑档位表格、区间预览、活动文案
- 补前后端单测与 zh/en 文案
2026-10-02 00:25:41 +08:00
feeeei 1b1039f4fd feat(payment): 充值赠送阶梯(按金额档位额外赠送到账额度)
- 后台支付设置新增「充值赠送阶梯」(满 X 送 Y%,RECHARGE_BONUS_TIERS)与 Markdown 活动文案
  (RECHARGE_BONUS_NOTICE):阈值按用户输入的支付金额命中(取不超过金额的最大档),
  赠送按到账基数(输入 × 充值倍率)计算;严格校验写入、宽松解析读取,订阅订单不参与
- 下单时按当时配置计算赠送并落库:payment_orders 新增 bonus_amount 列(迁移 241),
  amount 仍为到账总额,到账/兑换码/退款逻辑不变;推广返利基数改为 amount - bonus_amount
- checkout-info 下发 recharge_bonus_tiers / recharge_bonus_notice;创建订单与订单查询响应带 bonus_amount
- 充值页:快捷金额按钮右上角「+N%」角标 + 第二行到账金额,金额卡顶部渲染活动文案,
  订单摘要新增赠送额度行并在有赠送时始终显示到账余额;订单列表/详情/支付结果/成功面板显示含赠送
- 后台新增 RechargeBonusTierEditor(行内校验重复/非法阈值,自动排序并显示区间预览)
- 补后端单测、契约测试与前端 vitest,zh/en 文案
2026-10-02 00:25:41 +08:00
wucm667 1888de135d fix(models): list composite exact route aliases 2026-10-01 22:54:05 +08:00
SPHINX998andClaude Opus 5.5 b6a157c19c fix(openai): account image input usage on WS passthrough
The WS v2 passthrough relay only read output_tokens_details.image_tokens,
so image generation over WebSocket billed image input tokens at the text
input rate and ignored the hosted response.tool_usage.image_gen breakdown
that the HTTP Responses path already merges (extractOpenAIUsageFromJSONBytes
+ mergeHostedImageGenToolUsage).

The relay now reads image input tokens from input_tokens_details or
prompt_tokens_details, fills missing image input/output counters from
response.tool_usage.image_gen (usage values keep precedence), and carries
ImageInputTokens per turn and in the session aggregate into the
OpenAIForwardResult used for billing.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-01 22:31:39 +08:00
SPHINX998andClaude Opus 5.5 775c1a3c1d fix(apicompat): preserve refusals when converting Responses to Chat
A Responses refusal arrives as a {"type":"refusal","refusal":...} content
part (streamed as response.refusal.delta), not as output_text. The
Responses → Chat Completions converters only read output_text, and
ResponsesContentPart had no refusal field, so Chat clients received an
empty assistant message when the model refused.

Map refusal parts to choices[0].message.refusal, response.refusal.delta
to delta.refusal, and let BufferedResponseAccumulator keep refusal deltas
so non-streaming replies rebuilt from deltas keep them too.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-01 22:03:51 +08:00
SPHINX998andClaude Opus 5.5 15f469c86b fix(gateway): ignore negative content block index in buffered Anthropic paths
The buffered Anthropic aggregators index finalResp.Content with the
upstream content_block_delta index after checking only idx < len. A
malformed or third-party Anthropic-compatible upstream that sends a
negative index makes the request goroutine panic, so the client gets no
response and the forward never returns a result to record usage.

Add the lower bound in all four buffered aggregators (Chat Completions
and Responses, on both the Anthropic gateway and the OpenAI native
Anthropic paths); the out-of-range delta is skipped like an index past
the end.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-01 22:00:22 +08:00