- 30 Apr, 2026 1 commit
-
-
shaw authored
-
- 29 Apr, 2026 2 commits
-
-
shaw authored
- Security: force token_uri to Google default, preventing SSRF via crafted service account JSON - Dedup: extract shared getVertexServiceAccountAccessToken() to eliminate ~35 lines of duplication between ClaudeTokenProvider and GeminiTokenProvider - Fix: apply model mapping + Vertex model ID normalization in forward_as_responses and forward_as_chat_completions paths - Fix: exclude service_account from AI Studio endpoint selection (Vertex cannot serve generativelanguage.googleapis.com) - Feature: add model restriction/mapping UI for service_account in EditAccountModal - Dedup: extract VERTEX_LOCATION_OPTIONS to shared constants - i18n: replace all hardcoded Chinese strings in Vertex UI with translation keys
-
erio authored
Background / 背景 The ops cleanup task currently rejects retention days < 1 in both validate and normalize, so operators who want minimal-history setups (e.g. high churn deployments that prefer near-realtime cleanup) cannot express that intent through the UI. The only options are 1+ days, which keeps at least 24h of history regardless of cron frequency. ops 清理任务目前在 validate 和 normalize 两处都拒绝小于 1 的保留天数, 让希望尽量不留历史的运维场景(高吞吐部署 + 想用近实时清理)无法通过 UI 表达。最低只能配 1,等于不管 cron 多频繁,至少都会保留 24 小时的历史。 Purpose / 目的 Let admins set retention days to 0, meaning "every scheduled cleanup run wipes the corresponding table(s) entirely". Combined with a more frequent cron (e.g. `0 * * * *`) this yields effectively rolling cleanup. 允许管理员把保留天数设为 0,语义为"每次定时清理时把对应表全部清空"。 搭配更频繁的 cron(比如每小时整点)即可获得近似滚动清理的效果。 Changes / 改动内容 Backend - service/ops_settings.go: validate accepts [0, 365]; normalize only refills default 30 when value is < 0 (negative is treated as legacy bad data, 0 is honoured) - service/ops_cleanup_service.go: introduce `opsCleanupPlan(now, days)` returning `(cutoff, truncate, ok)`. days==0 returns truncate=true and short-circuits to a new `truncateOpsTable` helper that uses `TRUNCATE TABLE` (O(1), no WAL, no VACUUM pressure). days>0 keeps the existing batched DELETE path unchanged. Empty tables skip TRUNCATE to avoid the ACCESS EXCLUSIVE lock entirely - Extract `isMissingRelationError` helper to dedupe the "table not yet created" tolerance shared by both delete and truncate paths - Add unit tests for `opsCleanupPlan` (three branches) and `isMissingRelationError` 后端 - service/ops_settings.go: validate 接受 [0, 365];normalize 仅在 < 0 时回填默认 30(负数视为脏数据,0 被尊重) - service/ops_cleanup_service.go: 抽 `opsCleanupPlan(now, days)` 返回 `(cutoff, truncate, ok)`。days==0 → truncate=true,走新增 `truncateOpsTable`(TRUNCATE TABLE,O(1),无 WAL、无 VACUUM 压力); days>0 仍走原批量 DELETE 路径,行为完全不变。空表跳过 TRUNCATE, 避免无意义的 ACCESS EXCLUSIVE 锁 - 抽 `isMissingRelationError` helper 复用 delete / truncate 两处的 "表不存在"宽容判断 - 补 `opsCleanupPlan` 三分支 + `isMissingRelationError` 单元测试 Frontend - OpsSettingsDialog.vue: validation accepts [0, 365]; input min=0 - i18n (zh/en): hint mentions "0 = wipe all on every cleanup", validation message updated to 0-365 range 前端 - OpsSettingsDialog.vue: 校验放宽到 [0, 365],input min 改 0 - i18n(zh/en):hint 补"0 = 每次清理时清空所有",错误提示改 0-365 Trade-offs / 取舍 - TRUNCATE requires ACCESS EXCLUSIVE lock briefly, but ops tables only have the cleanup task as a writer, so the lock is invisible to other workloads - Empty-table guard avoids the lock when there is nothing to clean - Negative values are still treated as legacy bad data and replaced with default 30 to preserve compatibility
-
- 28 Apr, 2026 1 commit
-
-
DaydreamCoding authored
对称参照 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>
-
- 27 Apr, 2026 5 commits
-
-
陈曦 authored
-
- 修复返利不到账的根因:tryClaimAffiliateRebateAudit 中 PostgreSQL 参数类型推断冲突 - 补全 OAuth 注册路径(LinuxDo/OIDC/WeChat/Pending Flow)的邀请码绑定 - 前端 OAuth 注册页面传递 aff_code 参数 - 新增返利冻结期机制:可配置冻结时间,到期后自动解冻(懒解冻) - 新增返利有效期:绑定后 N 天内有效,过期不再产生返利 - 新增单人返利上限:超出上限部分精确截断 - 增强返利流程 slog 结构化日志,便于排查问题 - 已邀请用户列表增加返利明细列
-
- 在系统设置「功能开关」中新增邀请返利总开关,默认关闭; 关闭态:菜单隐藏、注册忽略 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 位系统码与新自定义码 - 补充单元测试与集成测试覆盖新方法、冲突路径与边界值
-
- 26 Apr, 2026 1 commit
-
-
shaw authored
- 修复返利不到账的根因:tryClaimAffiliateRebateAudit 中 PostgreSQL 参数类型推断冲突 - 补全 OAuth 注册路径(LinuxDo/OIDC/WeChat/Pending Flow)的邀请码绑定 - 前端 OAuth 注册页面传递 aff_code 参数 - 新增返利冻结期机制:可配置冻结时间,到期后自动解冻(懒解冻) - 新增返利有效期:绑定后 N 天内有效,过期不再产生返利 - 新增单人返利上限:超出上限部分精确截断 - 增强返利流程 slog 结构化日志,便于排查问题 - 已邀请用户列表增加返利明细列
-
- 25 Apr, 2026 2 commits
-
-
shaw authored
- 在系统设置「功能开关」中新增邀请返利总开关,默认关闭; 关闭态:菜单隐藏、注册忽略 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 位系统码与新自定义码 - 补充单元测试与集成测试覆盖新方法、冲突路径与边界值 -
shaw authored
将 vansour/sub2api#1555 的 OpenAI compact 能力建模手工移植到当前 main:账号 级 compact 状态/auto-force_on-force_off 模式、compact-only 模型映射、调度器 tier 分层(已支持 > 未知 > 已知不支持)、管理后台 compact 主动探测,以及对应 i18n/状态徽章。普通 /responses 流量行为不变,无数据库迁移。
-
- 24 Apr, 2026 1 commit
-
-
VpSanta33 authored
-
- 23 Apr, 2026 5 commits
-
-
erio authored
- Change overall status logic: >50% failed = UNAVAILABLE, any failed or degraded = DEGRADED, all ok = OPERATIONAL - Extract useAutoRefresh composable with localStorage persistence - Create AutoRefreshButton dropdown component (reusable) - Integrate auto-refresh into channel status page via MonitorHero
-
erio authored
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
-
erio authored
- 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
-
james-6-23 authored
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 故障不阻塞业务
-
zhoukailian authored
-
- 22 Apr, 2026 4 commits
-
-
IanShaw027 authored
-
IanShaw027 authored
-
shaw authored
-
shaw authored
-
- 21 Apr, 2026 4 commits
-
-
IanShaw027 authored
-
IanShaw027 authored
-
IanShaw027 authored
-
erio authored
Channel Monitor card now links to 渠道管理 > 渠道监控 and the Available Channels card links to 渠道管理 > 渠道定价 so admins know where to go after flipping the switch.
-
- 22 Apr, 2026 1 commit
-
-
erio authored
- 描述独立成列:渠道名与描述各占一列,均用 rowspan 纵向合并 - 渠道名单元格 text-center + align-middle,合并后视觉居中 - table-fixed:给 name/description/platform 显式宽度,groups 和 supported_models 在剩余空间均分。支持模型列此前在 table-auto 下 不会换行导致横向溢出遮挡(反馈截图),加 table-fixed 后天然 flex-wrap - i18n 增加 availableChannels.columns.description(zh/en)
-
- 21 Apr, 2026 9 commits
-
-
erio authored
Pricing popover: - Teleport to body + fixed-position re-measuring on hover/scroll/resize so it escapes the card's overflow-hidden clip that was chopping off the lower half of the panel. - Header + border adopt the platform theme via platformBorderClass / platformBadgeLightClass so each model card reads at a glance. Accessible groups: - Backend AvailableGroupRef / user DTO now expose subscription_type, rate_multiplier and is_exclusive. User-specific rates continue to come from /groups/rates (same pattern as the API keys page). - Table renders groups through the shared GroupBadge, which already deepens subscription-type colors and shows default vs custom rate as <s>default</s> <b>custom</b>. - Exclusive groups are labelled with a purple shield row, public groups with a grey globe row so admins and users can tell at a glance which groups they got via explicit grant vs. public access. i18n keys for exclusive / public / their tooltips added to zh + en.
-
erio authored
Switch the user-facing 'Available Channels' view from "one row per platform" to "one channel row-group with N platform sections". Backend: userAvailableChannel now holds Platforms []section instead of being exploded. buildPlatformSections replaces explodeChannelByPlatform with the same per-platform grouping logic. Frontend: drop the DataTable wrapper for this view and write a four-column grid table (渠道名 / 平台 / 分组 / 支持模型) where the channel name only renders on the first platform row of each channel — visual rowspan without hacking DataTable. - api/channels.ts: UserChannelPlatformSection + platforms[] - AvailableChannelsTable: rewritten as native grid (header + per- channel section with hover row highlight) - AvailableChannelsView: search now filters platforms sub-array; channel-name / description hits still keep the whole channel - i18n: add availableChannels.columns.platform (zh/en)
-
IanShaw027 authored
-
IanShaw027 authored
-
IanShaw027 authored
-
IanShaw027 authored
-
IanShaw027 authored
-
IanShaw027 authored
-
IanShaw027 authored
-
- 20 Apr, 2026 3 commits
-
-
erio authored
Follow-up to the available-channels review pass. No behavior change for end users; tightens internals based on three independent code reviews. Backend - service/channel.go: collapse buildPricingLookup + pricedNamesFor into a single platformPricingIndex (byLower + originalCase + ordered names), built once per SupportedModels call. Fixes a casing- consistency bug where the same logical model appeared with mapping case in the exact branch but pricing case in the wildcard branch — pricing's original case now wins everywhere. - service/channel.go: doc that a mapping key of just "*" expands to every priced model on the platform (intentional "passthrough all"). - service/channel_available.go: normalize empty BillingModelSource to channel_mapped at construction time, removing the same fallback duplicated in the admin DTO mapper and the admin Vue template. - handler/admin/available_channel_handler.go: unexport availableChannelToAdminResponse (same-package usage only); mapper is now a pure passthrough. - handler/available_channel_handler.go: drop the middleware2 alias (no name collision in this file). Frontend - utils/pricing.ts: extract formatScaled, used by SupportedModelChip and PricingRow. - api/admin/channels.ts: re-export BillingMode from constants/channel; tighten Channel.status / billing_model_source to ChannelStatus / BillingModelSource (and same for AvailableChannel). - components/channels/AvailableChannelsTable.vue: drop dead withDefaults wrapper (loading is required, both call sites pass it). - views/admin/AvailableChannelsView.vue: drop the redundant || BILLING_MODEL_SOURCE_CHANNEL_MAPPED fallback (now applied in service layer); remove unused import. - i18n zh + en: delete unused tierLabel and tokenRange keys from both availableChannels.pricing and admin.availableChannels.pricing. Tests - New: SupportedModels_ExactKeyUsesPricedCaseWhenAvailable locks the pricing-case-wins rule. - New: SupportedModels_AsteriskOnlyMappingExpandsAllPriced documents the "*" expansion rule. - Admin handler: existing tests adjusted to pass an explicit BillingModelSource (default-fill is now exercised by service tests).
-
IanShaw027 authored
-
erio authored
Add a read-only aggregate view per channel: its linked groups and a deterministic wildcard-free supported-model list with pricing details. Backend - service.Channel.SupportedModels(): combine ModelMapping keys with same-platform ModelPricing.Models; trailing "*" keys expand via pricing prefix match; platforms without a mapping produce no entries (intentional "no mapping = not shown" rule). - Extract splitWildcardSuffix() shared with toModelEntry. - Build a per-call pricing lookup map (platform+lowerName -> *pricing) to avoid O(N*M) scans in SupportedModels. - ChannelService.ListAvailable() aggregates channels + active groups; filters out group IDs no longer active. - Admin route GET /api/v1/admin/channels/available returns the full DTO (id, status, billing_model_source, restrict_models, groups, supported_models). - User route GET /api/v1/channels/available applies three filters: Status==active, visible-group intersection, and platform filter on supported_models (prevents cross-platform leak when a channel links to both a user-accessible group and an inaccessible one on another platform). Response is a plain array (matches the /groups/available sibling shape). Field whitelist omits billing_model_source, restrict_models, ids, status, sort_order. Frontend - New /admin/available-channels and /available-channels views backed by a shared AvailableChannelsTable component (admin adds status + billing-source columns via slots). - PricingRow extracted to its own SFC; SupportedModelChip references shared billing-mode constants in constants/channel.ts. - Sidebar: new entry above "渠道管理" for admin; matching entry in user nav. - i18n: zh + en coverage for both namespaces. Tests - SupportedModels: wildcard-only pricing skipped, prefix-matches- nothing, cross-platform bleed, case-insensitive dedup, empty platform mapping. - ListAvailable: nil groupRepo, inactive-group-ID dropped, stable case-insensitive name sort. - User handler: 401 on unauthenticated, visible-group intersection, platform filter on supported_models, JSON whitelist. - Admin handler: full DTO including default BillingModelSource fallback. Refs: issue #1729
-
- 21 Apr, 2026 1 commit
-
-
erio authored
- AdvancedRequestConfig 把 headers textarea 换成行式:每行 name 输入 + value 输入 + 删除按钮,底部「+ 添加 Header」。直观区分名/值,不用再一行 "Key: Value" 自己拆。 - 校验下放到行级:name 含空格或冒号才报错,未填仅占位不报错(避免输入时频繁红字)。 - 外部 props 同值不回写,避免 commit 后行被重排。 - chore: 移除 CLAUDE.md 里 silentflower remote 行(不再追踪)。
-