mirror of
https://github.com/farion1231/cc-switch.git
synced 2026-07-29 01:05:59 +08:00
feat(usage): real-time stats refresh + fix codex sync panic on non-ASCII model names (#3027)
The usage dashboard previously only refreshed on app restart for users who don't route through the cc-switch proxy. Two issues were involved: 1. The session-sync background task panicked when a Codex model name contained non-ASCII characters (e.g. `【官】glm-5.1`), because `normalize_codex_model` sliced `&name[name.len() - 11..]` without verifying char boundaries. Once the task panicked, no session logs were imported until the app was restarted (where startup-time `rollup_and_prune` happened to flush pending data). 2. Even with sync working, the dashboard only polled every 30s and skipped polling when the window was unfocused, so freshly-imported data was invisible until the next poll or window refocus. Fixes ----- * `normalize_codex_model`: guard the 11-byte ISO-date suffix slice with `is_char_boundary` + `is_ascii` checks. ASCII-only suffix means the date-stripping logic is correct, and non-ASCII names (which can never be valid date suffixes anyway) now bypass the slice safely. * New `usage_events` module that emits `usage-log-recorded` to the frontend whenever `proxy_request_logs` actually gains a new row. Sources covered: proxy `log_request`, Claude/Codex/Gemini session sync, and startup `rollup_and_prune`. Notifications use a global `OnceLock<AppHandle>` so call sites that don't already hold an `AppHandle` (e.g. `UsageLogger`) can notify without signature churn. * 200ms debounce in `notify_log_recorded` collapses bursts (a single Codex sync importing 3000+ entries triggers ~2 emits, not 3000) so the frontend's `invalidateQueries` is never spammed. * Frontend `useUsageEventBridge` listens for the event and invalidates `usageKeys.all`. Hook is mounted only on `UsageDashboard`, so the listener is unsubscribed automatically when the user navigates away. Verification ------------ * `cargo check` passes (existing 25 dead-code warnings in `commands/misc.rs` are pre-existing and unrelated). * `tsc --noEmit` passes. * Manually verified end-to-end: a Codex sync run that imported 3145 entries produced 2 debounced emits, both logged as `emit usage-log-recorded 成功`, and the dashboard updated within ~200ms. Behaviour notes --------------- * `INSERT OR IGNORE` paths (Claude/Codex session sync) only notify when the row is actually inserted, so dedup-skipped writes don't trigger empty refreshes. * Gemini's `INSERT … ON CONFLICT … DO UPDATE` path reuses the existing `conn.changes() > 0` check and only notifies when token counts truly changed. * `rollup_and_prune` notifies once per pruning cycle (at most once per app start) so the dashboard reflects the new aggregate state. Co-authored-by: in30mn1a <in30mn1a@users.noreply.github.com>
This commit is contained in:
@@ -102,6 +102,9 @@ impl<'a> UsageLogger<'a> {
|
||||
)
|
||||
.map_err(|e| AppError::Database(format!("记录请求日志失败: {e}")))?;
|
||||
|
||||
// 通知前端使用统计有更新(200ms 防抖合并,不阻塞写入路径)
|
||||
crate::usage_events::notify_log_recorded();
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user