shaw
8e102b3a0f
fix: 完善腾讯验证码区域适配与 CSP 白名单
...
修复国内站和国际站 SDK 构造、验证容器、票据重置及动态资源加载问题,并补充认证流程回归测试。
2026-08-06 20:34:37 +08:00
shaw
a19c9f8d8a
chore: update sponsors
2026-08-06 19:31:17 +08:00
shaw
a1936d42db
chore: update sponsors
2026-08-06 19:30:44 +08:00
shaw
c123caddd4
chore: update sponsors
2026-08-06 14:22:46 +08:00
Wesley Liddick
e08aee49ed
Merge pull request #5266 from shentry/fix/transient-streak-rate-dependence
...
fix(openai): keep transient failure streak from resetting on sparse traffic
2026-08-06 14:11:49 +08:00
Wesley Liddick
c9e60d1f26
Merge pull request #5031 from keaipiao/fix/easypay-error-utf8
...
fix(payment): preserve UTF-8 in EasyPay errors
2026-08-06 14:10:41 +08:00
Wesley Liddick
47c03c75d8
Merge pull request #5232 from fengshao1227/fix/billing-quantize-monetary-scale
...
fix(billing): quantize usage billing amounts to the NUMERIC(20,8) scale
2026-08-06 14:00:04 +08:00
shaw
00b8596176
chore: update sponsors
2026-08-04 21:55:34 +08:00
shaw
c5e046b7d7
chore: update sponsors
2026-08-04 21:54:50 +08:00
github-actions[bot]
aac53afe0e
chore: sync VERSION to 0.1.171 [skip ci]
2026-08-04 13:41:47 +00:00
Wesley Liddick
f0e7a9c7a2
Merge pull request #5223 from feeeei/main
...
feat(aliyun-captcha): 人机验证增加阿里云验证码 2.0
v0.1.171
2026-08-04 21:16:43 +08:00
feeeei
26e0a89323
人机验证增加阿里云验证码 2.0
...
沿用腾讯天御验证码引入的多服务商模型:aliyun_captcha_enabled 作为独立
开关,与 Cloudflare Turnstile、腾讯天御三方互斥(保存校验 + 运行时
CAPTCHA_PROVIDER_CONFLICT)。后台「安全与认证」合并为单张人机验证卡片:
总开关 + 服务商单选(Turnstile / 腾讯天御 / 阿里云),选中即启用该家并
关闭其它,落库仍是三个独立开关键,由前端映射保证互斥。
阿里云侧同时支持 aliyun 中国站与国际站(alibabacloud.com):两站前端脚本、
region 取值与服务端 API 完全一致,仅账号与 AccessKey 相互独立,因此由
「服务地域」决定线路即可——中国内地走 captcha.cn-shanghai.aliyuncs.com,
非中国内地(新加坡)走 captcha.ap-southeast-1.aliyuncs.com,AccessKey
取自持有该实例的账号,无需在配置中区分站点。
- AliyunCaptchaService 对称 TencentCaptchaService:服务端校验走官方 SDK
VerifyIntelligentCaptcha,调用异常按 fail-closed 拦截,与 Turnstile
网络错误行为对称;保存设置时真实探测 AK/SK 有效性
- 保护面对齐腾讯扩展入口:VerifyTencentCaptchaIfEnabled 通用化为
VerifyActionCaptchaIfEnabled,OAuth 登录启动、passkey 登录在阿里云
启用时同样拦截;Turnstile 维持既有覆盖不扩大
- 前端 AliyunCaptchaWidget 为表单内预验证按钮(popup 模式),同时暴露
verify() 供 OAuth 启动、passkey 等动作入口程序化弹窗;未预验证直接
提交时弹窗兜底。SDK 按钮绑定异步完成,弹窗未出现前按 tick 重试触发,
并轮询弹窗可见性识别用户关闭
- captchaVerifyParam 复用 turnstile_token 请求字段提交;公开设置下发
aliyun_captcha_enabled / scene_id / prefix / region
- CSP 放行验证码 CDN:script-src/style-src 加 *.alicdn.com
2026-08-04 20:57:15 +08:00
Wesley Liddick
d431c57f2e
Merge pull request #5268 from Wei-Shaw/fix/pending-oauth-captcha-submit-gate
...
fix(auth-ui): 第三方 OAuth 建号提交前必须持有验证码票据
2026-08-04 20:43:01 +08:00
shaw
635a27189f
fix(auth-ui): gate pending OAuth account creation on a captcha proof
...
#5261 added a captcha check to POST /auth/oauth/pending/create-account, but
the shared create-account form only gates its send-code button on the
Turnstile token — the submit button's disabled condition never included it.
Turnstile tokens are single-use, so handleSendCode resets the widget in its
finally block and clears the token. Submitting inside the window before the
widget re-solves omits turnstile_token from the payload, and the backend
answers ErrTurnstileVerificationFailed (turnstile_service.go:65). With an
interaction-required challenge the widget does not re-solve on its own, so
the failure persists until the user notices and verifies again — while the
button stays enabled and says nothing.
Gate the submit button on the token, mirroring the send-code button and
EmailVerifyView, which already gates its pending-OAuth submit the same way.
handleSubmit repeats the check because implicit form submission (Enter in a
text input) bypasses the button's disabled state.
The Tencent path is unaffected: handleSubmit already acquires a fresh proof
per submit, and turnstile_enabled is false in that configuration.
Verified with the component spec (11 passed) and vue-tsc --noEmit (clean).
2026-08-04 20:29:53 +08:00
shentry and Claude Opus 5
7d38e67120
fix(openai): keep transient failure streak from resetting on sparse traffic
...
The account+model transient breaker reset its failure streak whenever the
gap since the previous failure exceeded a one-minute window. That made the
breaker's sensitivity a function of request rate rather than upstream
health: a gateway called less often than once a minute never advanced past
streak 1, where the cooldown is zero, so a consistently broken account was
never blocked. Every request re-selected it, paid a full upstream attempt,
and only then failed over to a healthy account.
Observed on a low-traffic deployment: two accounts returning 500 and 503
stayed in rotation indefinitely, logging `failure_streak: 1, cooldown_ms: 0`
on every request and adding ~750ms to each one before a working account was
reached.
The streak is already cleared on success — recordSuccess deletes the entry,
and every OpenAI handler reports the schedule result — so the time-based
reset is not needed to recover a healthy account. Keep a TTL purely to bound
the map for account+model pairs that stopped being used, and raise it well
above the cooldowns so it no longer doubles as a streak reset.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com >
2026-08-04 17:59:16 +08:00
Wesley Liddick
8b3fe664dc
Merge pull request #5261 from lyen1688/feat/tencent-captcha-gate
...
新增腾讯天御验证码认证门禁
2026-08-04 16:39:55 +08:00
Wesley Liddick
a4d263f62f
Merge pull request #5224 from wucm667/fix/issue-5190-lock-subscription-renewal
...
fix(subscription): serialize concurrent renewals
2026-08-04 16:28:52 +08:00
Wesley Liddick
ae81dfd933
Merge pull request #5177 from r266-tech/fix-nonpassthrough-write-context
...
fix(openai-ws): preserve terminal event on lease loss
2026-08-04 16:28:22 +08:00
Wesley Liddick
35cab3c814
Merge pull request #5258 from feeeei/fix/model_plaza
...
fix(model-plaza): Model Plaza image model price display is inconsistent with the actual price
2026-08-04 16:27:53 +08:00
Wesley Liddick
770e35b474
Merge pull request #5040 from coo1white/upstream-pr/grok-cli-0.2.114
...
fix(grok): bump pinned Grok CLI version to 0.2.114
2026-08-04 16:16:20 +08:00
Wesley Liddick
846dd310a3
Merge pull request #5158 from neboyang/feat/claude-oauth-authorize-url
...
fix(oauth): use claude.com/cai authorize endpoint
2026-08-04 16:15:50 +08:00
Wesley Liddick
9b4575e434
Merge pull request #5243 from wucm667/feat/issue-5240-dashboard-username
...
fix(dashboard): show usernames in spending ranking
2026-08-04 16:15:21 +08:00
Wesley Liddick
1f4cfc44c1
Merge pull request #5226 from spongehah/codex/fix-prompt-audit-output-text
...
fix(prompt-audit): parse responses output text
2026-08-04 16:15:04 +08:00
lyen1688
e592c5f9e0
新增腾讯天御验证码认证门禁
2026-08-04 15:09:29 +08:00
Wesley Liddick
9fd7e76231
Merge pull request #5233 from Wei-Shaw/fix/codex-identity-enforcement
...
fix(codex): 强制统一出站身份并让客户端版本号跟随官方发布
2026-08-04 14:50:44 +08:00
feeeei
785b61d424
修复模型广场图片模型价格展示与实收口径不一致
...
图片计费模型的广场展示价按实收口径计算:档位单价取
分组图片价 > 渠道档位价 > 渠道默认按次价;分组开启生图
独立倍率时实付倍率取独立倍率,不取分组/专属倍率。
2026-08-04 13:48:19 +08:00
shaw
2d3e845205
perf(codex): 版本同步主路径改用 /releases/latest 并保留列表回退
...
自动同步原本每次拉 `/releases?per_page=30`,实测响应 10,017,686 字节——该仓库
预发布极密集,30 条里只有 2 条稳定版(0.145.0 已排到第 26 位),页大小是为了
「窗口里至少有一条稳定版」而定的,不能靠调小来省流量。
改为主路径走 `/releases/latest`(实测约 0.3MB):该端点本身排除 draft 与
prerelease,直接给出最新正式发布,不受预发布密度影响。复用端口上已有的
FetchLatestRelease,不新增任何 HTTP 代码,代理配置、User-Agent、可选 token
与跨主机重定向剥离 Authorization 的行为全部沿用。
保留列表扫描作为回退:latest 是跨 tag 家族按 published_at 取的,若同仓库其他
组件(如 rusty-v8-*)某天发布正式 release 而成为 latest,主路径会被 rust-v
前缀过滤挡掉,此时必须扫一页才能继续跟随,否则版本号会静默停更。
两条路径共用 latestCodexStableReleaseVersion 的同一套过滤(前缀 / draft /
prerelease / 版本号形态),语义不会分叉;单条 latest 也要过版本号校验——仓库里
确实存在 rust-vv0.99.0-alpha.8 这类畸形 tag。只向前推进、抓取失败保持既有值
两条不变式未改动。
测试覆盖:主路径命中时不再拉列表页、四种拿不到(异家族 / 预发布 / 抓取失败 /
空对象)都回退、畸形 tag 被主路径拒绝后由回退兜底、两路径皆失败时保值。
2026-08-04 10:51:04 +08:00
wucm667
80af44cad7
[verified] fix(dashboard): show usernames in spending ranking
2026-08-04 00:43:05 +08:00
shaw
c899c8cf37
fix(codex): 管理员配置的 UA 只贡献指纹,版本段一律用生效版本重建
...
面板「OpenAI Codex UA」此前只在客户端 UA 是浏览器型时才生效,本分支把它提升为
所有 OAuth 出站的规范身份来源,作用域扩大了一个数量级。而它的旧 placeholder 原文
就是 codex_cli_rs/0.144.1 (...):照抄填写过的存量部署会被永久钉在 0.144.1 上——
该值不低于上游门槛 0.144.0,version 头也随之变成 0.144.1,绕过版本自动同步,
稳定落在上游优先降载的那一侧,且面板、日志、审计都不提示。账号级自定义 UA
(credentials.user_agent)有完全相同的陷阱。
改为:管理员配置的 UA 只贡献客户端名与 OS / 架构 / 终端指纹(这是该输入框唯一
不可替代的价值),版本段一律用当前生效版本重建。存量的陈旧配置无需迁移即自愈,
UA 与 version 头从此由构造保证同源,原先「覆写 UA 陈旧时二者不一致」的次优解消失。
- 新增 openai.SetCodexUserAgentVersion:重建首段版本声明,OS / 架构 / 终端指纹
原样保留;尾部官方客户端标识组 (name; version) 与首段同源,一并更新,避免拼出
首段声明新版本、尾部仍是旧版本的自相矛盾身份;非官方括号组(如 OS 组)不动。
- resolveCodexOutboundIdentity 收敛为单一funnel:生效版本只从规范身份取,
候选 UA 的版本段不再参与判定。
- GetOpenAICodexCanonicalUserAgent 重建面板 UA 的版本段;原「面板值等于兜底常量
视同未填」的特例随之成为恒等变换,删除。
- 补齐 4 处静默丢弃账号级自定义 UA 的出站路径(WS 握手、账号测试 x2、用量探针):
它们先写 customUA 再调不带 override 的收口,赋值随即被覆盖,既是行为不一致
也是死代码;账号测试尤其要紧——注释写着「与真实转发一致」而实际不一致。
- 补测试:SetCodexUserAgentVersion / CodexUserAgentVersion 包内用例、陈旧面板 UA
与账号 UA 的版本重建回归、WS 握手尊重账号级 UA。
2026-08-03 21:50:49 +08:00
zhiyu
4c4ff36380
perf(codex): 版本同步间隔改为 6 小时并加启动防抖
...
- 同步间隔 3h → 6h:客户端版本是天级变化,6 小时足够跟上,
对 GitHub 的调用降到每天 4 次。
- 新增启动防抖:借同步设置行自身的 UpdatedAt 判断,若同步值仍在一个周期内
则跳过启动同步。频繁重启、滚动发布或崩溃重启原本会把「启动即同步」
放大成对 GitHub 的连续请求;首次部署尚无同步值时不受影响。
- 补测试:启动防抖两个方向,以及版本比较按段取数字的回归
(字典序会把 0.99.0 判为大于 0.146.0,同时让「取最大值」与
「只向前推进」两处判错)。
2026-08-03 20:55:37 +08:00
zhiyu
1e08c4c56f
test(server): 补齐 settings 契约用例的 Codex 版本号字段
...
新增的三个设置键会出现在 GET /api/v1/admin/settings 响应里,
契约用例的期望 JSON 需同步,否则 -tags=unit 下断言失败。
2026-08-03 20:26:50 +08:00
zhiyu
2eb24814fe
fix(codex): 强制统一出站身份并让客户端版本号跟随官方发布
...
上游 /backend-api/codex 在容量紧张时按客户端身份分优先级降载,被降载的请求
HTTP 200 后立刻推流内 server_is_overloaded。此前网关对配不出官方身份的客户端
整体回退到硬编码的 codex_cli_rs/0.144.1(落后官方 4 个发布),这些请求稳定
落在被优先丢弃的一侧。
- 强制统一出口:所有 OAuth 出站的 User-Agent / originator / version 一律改写
为网关规范身份,客户端自报身份不参与构造;HTTP / 透传 / WS / alpha-search /
探针全覆盖。compat 桥接故意删除 originator 的路径保持 no-op。
- 版本号收敛为单一来源,运行时优先级为面板覆写 → 自动同步值 → 内置常量;
UA 与 version 头同源派生,不再各自硬编码。
- 新增 3 小时自动同步官方客户端最新稳定版,面板可关闭,无需为跟版本而发版。
- 流内 server_is_overloaded / slow_down 改为先在同账号有界重试再切号,并标记为
请求级瞬时故障,不再据此临时封禁账号。
- 移除被取代的降载身份黑名单、浏览器 UA 兜底及其辅助函数。
2026-08-03 20:14:58 +08:00
li
e2652eb853
fix(billing): quantize usage billing amounts to the NUMERIC(20,8) scale
...
同一笔 ActualCost 会被分别写入两条方向相反的 SQL:
balance = balance - $1 -- 存剩余额度,舍入的是"减法结果"
quota_used = quota_used + $1 -- 存累计用量,舍入的是"加法结果"
两列都是 NUMERIC(20,8),PostgreSQL 按 half-away-from-zero 舍入运算结果。
金额在第 9 位落到 half 边界时,两侧朝相反方向舍入:
10 输入 token × 0.00000125 + 5 输出 token × 0.00001000 = 0.0000625
× 1.25(分组倍率) = 0.000078125
balance: 10000 - 0.000078125 = 9999.999921875 → 9999.99992188 delta 0.00007812
quota_used: 0 + 0.000078125 = 0.000078125 → 0.00007813 delta 0.00007813
余额少扣、API Key 配额多记,单次相差 1e-8 且随请求量线性累积(1000 次 ≈ 1e-5 USD),
余额、Key 配额与用量记录无法精确对账,只能靠 epsilon 比较勉强吻合。
修复:在参数进入 SQL 之前,把命令中的全部金额统一量化到 8 位小数
(half-away-from-zero,与 PostgreSQL NUMERIC 一致)。两条语句拿到的是
同一个已落在 8 位刻度上的金额,存储阶段不再发生舍入,delta 精确相等。
- 走 shopspring/decimal 而非 math.Round(v*1e8)/1e8:后者在乘除中引入额外
二进制误差,边界值可能被推到错误的一侧
- 量化排在指纹计算之后:指纹是请求幂等键,保持由原始金额派生,
避免升级前后同一 request_id 的重试算出不同指纹而被判为 fingerprint conflict
Fixes #5229
2026-08-03 20:00:32 +08:00
spongehah
1b04e03cc4
fix(prompt-audit): parse responses output text
2026-08-03 17:38:04 +08:00
wucm667
2be047d1cd
[verified] test(subscription): update unit repository stubs
2026-08-03 17:22:18 +08:00
wucm667
db725a775a
[verified] fix(subscription): serialize concurrent renewals
2026-08-03 16:59:05 +08:00
Wesley Liddick
825ca7b1fc
Merge pull request #5183 from rick147/codex/feat-openai-reset-credit-cache
...
feat(openai): refresh reset credit state after quota reset
2026-08-03 16:01:10 +08:00
shaw
54a2bcfd15
fix(openai): harden reset-credit refresh and account recovery
...
Review follow-ups on the reset-credit caching flow:
- Recover account state BEFORE (and independently of) the reset-credit
display cache. A failed cache refresh could previously abort the run and
leave the account rate-limited — the very reason the credit was spent
(#3672 / #3740 ). The recovered account row is now returned even when the
cache refresh fails.
- Run the post-reset bookkeeping on a detached, time-boxed context and give
the panel reset call a larger timeout. A client abort no longer strands a
consumed (non-refundable) credit with an unrecovered account, and the
chained upstream calls can no longer exceed the client timeout and invite a
retry that spends a second credit.
- Persist the reset-credit snapshot through POST /accounts/:id/quota/refresh
instead of a side-effecting GET flag, so the write is covered by the audit
middleware. A rejected snapshot write now degrades to cache_persisted=false
instead of turning a successful upstream read into a 502 that left the card
without a credit count and the reset button permanently disabled.
- Reject snapshots whose positive count carries no expiration entries, and
drop expired credits (clamping the count) when rehydrating, so a stale
cache can no longer light up the reset button.
- Keep nil quota / rate-limit services nil in the handler's interface fields;
storing a nil *Service made the "not enabled" guards non-nil.
- Time-box the usage-refresh suppression and reuse handleAccountUpdated so the
patched row also enters the auto-refresh silent window.
2026-08-03 14:40:55 +08:00
Wesley Liddick
27e8f69a9e
Merge pull request #5171 from heathermhuang/codex/composite-reasoning-policy
...
feat(composite): enforce reasoning effort policy
2026-08-03 11:28:38 +08:00
Wesley Liddick
684ab20a0b
Merge pull request #5164 from wucm667/fix/issue-5099-messages-temp-failover
...
fix(openai): fail over Messages temporary account errors
2026-08-03 11:28:16 +08:00
Wesley Liddick
0173830df6
Merge pull request #5193 from wucm667/fix/issue-5187-stripe-refund-idempotency
...
fix(payment): make Stripe refunds idempotent
2026-08-03 11:28:05 +08:00
Wesley Liddick
724565e4aa
Merge pull request #5199 from wucm667/fix/issue-5191-refund-balance-force
...
fix(payment): require force for insufficient refund balance
2026-08-03 11:27:52 +08:00
Wesley Liddick
a03de418c8
Merge pull request #5192 from luckydududu/up/admin-refund-require-force
...
fix(admin-ui): 退款 require_force 前端接线,补上无法完成的退款场景
2026-08-03 11:27:37 +08:00
Wesley Liddick
61ebdbdd43
Merge pull request #5200 from mrlitong/fix/auth-refresh-race
...
fix(auth): prevent refresh token races across tabs
2026-08-03 10:46:36 +08:00
Wesley Liddick
a1f1a0cc6b
Merge pull request #5194 from wucm667/fix/issue-5189-persist-unsettled-usage
...
fix(billing): retain usage logs on billing failure
2026-08-03 10:27:30 +08:00
Wesley Liddick
954d44c19c
Merge pull request #5198 from Wei-Shaw/fix/codex-originator-load-shed
...
fix(codex): 归一化降载 originator,缓解 Codex 账号频繁过载不可用
2026-08-03 09:59:34 +08:00
litongtongxue@gmail.com
38081ef72e
fix(auth): prevent refresh token rotation races
2026-08-02 08:13:15 -07:00
wucm667
3c20f9a666
[verified] fix(payment): require force for insufficient refund balance
2026-08-02 23:11:17 +08:00
shaw
e1b76e2245
fix(codex): normalize load-shed originators to avoid upstream capacity shedding
...
上游 /backend-api/codex 按 Originator 头分桶调度容量:落在降载桶的请求即使返回
HTTP 200,也会立刻推 SSE `event: error`(code=server_is_overloaded)并以
response.failed 收尾。2026-07-29 起 codex-tui 落入降载桶,codex_cli_rs 正常——
判定因子是 originator 而非 User-Agent(codex_cli_rs 配 curl UA 亦可正常返回)。
网关会把该错误判定为瞬时上游故障并冷却账号,对外表现为 Codex 账号频繁过载不可用:
server_is_overloaded → isOpenAITransientProcessingError →
shouldCooldownOpenAITransientUpstreamError → 账号冷却 → 客户端 503。
本项目有三处降载身份来源:浏览器 UA 兜底的默认 UA、客户端透传的真实 TUI 身份、
以及指纹缓存注入探针的 UA。
修复收口在 enforceCodexIdentityHeaders——HTTP / 透传 / WS 握手 / compat 桥接 /
探针 / PAT / 模型列表 / alpha-search 八条出站路径共用的唯一纯函数收口点:
- 新增 NormalizeCodexClientIdentityToCLI,把降载桶身份改写为 codex_cli_rs,
只替换身份段并裁掉尾部 (name; version) 客户端标识组,保留版本 / OS / 架构 /
终端指纹;改写后 originator 与 UA 首段仍然配套,不破坏 #3901 的配对不变式,
且改写幂等。
- DefaultOpenAICodexUserAgent 从 TUI 身份改为 CLI 身份(浏览器兜底路径上最大的
降载身份来源)。
- 管理端 Codex UA 的 placeholder / hint 原本在把管理员往降载桶引导,一并修正。
新增 gateway.disable_codex_originator_normalization(默认 false,即归一化开启),
供上游调整分桶后回滚。该开关经 NewOpenAIGatewayService 发布为进程级快照,故必须
保持反义命名:正向命名的 Go 零值 false 会让未经 viper 加载而手工构造的 Config
静默关掉全局保护,viper.SetDefault 救不了这条路径。已加用例钉住该属性。
降载桶集合是上游容量策略快照而非协议常量,上游调整分桶后需同步修订。
2026-08-02 23:00:12 +08:00
rick147
f970bd48c9
fix: stop scheduler work after request cancellation
2026-08-02 21:32:31 +08:00