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
This commit is contained in:
erio
2026-04-23 20:55:18 +08:00
parent d5dac84e12
commit 748a84d871
76 changed files with 1380 additions and 1699 deletions

View File

@@ -313,6 +313,31 @@ func (r *accountRepository) ListCRSAccountIDs(ctx context.Context) (map[string]i
return result, nil
}
// CountByTLSFingerprintProfile 按 TLS 指纹模板 ID 聚合绑定账号数。
// 走 108_add_tls_fingerprint_profile_id_index.sql 的表达式索引。
func (r *accountRepository) CountByTLSFingerprintProfile(ctx context.Context) (map[int64]int, error) {
rows, err := r.sql.QueryContext(ctx, `
SELECT (extra->>'tls_fingerprint_profile_id')::bigint AS profile_id, COUNT(*)
FROM accounts
WHERE deleted_at IS NULL AND extra ? 'tls_fingerprint_profile_id'
GROUP BY profile_id`)
if err != nil {
return nil, err
}
defer func() { _ = rows.Close() }()
counts := make(map[int64]int)
for rows.Next() {
var id int64
var n int
if err := rows.Scan(&id, &n); err != nil {
return nil, err
}
counts[id] = n
}
return counts, rows.Err()
}
func (r *accountRepository) Update(ctx context.Context, account *service.Account) error {
if account == nil {
return nil

View File

@@ -9,7 +9,9 @@ import (
"github.com/redis/go-redis/v9"
)
const stickySessionPrefix = "sticky_session:"
const (
stickySessionPrefix = "sticky_session:"
)
type gatewayCache struct {
rdb *redis.Client
@@ -41,12 +43,6 @@ func (c *gatewayCache) RefreshSessionTTL(ctx context.Context, groupID int64, ses
}
// DeleteSessionAccountID 删除粘性会话与账号的绑定关系。
// 当检测到绑定的账号不可用(如状态错误、禁用、不可调度等)时调用,
// 以便下次请求能够重新选择可用账号。
//
// DeleteSessionAccountID removes the sticky session binding for the given session.
// Called when the bound account becomes unavailable (e.g., error status, disabled,
// or unschedulable), allowing subsequent requests to select a new available account.
func (c *gatewayCache) DeleteSessionAccountID(ctx context.Context, groupID int64, sessionHash string) error {
key := buildSessionKey(groupID, sessionHash)
return c.rdb.Del(ctx, key).Err()

View File

@@ -3080,7 +3080,7 @@ func (r *usageLogRepository) GetGroupStatsWithFilters(ctx context.Context, start
query := `
SELECT
COALESCE(ul.group_id, 0) as group_id,
COALESCE(g.name, '') as group_name,
COALESCE(g.name, '(无分组)') as group_name,
COUNT(*) as requests,
COALESCE(SUM(ul.input_tokens + ul.output_tokens + ul.cache_creation_tokens + ul.cache_read_tokens), 0) as total_tokens,
COALESCE(SUM(ul.total_cost), 0) as cost,