cc_switch_lib/services/provider/
endpoints.rs1use std::time::{SystemTime, UNIX_EPOCH};
2
3use crate::app_config::AppType;
4use crate::error::AppError;
5use crate::provider::ProviderMeta;
6use crate::settings::CustomEndpoint;
7use crate::store::AppState;
8
9use super::ProviderService;
10
11impl ProviderService {
12 pub fn get_custom_endpoints(
14 state: &AppState,
15 app_type: AppType,
16 provider_id: &str,
17 ) -> Result<Vec<CustomEndpoint>, AppError> {
18 let cfg = state.config.read().map_err(AppError::from)?;
19 let manager = cfg
20 .get_manager(&app_type)
21 .ok_or_else(|| Self::app_not_found(&app_type))?;
22
23 let Some(provider) = manager.providers.get(provider_id) else {
24 return Ok(vec![]);
25 };
26 let Some(meta) = provider.meta.as_ref() else {
27 return Ok(vec![]);
28 };
29 if meta.custom_endpoints.is_empty() {
30 return Ok(vec![]);
31 }
32
33 let mut result: Vec<_> = meta.custom_endpoints.values().cloned().collect();
34 result.sort_by(|a, b| b.added_at.cmp(&a.added_at));
35 Ok(result)
36 }
37
38 pub fn add_custom_endpoint(
40 state: &AppState,
41 app_type: AppType,
42 provider_id: &str,
43 url: String,
44 ) -> Result<(), AppError> {
45 let normalized = url.trim().trim_end_matches('/').to_string();
46 if normalized.is_empty() {
47 return Err(AppError::localized(
48 "provider.endpoint.url_required",
49 "URL 不能为空",
50 "URL cannot be empty",
51 ));
52 }
53
54 {
55 let mut cfg = state.config.write().map_err(AppError::from)?;
56 let manager = cfg
57 .get_manager_mut(&app_type)
58 .ok_or_else(|| Self::app_not_found(&app_type))?;
59 let provider = manager.providers.get_mut(provider_id).ok_or_else(|| {
60 AppError::localized(
61 "provider.not_found",
62 format!("供应商不存在: {provider_id}"),
63 format!("Provider not found: {provider_id}"),
64 )
65 })?;
66 let meta = provider.meta.get_or_insert_with(ProviderMeta::default);
67
68 let endpoint = CustomEndpoint {
69 url: normalized.clone(),
70 added_at: now_millis(),
71 last_used: None,
72 };
73 meta.custom_endpoints.insert(normalized, endpoint);
74 }
75
76 state.save()?;
77 Ok(())
78 }
79
80 pub fn remove_custom_endpoint(
82 state: &AppState,
83 app_type: AppType,
84 provider_id: &str,
85 url: String,
86 ) -> Result<(), AppError> {
87 let normalized = url.trim().trim_end_matches('/').to_string();
88
89 {
90 let mut cfg = state.config.write().map_err(AppError::from)?;
91 if let Some(manager) = cfg.get_manager_mut(&app_type) {
92 if let Some(provider) = manager.providers.get_mut(provider_id) {
93 if let Some(meta) = provider.meta.as_mut() {
94 meta.custom_endpoints.remove(&normalized);
95 }
96 }
97 }
98 }
99
100 state.save()?;
101 Ok(())
102 }
103
104 pub fn update_endpoint_last_used(
106 state: &AppState,
107 app_type: AppType,
108 provider_id: &str,
109 url: String,
110 ) -> Result<(), AppError> {
111 let normalized = url.trim().trim_end_matches('/').to_string();
112
113 {
114 let mut cfg = state.config.write().map_err(AppError::from)?;
115 if let Some(manager) = cfg.get_manager_mut(&app_type) {
116 if let Some(provider) = manager.providers.get_mut(provider_id) {
117 if let Some(meta) = provider.meta.as_mut() {
118 if let Some(endpoint) = meta.custom_endpoints.get_mut(&normalized) {
119 endpoint.last_used = Some(now_millis());
120 }
121 }
122 }
123 }
124 }
125
126 state.save()?;
127 Ok(())
128 }
129}
130
131fn now_millis() -> i64 {
132 SystemTime::now()
133 .duration_since(UNIX_EPOCH)
134 .unwrap_or_default()
135 .as_millis() as i64
136}