Commit Graph
100 Commits
Author SHA1 Message Date
zhiyu 53aa5cd247 test: align ModelMappingPreservesOtherFields with max_tokens strip
The count_tokens path now strips max_tokens; this sibling test also
exercises ForwardCountTokens but still asserted max_tokens preservation.
Flip its assertion to expect the field is filtered, matching
CountTokensFiltersGenerationFields.
2026-07-31 11:47:21 +08:00
Wesley LiddickandGitHub 6fa784fdd0 Merge pull request #5118 from Wei-Shaw/fix/openai-proxy-stream-circuit-fail-open
fix(openai): 代理断流熔断改为 fail-open 偏好,修复共用代理部署下的调度不可用
2026-07-31 10:42:24 +08:00
shaw da49ce3f29 fix(openai): fail open proxy stream circuit and collapse burst disconnects
The proxy stream circuit introduced in v0.1.164 (#4749) removes every
account behind a quarantined proxy from scheduling. When all schedulable
accounts share one proxy (a common deployment), two mid-stream
disconnects within a minute zeroed out capacity for 10 minutes and every
request failed with 502. One HTTP/2 connection loss also killed all
multiplexed streams at once, tripping the threshold from a single event.

- Quarantine now degrades to a preference: when the only reason no
  account is available is proxy quarantine, selection retries once with
  the quarantine bypassed, so capacity can never reach zero.
- Disconnects within 3s per proxy collapse into one failure event.
- Add gateway.openai_proxy_stream_circuit.disabled escape hatch.
- A completed stream still clears the quarantine immediately; TTL,
  thresholds and recording guards are unchanged.
2026-07-31 10:30:30 +08:00
Wesley LiddickandGitHub 99c8e4bf75 Merge pull request #4973 from yiancode/fix/openai-live-store-resilience
fix(openai-live): Live 会话 finalize 与 observer 对 store 故障的容错,防止用量记录静默丢失
2026-07-29 09:42:56 +08:00
Wesley LiddickandGitHub f2d824836f Merge pull request #5008 from hansnow/fix/claude-sonnet-5-status-alias
fix(frontend): 补充 Claude Sonnet 5 模型状态别名
2026-07-29 09:41:54 +08:00
Wesley LiddickandGitHub 6e1cbed423 Merge pull request #5024 from Wei-Shaw/fix/passkey-disabled-toast
fix(profile): 修复未配置 Passkey 时 /profile 每次访问都弹「加载 Passkey 失败」
2026-07-29 09:41:06 +08:00
shaw acad7f1a09 fix(profile): stop passkey load error toast when feature is disabled
The PASSKEY_DISABLED silence guard compared the string error code
against error.code, but the api client puts the numeric envelope code
there and the string code in error.reason, so the guard never matched
and every /profile visit on deployments without WebAuthn configured
showed a spurious "failed to load passkeys" toast.

Read error.reason instead, and skip the credentials request entirely
when the feature is disabled so the card no longer issues a request
that is guaranteed to fail with 403.
2026-07-28 22:51:58 +08:00
Wesley LiddickandGitHub 8fd01c2814 Merge pull request #5003 from feeeei/main
feat(模型广场): add model plaza with group-scoped pricing showcase
2026-07-28 20:02:52 +08:00
Wesley LiddickandGitHub 39903f006e Merge pull request #5005 from Wei-Shaw/refactor/scoped-column-updates
refactor(repository): scope user/api-key updates to explicitly declared columns
2026-07-28 19:43:50 +08:00
shaw 86fb4781f4 refactor(repository): scope user/api-key updates to declared columns
UserRepository.Update and APIKeyRepository.Update rewrote the whole row on
every call, regardless of which fields the caller meant to change. Several
columns on those tables are maintained by dedicated atomic paths (balance
deduction, quota and rate-limit counters, limit adjustments, activity
timestamps), so a caller holding a slightly older snapshot could silently
roll them back - a lost update.

Both methods now take an explicit column mask and persist only the columns
the caller declares; everything else keeps its current database value.

- All user and API-key call sites declare exactly what they mutate, which
  turns admin edits and profile saves into genuine partial updates.
- Email uniqueness locking/lookup and allowed_groups sync only run when
  those fields are part of the update.
- UserUpdateFields deliberately has no balance/total_recharged members, so
  Update cannot touch them. New AdjustBalance/SetBalance apply the change in
  a single statement and return before/after values; admin balance
  adjustment uses them instead of read-modify-write.
- promo_codes.used_count is no longer written by Update; it is only ever
  incremented by the redemption path.
- The billing hot path that marks an API key quota-exhausted writes only
  status.
- Dropped a no-op row write in RevokeAllUserTokens: users has no
  token_version column, so it persisted nothing while still overwriting
  concurrently-updated columns.

Adds integration coverage that a stale snapshot cannot revert concurrent
atomic writes, and unit coverage pinning the column set each entry point
declares.
2026-07-28 17:21:32 +08:00
Wesley LiddickandGitHub 2e432173f7 Merge pull request #4920 from alexj11324/feat/passkey-auth
feat: add passkey authentication
2026-07-28 14:58:37 +08:00
shaw 38ef8dc069 feat: require account password for passkey enrollment and revocation
A hijacked session must not be able to silently add a passkey as a
persistent backdoor or remove the victim's credentials. Registration
(begin) and deletion now verify the account password server-side,
reusing the existing PASSWORD_REQUIRED / PASSWORD_INCORRECT errors.

The password is used instead of TOTP step-up so the guard also protects
deployments that never configured a TOTP encryption key. The password
key in both request bodies is covered by the audit middleware's
key-substring redaction, so no credential material reaches audit_logs.

Frontend: the add-passkey form gains a current-password field, and the
delete confirmation is now a dialog with a password input (replacing
window.confirm), mirroring the TOTP disable dialog. Backend error
messages (e.g. wrong password) are surfaced instead of the generic
failure toast. Rename remains password-free as it is cosmetic.
2026-07-28 14:12:46 +08:00
shaw 97f44b21bb fix: keep passkey switch coupled to WebAuthn config and fix CI issues
- parseSettings now reports passkey_enabled=false whenever the WebAuthn
  deployment config is absent: a stale "true" row left behind after the
  config is removed previously made the admin update gate reject every
  settings save while the UI toggle was disabled, leaving no recovery
  path from the admin panel. Added a regression test.
- update the admin settings API contract goldens with the new
  passkey_enabled/passkey_configured/passkey_rp_id/passkey_rp_origins
  fields.
- errcheck: check rows.Close in passkey repository (repo convention).
- staticcheck QF1001: apply De Morgan's law in WebAuthn origin scheme
  validation.
2026-07-28 11:45:18 +08:00
Wesley LiddickandGitHub 0ef2228ce9 Merge pull request #4906 from alfadb/feature/kimi-k3-support
feat: 支持 Kimi K3 与 Kimi Code 模型 ID
2026-07-28 11:23:53 +08:00
Wesley LiddickandGitHub 1aeacf4d41 Merge pull request #4983 from Wei-Shaw/fix/issue-4887-prompt-audit-recovery
fix(security-audit): 解密失败不再吞掉整份配置,修复升级后配置消失且无法保存的死锁
2026-07-28 09:49:40 +08:00
Wesley LiddickandGitHub d95f6b98ce Merge pull request #4975 from Vibeone/fix/msg-id-format-authentic
fix(gateway): 修复模拟响应 message ID 格式,改为正宗 Anthropic msg_01 格式
2026-07-28 09:49:31 +08:00
shaw bfbe113f5e fix(security-audit): 解密失败不再吞掉整份配置,修复升级后配置消失且无法保存的死锁 (#4887)
根因:prompt audit 是共享 TOTP_ENCRYPTION_KEY 加密器的功能中唯一不校验
EncryptionKeyConfigured 的落点。未配置固定密钥的部署每次重启自动生成新
密钥,v162 保存的节点 Token 密文在升级重启后永久无法解密,Reload 中
ActiveFromStorage 整体失败导致快照永远装不上:管理端 GET 回退默认 v1
(v166 起为 503),而保存路径直读数据库做 CAS 版本对比,必然冲突——
配置既看不见也改不掉。PR #4893 仅改变了报错形态,未修复根因。

修复:
- ActiveFromStorage 对单节点解密失败降级容忍:该节点运行时禁用并标记
  TokenInvalid,配置整体照常激活;管理端恢复显示真实版本号,重新输入
  Token 即可自愈(密文保留,密钥恢复后自动复原)
- blocking 意图下零可用节点时 evaluator 仍返回 unavailable,请求照旧
  被拒,fail-closed 语义不回归;async 意图下 enqueue 直接 drop 并告警
- Save 在未配置固定加密密钥时拒绝保存新 Token(与 TOTP/Ollama/备份
  一致的门控),错误码 prompt_audit_encryption_key_required
- token_status 新增 invalid 状态,前端凭据列与编辑框提示重新输入
- 新增 config_token_invalid 告警日志(集合变化时记录一次,不随 5s
  刷新刷屏)
2026-07-28 09:31:36 +08:00
shaw dc893dd0b8 chore: update sponsors 2026-07-27 15:49:14 +08:00
Wesley LiddickandGitHub f18f3143e2 Merge pull request #4946 from Wei-Shaw/feat/panel-api-rate-limit
feat(security): 面板 API 限流保护——防止高频刷接口打爆数据库
2026-07-27 15:44:06 +08:00
shaw fead4c7ec3 feat(security): add panel API rate limiting to protect DB from high-frequency requests
用户可高频刷面板接口(usage/dashboard 等重聚合查询)直接打爆数据库:
现有限流器只覆盖登录/注册等公开认证入口,登录后的全部面板端点无任何限流。

三层防护(阈值均可在后台可视化配置,panel_rate_limit_settings):

1. 认证面板接口按「用户 ID」限流,与来源 IP 无关——反向代理/NAT 共享出口
   (所有请求源地址坍缩为 127.0.0.1 等)不会互相误伤:
   - Global 档(默认 240 rpm/账号):user/auth/payment/admin 全部登录后路由
   - Heavy 档(默认 60 rpm/账号):/usage、/usage/dashboard/*、
     /user/api-keys/:id/usage/daily 等重 SQL 聚合端点叠加计数
   - 管理员默认豁免(可关闭)

2. 无认证公开接口(/api/v1/settings/*,每次请求都查 DB)按安全客户端 IP
   限流(默认 300 rpm/IP);回环/私网/链路本地地址(反代内部转发地址)
   一律跳过计数,杜绝把整条反代链路合并进同一个桶造成大面积误拦截。

3. 修复既有隐患:auth 入口限流的 IP 取值从 c.ClientIP() 切换到与审计日志/
   会话绑定/API Key ACL 同源的安全客户端 IP 解析(尊重后台「信任反代转发
   IP」开关快照)。原实现下默认反代部署(未配置 server.trusted_proxies)
   所有用户共享同一个登录限流桶,既会全员误拦也可被单人恶意占满形成登录
   DoS;开关关闭时行为与原来完全一致。

工程约束:
- 配置热路径走进程内缓存(atomic.Value + singleflight,60s TTL),
  限流中间件零 DB 访问;保存后当前节点立即生效
- 面板限流 Redis 故障 fail-open(auth 入口保持原有 fail-close)
- 429 响应携带 Retry-After;错误码 RATE_LIMITED
- 支付 webhook / 公开支付回调有意不挂限流
- 新增 GET/PUT /api/v1/admin/settings/panel-rate-limit;设置页安全 tab
  新增「面板接口限流」卡片(zh/en i18n 全量)

测试:rate_limiter/panel_rate_limit/setting_panel_rate_limit 单测全绿;
routes、handler/admin、-tags unit 契约测试通过;前端 vue-tsc/ESLint/
SettingsView spec(26/26,含新增交互用例)/i18n 守卫全部通过。
2026-07-27 15:12:51 +08:00
Wesley LiddickandGitHub ab73bc0c77 Merge pull request #4924 from Cynicismcart/fix/group-description-wrapping
修复分组描述换行与下拉框溢出
2026-07-27 13:52:44 +08:00
shaw 7d3a896fcd chore: update sponsors 2026-07-27 08:59:12 +08:00
Wesley LiddickandGitHub e9a58c1cb8 Merge pull request #4814 from yiancode/fix/email-alias-registration-dedup
fix(auth): 注册查重归一化邮箱别名,防止单收件箱批量注册
2026-07-25 20:54:37 +08:00
shaw ef0ca5bdf5 style: 修正 dotStrippedEmailExpr 注释以满足 gofmt 文档注释规则 2026-07-25 19:51:19 +08:00
shaw bc3acd6e28 fix(auth): 收紧注册别名查重(根点绕过 / 误拒 / 无界扫描 / 并发竞态)
对 #4814 的审计跟进修复:

- 域名尾随点绕过:user@gmail.com. 的域名不在 gmail 家族名单内,点号折叠与
  googlemail 归一被整体跳过,别名刷号原样可复现。归一化入口统一去掉 FQDN 根点。
- 误拒合法用户:剥 "+后缀" 缺空串守卫,+alice@ 与 +bob@ 都折叠成 @domain,
  该域后续 "+x@" 注册会永久 EMAIL_EXISTS 且无自助恢复。改为仅当 "+" 不在首位时剥离。
- 无界不可索引全表扫描:原实现按 LOWER(email) LIKE '%@domain' 把整域邮箱读进内存,
  且挂在公开未鉴权的 send-verify-code 上。改为按去点邮箱
  REPLACE(LOWER(TRIM(email)), '.', '') 做等值 + "local+%@domain" 前缀探针并带 LIMIT,
  新增同表达式的部分索引(migrations/190)。TRIM 口径与既有精确匹配一致,
  历史带首尾空白的行同样命中;LIKE 元字符转义,% 与 _ 不会扩大匹配面。
- 并发竞态:注册改走 CreateWithEmailAliasGuard,在邮箱唯一性锁上追加收件箱身份锁并在
  锁内复查,避免同一收件箱的多个别名变体同时通过服务层前置查重。管理员建号仍走
  Create,不受别名限制。
- 能力断言静默 fail-open:别名查重方法上提到 UserRepository 端口(编译期强制),
  移除可选接口类型断言与静默降级分支。
- OAuth 邮箱注册的两条建号路径(同样发放注册赠额)纳入同一查重口径;邮箱换绑/绑定
  不纳入,否则用户把邮箱改成自己收件箱的别名会被误拒。
2026-07-25 19:40:53 +08:00
Wesley LiddickandGitHub 2e2638c01d Merge pull request #4864 from Wei-Shaw/fix/ollama-usage-pg-compat-and-fetch-floor
fix(ollama): 修复 PG<=16 上 due 判定失效并恢复抓取下限
2026-07-25 18:58:06 +08:00
shaw 1763db3a2d fix(ollama): 修复 PG<=16 上 due 判定失效并恢复抓取下限
#4850 的用量刷新调度有三个问题,本次一并修复。

jsonpath .datetime() 直到 PostgreSQL 17 才接受 ISO-8601 的 Z 标识符,
而服务写入的快照时间戳全部是 UTC(即 Z 形式)。在 PG 14/15/16 上
ollamaCloudUsageParseRFC3339SQL 因此把 fetched_at / last_attempt_at /
next_refresh_at 全部解析成 NULL,due 判定整条退化为 fail-open 分支:
每轮 20 个刷新额度被 id 最小的非 due 组占满,id 更大的组永远不会刷新——
正是该 PR 声称修复的饥饿场景。项目文档声明支持 PG 14/15/16,而集成测试
harness 固定使用 postgres:18.1,因此现有测试无法发现。

修法是在送入 jsonpath 前把结尾的 Z 改写为 +00:00。仍保留 jsonpath 而不
直接 ::timestamptz,因为通过形状正则但日历非法的值(如 2026-02-30)需要
fail-open 成 NULL 而非中断整条查询。已在真实 PG 14/15/16/17/18 上验证
五个版本行为一致,且非法日历与垃圾输入仍正确 fail-open。

成功路径不再查阅 next_refresh_at,而 nextOllamaCloudUsageDelay 的
15 分钟下限正作用于该字段,导致同组对 ollama.com 的抓取下限从 15 分钟
降到一个 runner 周期。请求间隔略大于 debounce 的交互式流量(典型 Claude
Code 用法)会把单组 24 小时抓取次数从 24-96 抬高到数百次。改为对成功路径
显式施加 fetched_at + OllamaCloudUsageMinFetchInterval 的下限,Go 与 SQL
两侧同步;空闲账号不再轮询这一主要收益不受影响。

debounce_minutes 与 interval_minutes 此前各自独立校验,因此
debounce >= interval 是合法组合;此时 min(lastUsed+debounce,
fetchedAt+maxWait) 中的 debounce 项恒为死项,管理员配置被静默忽略。
改为在写入时拒绝该组合。

另修复 refreshAccount 中 ListOllamaCloudUsageGroupAccounts 的错误被
静默吞掉(违反 CLAUDE.md 禁止忽略错误):失败时回退到更窄的活动信号会
改变 due 语义,现在记录日志。

测试:
- 恢复被删除的 7/8/9 位小数秒解析覆盖,并改写为可判别形式(断言"不应
  返回",解析失败会落入 fail-open 而被捕获)。已验证该测试在 PG 15 上
  无此修复时失败、有修复时通过。
- 新增 min fetch interval 下限与 debounce/interval 交叉校验的单元测试。
- integration harness 新增 SUB2API_TEST_POSTGRES_IMAGE 覆盖,使套件可
  针对最低受支持版本运行。
2026-07-25 18:34:37 +08:00
Wesley LiddickandGitHub bb0c38306f Merge pull request #4850 from alfadb/feat/ollama-usage-request-debounce
feat(ollama): 按模型请求刷新云端用量
2026-07-25 18:09:21 +08:00
shaw 5374ce2a0f Merge remote-tracking branch 'origin/main' into feature/openai-live-gateway 2026-07-25 15:16:05 +08:00
shaw 7acc13a29b fix(live): 租约丢失终止会话并补齐过期时的 usage log
三处功能修复(审计发现):

1. 租约续租失败按会话终结处理。RefreshLiveLease 的 Lua 在 leaseID 被 GC 后不会
   重新 ZADD,重连拿不回并发槽;原先把 ErrLiveUnavailable 当临时错误交给 observer
   重连,会让会话以约 1 秒一轮的节奏空转到 ExpiresAt(最长 60 分钟),期间持着
   上游 WS 连接却不计入账号/用户/API Key 的任何并发限制。

2. observer 因过期放弃时补写 usage log。waitForLiveObserverRetry 原先把过期判定
   混在重试条件里并返回 false,直接 return 绕过了 observeLiveCall 循环顶部的过期
   分支,导致该路径既不写 usage log 也不释放租约(静默结束)。改为只判控制权归属,
   过期交回循环顶部 finalize。

3. 把两处重复的三项终止判据抽成 liveSessionEnded,消除 ProxyLiveSideband 与
   observeLiveCall 之间的判据漂移风险。

迁移改名 186/187 -> 188/189:原编号会成为第三个 186,且 187 与已合入 main 的
#4801 的 187_add_usage_log_session_id.sql 撞号。文件名即迁移主键,功能无害但易误读。

新增两个测试均经变异验证(去掉修复后会失败)。
2026-07-25 15:15:59 +08:00
shaw 6b267e4f40 Merge branch 'main' into fix/announcement-display-and-preview
解决 frontend/pnpm-lock.yaml 冲突:采用 main 的 frontend/package.json 与
frontend/pnpm-lock.yaml。本 PR 附带的 postcss ^8.4.32 -> ^8.5.22 并非安全降级
(高于 main 的 pnpm.overrides 下限 >=8.5.18),但与本 PR 的公告弹窗主题无关,
回退后 PR 范围回归其本职改动。
本 PR 的功能改动(AnnouncementBell/Popup、announcement-markdown.css、
AnnouncementsView、i18n)未受影响。
2026-07-25 14:33:58 +08:00
shaw f06c8c5698 Merge branch 'main' into fix/issue-4798-gemini-image-chat-content
解决 frontend/pnpm-lock.yaml 冲突:采用 main 的 frontend/package.json 与
frontend/pnpm-lock.yaml,回退本 PR 附带的 postcss ^8.4.32 -> ^8.5.12 改动。
main 已通过 pnpm.overrides ("postcss@<8.5.18": ">=8.5.18") 强制安全下限,
本 PR 的直接依赖下限低于该值,保留会与 overrides 冲突且无安全收益。
本 PR 的功能改动(gemini_* compat service 及其测试)未受影响。
2026-07-25 14:33:06 +08:00
shaw 8bed40c67b Merge branch 'main' into fix/issue-4819-drop-orphan-tool-choice
解决 frontend/pnpm-lock.yaml 冲突:采用 main 的 frontend/package.json 与
frontend/pnpm-lock.yaml,回退本 PR 附带的 postcss ^8.4.32 -> ^8.5.12 改动。
main 已通过 pnpm.overrides ("postcss@<8.5.18": ">=8.5.18") 强制安全下限,
本 PR 的直接依赖下限低于该值,保留会与 overrides 冲突且无安全收益。
本 PR 的功能改动(openai_gateway_grok.go 及其测试)未受影响。
2026-07-25 14:32:18 +08:00
Wesley LiddickandGitHub 6d956bdc20 Merge pull request #4801 from SemonCat/feat/persist-session-id
feat(usage): persist client session identifiers
2026-07-25 14:03:32 +08:00
Wesley LiddickandGitHub 333acde7d1 Merge pull request #4832 from ListenCodes/fix/openai-apikey-responses-item-id-v2
fix(openai): sanitize API-key responses item IDs
2026-07-25 13:42:35 +08:00
Wesley LiddickandGitHub 9994eaa70f Merge pull request #4804 from scp-planet/fix/openai-strip-input-namespace
fix(openai): strip input item namespaces before HTTP forwarding / HTTP 转发前移除 input 项 namespace
2026-07-25 13:42:17 +08:00
Wesley LiddickandGitHub a9866f03db Merge pull request #4841 from wucm667/fix/issue-4838-affiliate-mobile-copy
fix(frontend): adapt affiliate copy controls for mobile
2026-07-25 13:42:03 +08:00
Wesley LiddickandGitHub 0e39e21fa2 Merge pull request #4796 from 404QAQ/codex/fix-image-request-diagnostics
fix(images): log requested quality and size
2026-07-25 13:41:52 +08:00
Wesley LiddickandGitHub 95cc6ee2c0 Merge pull request #4793 from 404QAQ/codex/fix-pricing-empty-remote-url
fix(pricing): skip remote scheduler without URL
2026-07-25 13:41:40 +08:00
Wesley LiddickandGitHub 37ed639d1e Merge pull request #4852 from Wei-Shaw/fix/bump-postcss-audit
fix(security): 升 postcss 到 >=8.5.18 修复 frontend-security 红灯
2026-07-25 12:01:42 +08:00
shaw a5aae5db9a fix(security): 升 postcss 到 >=8.5.18 修复 frontend-security 红灯
新披露两条 high 级公告命中锁文件里的 postcss@8.5.6,frontend-security 的
audit exception 检查失败:

- GHSA-6g55-p6wh-862q(2026-07-23 披露,修复版 8.5.12)
  CSS 注释中攻击者可控的 sourceMappingURL 导致任意文件读取与信息泄露
- GHSA-r28c-9q8g-f849(2026-07-24 披露,修复版 8.5.18)
  Previous Source Map 自动加载存在路径穿越,导致任意 .map 文件泄露

postcss 不只是 devDependency —— 它经 vue → @vue/compiler-sfc 进入生产依赖树,
因此 `pnpm audit --prod` 会命中。用 pnpm.overrides 而非只升直接依赖,可保证
所有引入路径的实例都被抬到修复版(沿用本仓 form-data@<4.0.6 的既有写法)。

锁文件用 pnpm 10 重新解析以匹配现有锁文件的生成工具,避免 pnpm 9 误删
11 处 libc: [glibc|musl] 平台门控字段;lockfileVersion 保持 9.0。
实际解析到 postcss 8.5.23,nanoid 3.3.11→3.3.16 是 postcss 自身依赖的
补丁级跟随,diff 无其他无关变动。

验证:复现 CI 失败步骤(pnpm audit --prod --audit-level=high +
tools/check_pnpm_audit_exceptions.py)已通过;CI 所用 pnpm 9 的
--frozen-lockfile 接受该锁文件;vue-tsc --noEmit、pnpm build、vitest 均通过。
2026-07-25 11:45:42 +08:00
shaw 6c9b84cc7a feat: 适配 Anthropic 新模型 claude-opus-5
模型登记:/v1/models 清单、Bedrock 默认映射(us.anthropic.claude-opus-5-v1)、
定价条目($5/$25 per MTok、1M 上下文、128K 输出)、前端模型清单与
Anthropic/Bedrock 预设映射、限流 scope 简称。

同时修复两个会静默出错的问题:

- 定价家族兜底 3 倍超收:定价数据缺 claude-opus-5 时,matchByModelFamily
  的 Phase 2 关键字兜底会落到 opus-4 系列、getFallbackPricing 会落到
  claude-3-opus,两条路都按 $15/$75 计费(官方 $5/$25),输入输出双双
  3 倍超收且无任何报错。两处补 opus-5 家族并回退到同价的 4.8;判断用
  opus-5/opus5 子串而非裸 "5",避免误伤 claude-opus-4-5。顺带补齐兜底表
  缺失的 claude-opus-4.8(此前同样会掉到 claude-3-opus)。

- Bedrock 版本闸门降级:claudeVersionRe 强制要求 major-minor 两段版本号,
  只有主版本号的 claude-opus-5 / claude-sonnet-5 完全不匹配,被当成旧模型:
  isBedrockOpus47OrNewer 假导致 thinking.enabled 不转 adaptive(Opus 5 上游
  已移除 budget_tokens,透传直接 400)、isBedrockClaude45OrNewer 假导致
  cache_control.ttl 被剥离、bedrockModelSupportsToolSearch 假导致 tool search
  被过滤。改为 minor 可选(缺省 minor=0),claude-sonnet-5 的同一问题一并修复。

Vertex 无需改动:normalizeVertexAnthropicModelID 只处理 -YYYYMMDD→@YYYYMMDD,
无日期后缀的裸 ID 原样透传即正确。context-1m-2025-08-07 白名单不动:Opus 系
上游不接受该 beta,且 Opus 5 的 1M 上下文是默认能力。

Antigravity 暂不接入:无上游支持证据,mapAntigravityModel 对未映射模型返回
空字符串即"该账号不支持",fails closed 安全。

回归测试 internal/service/claude_opus5_test.go 覆盖定价两层兜底、Bedrock
三个闸门、thinking 转换与模型清单;逐个回退上述修复已确认测试会红。
2026-07-25 11:22:42 +08:00
Wesley LiddickandGitHub cd8bb98c44 Merge pull request #4774 from superman2003/fix/issues-4763-4765-4769-20260723
fix: optimize Codex identity imports and OpenAI account tests
2026-07-23 17:38:05 +08:00
shaw 1be6f30188 fix(ollama): 测试仓储到期查询深拷贝消除数据竞争
ollamaUsageTestRepo.ListDueOllamaCloudUsageAccounts 返回浅拷贝共享
Extra/Credentials map,RunDue 过滤循环的无锁读与组写协程在 r.mu 下的
map 删改构成数据竞争(-race 可复现;无 -race 时也可能触发 runtime
concurrent map read/write fatal 导致 CI 偶发崩溃)。两条返回路径改为
r.mu 下 mergeMap 深拷贝,并补全 ListOllamaCloudUsageGroupAccounts
只拷 Extra 不拷 Credentials 的半克隆,与基类 GetByID 惯例对齐。
生产仓储每次查询返回全新行,不受影响。
2026-07-23 17:14:10 +08:00
shaw 2faa0891e4 fix(ollama): 审计日志不落会话明文并收紧凭证清理守卫
- PUT /admin/accounts/:id/ollama-cloud-usage/session 加入审计整体不入库
  路由,并把裸键 session 纳入键级脱敏兜底,防止浏览器会话 Cookie 明文
  留存 audit_logs.request_body
- UpdateCredentials 的 Ollama 清理分支加顶层 credentials DISTINCT 守卫,
  凭证未变化的持久化不再误清 openai 探测快照或重写 NULL extra
2026-07-23 16:43:18 +08:00
Wesley LiddickandGitHub 2c76506e07 Merge pull request #4734 from wjx2951874/feat/alipay-mobile-precreate-deep-link
feat(payment): add mobile Alipay precreate deep link
2026-07-23 14:06:18 +08:00
shaw ba88cc239c fix(billing): bill composite alias requests by the concrete forwarded model
Composite public aliases (e.g. all/claude) reach the Anthropic/Gemini
billing core via OriginalModel/ChannelMappedModel source overrides.
Unknown aliases resolved to no pricing and silently recorded $0 cost,
while family-word aliases were mispriced by the fallback family match
(Opus traffic billed at the Sonnet fallback rate). The OpenAI path
already guards this via usageBillingModelCandidates; the shared
recordUsageCore had neither the guard nor a fallback.

- composite groups: unless the admin explicitly configured channel
  pricing for the alias (OpenRouter-style custom pricing), bill by the
  concrete forwarded model
- general safety net: when the selected billing model has no resolvable
  pricing at all, fall back to the concrete forwarded model instead of
  silently recording $0
- grok media usage records now attribute OriginalModel to the client
  requested public alias, consistent with every other endpoint
  (billing unaffected: empty BillingModelSource never triggers source
  overrides)

Priced traffic and non-composite groups are unaffected.
2026-07-23 10:26:58 +08:00
shaw 90c4f50a5e fix(admin): restore currency and timestamps in admin plan list response
The composite-groups PR (#3581) replaced the raw ent SubscriptionPlan
response of GET /admin/payment/plans with a projection struct but
dropped the currency field added by #4323. PlanEditDialog then read an
undefined currency, sent an empty string on save, and silently wiped
the stored plan currency. Restore currency plus created_at/updated_at
so the projection preserves the full original response shape.
2026-07-23 10:26:46 +08:00
shaw fa2da0409e chore: update sponsors 2026-07-23 09:04:08 +08:00
shaw 63cef60594 chore: update sponsors 2026-07-22 22:21:43 +08:00
shaw d0bdd7e771 fix(usage): keep significant decimals in cost tooltip rate multiplier
formatMultiplier rounded any value >= 0.01 with toFixed(2), so a
configured multiplier like 0.035 displayed as 0.04x in the /usage and
/admin/usage cost tooltips. Format with up to 4 decimals and trim
trailing zeros while keeping at least 2 decimals; display-only fix,
billing amounts were already computed from the true multiplier.
2026-07-22 14:57:43 +08:00
Wesley LiddickandGitHub cffea2979b Merge pull request #4701 from nagi330/fix/records-model-filter-0721
fix(usage): 统一后台使用记录模型筛选口径,解决 grok 相关模型无法过滤问题
2026-07-22 14:51:15 +08:00
Wesley LiddickandGitHub 5e9d75b863 Merge pull request #4719 from superman2003/fix/grok-403-model-sync-4713-4715
fix(grok): sync OAuth models and isolate policy 403s
2026-07-22 14:26:07 +08:00
Wesley LiddickandGitHub 4126c4c091 Merge pull request #4716 from superman2003/fix/grok-codex-tool-protocol-4710
fix(grok): round-trip Codex client tools on Responses
2026-07-22 14:25:53 +08:00
shaw c5971a6fcb fix(deps): bump golang.org/x/text to v0.39.0 to resolve GO-2026-5970
govulncheck newly flags GO-2026-5970 (infinite loop on invalid input in
golang.org/x/text < v0.39.0), causing every fresh backend-security CI run
to fail. Upgrading x/text pulls sibling golang.org/x/* modules
(crypto/mod/net/sync/sys/term/tools) to their matching minor versions.
2026-07-22 09:33:47 +08:00
Wesley LiddickandGitHub 5a8d6c4e41 Merge pull request #4689 from moonfunjohn/codex/docs-payment-kyren-details
docs(payment): update Kyren Topup provider details
2026-07-21 17:27:06 +08:00
Wesley LiddickandGitHub a978d56c7b Merge pull request #4661 from J606y/fix/mobile-adaptation
fix(mobile): 修复 iOS 页面自动缩放并完善运维监控及全站移动端适配
2026-07-21 14:43:30 +08:00
shaw ef3c770d95 fix(deps): 升级 axios 至 1.18.1 修复 GHSA-gcfj-64vw-6mp9 前端安全审计失败 2026-07-21 10:30:41 +08:00
Wesley LiddickandGitHub 9da816154e Merge pull request #4654 from yyyyyzc/main
docs (dcoker-cpmpose): 修正示例 compose 中错误的镜像地址
2026-07-21 10:01:02 +08:00
Wesley LiddickandGitHub 27f094e096 Merge pull request #4638 from superman2003/fix/s3-secret-ephemeral-encryption-key
fix(backup): 拒绝用自动生成的临时密钥持久化 S3 SecretAccessKey,修复重启后解密失败
2026-07-20 16:21:46 +08:00
shaw fa402b909a feat(branding): 启用新版 SVG logo 并在 README 头部展示
- 新增 assets/logo.svg 作为项目默认 logo(512x512 深色圆角底 + 渐变 S 标)
- 前端 favicon 与组件兜底 logo 由 logo.png 切换为 frontend/public/logo.svg
- 三个 README(EN/CN/JA)头部新增居中 logo 与标题布局
- 删除旧 logo.png 及多余的 lobe 变体 SVG
2026-07-20 16:06:59 +08:00
shaw 3c41984733 style(apicompat): gofmt types.go after merge 2026-07-20 11:47:13 +08:00
shaw 04617cc0ff Merge branch 'main' into fix/grok-claude-messages-prompt-cache
Resolve const-block conflict in openai_gateway_grok_cache.go:
keep #4590 client tool cache constants and #4585 xai-package limit
(grokFreeRolling24hTokenLimit removed), add claudeCodeSessionHeader.
2026-07-20 11:39:37 +08:00
shaw a90c18cbea Merge branch 'main' into fix/issues-4561-4562-4566-4582
Resolve const-block conflict in openai_gateway_grok_cache.go:
keep #4590's client tool cache constants, drop grokFreeRolling24hTokenLimit
(moved to pkg/xai as IsGrokFreeRolling24hTokenLimit with legacy 2M support).
2026-07-20 11:25:11 +08:00
shaw 97afb4089c fix(lint): check strings.Builder WriteString return value (errcheck) 2026-07-20 10:46:37 +08:00
shaw 2518691dbc fix(config): 为 trusted_proxies 与 forwarded_client_ip_headers 注册 BindEnv,修复 env 可达性守卫测试
#4593 引入的 TestConfigKeysAreEnvReachable 与 #4604 新增的两个配置键语义相撞:
两键在 setDefaults 中无注册,守卫测试判定其环境变量不可达。

不能按守卫注释直接 SetDefault:viper 的 IsSet 会连同 defaults 一起上报,
任何已注册默认值都会让 load() 中 trustedProxiesConfigured 的显式配置探测
永远为真,摧毁 #4600 的 absent/empty 区分。改用 BindEnv 注册——绑定键同样
进入 AllKeys()(守卫测试与 Unmarshal 均可见),而变量缺席时不影响 IsSet。
两个环境变量本就由 load() 中 os.LookupEnv 手工解析,行为不变。
2026-07-20 09:41:02 +08:00
Wesley LiddickandGitHub bfabfe60c8 Merge pull request #4593 from StarryKira/fix/image-storage-env-unreachable
fix: 异步生图开关配了却不生效(环境变量被静默丢弃 + 迁移到后台开关)
2026-07-20 09:20:15 +08:00
Wesley LiddickandGitHub f3312cf7d9 Merge pull request #4604 from BenjaminAaron196/codex/fix-issue-4600-client-ip
fix: 修复客户端 IP 回退并支持自定义 CDN 请求头
2026-07-20 09:20:02 +08:00
shaw 30202f8266 chore: update sponsors 2026-07-20 08:55:15 +08:00
Wesley LiddickandGitHub 19149ca196 Merge pull request #4558 from fengshao1227/fix/antigravity-plan-type-preserve
fix(antigravity): 保留付费 tier 的 PlanType,IneligibleTiers 仅标记异常状态
2026-07-18 21:48:44 +08:00
Wesley LiddickandGitHub 831812b39d Merge pull request #4556 from superman2003/fix/grok-free-probe-encrypted-recovery
fix(grok): stabilize Free probes and encrypted reasoning recovery
2026-07-18 21:48:31 +08:00
Wesley LiddickandGitHub d98ac48eb9 Merge pull request #4543 from heathermhuang/codex/secure-protected-video-content-4498
fix(grok): securely proxy protected video content
2026-07-18 21:46:31 +08:00
shaw 14608dc6d4 Merge origin/main into codex/secure-protected-video-content-4498
Reconcile with #4539 (grok media account model mapping), now on main:
- handler/grok_media.go non-failover error path keeps both changes —
  #4539's grokMediaScheduleModel(account, routingModel, nil) schedule
  attribution and this branch's IsResponseCommitted guard
- auto-merged sections verified: routing/classify use #4539's routingModel,
  video lookup owner-binding and no-failover semantics intact, ForwardGrokMedia
  keeps mapping block (skipped for lookup endpoints via RequiresRequestBody),
  empty-image failover, and video-status URL rewrite in order
2026-07-18 21:38:17 +08:00
Wesley LiddickandGitHub daa4989124 Merge pull request #4539 from heathermhuang/codex/fix-grok-media-model-mapping-4503
fix(grok): apply account model mapping to media
2026-07-18 21:34:10 +08:00
shaw 8a95a46a69 Merge origin/main into codex/secure-protected-video-content-4498
Resolve conflicts with main:
- service/grok_media.go: keep both post-response blocks — #4497's empty
  image-output failover (main) runs first for image endpoints, then this
  branch's video-status content-URL rewrite; the endpoint conditions are
  mutually exclusive
- handler/openai_gateway_credential_failover_loop_test.go: mark the stub
  OAuth accounts media-eligible via the grok_media_eligible extra override,
  because this branch moved grok media failover coverage to the generation
  endpoint, which is now gated by #4540's paid-eligibility probe on main
2026-07-18 21:31:28 +08:00
shaw 9d498c2474 Merge origin/main into codex/fix-grok-media-model-mapping-4503
Resolve conflicts with main:
- handler/grok_media_test.go: keep both new tests (schedule model test from
  this branch, eligibility gating tests from #4540)
- service/openai_gateway_grok_test.go: keep both new tests; update the image
  cases of the mapping table test to return a non-empty image payload because
  #4497 (already on main) now converts empty image responses into an upstream
  failover error
2026-07-18 21:25:43 +08:00
Wesley LiddickandGitHub a2f802d409 Merge pull request #4541 from wucm667/fix/issue-4532-renew-expired-subscription
fix(subscription): renew expired admin assignments
2026-07-18 21:14:23 +08:00
Wesley LiddickandGitHub b01196a7a8 Merge pull request #4553 from wp-a/fix/openai-ws-turn-lifecycle
[codex] enforce websocket passthrough turn lifecycle
2026-07-18 21:10:48 +08:00
shaw 4e8ea7d568 fix(test): 池模式临时规则测试适配 #4547 模型级隔离语义
#4496 与 #4547 文本无冲突但语义相撞:#4496 的测试断言规则命中后账号级
跨模型封锁,#4547(issue 4527 第4点)将已知模型的临时不可调度改为按
模型隔离(SetModelRateLimit,不再触发账号级 runtime block)。

按 #4547 的语义更新断言:命中模型 gpt-5.4 记模型级封锁、gpt-5.5 不受
影响、不再调用账号级 SetTempUnschedulable;池模式规则仍生效(停止同
账号重试),issue 4470 的诉求不受影响。未知模型的账号级兜底已由
TestOpenAITempUnschedulable_UnknownModelKeepsAccountRuntimeBlock 覆盖。
2026-07-18 21:02:23 +08:00
Wesley LiddickandGitHub a3f2b8fd87 Merge pull request #4540 from heathermhuang/codex/investigate-grok-oauth-test-4525
fix(grok): retry CLI chat permission denial
2026-07-18 20:50:07 +08:00
Wesley LiddickandGitHub 23cdd20606 Merge pull request #4537 from heathermhuang/codex/refresh-anthropic-monitor-1953
fix(monitor): refresh Anthropic text-block extraction
2026-07-18 20:49:40 +08:00
Wesley LiddickandGitHub a62b821b5f Merge pull request #4478 from yardbirds0/codex/fix-upstream-billing-probe-refresh
fix: 完善上游 Sub2API 倍率探测刷新、展示与账号配置
2026-07-18 20:49:14 +08:00
Wesley LiddickandGitHub 774ff5d8c8 Merge pull request #4515 from BenjaminAaron196/feat/filter-noise-rejected-requests
(fix) 过滤入口拒绝日志并强化鉴权安全边界
2026-07-18 20:46:50 +08:00
Wesley LiddickandGitHub d2667393b3 Merge pull request #4522 from wucm667/docs/issue-4518-http-bridge-prerequisite
docs: clarify OpenAI WS mode router prerequisite
2026-07-18 20:43:01 +08:00
Wesley LiddickandGitHub e002fbb349 Merge pull request #4508 from wucm667/fix/model-not-found-transient-misclassification
fix: 临时账号耗尽时保留 503 错误分类
2026-07-18 20:41:35 +08:00
Wesley LiddickandGitHub 005bc5c8d4 Merge pull request #4497 from heathermhuang/agent/fix-grok-media-fallback-4471
fix(grok): fail closed for ineligible OAuth media
2026-07-18 20:41:24 +08:00
Wesley LiddickandGitHub bc4bd118d8 Merge pull request #4547 from heathermhuang/codex/fix-model-scoped-temp-cooldown-4527
fix(routing): isolate temporary cooldowns by model
2026-07-18 20:41:12 +08:00
Wesley LiddickandGitHub f3925db11b Merge pull request #4496 from StarryKira/agent/fix-4470-pool-temp-unschedulable
fix: honor temp unschedulable rules in pool mode
2026-07-18 20:40:12 +08:00
Wesley LiddickandGitHub f9a467a4c0 Merge pull request #4520 from StarryKira/codex/fix-4487
fix: report Chat Completions stream transport failures
2026-07-18 20:39:49 +08:00
Wesley LiddickandGitHub 8ce9288a6a Merge pull request #4489 from fengshao1227/fix/grok-free-cache-tool-injection
fix(grok): 纯客户端函数工具不再注入原生搜索工具
2026-07-18 20:39:37 +08:00
Wesley LiddickandGitHub c1e702be5e Merge pull request #4505 from cyhhao/fix/claude-1m-model-suffix
fix(gateway): normalize Claude Code 1m model suffix
2026-07-18 20:39:27 +08:00
Wesley LiddickandGitHub bc6b69289c Merge pull request #4468 from docooler/fix/responses-stream-content-part
fix(apicompat): emit content_part events and full output in Responses stream
2026-07-18 20:39:15 +08:00
Wesley LiddickandGitHub 5a0492bf88 Merge pull request #4517 from weiness/fix/custom-branding-flash
fix: prevent custom branding flash on initial load
2026-07-18 20:39:00 +08:00
Wesley LiddickandGitHub fdc9d92fdb Merge pull request #4528 from feitianbubu/fix/plan-validity-label-unit
fix(i18n): 套餐有效期表头与表单标签去掉写死的"天",与动态单位一致
2026-07-18 20:38:47 +08:00
Wesley LiddickandGitHub 8cdd372b7e Merge pull request #4507 from coo1white/fix-dockerfile-cross-compile
fix(docker): cross-compile the image instead of running Go under QEMU
2026-07-18 20:38:37 +08:00
Wesley LiddickandGitHub 080a52121a Merge pull request #4506 from coo1white/fix-compose-redis-command
fix(deploy): make the redis command flags take effect
2026-07-18 20:38:27 +08:00
Wesley LiddickandGitHub b1a6b80267 Merge pull request #4526 from Wei-Shaw/feat/security-switches-default-off
feat(security): 敏感操作 step-up 2FA 开关化,安全开关默认关闭
2026-07-18 11:32:17 +08:00
shaw 539bfc8bad feat(security): 敏感操作 step-up 2FA 开关化,安全开关默认关闭
新增系统设置 step_up_enabled(默认关闭),把敏感操作 2FA 门控做成可开关;
同时将会话 IP/UA 绑定默认值从开启改为关闭,避免用户因 IP 变动登录后掉线。

## 新增功能
- 敏感操作 step-up 2FA 总开关 step_up_enabled(默认关闭):关闭时账号/代理导出、
  备份创建/下载、S3 配置修改、提升管理员等操作恢复门控引入前的直接放行行为;
  开启后要求当前会话在 15 分钟内完成过 TOTP step-up 验证。

## 优化改进
- 会话 IP/UA 绑定默认改为关闭(功能保留,可在设置页按需开启)。
- 开启 step-up 开关需操作者本人已启用 TOTP(防自锁);关闭开关本身作为敏感操作,
  需通过 step-up 验证(防止攻击者拿到会话后先关闸再导出/备份)。
- 两个安全开关请求字段改为可空指针(省略=保持现值),避免旧客户端全量保存时
  静默重置安全开关。
- 备份恢复(整库覆盖可回滚安全设置)纳入 step-up 门控。
- 审计摘要 diffSettings 补记 step_up_enabled / session_binding_enabled 变更。

## Bug 修复
- 修复 BackupView 恢复操作 409(恢复进行中)判断未适配 apiClient 扁平化错误对象。
2026-07-18 10:46:42 +08:00
Wesley LiddickandGitHub 8bfbc5ca99 Merge pull request #4485 from Sub2API-Devs/dev
feat(security-audit): 新增 OpenAI 兼容提示词审计能力与安全审计控制台
2026-07-17 16:15:26 +08:00
Wesley LiddickandGitHub cb40288fb8 Merge pull request #4479 from fengshao1227/fix/backup-s3-stepup-totp
fix(frontend): saveS3Config 接入 step-up TOTP 门控
2026-07-17 16:14:49 +08:00