Commit Graph

2288 Commits

Author SHA1 Message Date
Wesley Liddick
e6326b2929 Merge pull request #1108 from DaydreamCoding/feat/admin-group-capacity-and-usage
feat(admin): 分组管理列表新增用量、账号分类与容量列
2026-03-18 11:12:43 +08:00
Wesley Liddick
17cdcebd04 Merge pull request #1109 from GuangYiDing/feat/subscription-guide
feat(subscriptions): 订阅管理页面添加教程指南弹窗
2026-03-18 11:12:33 +08:00
shaw
a14babdc73 fix: 兼容 Claude Code v2.1.78+ 新 JSON 格式 metadata.user_id
Claude Code v2.1.78 起将 metadata.user_id 从拼接字符串改为 JSON:
旧: user_{hex}_account_{uuid}_session_{uuid}
新: {"device_id":"...","account_uuid":"...","session_id":"..."}

新增集中解析/格式化模块 metadata_userid.go:
- ParseMetadataUserID: 自动识别两种格式,提取 DeviceID/AccountUUID/SessionID
- FormatMetadataUserID: 根据 UA 版本输出对应格式(>= 2.1.78 输出 JSON)
- ExtractCLIVersion: 从 UA 提取版本号,消除与 ClaudeCodeValidator.ExtractVersion 的重复

修改消费者统一使用新模块:
- claude_code_validator: 用 ParseMetadataUserID 替代只匹配旧格式的 userIDPattern
- identity_service: RewriteUserID/WithMasking 增加 fingerprintUA 参数,
  解析用 ParseMetadataUserID,输出用 FormatMetadataUserID(版本感知)
- gateway_service: GenerateSessionHash 用 ParseMetadataUserID 提取 session_id,
  buildOAuthMetadataUserID 用 FormatMetadataUserID 输出版本匹配格式,
  两处 RewriteUserIDWithMasking 调用传入 fp.UserAgent
- account_test_service: generateSessionString 改用 FormatMetadataUserID,
  自动跟随 DefaultHeaders UA 版本

删除三个旧正则: userIDPattern, userIDRegex, sessionIDRegex
统一 hex 匹配为 [a-fA-F0-9],修复旧 userIDRegex 只匹配小写的不一致
2026-03-18 11:08:58 +08:00
Rose Ding
aadc6a763a feat(subscriptions): 订阅管理页面添加教程指南弹窗
在订阅管理页面工具栏添加教程指南按钮(? 图标),点击弹出模态框,
引导管理员完成订阅功能的完整使用流程:

- 步骤一:创建订阅分组(含跳转分组管理链接)
- 步骤二:分配订阅给用户(搜索用户、选择分组、设置有效期)
- 步骤三:管理已有订阅(调整/重置配额/撤销操作说明表格)
- 底部提示:说明下拉列表为空时的解决方案

弹窗样式参照 BackupView 的 R2 Guide 模态框实现,保持 UI 一致性。

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-18 10:49:41 +08:00
Rose Ding
f16af8bf88 feat(i18n): 添加订阅管理教程指南英文翻译
在 en.ts 中为订阅管理页面新增 guide 相关翻译词条,
与中文翻译保持结构一致,支持中英文切换。

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-18 10:49:32 +08:00
Rose Ding
5ceaef4500 feat(i18n): 添加订阅管理教程指南中文翻译
在 zh.ts 中为订阅管理页面新增 guide 相关翻译词条,包括:
- 教程弹窗标题与副标题
- 三步操作引导文案(创建分组、分配订阅、管理订阅)
- 操作说明表格(调整/重置配额/撤销)
- 底部提示信息

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-18 10:49:13 +08:00
QTom
d4cc9871c4 feat(admin): 分组管理新增容量列(并发/会话/RPM 实时聚合)
复用 GroupCapacityService,在 admin 分组列表中添加容量列,
显示每个分组的实时并发/会话/RPM 使用量和上限。

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-18 10:06:35 +08:00
QTom
961c30e7c0 feat(admin): 分组管理列表新增用量列与账号数分类
分组管理列表增强:

1. 今日/累计用量列:
   - 新增独立端点 GET /admin/groups/usage-summary
   - 一次查询返回所有分组的今日费用和累计费用(actual_cost)
   - 前端异步加载后合并显示在分组列表中

2. 账号数区分可用/限流/总量:
   - 将账号数列从单一总量改为 badge 内多行展示
   - 可用: active + schedulable 的账号数(绿色)
   - 限流: rate_limit/overload/temp_unschedulable 的账号数(橙色,无限流时隐藏)
   - 总量: 全部关联账号数

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-18 10:06:35 +08:00
Wesley Liddick
045cba78b4 Merge pull request #1083 from StarryKira/fix/claude-code-version-pattern-validation
fix(settings): remove pattern attribute blocking Claude Code version save fix issue #1081
2026-03-17 14:49:34 +08:00
Wesley Liddick
8989d0d4b6 Merge pull request #1085 from protondrift/main
feat: 个人资料弹窗 GitHub 链接仅对管理员可见
2026-03-17 14:49:07 +08:00
Wesley Liddick
c521117b99 Merge pull request #1074 from StarryKira/fix/session-window-reset-from-header
fix(usage): use real reset header for 5h session window countdown fix issue #1064 #1065
2026-03-17 14:48:16 +08:00
Eric
e0f52a8ab8 feat: 个人资料弹窗 GitHub 链接仅对管理员可见
目前作者已有商业站信息,面向管理可提供赞助渠道,面向普通用户请考虑提供信息隐藏措施
2026-03-17 12:51:34 +08:00
haruka
6c23fadf7e fix(settings): remove pattern attribute blocking Claude Code version save
The `pattern="\d+\.\d+\.\d+"` on the min_claude_code_version input caused
the browser's native HTML5 form validation to silently block form submission
when the value was invalid or when the hidden gateway tab was active. This
resulted in no network request being sent when clicking Save on any tab.

Backend already validates semver format and returns a proper 400 error,
so the frontend pattern attribute is redundant.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-17 11:33:57 +08:00
haruka
869952d113 fix(review): address Copilot PR feedback
- Add compile-time interface assertion for sessionWindowMockRepo
- Fix flaky fallback test by capturing time.Now() before calling UpdateSessionWindow
- Replace stale hardcoded timestamps with dynamic future values
- Add millisecond detection and bounds validation for reset header timestamp
- Use pause/resume pattern for interval in UsageProgressBar to avoid idle timers on large lists
- Fix gofmt comment alignment

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-17 10:19:20 +08:00
Wesley Liddick
07ab051ee4 Merge pull request #1078 from luxiang0412/main
fix(proxy): encode special chars in proxy credentials
2026-03-17 09:33:02 +08:00
Wesley Liddick
f2d98fc0c7 Merge pull request #1077 from Clov614/main
fix(auto setup): 修复初始测试连接硬编码问题导致使用自定义数据库测试失败无法执行 auto setup流程
2026-03-17 09:32:52 +08:00
Wesley Liddick
2b41cec840 Merge pull request #1076 from touwaeriol/pr/antigravity-test-connection-unify
refactor(antigravity): unify TestConnection with dispatch retry loop
2026-03-17 09:26:06 +08:00
Wesley Liddick
6cf77040e7 Merge pull request #1075 from touwaeriol/feat/dashboard-user-breakdown
feat(dashboard): add per-user drill-down for distribution charts
2026-03-17 09:25:43 +08:00
Wesley Liddick
20b70bc5fd Merge pull request #1070 from StarryKira/fix/oauth-system-role-to-instructions
fix(oauth): extract system-role input items into instructions field fix issue #1066
2026-03-17 09:24:17 +08:00
Wesley Liddick
4905e7193a Merge pull request #1069 from Ethan0x0000/pr/codex-usage-single-source
fix(openai): use /usage as single source and zero expired codex windows
2026-03-17 09:09:16 +08:00
Wesley Liddick
9c1f4b8e72 Merge pull request #1068 from Ethan0x0000/pr/frontend-last24h
feat(frontend): set last 24h as default range in Usage and Dashboard
2026-03-17 09:06:52 +08:00
Wesley Liddick
9857c17631 Merge pull request #1067 from DaydreamCoding/feat/async-backup
feat(backup): 备份/恢复异步化,解决 504 超时
2026-03-17 08:59:36 +08:00
luxiang
7e34bb946f fix(proxy): encode special chars in proxy credentials 2026-03-17 08:40:08 +08:00
clover614
47b748851b fix(auto setup): 修复初始测试连接硬编码问题导致使用自定义数据库测试失败无法执行 auto setup流程 2026-03-17 06:34:20 +08:00
erio
a6f99cf534 refactor(antigravity): unify TestConnection with dispatch retry loop
TestConnection now reuses antigravityRetryLoop instead of a standalone
HTTP loop, gaining credits overages, smart retry, and 429/503 backoff
for free. AccountSwitchError is caught and surfaced as a friendly
message. Also populates RateLimitedModel in TempUnscheduled switch error.

Test fixes:
- Use RATE_LIMIT_EXCEEDED in 503 short-delay test to avoid 60x1s timeout
- Clamp waitDuration=0 instead of 999s to avoid 15s max-wait timeout
- Enhance mockSmartRetryUpstream with repeatLast and body caching
2026-03-17 01:47:08 +08:00
erio
a120a6bc32 fix(ui): remove redundant sub-table header in user breakdown
The expanded user breakdown rows already align with the parent table
columns (Requests, Token, Actual, Standard), so the repeated sub-header
wastes vertical space. Remove the <thead> from UserBreakdownSubTable.
2026-03-17 00:49:43 +08:00
erio
d557d1a190 fix(ui): restore original max-h-48 height for distribution tables 2026-03-17 00:47:45 +08:00
erio
e0286e5085 test(dashboard): add unit tests for user-breakdown API
Handler tests (9 cases): group_id/model/endpoint filters, default
endpoint_type, custom limit, limit clamping, response format,
empty result, no-filter pass-through.

Repository test: resolveEndpointColumn mapping for inbound/upstream/path.
2026-03-17 00:47:33 +08:00
erio
4b41e898a4 feat(dashboard): add per-user drill-down for group, model, and endpoint distributions
Click on a group name, model name, or endpoint name in the distribution
tables to expand and show per-user usage breakdown (requests, tokens,
actual cost, standard cost).

Backend: new GET /admin/dashboard/user-breakdown API with group_id,
model, endpoint, endpoint_type filters.
Frontend: clickable rows with expand/collapse sub-table in all three
distribution charts.
2026-03-17 00:47:20 +08:00
Elysia
668e164793 fix(usage): use real reset header for session window instead of prediction
The 5h window reset time displayed for Setup Token accounts was inaccurate
because UpdateSessionWindow predicted the window end as "current hour + 5h"
instead of reading the actual `anthropic-ratelimit-unified-5h-reset` response
header. This caused the countdown to differ from the official Claude page.

Backend: parse the reset header (Unix timestamp) and use it as the real
window end, falling back to the hour-truncated prediction only when the
header is absent. Also correct stale predictions when a subsequent request
provides the real reset time.

Frontend: add a reactive 60s timer so the reset countdown in
UsageProgressBar ticks down in real-time instead of freezing at the
initial value.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-17 00:13:45 +08:00
Elysia
fa2e6188d0 fix(oauth): extract system-role input items into instructions field
OAuth upstreams (ChatGPT) reject requests containing role:"system" in
the input array with HTTP 400 "System messages are not allowed". Extract
such items before forwarding and merge their content into the top-level
instructions field, prepending to any existing value.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-16 21:20:46 +08:00
Ethan0x0000
7fde9ebbc2 fix: zero expired codex windows in backend, use /usage API as single frontend data source 2026-03-16 21:14:52 +08:00
Ethan0x0000
aef7c3b9bb feat: set last 24 hours as default date range in DashboardView 2026-03-16 21:14:26 +08:00
Ethan0x0000
a0b76bd608 feat: implement last 24 hours date range preset and update filters in UsageView 2026-03-16 21:14:26 +08:00
QTom
c1fab7f8d8 feat(backup): 备份/恢复异步化,解决 504 超时
POST /backups 和 POST /backups/:id/restore 改为异步:立即返回 HTTP 202,
后台 goroutine 独立执行 pg_dump → gzip → S3 上传,前端每 2s 轮询状态。

后端:
- 新增 StartBackup/StartRestore 方法,后台 goroutine 不依赖 HTTP 连接
- Graceful shutdown 等待活跃操作完成,启动时清理孤立 running 记录
- BackupRecord 新增 progress/restore_status 字段支持进度和恢复状态追踪

前端:
- 创建备份/恢复后轮询 GET /backups/:id 直到完成或失败
- 标签页切换暂停/恢复轮询,组件卸载清理定时器
- 正确处理 409(备份进行中)和轮询超时

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-16 20:22:10 +08:00
Wesley Liddick
f42c8f2abe Merge pull request #1062 from kunish/fix/antigravity-stream-keepalive
fix(antigravity): add stream keepalive to prevent connection drops
2026-03-16 19:57:13 +08:00
shaw
aa5846b282 fix(docker): resolve /app/data permission denied on volume mounts
Docker named volumes and host bind-mounts may be owned by root,
causing "open data/model_pricing.sha256: permission denied" when
the container runs as the non-root sub2api user.

Add an entrypoint script that fixes /app/data ownership before
dropping to sub2api via su-exec. Replace USER directive with the
entrypoint approach across all three Dockerfiles and update both
GoReleaser configs to include the script in Docker build contexts.
2026-03-16 19:52:14 +08:00
Wesley Liddick
594a0ade38 Merge pull request #1063 from touwaeriol/fix/usage-label-semantic
fix(i18n): correct usage label from "Total" to "Last 30d"
2026-03-16 19:04:36 +08:00
erio
d45cc23171 fix(i18n): correct usage label from "Total" to "Last 30d"
The usage stats query defaults to a 30-day rolling window, but the
UI label said "Total"/"累计" implying lifetime aggregation. Rename
to "Last 30d"/"近30天" so the label matches the actual query semantics.

Closes #1060
2026-03-16 18:25:41 +08:00
kunish
d795734352 fix(antigravity): add stream keepalive to prevent connection drops
Antigravity streaming handlers were missing the keepalive mechanism
that exists in the standard gateway, causing proxy/CDN idle timeouts
to break connections during long thinking phases (e.g. claude-opus-4-6).
This resulted in truncated responses with missing tool calls.

Add StreamKeepaliveInterval support to all three Antigravity streaming
paths: Claude SSE, Gemini SSE, and upstream passthrough.
2026-03-16 17:37:15 +08:00
Wesley Liddick
4da9fdd1d5 Merge pull request #1058 from Ethan0x0000/main
fix(admin/accounts): make usage window refresh deterministic and restore missing stats
2026-03-16 17:06:13 +08:00
Wesley Liddick
6b218caa21 Merge pull request #1053 from touwaeriol/chore/antigravity-ua-1.20.5
chore(antigravity): bump default User-Agent version to 1.20.5
2026-03-16 16:57:22 +08:00
shaw
5c138007d0 chore: update docs 2026-03-16 16:56:42 +08:00
Ethan0x0000
1acfc46f46 fix: always show usage stats for OpenAI OAuth and hide zero-value badges
- Simplify OpenAI rendering: always fetch /usage, prefer fetched data over
  codex snapshot (snapshot serves as loading placeholder only)
- Remove dead code: preferFetchedOpenAIUsage, isOpenAICodexSnapshotStale,
  and unreachable template branch
- Add today-stats support for key accounts (req/tokens/A/U badges)
- Use formatCompactNumber for consistent number formatting
- Add A/U badge titles for clarity
- Filter zero-value window stats in UsageProgressBar to avoid empty badges
- Update tests to match new fetched-data-first behavior
2026-03-16 16:23:13 +08:00
Ethan0x0000
fbffb08aae feat: add today-stats and manual refresh token propagation to usage cells
- Pass todayStats/todayStatsLoading to AccountUsageCell for key accounts
- Propagate usageManualRefreshToken to force usage reload on explicit refresh
- Refresh today stats when toggling usage/today_stats columns visible
2026-03-16 16:23:00 +08:00
Ethan0x0000
8640a62319 refactor: extract formatCompactNumber util and add last_used_at to refresh key
- Add formatCompactNumber() for consistent large-number formatting (K/M/B)
- Include last_used_at in OpenAI usage refresh key for better change detection
- Add .gitattributes eol=lf rules for frontend source files
2026-03-16 16:22:51 +08:00
Ethan0x0000
fa782e70a4 fix: always attach OpenAI 5h/7d window stats regardless of zero values
Removes hasMeaningfulWindowStats guard so the /usage endpoint consistently
returns WindowStats for both time windows. The frontend now controls
zero-value display filtering at the component level.
2026-03-16 16:22:42 +08:00
Ethan0x0000
afd72abc6e fix: allow empty extra payload to clear account quota limits
UpdateAccount previously required len(input.Extra) > 0, causing explicit
empty payloads (extra:{}) to be silently skipped. Change condition to
input.Extra != nil so clearing quota keys actually persists.
2026-03-16 16:22:31 +08:00
erio
71f72e167e chore(antigravity): bump default User-Agent version to 1.20.5 2026-03-16 15:47:32 +08:00
Wesley Liddick
6595c7601e Merge pull request #1050 from touwaeriol/fix/rate-limit-redis-window-reset
fix(billing): add window expiration check to Redis rate limit Lua script
2026-03-16 14:17:41 +08:00