feat(keyring): 集成系统密钥环安全存储 API key
This commit is contained in:
@@ -1,4 +1,5 @@
|
||||
use super::{AppConfig, GitProfile, TokenConfig};
|
||||
use crate::utils::keyring::{KeyringManager, get_default_base_url, get_default_model, provider_needs_api_key};
|
||||
use anyhow::{bail, Context, Result};
|
||||
use std::collections::HashMap;
|
||||
use std::path::{Path, PathBuf};
|
||||
@@ -8,6 +9,7 @@ pub struct ConfigManager {
|
||||
config: AppConfig,
|
||||
config_path: PathBuf,
|
||||
modified: bool,
|
||||
keyring: KeyringManager,
|
||||
}
|
||||
|
||||
impl ConfigManager {
|
||||
@@ -28,6 +30,7 @@ impl ConfigManager {
|
||||
config,
|
||||
config_path: path.to_path_buf(),
|
||||
modified: false,
|
||||
keyring: KeyringManager::new(),
|
||||
})
|
||||
}
|
||||
|
||||
@@ -37,6 +40,7 @@ impl ConfigManager {
|
||||
config: AppConfig::default(),
|
||||
config_path: path.to_path_buf(),
|
||||
modified: true,
|
||||
keyring: KeyringManager::new(),
|
||||
})
|
||||
}
|
||||
|
||||
@@ -262,96 +266,140 @@ impl ConfigManager {
|
||||
|
||||
/// Set LLM provider
|
||||
pub fn set_llm_provider(&mut self, provider: String) {
|
||||
self.config.llm.provider = provider;
|
||||
let default_model = get_default_model(&provider);
|
||||
self.config.llm.provider = provider.clone();
|
||||
if self.config.llm.model.is_empty() || self.config.llm.model == "llama2" {
|
||||
self.config.llm.model = default_model.to_string();
|
||||
}
|
||||
self.modified = true;
|
||||
}
|
||||
|
||||
/// Get OpenAI API key
|
||||
pub fn openai_api_key(&self) -> Option<&String> {
|
||||
self.config.llm.openai.api_key.as_ref()
|
||||
/// Get model
|
||||
pub fn llm_model(&self) -> &str {
|
||||
&self.config.llm.model
|
||||
}
|
||||
|
||||
/// Set OpenAI API key
|
||||
pub fn set_openai_api_key(&mut self, key: String) {
|
||||
self.config.llm.openai.api_key = Some(key);
|
||||
/// Set model
|
||||
pub fn set_llm_model(&mut self, model: String) {
|
||||
self.config.llm.model = model;
|
||||
self.modified = true;
|
||||
}
|
||||
|
||||
/// Get Anthropic API key
|
||||
pub fn anthropic_api_key(&self) -> Option<&String> {
|
||||
self.config.llm.anthropic.api_key.as_ref()
|
||||
/// Get base URL (returns provider default if not set)
|
||||
pub fn llm_base_url(&self) -> String {
|
||||
match &self.config.llm.base_url {
|
||||
Some(url) => url.clone(),
|
||||
None => get_default_base_url(&self.config.llm.provider).to_string(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Set Anthropic API key
|
||||
pub fn set_anthropic_api_key(&mut self, key: String) {
|
||||
self.config.llm.anthropic.api_key = Some(key);
|
||||
/// Set base URL
|
||||
pub fn set_llm_base_url(&mut self, url: Option<String>) {
|
||||
self.config.llm.base_url = url;
|
||||
self.modified = true;
|
||||
}
|
||||
|
||||
/// Get Kimi API key
|
||||
pub fn kimi_api_key(&self) -> Option<&String> {
|
||||
self.config.llm.kimi.api_key.as_ref()
|
||||
/// Get API key from configured storage method
|
||||
pub fn get_api_key(&self) -> Option<String> {
|
||||
// First try environment variables (always checked)
|
||||
if let Some(key) = self.keyring.get_api_key(&self.config.llm.provider).unwrap_or(None) {
|
||||
return Some(key);
|
||||
}
|
||||
|
||||
// Then try config file if configured
|
||||
if self.config.llm.api_key_storage == "config" {
|
||||
return self.config.llm.api_key.clone();
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
|
||||
/// Set Kimi API key
|
||||
pub fn set_kimi_api_key(&mut self, key: String) {
|
||||
self.config.llm.kimi.api_key = Some(key);
|
||||
self.modified = true;
|
||||
/// Store API key in configured storage method
|
||||
pub fn set_api_key(&self, api_key: &str) -> Result<()> {
|
||||
match self.config.llm.api_key_storage.as_str() {
|
||||
"keyring" => {
|
||||
if !self.keyring.is_available() {
|
||||
bail!("Keyring is not available. Set QUICOMMIT_API_KEY environment variable instead or change api_key_storage to 'config'.");
|
||||
}
|
||||
self.keyring.store_api_key(&self.config.llm.provider, api_key)
|
||||
},
|
||||
"config" => {
|
||||
// We can't modify self.config here since self is immutable
|
||||
// This will be handled by the caller updating the config
|
||||
Ok(())
|
||||
},
|
||||
"environment" => {
|
||||
bail!("API key storage set to 'environment'. Please set QUICOMMIT_{}_API_KEY environment variable.", self.config.llm.provider.to_uppercase());
|
||||
},
|
||||
_ => {
|
||||
bail!("Invalid API key storage method: {}", self.config.llm.api_key_storage);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Get Kimi base URL
|
||||
pub fn kimi_base_url(&self) -> &str {
|
||||
&self.config.llm.kimi.base_url
|
||||
/// Delete API key from configured storage method
|
||||
pub fn delete_api_key(&self) -> Result<()> {
|
||||
match self.config.llm.api_key_storage.as_str() {
|
||||
"keyring" => {
|
||||
if self.keyring.is_available() {
|
||||
self.keyring.delete_api_key(&self.config.llm.provider)?;
|
||||
}
|
||||
},
|
||||
"config" => {
|
||||
// We can't modify self.config here since self is immutable
|
||||
// This will be handled by the caller updating the config
|
||||
},
|
||||
"environment" => {
|
||||
// Environment variables are not managed by the app
|
||||
},
|
||||
_ => {
|
||||
bail!("Invalid API key storage method: {}", self.config.llm.api_key_storage);
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Set Kimi base URL
|
||||
pub fn set_kimi_base_url(&mut self, url: String) {
|
||||
self.config.llm.kimi.base_url = url;
|
||||
self.modified = true;
|
||||
/// Check if API key is configured
|
||||
pub fn has_api_key(&self) -> bool {
|
||||
if !provider_needs_api_key(&self.config.llm.provider) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Check environment variables
|
||||
if self.keyring.get_api_key(&self.config.llm.provider).unwrap_or(None).is_some() {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Check config file if configured
|
||||
if self.config.llm.api_key_storage == "config" {
|
||||
return self.config.llm.api_key.is_some();
|
||||
}
|
||||
|
||||
false
|
||||
}
|
||||
|
||||
/// Get DeepSeek API key
|
||||
pub fn deepseek_api_key(&self) -> Option<&String> {
|
||||
self.config.llm.deepseek.api_key.as_ref()
|
||||
/// Get keyring manager reference
|
||||
pub fn keyring(&self) -> &KeyringManager {
|
||||
&self.keyring
|
||||
}
|
||||
|
||||
/// Set DeepSeek API key
|
||||
pub fn set_deepseek_api_key(&mut self, key: String) {
|
||||
self.config.llm.deepseek.api_key = Some(key);
|
||||
self.modified = true;
|
||||
}
|
||||
|
||||
/// Get DeepSeek base URL
|
||||
pub fn deepseek_base_url(&self) -> &str {
|
||||
&self.config.llm.deepseek.base_url
|
||||
}
|
||||
|
||||
/// Set DeepSeek base URL
|
||||
pub fn set_deepseek_base_url(&mut self, url: String) {
|
||||
self.config.llm.deepseek.base_url = url;
|
||||
self.modified = true;
|
||||
}
|
||||
|
||||
/// Get OpenRouter API key
|
||||
pub fn openrouter_api_key(&self) -> Option<&String> {
|
||||
self.config.llm.openrouter.api_key.as_ref()
|
||||
}
|
||||
|
||||
/// Set OpenRouter API key
|
||||
pub fn set_openrouter_api_key(&mut self, key: String) {
|
||||
self.config.llm.openrouter.api_key = Some(key);
|
||||
self.modified = true;
|
||||
}
|
||||
|
||||
/// Get OpenRouter base URL
|
||||
pub fn openrouter_base_url(&self) -> &str {
|
||||
&self.config.llm.openrouter.base_url
|
||||
}
|
||||
|
||||
/// Set OpenRouter base URL
|
||||
pub fn set_openrouter_base_url(&mut self, url: String) {
|
||||
self.config.llm.openrouter.base_url = url;
|
||||
self.modified = true;
|
||||
/// Configure LLM provider with all settings
|
||||
pub fn configure_llm(&mut self, provider: String, model: Option<String>, base_url: Option<String>, api_key: Option<&str>) -> Result<()> {
|
||||
self.set_llm_provider(provider.clone());
|
||||
|
||||
if let Some(m) = model {
|
||||
self.set_llm_model(m);
|
||||
}
|
||||
|
||||
self.set_llm_base_url(base_url);
|
||||
|
||||
if let Some(key) = api_key {
|
||||
if provider_needs_api_key(&provider) {
|
||||
self.set_api_key(key)?;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// Commit configuration
|
||||
@@ -471,6 +519,7 @@ impl Default for ConfigManager {
|
||||
config: AppConfig::default(),
|
||||
config_path: PathBuf::new(),
|
||||
modified: false,
|
||||
keyring: KeyringManager::new(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user