mirror of
https://github.com/farion1231/cc-switch.git
synced 2026-07-28 00:35:32 +08:00
feat: add Universal Provider feature (#348)
* feat: add Universal Provider feature - Add Universal Provider data structures and type definitions - Implement backend CRUD operations and sync functionality - Add frontend UI components (UniversalProviderPanel, Card, FormModal) - Add NewAPI icon and preset configuration - Support cross-app (Claude/Codex/Gemini) configuration sync - Add website URL field for providers - Implement real-time refresh via event notifications - Add i18n support (Chinese/English/Japanese) * feat: integrate universal provider presets into add provider dialog - Add universal provider presets (NewAPI, Custom Gateway) to preset selector - Show universal presets with Layers icon badge in preset selector - Open UniversalProviderFormModal when universal preset is clicked - Pass initialPreset to auto-fill form when opened from add dialog - Add i18n keys for addSuccess/addFailed messages - Keep separate universal provider panel for management * refactor: move universal provider management to add dialog - Remove Layers button from main navigation header - Add 'Manage' button next to universal provider presets - Open UniversalProviderPanel from within add provider dialog - Add i18n keys for 'manage' in all locales * style: display universal provider presets on separate line - Move universal provider section to a new row with border separator - Add label '统一供应商:' to clarify the section * style: unify universal provider label style with preset label - Use FormLabel component for consistent styling - Add background to 'Manage' button matching preset buttons - Update icon size and button padding for consistency * feat: add sync functionality and JSON preview for Universal Provider * fix: add missing in_failover_queue field to Provider structs After rebasing to main, the Provider struct gained a new `in_failover_queue` field. This fix adds the missing field to the three to_*_provider() methods in UniversalProvider. * refactor: redesign AddProviderDialog with tab-based layout - Add tabs to separate app-specific providers and universal providers - Move "Add Universal Provider" button from panel header to footer - Remove unused handleAdd callback and clean up imports - Update emptyHint i18n text to reference the footer button * fix: append /v1 suffix to Codex base_url in Universal Provider Codex uses OpenAI-compatible API which requires the /v1 endpoint suffix. The Universal Provider now automatically appends /v1 to base_url when generating Codex provider config if not already present. - Handle trailing slashes to avoid double slashes - Apply fix to both backend (to_codex_provider) and frontend preview * feat: auto-sync universal provider to apps on creation Previously, users had to manually click sync after adding a universal provider. Now it automatically syncs to Claude/Codex/Gemini on creation, providing a smoother user experience. --------- Co-authored-by: Jason <farion1231@gmail.com>
This commit is contained in:
@@ -10,6 +10,7 @@ pub mod proxy;
|
||||
pub mod settings;
|
||||
pub mod skills;
|
||||
pub mod stream_check;
|
||||
pub mod universal_providers;
|
||||
|
||||
// 所有 DAO 方法都通过 Database impl 提供,无需单独导出
|
||||
// 导出 FailoverQueueItem 供外部使用
|
||||
|
||||
@@ -0,0 +1,75 @@
|
||||
//! 统一供应商 (Universal Provider) DAO
|
||||
//!
|
||||
//! 提供统一供应商的 CRUD 操作。
|
||||
|
||||
use crate::database::{lock_conn, to_json_string, Database};
|
||||
use crate::error::AppError;
|
||||
use crate::provider::UniversalProvider;
|
||||
use std::collections::HashMap;
|
||||
|
||||
/// 统一供应商的 Settings Key
|
||||
const UNIVERSAL_PROVIDERS_KEY: &str = "universal_providers";
|
||||
|
||||
impl Database {
|
||||
/// 获取所有统一供应商
|
||||
pub fn get_all_universal_providers(&self) -> Result<HashMap<String, UniversalProvider>, AppError> {
|
||||
let conn = lock_conn!(self.conn);
|
||||
|
||||
let mut stmt = conn
|
||||
.prepare("SELECT value FROM settings WHERE key = ?")
|
||||
.map_err(|e| AppError::Database(e.to_string()))?;
|
||||
|
||||
let result: Option<String> = stmt
|
||||
.query_row([UNIVERSAL_PROVIDERS_KEY], |row| row.get(0))
|
||||
.ok();
|
||||
|
||||
match result {
|
||||
Some(json) => {
|
||||
serde_json::from_str(&json)
|
||||
.map_err(|e| AppError::Database(format!("解析统一供应商数据失败: {e}")))
|
||||
}
|
||||
None => Ok(HashMap::new()),
|
||||
}
|
||||
}
|
||||
|
||||
/// 获取单个统一供应商
|
||||
pub fn get_universal_provider(&self, id: &str) -> Result<Option<UniversalProvider>, AppError> {
|
||||
let providers = self.get_all_universal_providers()?;
|
||||
Ok(providers.get(id).cloned())
|
||||
}
|
||||
|
||||
/// 保存统一供应商(添加或更新)
|
||||
pub fn save_universal_provider(&self, provider: &UniversalProvider) -> Result<(), AppError> {
|
||||
let mut providers = self.get_all_universal_providers()?;
|
||||
providers.insert(provider.id.clone(), provider.clone());
|
||||
self.save_all_universal_providers(&providers)
|
||||
}
|
||||
|
||||
/// 删除统一供应商
|
||||
pub fn delete_universal_provider(&self, id: &str) -> Result<bool, AppError> {
|
||||
let mut providers = self.get_all_universal_providers()?;
|
||||
let existed = providers.remove(id).is_some();
|
||||
if existed {
|
||||
self.save_all_universal_providers(&providers)?;
|
||||
}
|
||||
Ok(existed)
|
||||
}
|
||||
|
||||
/// 保存所有统一供应商(内部方法)
|
||||
fn save_all_universal_providers(
|
||||
&self,
|
||||
providers: &HashMap<String, UniversalProvider>,
|
||||
) -> Result<(), AppError> {
|
||||
let conn = lock_conn!(self.conn);
|
||||
let json = to_json_string(providers)?;
|
||||
|
||||
conn.execute(
|
||||
"INSERT OR REPLACE INTO settings (key, value) VALUES (?, ?)",
|
||||
[UNIVERSAL_PROVIDERS_KEY, &json],
|
||||
)
|
||||
.map_err(|e| AppError::Database(e.to_string()))?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user