Files
CC-Switch/src-tauri/src/services/provider/endpoints.rs
T
Jason 014a7c0e30 refactor(services): split monolithic provider.rs into modular structure
Split the 1446-line services/provider.rs into 5 focused modules:

- gemini_auth.rs (250 lines): Gemini authentication type detection
  - PackyCode, Google OAuth, and generic provider detection
  - Security flag management for different auth types

- live.rs (300 lines): Live configuration operations
  - LiveSnapshot backup/restore
  - Reading and writing live config files
  - Sync current provider to live config

- usage.rs (150 lines): Usage script execution
  - Query and test usage scripts
  - Format usage results
  - Validate usage script configuration

- endpoints.rs (80 lines): Custom endpoints management
  - CRUD operations for provider custom endpoints
  - Last-used timestamp tracking

- mod.rs (650 lines): Core provider CRUD and service facade
  - ProviderService struct with all public methods
  - Provider add/update/delete/switch operations
  - Claude model key normalization
  - Credential extraction and validation

All 107 tests pass. This improves maintainability by:
- Separating concerns into cohesive modules
- Making Gemini-specific logic easier to find and modify
- Reducing cognitive load when working on specific features
2025-11-25 12:19:26 +08:00

99 lines
2.7 KiB
Rust

//! Custom endpoints management
//!
//! Handles CRUD operations for provider custom endpoints.
use std::time::{SystemTime, UNIX_EPOCH};
use crate::app_config::AppType;
use crate::error::AppError;
use crate::settings::CustomEndpoint;
use crate::store::AppState;
/// Get custom endpoints list for a provider
pub fn get_custom_endpoints(
state: &AppState,
app_type: AppType,
provider_id: &str,
) -> Result<Vec<CustomEndpoint>, AppError> {
let providers = state.db.get_all_providers(app_type.as_str())?;
let Some(provider) = providers.get(provider_id) else {
return Ok(vec![]);
};
let Some(meta) = provider.meta.as_ref() else {
return Ok(vec![]);
};
if meta.custom_endpoints.is_empty() {
return Ok(vec![]);
}
let mut result: Vec<_> = meta.custom_endpoints.values().cloned().collect();
result.sort_by(|a, b| b.added_at.cmp(&a.added_at));
Ok(result)
}
/// Add a custom endpoint to a provider
pub fn add_custom_endpoint(
state: &AppState,
app_type: AppType,
provider_id: &str,
url: String,
) -> Result<(), AppError> {
let normalized = url.trim().trim_end_matches('/').to_string();
if normalized.is_empty() {
return Err(AppError::localized(
"provider.endpoint.url_required",
"URL 不能为空",
"URL cannot be empty",
));
}
state
.db
.add_custom_endpoint(app_type.as_str(), provider_id, &normalized)?;
Ok(())
}
/// Remove a custom endpoint from a provider
pub fn remove_custom_endpoint(
state: &AppState,
app_type: AppType,
provider_id: &str,
url: String,
) -> Result<(), AppError> {
let normalized = url.trim().trim_end_matches('/').to_string();
state
.db
.remove_custom_endpoint(app_type.as_str(), provider_id, &normalized)?;
Ok(())
}
/// Update endpoint last used timestamp
pub fn update_endpoint_last_used(
state: &AppState,
app_type: AppType,
provider_id: &str,
url: String,
) -> Result<(), AppError> {
let normalized = url.trim().trim_end_matches('/').to_string();
// Get provider, update last_used, save back
let mut providers = state.db.get_all_providers(app_type.as_str())?;
if let Some(provider) = providers.get_mut(provider_id) {
if let Some(meta) = provider.meta.as_mut() {
if let Some(endpoint) = meta.custom_endpoints.get_mut(&normalized) {
endpoint.last_used = Some(now_millis());
state.db.save_provider(app_type.as_str(), provider)?;
}
}
}
Ok(())
}
/// Get current timestamp in milliseconds
fn now_millis() -> i64 {
SystemTime::now()
.duration_since(UNIX_EPOCH)
.unwrap_or_default()
.as_millis() as i64
}