- Move git_token and Bedrock credentials to OS keychain instead of storing in plaintext projects.json via skip_serializing + keyring - Fix project status stuck in Starting on container creation failure by resetting to Stopped on any error path - Add granular store methods to reduce TOCTOU race window - Add auth_mode, project path, and bedrock config change detection to container_needs_recreation with label-based fingerprinting - Fix mutex held across async Docker API call in exec resize by cloning exec_id under lock then releasing before API call - Add graceful shutdown via on_window_event to clean up exec sessions - Extract compute_env_fingerprint and merge_claude_instructions helpers to eliminate code duplication in container.rs - Remove unused thiserror dependency - Return error instead of falling back to CWD when data dir unavailable Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
77 lines
2.5 KiB
Rust
77 lines
2.5 KiB
Rust
use std::fs;
|
|
use std::path::PathBuf;
|
|
use std::sync::Mutex;
|
|
|
|
use crate::models::AppSettings;
|
|
|
|
pub struct SettingsStore {
|
|
settings: Mutex<AppSettings>,
|
|
file_path: PathBuf,
|
|
}
|
|
|
|
impl SettingsStore {
|
|
pub fn new() -> Result<Self, String> {
|
|
let data_dir = dirs::data_dir()
|
|
.ok_or_else(|| "Could not determine data directory. Set XDG_DATA_HOME on Linux.".to_string())?
|
|
.join("triple-c");
|
|
|
|
fs::create_dir_all(&data_dir).ok();
|
|
|
|
let file_path = data_dir.join("settings.json");
|
|
|
|
let settings = if file_path.exists() {
|
|
match fs::read_to_string(&file_path) {
|
|
Ok(data) => match serde_json::from_str(&data) {
|
|
Ok(parsed) => parsed,
|
|
Err(e) => {
|
|
log::error!("Failed to parse settings.json: {}. Using defaults.", e);
|
|
let backup = file_path.with_extension("json.bak");
|
|
if let Err(be) = fs::copy(&file_path, &backup) {
|
|
log::error!("Failed to back up corrupted settings.json: {}", be);
|
|
}
|
|
AppSettings::default()
|
|
}
|
|
},
|
|
Err(e) => {
|
|
log::error!("Failed to read settings.json: {}", e);
|
|
AppSettings::default()
|
|
}
|
|
}
|
|
} else {
|
|
AppSettings::default()
|
|
};
|
|
|
|
Ok(Self {
|
|
settings: Mutex::new(settings),
|
|
file_path,
|
|
})
|
|
}
|
|
|
|
fn lock(&self) -> std::sync::MutexGuard<'_, AppSettings> {
|
|
self.settings.lock().unwrap_or_else(|e| e.into_inner())
|
|
}
|
|
|
|
fn save(&self, settings: &AppSettings) -> Result<(), String> {
|
|
let data = serde_json::to_string_pretty(settings)
|
|
.map_err(|e| format!("Failed to serialize settings: {}", e))?;
|
|
|
|
let tmp_path = self.file_path.with_extension("json.tmp");
|
|
fs::write(&tmp_path, data)
|
|
.map_err(|e| format!("Failed to write temp settings file: {}", e))?;
|
|
fs::rename(&tmp_path, &self.file_path)
|
|
.map_err(|e| format!("Failed to rename settings file: {}", e))?;
|
|
Ok(())
|
|
}
|
|
|
|
pub fn get(&self) -> AppSettings {
|
|
self.lock().clone()
|
|
}
|
|
|
|
pub fn update(&self, new_settings: AppSettings) -> Result<AppSettings, String> {
|
|
let mut settings = self.lock();
|
|
*settings = new_settings.clone();
|
|
self.save(&settings)?;
|
|
Ok(new_settings)
|
|
}
|
|
}
|