Commit Graph
5271 Commits
Author SHA1 Message Date
wucm667 0875143d98 fix: show optional affiliate code on registration 2026-07-26 02:47:35 +08:00
github-actions[bot] 2730c1c43b chore: sync VERSION to 0.1.165 [skip ci] 2026-07-25 13:59:09 +00:00
Wesley Liddick e9a58c1cb8 Merge pull request #4814 from yiancode/fix/email-alias-registration-dedup
fix(auth): 注册查重归一化邮箱别名,防止单收件箱批量注册
v0.1.165
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
yianandClaude Fable 5 b6f9277515 fix(auth): normalize email aliases in registration dedup
Registration duplicate checks compared the email verbatim (lowercase +
trim only), so a single inbox could spawn unlimited accounts via provider
alias features: plus addressing (user+tag@gmail.com) and the Gmail dot
trick (u.s.e.r@gmail.com) both deliver to the same mailbox but were seen
as distinct, verifiable addresses. This lets abusers bulk-register to farm
signup grants while the domain whitelist and email verification pass.

Add NormalizeEmailForAliasDedup to collapse these variants to a single
"inbox identity" and use it (via existsByEmailOrAlias) on the three local
email-registration paths: register, send-verify-code, and its async
variant. The exact ExistsByEmail check runs first; only on a miss do we
scan the candidate domains (gmail.com/googlemail.com are mutual aliases)
and compare normalized forms. Lookup errors fail closed, matching the
existing check, so the path cannot be bypassed by inducing errors.

Scoped to registration only — email storage, display, login, and delivery
are unchanged. OAuth-bound emails (already provider-verified) are out of
scope.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-25 19:19:15 +08:00
Wesley Liddick 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 Liddick bb0c38306f Merge pull request #4850 from alfadb/feat/ollama-usage-request-debounce
feat(ollama): 按模型请求刷新云端用量
2026-07-25 18:09:21 +08:00
alfadb b403f88f51 fix(ollama): 避免刷新候选饥饿
ListDue 在 LIMIT 前用与 service 纯函数一致的 debounce/max-wait/backoff
规则筛真正 due 组,防止有活动但未到期的组占满每轮 20 名额。
2026-07-25 15:37:06 +08:00
alfadb f1eaed31ed fix(ollama): 写入设置时缺省 debounce 补 1
兼容旧客户端省略 debounce_minutes 的 PUT 请求。
2026-07-25 15:37:06 +08:00
alfadb 0f72b7dca7 feat(ollama): 按模型请求刷新云端用量
将 Ollama Cloud settings HTML 自动抓取从固定间隔轮询改为请求驱动的
trailing debounce + max-wait:无新请求不再抓取,连续请求最晚在
max-wait 强制刷新;失败退避仍优先于活动 due。新增 debounce_minutes
(默认 1),interval_minutes 保留为最长等待兼容字段。
2026-07-25 15:37:05 +08:00
Wesley Liddick 43d4bae246 Merge pull request #4843 from slovx2/feature/openai-live-gateway
feat(openai): 支持 ChatGPT Live / Frameless 网关
2026-07-25 15:27:10 +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
Wesley Liddick b99ea955d9 Merge pull request #4818 from weiness/fix/announcement-display-and-preview
fix(frontend): 修复home页公告弹窗样式错乱问题,增加后台公告预览功能
2026-07-25 14:44:04 +08:00
Wesley Liddick e6a3030beb Merge pull request #4803 from wucm667/fix/issue-4798-gemini-image-chat-content
fix(gemini): preserve image output in chat completions
2026-07-25 14:43:30 +08:00
Wesley Liddick 3176cdee17 Merge pull request #4823 from wucm667/fix/issue-4819-drop-orphan-tool-choice
Supersedes the earlier attempt #4439 by @KnWsdHunter, which its own author closed on 2026-07-20 and which was never merged. This PR discloses that prior work in its description and contributes a broader 6-case test table. Courtesy credit to @KnWsdHunter for the original diagnosis.
2026-07-25 14:43:01 +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 Liddick a29a62112b Merge pull request #4842 from Cynicismcart/fix/openai-pool-retry-transient-cooldown
fix(openai): preserve pool-mode same-account retries
2026-07-25 14:30:04 +08:00
Wesley Liddick eb03be0dbf Merge pull request #4788 from wucm667/fix/issue-4782-pool-5xx-cooldown
fix(grok): avoid pool cooldown on 5xx
2026-07-25 14:29:36 +08:00
Wesley Liddick 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 Liddick 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 Liddick 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 Liddick 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 Liddick 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 Liddick 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
song db6fbdbf29 fix(openai): satisfy Live CI checks 2026-07-25 12:51:20 +08:00
song 988d4b577e feat(openai): add macOS Live attestation 2026-07-25 12:50:46 +08:00
song ec23716ee4 fix(openai): align Live request metadata 2026-07-25 12:50:46 +08:00
song e6eb23eaac feat(openai): add Live gateway support 2026-07-25 12:50:46 +08:00
Wesley Liddick 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
Cynicismcart 521db6869e fix(openai): preserve pool-mode same-account retries
Avoid recording the generic account-model transient cooldown for pool-mode statuses explicitly configured for same-account retry. This lets the bounded request-local retry budget complete while preserving cooldowns for other statuses and non-pool accounts.
2026-07-25 05:31:31 +08:00
wucm667 30d146e8b5 fix(frontend): adapt affiliate copy controls for mobile 2026-07-25 04:27:19 +08:00
LIULIXING 1891faa68d fix(openai): keep item ID sanitization linear 2026-07-25 01:40:26 +08:00
wucm667 41456b69dd fix(frontend): upgrade postcss for security audit 2026-07-24 22:37:42 +08:00
ListenCodes c5d9d57940 fix(openai): sanitize API-key responses item IDs 2026-07-24 20:15:58 +08:00
wucm667 41f12e874d fix(grok): drop orphaned Responses tool choice 2026-07-24 16:25:53 +08:00
weiness 67bb446b5c fix(deps): upgrade postcss for GHSA-6g55-p6wh-862q 2026-07-24 16:12:34 +08:00
weiness 972a4a471e feat(admin): add announcement preview action 2026-07-24 15:44:34 +08:00
weiness 7b387e479d fix(frontend): share announcement rich text styles 2026-07-24 15:44:27 +08:00
wucm667 b5979050f3 fix(frontend): upgrade postcss for security audit 2026-07-24 08:33:58 +08:00
wucm667 8276fdf605 fix: resolve Gemini compat test lint 2026-07-24 03:00:26 +08:00
scp-planet d2d4fb9221 fix(openai): satisfy buffer write lint checks 2026-07-24 02:59:32 +08:00
scp-planet 1bdf99109b fix(openai): strip input namespaces for api key accounts 2026-07-24 02:59:32 +08:00
scp-planet e70fbf3204 fix(openai): strip residual input namespaces before HTTP forwarding 2026-07-24 02:59:32 +08:00