mirror of
https://github.com/farion1231/cc-switch.git
synced 2026-07-25 13:45:03 +08:00
e6f18ba801
* feat(proxy): extract model name from API response for accurate usage tracking - Add model field extraction in TokenUsage parsing for Claude, OpenAI, and Codex - Prioritize response model over request model in usage logging - Update model extractors to use parsed usage.model first - Add tests for model extraction in stream and non-stream responses * feat(proxy): implement streaming timeout control with validation - Add first byte timeout (0 or 1-180s) for streaming requests - Add idle timeout (0 or 60-600s) for streaming data gaps - Add non-streaming timeout (0 or 60-1800s) for total request - Implement timeout logic in response processor - Add 1800s global timeout fallback when disabled - Add database schema migration for timeout fields - Add i18n translations for timeout settings * feat(proxy): add model mapping module for provider-based model substitution - Add model_mapper.rs with ModelMapping struct to extract model configs from Provider - Support ANTHROPIC_MODEL, ANTHROPIC_REASONING_MODEL, and default models for haiku/sonnet/opus - Implement thinking mode detection for reasoning model priority - Include comprehensive unit tests for all mapping scenarios * fix(proxy): bypass circuit breaker for single provider scenario When failover is disabled (single provider), circuit breaker open state would block all requests causing poor UX. Now bypasses circuit breaker check in this scenario. Also integrates model mapping into request flow. * feat(ui): add reasoning model field to Claude provider form Add ANTHROPIC_REASONING_MODEL configuration field for Claude providers, allowing users to specify a dedicated model for thinking/reasoning tasks. * feat(proxy): add openrouter_compat_mode for optional format conversion Add configurable OpenRouter compatibility mode that enables Anthropic to OpenAI format conversion. When enabled, rewrites endpoint to /v1/chat/completions and transforms request/response formats. Defaults to enabled for OpenRouter. * feat(ui): add OpenRouter compatibility mode toggle Add UI toggle for OpenRouter providers to enable/disable compatibility mode which uses OpenAI Chat Completions format with SSE conversion. * feat(stream-check): use provider-configured model for health checks Extract model from provider's settings_config (ANTHROPIC_MODEL, GEMINI_MODEL, or Codex config.toml) instead of always using default test models. * refactor(ui): remove timeout settings from AutoFailoverConfigPanel Remove streaming/non-streaming timeout configuration from failover panel as these settings have been moved to a dedicated location. * refactor(database): migrate proxy_config to per-app three-row structure Replace singleton proxy_config table with app_type primary key structure, allowing independent proxy settings for Claude, Codex, and Gemini. Add GlobalProxyConfig queries and per-app config management in DAO layer. * feat(proxy): add GlobalProxyConfig and AppProxyConfig types Add new type definitions for the refactored proxy configuration: - GlobalProxyConfig: shared settings (enabled, address, port, logging) - AppProxyConfig: per-app settings (failover, timeouts, circuit breaker) * refactor(proxy): update service layer for per-app config structure Adapt proxy service, handler context, and provider router to use the new per-app configuration model. Read enabled/timeout settings from proxy_config table instead of settings table. * feat(commands): add global and per-app proxy config commands Add new Tauri commands for the refactored proxy configuration: - get_global_proxy_config / update_global_proxy_config - get_proxy_config_for_app / update_proxy_config_for_app Update startup restore logic to read from proxy_config table. * feat(api): add frontend API and Query hooks for proxy config Add TypeScript wrappers and TanStack Query hooks for: - Global proxy config (address, port, logging) - Per-app proxy config (failover, timeouts, circuit breaker) - Proxy takeover status management * refactor(ui): redesign proxy panel with inline config controls Replace ProxySettingsDialog with inline controls in ProxyPanel. Add per-app takeover switches and global address/port settings. Simplify AutoFailoverConfigPanel by removing timeout settings. * feat(i18n): add proxy takeover translations and update types Add i18n strings for proxy takeover status in zh/en/ja. Update TypeScript types for GlobalProxyConfig and AppProxyConfig. * refactor(proxy): load circuit breaker config per-app instead of globally Extract app_type from router key and read circuit breaker settings from the corresponding proxy_config row for each application.
132 lines
4.5 KiB
Rust
132 lines
4.5 KiB
Rust
//! 通用设置数据访问对象
|
||
//!
|
||
//! 提供键值对形式的通用设置存储。
|
||
|
||
use crate::database::{lock_conn, Database};
|
||
use crate::error::AppError;
|
||
use rusqlite::params;
|
||
|
||
impl Database {
|
||
/// 获取设置值
|
||
pub fn get_setting(&self, key: &str) -> Result<Option<String>, AppError> {
|
||
let conn = lock_conn!(self.conn);
|
||
let mut stmt = conn
|
||
.prepare("SELECT value FROM settings WHERE key = ?1")
|
||
.map_err(|e| AppError::Database(e.to_string()))?;
|
||
|
||
let mut rows = stmt
|
||
.query(params![key])
|
||
.map_err(|e| AppError::Database(e.to_string()))?;
|
||
|
||
if let Some(row) = rows.next().map_err(|e| AppError::Database(e.to_string()))? {
|
||
Ok(Some(
|
||
row.get(0).map_err(|e| AppError::Database(e.to_string()))?,
|
||
))
|
||
} else {
|
||
Ok(None)
|
||
}
|
||
}
|
||
|
||
/// 设置值
|
||
pub fn set_setting(&self, key: &str, value: &str) -> Result<(), AppError> {
|
||
let conn = lock_conn!(self.conn);
|
||
conn.execute(
|
||
"INSERT OR REPLACE INTO settings (key, value) VALUES (?1, ?2)",
|
||
params![key, value],
|
||
)
|
||
.map_err(|e| AppError::Database(e.to_string()))?;
|
||
Ok(())
|
||
}
|
||
|
||
// --- Config Snippets 辅助方法 ---
|
||
|
||
/// 获取通用配置片段
|
||
pub fn get_config_snippet(&self, app_type: &str) -> Result<Option<String>, AppError> {
|
||
self.get_setting(&format!("common_config_{app_type}"))
|
||
}
|
||
|
||
/// 设置通用配置片段
|
||
pub fn set_config_snippet(
|
||
&self,
|
||
app_type: &str,
|
||
snippet: Option<String>,
|
||
) -> Result<(), AppError> {
|
||
let key = format!("common_config_{app_type}");
|
||
if let Some(value) = snippet {
|
||
self.set_setting(&key, &value)
|
||
} else {
|
||
// 如果为 None 则删除
|
||
let conn = lock_conn!(self.conn);
|
||
conn.execute("DELETE FROM settings WHERE key = ?1", params![key])
|
||
.map_err(|e| AppError::Database(e.to_string()))?;
|
||
Ok(())
|
||
}
|
||
}
|
||
|
||
// --- 代理接管状态管理(已废弃,使用 proxy_config.enabled 替代)---
|
||
|
||
/// 获取指定应用的代理接管状态
|
||
///
|
||
/// **已废弃**: 请使用 `proxy_config.enabled` 字段替代
|
||
/// 此方法仅用于数据库迁移时读取旧数据
|
||
#[deprecated(since = "3.9.0", note = "使用 get_proxy_config_for_app().enabled 替代")]
|
||
pub fn get_proxy_takeover_enabled(&self, app_type: &str) -> Result<bool, AppError> {
|
||
let key = format!("proxy_takeover_{app_type}");
|
||
match self.get_setting(&key)? {
|
||
Some(value) => Ok(value == "true"),
|
||
None => Ok(false),
|
||
}
|
||
}
|
||
|
||
/// 设置指定应用的代理接管状态
|
||
///
|
||
/// **已废弃**: 请使用 `proxy_config.enabled` 字段替代
|
||
#[deprecated(
|
||
since = "3.9.0",
|
||
note = "使用 update_proxy_config_for_app() 修改 enabled 字段"
|
||
)]
|
||
pub fn set_proxy_takeover_enabled(
|
||
&self,
|
||
app_type: &str,
|
||
enabled: bool,
|
||
) -> Result<(), AppError> {
|
||
let key = format!("proxy_takeover_{app_type}");
|
||
let value = if enabled { "true" } else { "false" };
|
||
self.set_setting(&key, value)
|
||
}
|
||
|
||
/// 检查是否有任一应用开启了代理接管
|
||
///
|
||
/// **已废弃**: 请使用 `is_live_takeover_active()` 替代
|
||
#[deprecated(since = "3.9.0", note = "使用 is_live_takeover_active() 替代")]
|
||
pub fn has_any_proxy_takeover(&self) -> Result<bool, AppError> {
|
||
let conn = lock_conn!(self.conn);
|
||
let count: i64 = conn
|
||
.query_row(
|
||
"SELECT COUNT(*) FROM settings WHERE key LIKE 'proxy_takeover_%' AND value = 'true'",
|
||
[],
|
||
|row| row.get(0),
|
||
)
|
||
.map_err(|e| AppError::Database(e.to_string()))?;
|
||
Ok(count > 0)
|
||
}
|
||
|
||
/// 清除所有代理接管状态(将所有 proxy_takeover_* 设置为 false)
|
||
///
|
||
/// **已废弃**: settings 表不再用于存储代理状态
|
||
#[deprecated(
|
||
since = "3.9.0",
|
||
note = "使用 update_proxy_config_for_app() 清除各应用的 enabled 字段"
|
||
)]
|
||
pub fn clear_all_proxy_takeover(&self) -> Result<(), AppError> {
|
||
let conn = lock_conn!(self.conn);
|
||
conn.execute(
|
||
"UPDATE settings SET value = 'false' WHERE key LIKE 'proxy_takeover_%'",
|
||
[],
|
||
)
|
||
.map_err(|e| AppError::Database(e.to_string()))?;
|
||
log::info!("已清除所有代理接管状态");
|
||
Ok(())
|
||
}
|
||
}
|