wucm667
c9caadb378
fix(account): address second-round review on quota auto-pause
...
- TopK initial filter now drops quota-paused accounts: fold the quota check
into isAccountRequestCompatible so session-hash, TopK pool, and per-candidate
rechecks all skip paused accounts. Previously the candidate pool was built
without the quota check, so paused accounts could fill TopK and leave the
scheduler returning "no available accounts" even with healthy ones available.
- Add per-account explicit disable flags auto_pause_5h_disabled /
auto_pause_7d_disabled with toggles in EditAccountModal. Without these,
leaving the account threshold blank silently falls back to the global default,
so admins could not exempt a single account once a global default existed.
Disable is per-window: an account can opt out of 5h auto-pause while still
honoring 7d. Schedule snapshot whitelist includes the new fields, i18n EN/ZH
updated, threshold-hint text revised to explain "blank = global default".
- Move quota auto-pause settings off the request hot path: replace the per-repo
TTL+singleflight sync DB read with a per-SettingService stale-while-revalidate
in-memory snapshot. Get is non-blocking (atomic.Pointer load + async refresh
on staleness); writes via UpdateOpsAdvancedSettings push directly into the
cache through an injected sink; wire warms the cache at startup. Adds Warm
(sync) for tests/init and SetOpenAIQuotaAutoPauseSettings (sink target).
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-29 14:32:45 +08:00
wucm667
ead471d64b
feat(account): 支持按 5h/7d 用量阈值自动暂停账号调度
2026-05-29 10:47:47 +08:00
DaydreamCoding
56908d3c4c
feat(openai): codex_cli_only 新增放行 Claude Code Codex 插件的机制
...
适用场景:在 Claude Code 中使用 https://github.com/openai/codex-plugin-cc
插件时,插件经官方 codex app-server 以 clientInfo.name="Claude Code" 完成
initialize 握手,请求头被设为 originator=Claude Code、User-Agent 含
"Claude Code/",不在官方客户端白名单内,原本会被 codex_cli_only 拦截 403。
在官方客户端白名单未命中时评估两层独立放行(OR 语义):
- 按账号:account.Extra.codex_cli_only_allowed_clients 引用命名预设
(目前仅 claude_code),detector reason=allowed_client_matched
- 全局开关:/admin/settings 网关服务 OpenAI 区块新增
openai_allow_claude_code_codex_plugin(默认 false),开启后对所有
codex_cli_only 账号统一放行,detector reason=global_allowed_client_matched
签名仍要求 originator=Claude Code 精确等值 + UA 含 "Claude Code/"。
上游转发保持透传不变。
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-27 23:55:34 +08:00
DaydreamCoding
6b39b344d8
feat(quota): 用户 × 平台 USD 配额
...
为用户在 anthropic/openai/gemini/antigravity 四个平台上提供日/周/月
三个窗口的 USD 配额管控。配额语义:未设置=不限制,0=禁用,>0=美元上限。
两层模型:
- 配置层:系统默认配额,以及 email/linuxdo/oidc/wechat/github/google/
dingtalk 七个鉴权来源的默认配额,存于 settings,以嵌套 JSON 整体读写
(系统 1 个 key + 每个来源 1 个 key),整体替换语义。
- 运行时层:user_platform_quota 表按用户记录实际配额,与配置层解耦。
后端:新增 ent schema 与 140_user_platform_quotas.sql 迁移、repository
与 service 端口、计费链路集成、管理端与用户端读写接口。
前端:管理端设置页配额编辑、用户配额管理 Modal、用户 Dashboard 展示、
中英文案。
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-26 10:49:20 +08:00
shaw
a613a587ba
feat: add subscription expiry email toggle
2026-05-21 14:27:50 +08:00
lyen1688
1d2445ff52
修复 API Key ACL 开关的 CI 校验
2026-05-20 23:51:39 +08:00
lyen1688
08c8c67df7
为 API Key ACL 增加反代真实 IP 开关
2026-05-20 22:51:46 +08:00
shaw
878ad3b569
feat(openai-gateway): Codex OAuth 账号浏览器 UA 自动改写规避 Cloudflare
...
质询
2026-05-20 14:33:51 +08:00
DaydreamCoding
b19da9c7fe
feat(dingtalk): 钉钉 OAuth 登录接入与 internal_only 用户属性同步
...
⚠️ 应用类型约束:当前实现仅支持「钉钉登录-企业内部应用」(DingTalk 开放平台
internal_app 类型)。第三方个人应用、第三方企业应用类型暂不支持——OAuth 流程
相同但 corp 校验、跨企业行为不同。backend 通过 DingTalkAppKind 校验对非
internal_app 类型 fail-closed(硬约束)。
钉钉 OAuth 登录主链
- 4 步 OAuth 链:ExchangeCodeForUserToken / GetUnionIdByUserToken /
GetUserIdByUnionId / GetStaffInfoByUserId;app token 缓存
- pending session 机制持久化 OAuth 中间态;cookie-only token 持久化
- 三种分流:bind_login_required / email_completion / choose_account_action
- corp_restriction_policy 支持 none + internal_only;stale "whitelist" 在
加载层与写入层均静默 coerce 为 none + slog.Warn
- bypass_registration 开关:企业内部模式豁免全局 REGISTRATION_DISABLED
- isReservedEmail / signup_source / canUnbindProvider / OAuth pending flow
等横切点支持 dingtalk provider
- migration 136:4 表 CHECK 约束加入 'dingtalk' provider 值
internal_only 模式同步企业邮箱/姓名/部门到用户属性
- SyncCorpEmail / SyncDisplayName / SyncDept 三个独立开关 + 对应
SyncXxxAttrKey 目标属性 key(默认 dingtalk_email / dingtalk_name /
dingtalk_department);非 internal_only policy 在写入层与加载层均
coerce 为 false,admin handler 与 setting_service 双层兜底
- 同步语义:首次注册写 users.username(昵称优先 → 企业姓名 fallback),
之后每次登录刷新 3 个属性;空值也写入以覆盖旧值
- 邮箱三级 fallback:org_email > email > extension["企业邮箱"]
(钉钉自定义字段 JSON)
- 部门路径递归向上拼接,跳过 dept_id=1 选首个真实子部门,剥离根组织名
- GetUnionIdByUserToken 同时返回 OIDC /contact/users/me 的 nick 字段;
新增 GetDeptInfo 调用 OAPI /topapi/v2/department/get
- AuthHandler 注入 UserAttributeService;OAuth pending flow 在
createPendingOAuthAccount / bindPendingOAuthLogin 分别派发到
AfterRegistration(syncUsername=true)/ AfterLogin
- migration 137 seed dingtalk_email/name/department 三个用户属性定义
附带修复(同集成路径暴露的两个 OAuth 注册回归)
- LoginOrRegisterOAuthWithTokenPair 新建用户分支用 inferLegacySignupSource
覆写 caller 显式传入的 signupSource,导致 dingtalk/linuxdo/oidc/wechat
渠道授权按 email 渠道读取;改为只在 caller 未显式传入时回退邮箱推断
- mergeProviderDefaultGrantSettings 把 parse fallback 默认值
(Concurrency=5 / Balance=0) 当作"未配置"哨兵,admin 显式设 5 时被误判
退回全局默认(复现:全局默认 1 + 渠道默认并发 5 + grant_on_signup → 新
用户实际 concurrency=1);去掉哨兵,admin 任何 >=0 值都覆盖 globalDefaults
前端
- DingTalk Login / Callback / EmailCompletion / ChoiceAccount / Error
视图;router + auth API client
- admin SettingsView:corp policy radio(none / internal_only)+ bypass
注册开关 + i18n;internal_only 下展示三同步开关 + 目标 attr key 下拉
(拉取 user attribute definitions),展示 fieldEmail /
qyapi_get_department_list 钉钉权限申请提示
- Profile:S1 主动绑定 / S5 解绑钉钉按钮 + 合成邮箱防自锁
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-19 15:27:47 +08:00
shaw
a07a0dac63
feat: add configurable Antigravity user agent version
2026-05-11 22:25:20 +08:00
shaw
9377c96746
fix: 让消息 cache_control 改写默认关闭
2026-05-11 21:26:41 +08:00
shaw
8a835b22bb
ci: fix lint and test failures
2026-05-07 19:26:18 +08:00
shaw
e872cbec0b
feat: 添加登录注册条款确认
2026-05-07 17:35:05 +08:00
Wesley Liddick
e69319e747
Merge pull request #2224 from lyen1688/feat-email-oauth-github-google
...
feat: 增加 GitHub 和 Google 邮箱快捷登录
2026-05-07 10:07:28 +08:00
Wesley Liddick
d52da45363
Merge pull request #2202 from Michael-Jetson/main
...
新增三大功能:兑换码邀请返利、批量修改用户并发数、Markdown页面渲染
2026-05-07 09:35:14 +08:00
shaw
fff4a300c6
feat(risk-control): add content moderation audit
2026-05-07 09:14:47 +08:00
lyen1688
af550fa64e
feat: 增加 GitHub 和 Google 邮箱快捷登录
2026-05-06 16:06:11 +08:00
Michael-Jetson
cf2d5067c3
fix(security): add JWT auth + visibility check to pages API
...
- GET /pages/:slug now requires JWT + checks custom_menu_items visibility
- GET /pages (list) is admin-only
- GET /pages/:slug/images/* uses visibility check without JWT (browser
img tags cannot carry auth headers), blocks admin-only page images
- Frontend fetch adds Authorization header from authStore.token
- settingService nil guard changed to fail-closed (deny access)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-05-05 07:00:08 -07:00
Wesley Liddick
37f7c7128c
Merge pull request #2120 from gaoren002/fix/rate-limit-429-cooldown-config
...
fix(rate-limit): make 429 fallback cooldown configurable
2026-05-05 19:46:11 +08:00
shaw
73b872998e
feat: 添加 Anthropic 缓存 TTL 注入开关
2026-04-30 13:38:22 +08:00
gaoren002
4b904c887c
fix(rate-limit): make 429 fallback cooldown configurable
2026-04-30 03:01:39 +00:00
DaydreamCoding
30f55a1f72
feat(openai): OpenAI Fast/Flex Policy 完整实现(HTTP + WebSocket + Admin)
...
对称参照 Claude BetaPolicy 的 fast-mode 过滤实现,新增针对 OpenAI 上游
service_tier 字段(priority / flex,含客户端 "fast" → "priority" 归一化)的
pass / filter / block 三态策略,覆盖全部 OpenAI 入口 + admin 配置入口。
后端核心
- 新增 SettingKeyOpenAIFastPolicySettings、OpenAIFastPolicyRule、
OpenAIFastPolicySettings 配置模型,含规则的 service_tier × action × scope
× 模型白名单 × fallback action 维度。
- SettingService.Get/SetOpenAIFastPolicySettings;缺失时返回内置默认策略
(所有模型的 priority 走 filter,whitelist 为空,fallback=pass)。设计
依据:service_tier=fast 是用户级开关,与 model 字段正交,默认锁定特定
model slug 会留下"用 gpt-4 + fast 透传 priority 上游"的绕过路径。JSON
解析失败不再静默 fallback,slog.Warn 记录脏数据,便于运维定位。
- service_tier 归一化(trim + ToLower + fast→priority + 白名单 priority/flex)
与策略评估(evaluateOpenAIFastPolicy)作为唯一真实来源,HTTP / WS 共用。
抽出纯函数 evaluateOpenAIFastPolicyWithSettings,配合 ctx-bound settings
快照(withOpenAIFastPolicyContext / openAIFastPolicySettingsFromContext),
WS 长会话入口预取一次后所有帧复用,避免每帧打到 settingService。
HTTP 入口(4 个)
- Chat Completions、Anthropic 兼容(Messages,含 BetaFastMode→priority 二次
命中)、原生 Responses、Passthrough Responses 全部接入
applyOpenAIFastPolicyToBody,filter 走 sjson 顶层删除 service_tier,block
返回 403 forbidden_error JSON。
- 4 入口统一使用 upstream 视角的 model(GetMappedModel +
normalizeOpenAIModelForUpstream + Codex OAuth normalize 后的 slug),
避免 chat/messages/native /responses/passthrough 因为 model 维度不同
造成 whitelist 命中差异。
- 在 pass 路径也把客户端 "fast" 别名归一化为 "priority" 写回 body,
否则 native /responses 与 passthrough 入口会把 "fast" 原样透传给上游
导致 400/拒绝(chat-completions 入口的 normalizeResponsesBodyServiceTier
此前已具备同等行为)。
WebSocket 入口
- 新增 applyOpenAIFastPolicyToWSResponseCreate:严格匹配
type="response.create",仅处理顶层 service_tier;filter 用 sjson 删字段,
block 返回 typed *OpenAIFastBlockedError。
- ingress 路径在 parseClientPayload 内调用,block 命中先 Write Realtime
风格 error event 再返回 OpenAIWSClientCloseError(StatusPolicyViolation
=1008),依赖底层 WebSocket Conn.Write 的同步 flush 保证 error 先于
close。
- passthrough 路径在 RunEntry 前对 firstClientMessage 应用策略,并通过
openAIWSPolicyEnforcingFrameConn 包装 ReadFrame 对每个 client→upstream
帧执行策略;后续帧无 model 字段时回退到 capturedSessionModel。
filter 闭包内同时侦测 session.update / session.created 帧的 session.model
字段刷新 capturedSessionModel,封堵"首帧 model=gpt-4o(pass)→
session.update 改为 gpt-5.5 → 不带 model 的 response.create fallback
到 gpt-4o"的 mid-session 绕过路径。
- passthrough billing:requestServiceTier 在策略 filter 之后再从
firstClientMessage 提取,filter 命中时 OpenAIForwardResult.ServiceTier
上报 nil(default tier),与 HTTP 入口(reqBody 来自 post-filter map)
/ WS ingress(payload 来自 post-filter bytes)的语义一致。
- 错误事件 schema:{event_id: "evt_<32hex>", type: "error",
error: {type: "forbidden_error", code: "policy_violation", message}},
与 OpenAI codex 客户端 error event 解析兼容。
Admin / Frontend
- dto.SystemSettings / UpdateSettingsRequest 新增
openai_fast_policy_settings 字段(omitempty),bulk GET/PUT 接入。
- Settings 页 Gateway 页签新增 Fast/Flex Policy 表单卡片:
service_tier × action × scope × 模型白名单 × fallback action 全字段配置。
- 前端守门:openaiFastPolicyLoaded 标志仅在 GET 真带回字段时才允许回写,
避免 rollout/错误把默认规则覆盖成空;saveSettings 回写循环 skip 该字段,
由专用刷新逻辑处理;仅 action=block 时发送 error_message,匹配后端
omitempty 行为。
测试
- HTTP 路径:openai_fast_policy_test.go 覆盖默认配置(whitelist=[],所有
模型 priority filter)/ block 自定义错误 / scope 区分 / filter 删字段 /
block 不改 body / block 短路上游 / Anthropic BetaFastMode 触发 OpenAI
fast policy 等场景。
- WebSocket 路径:openai_fast_policy_ws_test.go 覆盖
helper 单元(filter / fast→priority 归一化 / flex 透传 / block typed
error / 无 service_tier 字节不变 / 非 response.create 帧不动 / 空 type
帧不动 / event_id+code 字段断言 / 非字符串 service_tier 容错)+
pass 路径 fast 别名归一化回归 +
ingress 端到端(filter 后上游不含 service_tier / block 后客户端先收
error event 再收 close 1008 且上游 0 写)+
passthrough capturedSessionModel fallback 用例(whitelist 策略下首帧
建立、缺 model 命中 fallback、缺少 fallback 时的 leak 文档化)+
passthrough session.update / session.created 旋转 capturedSessionModel
的 mid-session 绕过回归 +
passthrough billing post-filter ServiceTier 与 idempotent filter 回归。
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-28 11:15:09 +08:00
shaw
9b6dcc57bd
feat(affiliate): 完善邀请返利系统
...
- 修复返利不到账的根因:tryClaimAffiliateRebateAudit 中 PostgreSQL 参数类型推断冲突
- 补全 OAuth 注册路径(LinuxDo/OIDC/WeChat/Pending Flow)的邀请码绑定
- 前端 OAuth 注册页面传递 aff_code 参数
- 新增返利冻结期机制:可配置冻结时间,到期后自动解冻(懒解冻)
- 新增返利有效期:绑定后 N 天内有效,过期不再产生返利
- 新增单人返利上限:超出上限部分精确截断
- 增强返利流程 slog 结构化日志,便于排查问题
- 已邀请用户列表增加返利明细列
2026-04-26 12:42:35 +08:00
shaw
4e1bb2b445
feat(affiliate): add feature toggle and per-user custom invite settings
...
- 在系统设置「功能开关」中新增邀请返利总开关,默认关闭;
关闭态:菜单隐藏、注册忽略 aff、新充值不返利,但已有 quota 仍可转余额
- 支持管理员为指定用户设置专属邀请码(覆盖随机码,全局唯一)
- 支持管理员为指定用户设置专属返利比例(覆盖全局比例,可单条/批量调整)
- 在系统设置邀请返利卡片内嵌入专属用户管理表格(搜索/编辑/批量/删除),
删除采用项目通用 ConfirmDialog,会同时清除专属比例并把邀请码重置为系统随机码
- /affiliate 用户页新增「我的返利比例」卡片与动态使用说明,让用户直观看到
分享后能拿到多少(同源 resolveRebateRatePercent 计算,与实际充值一致)
- 新增数据库迁移 132 添加 aff_rebate_rate_percent 与 aff_code_custom 列
- 新增 admin 路由组 /api/v1/admin/affiliates/users/* 共 5 个端点
- AffiliateService 改为只依赖 *SettingService,去除冗余的 SettingRepository
- 邀请码格式校验放宽到 [A-Z0-9_-]{4,32},兼容旧 12 位系统码与新自定义码
- 补充单元测试与集成测试覆盖新方法、冲突路径与边界值
2026-04-25 20:22:07 +08:00
VpSanta33
f03de00cb9
feat: add affiliate invite rebate flow and admin rebate-rate setting
2026-04-24 22:22:26 +08:00
erio
5e060b2222
Merge remote-tracking branch 'upstream/main' into feat/channel-insights
...
# Conflicts:
# backend/cmd/server/wire_gen.go
2026-04-23 22:30:45 +08:00
erio
67518a59ac
revert: remove fork-only changes from release sync
...
Revert payment/wechat, sora/claude-max cleanup, fork-only migrations,
and cosmetic changes that were brought in by the release sync commit.
Keep only channel-monitor related improvements:
- PublicSettingsInjectionPayload named struct with drift test
- ChannelMonitorRunner graceful shutdown in wire
- image_output_price in SupportedModelChip
- Simplified buildSelfNavItems in AppSidebar
- Gateway WARN logs for 503 branches
2026-04-23 21:40:58 +08:00
erio
748a84d871
sync: bring over remaining release/custom-0.1.115 changes
...
- Extract PublicSettingsInjectionPayload named struct with drift test
- Add channel_monitor_default_interval_seconds to SSR injection
- Add image_output_price to SupportedModelChip
- Simplify AppSidebar buildSelfNavItems (admins see available channels)
- Add gateway WARN logs for 503 no-available-accounts branches
- Wire ChannelMonitorRunner into provideCleanup for graceful shutdown
- Add migrations 130/131 (CC template userid fix + mimicry field cleanup)
- Clean up fork-only features (sora, claude max simulation, client affinity)
- Remove ~320 obsolete i18n keys
- Add codexUsage utility, WechatServiceButton, BulkEditAccountModal
- Tidy go.sum
2026-04-23 20:55:18 +08:00
james-6-23
dc5d42addc
feat(rpm): RPM 限流模块优化
...
P0:
- rpm_override 嵌入 Auth Cache Snapshot,消除每请求 DB 查询 (snapshot v6→v7)
- 429 RPM 响应返回 Retry-After 头(当前分钟剩余秒数)
P1:
- ClearAll 按钮直连 DELETE API,带 loading 防重复
- 新增 GET /admin/users/:id/rpm-status 管理员 RPM 用量查询端点
优化:
- checkRPM 从级联互斥改为并行取最严,user.rpm_limit 作为全局硬上限始终生效
- Override/Group 变更后自动失效 auth cache
- fail-open 语义不变,Redis 故障不阻塞业务
2026-04-23 16:34:37 +08:00
IanShaw027
3419cb0112
fix(admin): preserve legacy oidc security write defaults
2026-04-22 17:22:24 +08:00
IanShaw027
a94d89efa7
fix(unit): restore secure oidc defaults and wechat alias reuse
2026-04-22 16:51:23 +08:00
IanShaw027
36aed35957
fix(auth): harden oauth identity upgrade paths
2026-04-22 14:56:56 +08:00
IanShaw027
06136af805
fix(upgrade): preserve legacy auth and payment compatibility
2026-04-22 13:18:10 +08:00
IanShaw027
b2e0712190
fix(settings): preserve oauth config compatibility on upgrade
2026-04-22 12:30:07 +08:00
IanShaw027
84628108fc
fix(auth): preserve backward-compatible oauth defaults
2026-04-22 11:17:32 +08:00
IanShaw027
54dc176725
feat(settings): support per-channel WeChat OAuth and persist payment options
2026-04-21 07:51:41 -07:00
erio
84b03efa0b
fix(settings): inject channel_monitor & available_channels into SSR payload
...
Root cause: GetPublicSettingsForInjection used an inline struct that silently
drifted from dto.PublicSettings and omitted channel_monitor_enabled /
available_channels_enabled. On refresh window.__APP_CONFIG__ lacked these
keys, so cachedPublicSettings.available_channels_enabled resolved to
undefined and the opt-in sidebar entry (=== true) disappeared.
Backend: extract PublicSettingsInjectionPayload as a named type with all
feature-flag fields wired, and add a reflect-based drift test in the dto
package so forgetting a future flag fails CI instead of the browser.
Frontend: introduce utils/featureFlags.ts as the single registry for
public-settings-driven toggles, with explicit opt-in / opt-out modes that
encode the pre-load fallback. AppSidebar switches to makeSidebarFlag() so
adding a new switch only touches the registry.
2026-04-21 21:08:10 +08:00
IanShaw027
2cebb0dc60
feat(settings): support dual-mode wechat oauth defaults
2026-04-21 20:36:10 +08:00
IanShaw027
ee3f158f4e
fix(settings): restore wechat and payment config persistence
2026-04-21 17:35:12 +08:00
erio
9ba42aa556
feat(channels): gate available channels behind feature switch (backend)
...
Add a DB-backed soft switch "available_channels_enabled" controlling
the user-facing /channels/available endpoint and sidebar entry. Default
to false (opt-in) — the feature stays invisible until an admin enables
it under Admin Settings > Features.
- domain_constants: SettingKeyAvailableChannelsEnabled
- settings_view: AllSettings/PublicSettings + AvailableChannelsEnabled
- setting_service: public+all read/write, seed default "false",
GetAvailableChannelsRuntime helper (fail-closed on read error)
- admin setting_handler: UpdateSettingsRequest *bool + update branch
+ audit diff entry
- public setting_handler: expose via GET /api/v1/settings
- available_channel_handler: featureEnabled() guard — returns empty
list after auth when disabled (401 precedes the feature check to
preserve existing behavior)
2026-04-21 17:23:20 +08:00
IanShaw027
e12599c1b9
fix settings auth source default persistence
2026-04-21 02:08:04 +08:00
IanShaw027
067eb23d8e
Tighten WeChat OAuth capability mode selection
2026-04-21 00:46:40 +08:00
erio
7da5124067
feat(channel-monitor): add feature switch settings + fix extra_models save
...
Settings:
- New "功能开关" tab between 通用设置 and 安全与认证
- ChannelMonitorEnabled toggle: runner skips scheduling when false,
user-facing list returns empty
- ChannelMonitorDefaultIntervalSeconds (15-3600): pre-fills interval
when creating a new monitor; each monitor can still override
Bug fix:
- ModelTagInput now commits pending input on blur, not just Enter/Tab.
Previously clicking "save" with an un-Enter'd extra model would drop
the value (DB stored extra_models=[] even when user typed entries).
Backend:
- domain_constants: SettingKeyChannelMonitor{Enabled,DefaultIntervalSeconds}
- SettingService.GetChannelMonitorRuntime: lightweight getter used by
runner tick + user handler per-request (fail-open on DB error)
- Runner tickDueChecks: bails early when feature disabled
- ChannelMonitorUserHandler: checks feature flag before serving
- Comment on runner doc: scheduler state is implicit (every tick re-reads
ListEnabled from DB), so CRUD ops on monitors self-maintain the schedule
Bump VERSION to 0.1.114.25
2026-04-21 00:21:29 +08:00
IanShaw027
9e84e2fd2b
fix: persist admin payment visibility and scheduler settings
2026-04-21 00:05:17 +08:00
IanShaw027
6a75bd77e3
feat: add pending oauth email onboarding flow
2026-04-20 19:30:09 +08:00
IanShaw027
e9de839d87
feat: rebuild auth identity foundation flow
2026-04-20 17:39:57 +08:00
erio
c1eb79e4ba
feat(notify): add platform/ID to quota alert email, add recharge URL to balance alert
...
- Quota alert email now shows account ID and platform
- Balance low email includes a "Top Up Now" button when recharge URL is configured
- New setting: balance_low_notify_recharge_url in admin settings
2026-04-14 09:30:51 +08:00
erio
a68df457d8
fix: address audit findings across websearch, notify, and channel pricing
...
Backend fixes:
- Fix balance notify ignoring percentage threshold type (was treating
percentage value as fixed USD amount)
- Remove dead code parseJSONStringArray
- Add ImageOutputTokens to tryModelFilePricing calculation
- Unify zero-value check: cost == 0 → cost <= 0 in calculateTokenStatsCost
- Use MarshalNotifyEmails instead of json.Marshal for consistency
- Rename quotaDim.oldUsed → currentUsed for clarity
- Extract HTML email templates to const variables (function ≤30 lines)
Test fixes:
- Rewrite account_websearch_test.go for GetWebSearchEmulationMode tri-state
- Add 6 tryModelFilePricing test cases
Frontend fixes:
- Replace hardcoded '未命名' with i18n key
- Extract getBillingModeLabel/getBillingModeBadgeClass to shared utils
- Replace inline type with imported NotifyEmailEntry
- Pass platform to AccountStats pricing rules via inferRulePlatform()
- Add billing mode constants (BILLING_MODE_TOKEN/PER_REQUEST/IMAGE)
2026-04-14 09:26:08 +08:00
erio
915b7a4a56
feat(notify): convert email lists to NotifyEmailEntry struct with toggle support
...
- Change balance_notify_extra_emails and account_quota_notify_emails
from []string to []NotifyEmailEntry{email, disabled, verified}
- Add per-email enable/disable toggle for both user and admin notifications
- Add PUT /user/notify-email/toggle API endpoint
- Fix critical bug: API key auth cache snapshot missing balance notify
fields (Email, Username, BalanceNotifyEnabled, etc.), causing
notifications to never fire on cached request paths
- Bump cache snapshot version 3→4 to invalidate stale entries
- Add SQL migration 104 to convert old format data
- Backward compatible: parseNotifyEmails auto-detects old/new format
- User balance notify: max 3 emails (primary + 2 extra)
- Admin quota notify: unlimited emails, each with toggle
2026-04-14 09:26:07 +08:00
erio
81287e960c
feat(notify): improve balance notify card UX
...
- Show system default threshold as placeholder in custom threshold input
- Display user's primary email with "Primary" badge
- Support adding multiple pending emails before verification
- Each pending email has independent send/verify/resend flow
- Expose balance_low_notify_threshold in PublicSettings API
- Clean up timers on unmount to prevent leaks
2026-04-14 09:25:50 +08:00