hank9999 commited on
Commit ·
ae610e9
1
Parent(s): 7cd244e
feat: 支持多凭据格式回写功能
Browse files- src/kiro/model/credentials.rs +5 -0
- src/kiro/provider.rs +1 -1
- src/kiro/token_manager.rs +64 -7
- src/main.rs +14 -5
src/kiro/model/credentials.rs
CHANGED
|
@@ -102,6 +102,11 @@ impl CredentialsConfig {
|
|
| 102 |
CredentialsConfig::Multiple(creds) => creds.is_empty(),
|
| 103 |
}
|
| 104 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 105 |
}
|
| 106 |
|
| 107 |
impl KiroCredentials {
|
|
|
|
| 102 |
CredentialsConfig::Multiple(creds) => creds.is_empty(),
|
| 103 |
}
|
| 104 |
}
|
| 105 |
+
|
| 106 |
+
/// 判断是否为多凭据格式(数组格式)
|
| 107 |
+
pub fn is_multiple(&self) -> bool {
|
| 108 |
+
matches!(self, CredentialsConfig::Multiple(_))
|
| 109 |
+
}
|
| 110 |
}
|
| 111 |
|
| 112 |
impl KiroCredentials {
|
src/kiro/provider.rs
CHANGED
|
@@ -268,7 +268,7 @@ mod tests {
|
|
| 268 |
use crate::model::config::Config;
|
| 269 |
|
| 270 |
fn create_test_provider(config: Config, credentials: KiroCredentials) -> KiroProvider {
|
| 271 |
-
let tm = MultiTokenManager::new(config, vec![credentials], None).unwrap();
|
| 272 |
KiroProvider::new(Arc::new(tm))
|
| 273 |
}
|
| 274 |
|
|
|
|
| 268 |
use crate::model::config::Config;
|
| 269 |
|
| 270 |
fn create_test_provider(config: Config, credentials: KiroCredentials) -> KiroProvider {
|
| 271 |
+
let tm = MultiTokenManager::new(config, vec![credentials], None, None, false).unwrap();
|
| 272 |
KiroProvider::new(Arc::new(tm))
|
| 273 |
}
|
| 274 |
|
src/kiro/token_manager.rs
CHANGED
|
@@ -8,6 +8,8 @@ use chrono::{DateTime, Duration, Utc};
|
|
| 8 |
use parking_lot::Mutex;
|
| 9 |
use tokio::sync::Mutex as TokioMutex;
|
| 10 |
|
|
|
|
|
|
|
| 11 |
use crate::http_client::{build_client, ProxyConfig};
|
| 12 |
use crate::kiro::machine_id;
|
| 13 |
use crate::kiro::model::credentials::KiroCredentials;
|
|
@@ -385,6 +387,10 @@ pub struct MultiTokenManager {
|
|
| 385 |
current_index: Mutex<usize>,
|
| 386 |
/// Token 刷新锁,确保同一时间只有一个刷新操作
|
| 387 |
refresh_lock: TokioMutex<()>,
|
|
|
|
|
|
|
|
|
|
|
|
|
| 388 |
}
|
| 389 |
|
| 390 |
/// 每个凭据最大 API 调用失败次数
|
|
@@ -411,10 +417,14 @@ impl MultiTokenManager {
|
|
| 411 |
/// * `config` - 应用配置
|
| 412 |
/// * `credentials` - 按优先级排序的凭据列表
|
| 413 |
/// * `proxy` - 可选的代理配置
|
|
|
|
|
|
|
| 414 |
pub fn new(
|
| 415 |
config: Config,
|
| 416 |
credentials: Vec<KiroCredentials>,
|
| 417 |
proxy: Option<ProxyConfig>,
|
|
|
|
|
|
|
| 418 |
) -> anyhow::Result<Self> {
|
| 419 |
if credentials.is_empty() {
|
| 420 |
anyhow::bail!("至少需要一个凭据");
|
|
@@ -435,6 +445,8 @@ impl MultiTokenManager {
|
|
| 435 |
entries: Mutex::new(entries),
|
| 436 |
current_index: Mutex::new(0),
|
| 437 |
refresh_lock: TokioMutex::new(()),
|
|
|
|
|
|
|
| 438 |
})
|
| 439 |
}
|
| 440 |
|
|
@@ -559,8 +571,14 @@ impl MultiTokenManager {
|
|
| 559 |
}
|
| 560 |
|
| 561 |
// 更新凭据
|
| 562 |
-
|
| 563 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 564 |
new_creds
|
| 565 |
} else {
|
| 566 |
// 其他请求已经完成刷新,直接使用新凭据
|
|
@@ -583,6 +601,45 @@ impl MultiTokenManager {
|
|
| 583 |
})
|
| 584 |
}
|
| 585 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 586 |
/// 报告指定凭据 API 调用成功
|
| 587 |
///
|
| 588 |
/// 重置该凭据的失败计数
|
|
@@ -771,7 +828,7 @@ mod tests {
|
|
| 771 |
let mut cred2 = KiroCredentials::default();
|
| 772 |
cred2.priority = 1;
|
| 773 |
|
| 774 |
-
let manager = MultiTokenManager::new(config, vec![cred1, cred2], None).unwrap();
|
| 775 |
assert_eq!(manager.total_count(), 2);
|
| 776 |
assert_eq!(manager.available_count(), 2);
|
| 777 |
}
|
|
@@ -779,7 +836,7 @@ mod tests {
|
|
| 779 |
#[test]
|
| 780 |
fn test_multi_token_manager_empty_credentials() {
|
| 781 |
let config = Config::default();
|
| 782 |
-
let result = MultiTokenManager::new(config, vec![], None);
|
| 783 |
assert!(result.is_err());
|
| 784 |
}
|
| 785 |
|
|
@@ -789,7 +846,7 @@ mod tests {
|
|
| 789 |
let cred1 = KiroCredentials::default();
|
| 790 |
let cred2 = KiroCredentials::default();
|
| 791 |
|
| 792 |
-
let manager = MultiTokenManager::new(config, vec![cred1, cred2], None).unwrap();
|
| 793 |
|
| 794 |
// 前两次失败不会禁用(使用 index 0)
|
| 795 |
assert!(manager.report_failure(0));
|
|
@@ -812,7 +869,7 @@ mod tests {
|
|
| 812 |
let config = Config::default();
|
| 813 |
let cred = KiroCredentials::default();
|
| 814 |
|
| 815 |
-
let manager = MultiTokenManager::new(config, vec![cred], None).unwrap();
|
| 816 |
|
| 817 |
// 失败两次(使用 index 0)
|
| 818 |
manager.report_failure(0);
|
|
@@ -835,7 +892,7 @@ mod tests {
|
|
| 835 |
let mut cred2 = KiroCredentials::default();
|
| 836 |
cred2.refresh_token = Some("token2".to_string());
|
| 837 |
|
| 838 |
-
let manager = MultiTokenManager::new(config, vec![cred1, cred2], None).unwrap();
|
| 839 |
|
| 840 |
// 初始是第一个凭据
|
| 841 |
assert_eq!(
|
|
|
|
| 8 |
use parking_lot::Mutex;
|
| 9 |
use tokio::sync::Mutex as TokioMutex;
|
| 10 |
|
| 11 |
+
use std::path::PathBuf;
|
| 12 |
+
|
| 13 |
use crate::http_client::{build_client, ProxyConfig};
|
| 14 |
use crate::kiro::machine_id;
|
| 15 |
use crate::kiro::model::credentials::KiroCredentials;
|
|
|
|
| 387 |
current_index: Mutex<usize>,
|
| 388 |
/// Token 刷新锁,确保同一时间只有一个刷新操作
|
| 389 |
refresh_lock: TokioMutex<()>,
|
| 390 |
+
/// 凭据文件路径(用于回写)
|
| 391 |
+
credentials_path: Option<PathBuf>,
|
| 392 |
+
/// 是否为多凭据格式(数组格式才回写)
|
| 393 |
+
is_multiple_format: bool,
|
| 394 |
}
|
| 395 |
|
| 396 |
/// 每个凭据最大 API 调用失败次数
|
|
|
|
| 417 |
/// * `config` - 应用配置
|
| 418 |
/// * `credentials` - 按优先级排序的凭据列表
|
| 419 |
/// * `proxy` - 可选的代理配置
|
| 420 |
+
/// * `credentials_path` - 凭据文件路径(用于回写)
|
| 421 |
+
/// * `is_multiple_format` - 是否为多凭据格式(数组格式才回写)
|
| 422 |
pub fn new(
|
| 423 |
config: Config,
|
| 424 |
credentials: Vec<KiroCredentials>,
|
| 425 |
proxy: Option<ProxyConfig>,
|
| 426 |
+
credentials_path: Option<PathBuf>,
|
| 427 |
+
is_multiple_format: bool,
|
| 428 |
) -> anyhow::Result<Self> {
|
| 429 |
if credentials.is_empty() {
|
| 430 |
anyhow::bail!("至少需要一个凭据");
|
|
|
|
| 445 |
entries: Mutex::new(entries),
|
| 446 |
current_index: Mutex::new(0),
|
| 447 |
refresh_lock: TokioMutex::new(()),
|
| 448 |
+
credentials_path,
|
| 449 |
+
is_multiple_format,
|
| 450 |
})
|
| 451 |
}
|
| 452 |
|
|
|
|
| 571 |
}
|
| 572 |
|
| 573 |
// 更新凭据
|
| 574 |
+
{
|
| 575 |
+
let mut entries = self.entries.lock();
|
| 576 |
+
entries[index].credentials = new_creds.clone();
|
| 577 |
+
}
|
| 578 |
+
|
| 579 |
+
// 回写凭据到文件(仅多凭据格式)
|
| 580 |
+
self.persist_credentials();
|
| 581 |
+
|
| 582 |
new_creds
|
| 583 |
} else {
|
| 584 |
// 其他请求已经完成刷新,直接使用新凭据
|
|
|
|
| 601 |
})
|
| 602 |
}
|
| 603 |
|
| 604 |
+
/// 将凭据列表回写到源文件
|
| 605 |
+
///
|
| 606 |
+
/// 仅在以下条件满足时回写:
|
| 607 |
+
/// - 源文件是多凭据格式(数组)
|
| 608 |
+
/// - credentials_path 已设置
|
| 609 |
+
fn persist_credentials(&self) {
|
| 610 |
+
// 仅多凭据格式才回写
|
| 611 |
+
if !self.is_multiple_format {
|
| 612 |
+
return;
|
| 613 |
+
}
|
| 614 |
+
|
| 615 |
+
let path = match &self.credentials_path {
|
| 616 |
+
Some(p) => p,
|
| 617 |
+
None => return,
|
| 618 |
+
};
|
| 619 |
+
|
| 620 |
+
// 收集所有凭据
|
| 621 |
+
let credentials: Vec<KiroCredentials> = {
|
| 622 |
+
let entries = self.entries.lock();
|
| 623 |
+
entries.iter().map(|e| e.credentials.clone()).collect()
|
| 624 |
+
};
|
| 625 |
+
|
| 626 |
+
// 序列化为 pretty JSON
|
| 627 |
+
let json = match serde_json::to_string_pretty(&credentials) {
|
| 628 |
+
Ok(j) => j,
|
| 629 |
+
Err(e) => {
|
| 630 |
+
tracing::error!("序列化凭据失败: {}", e);
|
| 631 |
+
return;
|
| 632 |
+
}
|
| 633 |
+
};
|
| 634 |
+
|
| 635 |
+
// 写入文件
|
| 636 |
+
if let Err(e) = std::fs::write(path, json) {
|
| 637 |
+
tracing::error!("回写凭据文件失败: {}", e);
|
| 638 |
+
} else {
|
| 639 |
+
tracing::debug!("已回写凭据到文件: {:?}", path);
|
| 640 |
+
}
|
| 641 |
+
}
|
| 642 |
+
|
| 643 |
/// 报告指定凭据 API 调用成功
|
| 644 |
///
|
| 645 |
/// 重置该凭据的失败计数
|
|
|
|
| 828 |
let mut cred2 = KiroCredentials::default();
|
| 829 |
cred2.priority = 1;
|
| 830 |
|
| 831 |
+
let manager = MultiTokenManager::new(config, vec![cred1, cred2], None, None, false).unwrap();
|
| 832 |
assert_eq!(manager.total_count(), 2);
|
| 833 |
assert_eq!(manager.available_count(), 2);
|
| 834 |
}
|
|
|
|
| 836 |
#[test]
|
| 837 |
fn test_multi_token_manager_empty_credentials() {
|
| 838 |
let config = Config::default();
|
| 839 |
+
let result = MultiTokenManager::new(config, vec![], None, None, false);
|
| 840 |
assert!(result.is_err());
|
| 841 |
}
|
| 842 |
|
|
|
|
| 846 |
let cred1 = KiroCredentials::default();
|
| 847 |
let cred2 = KiroCredentials::default();
|
| 848 |
|
| 849 |
+
let manager = MultiTokenManager::new(config, vec![cred1, cred2], None, None, false).unwrap();
|
| 850 |
|
| 851 |
// 前两次失败不会禁用(使用 index 0)
|
| 852 |
assert!(manager.report_failure(0));
|
|
|
|
| 869 |
let config = Config::default();
|
| 870 |
let cred = KiroCredentials::default();
|
| 871 |
|
| 872 |
+
let manager = MultiTokenManager::new(config, vec![cred], None, None, false).unwrap();
|
| 873 |
|
| 874 |
// 失败两次(使用 index 0)
|
| 875 |
manager.report_failure(0);
|
|
|
|
| 892 |
let mut cred2 = KiroCredentials::default();
|
| 893 |
cred2.refresh_token = Some("token2".to_string());
|
| 894 |
|
| 895 |
+
let manager = MultiTokenManager::new(config, vec![cred1, cred2], None, None, false).unwrap();
|
| 896 |
|
| 897 |
// 初始是第一个凭据
|
| 898 |
assert_eq!(
|
src/main.rs
CHANGED
|
@@ -40,6 +40,9 @@ async fn main() {
|
|
| 40 |
std::process::exit(1);
|
| 41 |
});
|
| 42 |
|
|
|
|
|
|
|
|
|
|
| 43 |
// 转换为按优先级排序的凭据列表
|
| 44 |
let credentials_list = credentials_config.into_sorted_credentials();
|
| 45 |
tracing::info!("已加载 {} 个凭据配置", credentials_list.len());
|
|
@@ -68,11 +71,17 @@ async fn main() {
|
|
| 68 |
}
|
| 69 |
|
| 70 |
// 创建 MultiTokenManager 和 KiroProvider
|
| 71 |
-
let token_manager = MultiTokenManager::new(
|
| 72 |
-
.
|
| 73 |
-
|
| 74 |
-
|
| 75 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 76 |
let token_manager = Arc::new(token_manager);
|
| 77 |
let kiro_provider = KiroProvider::with_proxy(token_manager.clone(), proxy_config.clone());
|
| 78 |
|
|
|
|
| 40 |
std::process::exit(1);
|
| 41 |
});
|
| 42 |
|
| 43 |
+
// 判断是否为多凭据格式(用于刷新后回写)
|
| 44 |
+
let is_multiple_format = credentials_config.is_multiple();
|
| 45 |
+
|
| 46 |
// 转换为按优先级排序的凭据列表
|
| 47 |
let credentials_list = credentials_config.into_sorted_credentials();
|
| 48 |
tracing::info!("已加载 {} 个凭据配置", credentials_list.len());
|
|
|
|
| 71 |
}
|
| 72 |
|
| 73 |
// 创建 MultiTokenManager 和 KiroProvider
|
| 74 |
+
let token_manager = MultiTokenManager::new(
|
| 75 |
+
config.clone(),
|
| 76 |
+
credentials_list,
|
| 77 |
+
proxy_config.clone(),
|
| 78 |
+
Some(credentials_path.into()),
|
| 79 |
+
is_multiple_format,
|
| 80 |
+
)
|
| 81 |
+
.unwrap_or_else(|e| {
|
| 82 |
+
tracing::error!("创建 Token 管理器失败: {}", e);
|
| 83 |
+
std::process::exit(1);
|
| 84 |
+
});
|
| 85 |
let token_manager = Arc::new(token_manager);
|
| 86 |
let kiro_provider = KiroProvider::with_proxy(token_manager.clone(), proxy_config.clone());
|
| 87 |
|