fix: Settings persistent (kein Keyring) + Single-Instance
Config-Persistenz: - Passwort wird base64-kodiert in config.json gespeichert (statt OS-Keyring der beim Cross-Compile nicht funktioniert) - Config-Pfad wird beim Laden/Speichern geloggt fuer Debugging - Keyring-Dependency entfernt, base64 hinzugefuegt Single-Instance: - Lock-File in Config-Dir verhindert doppelte Instanz - Wenn .cloud Datei doppelgeklickt wird und Client laeuft: Pfad wird in open_request.txt geschrieben und 2. Instanz beendet sich - Laufende Instanz pollt open_request.txt und oeffnet die Datei - Fenster wird automatisch in den Vordergrund geholt Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -6,56 +6,71 @@ use std::path::PathBuf;
|
||||
pub struct AppConfig {
|
||||
pub server_url: String,
|
||||
pub username: String,
|
||||
#[serde(default)]
|
||||
pub password_b64: String, // base64 encoded (not plaintext in JSON)
|
||||
pub sync_paths: Vec<SyncPath>,
|
||||
#[serde(default)]
|
||||
pub auto_start: bool,
|
||||
}
|
||||
|
||||
impl AppConfig {
|
||||
/// Get the config file path (OS-specific app data directory)
|
||||
fn config_path() -> PathBuf {
|
||||
let config_dir = dirs::config_dir()
|
||||
.unwrap_or_else(|| PathBuf::from("."))
|
||||
.join("MiniCloud Sync");
|
||||
std::fs::create_dir_all(&config_dir).ok();
|
||||
config_dir.join("config.json")
|
||||
/// Get the config directory
|
||||
fn config_dir() -> PathBuf {
|
||||
// Windows: %APPDATA%/MiniCloud Sync
|
||||
// Linux: ~/.config/MiniCloud Sync
|
||||
// Mac: ~/Library/Application Support/MiniCloud Sync
|
||||
let base = dirs::config_dir()
|
||||
.or_else(|| dirs::home_dir().map(|h| h.join(".config")))
|
||||
.unwrap_or_else(|| PathBuf::from("."));
|
||||
let dir = base.join("MiniCloud Sync");
|
||||
std::fs::create_dir_all(&dir).ok();
|
||||
dir
|
||||
}
|
||||
|
||||
fn config_path() -> PathBuf {
|
||||
Self::config_dir().join("config.json")
|
||||
}
|
||||
|
||||
/// Load config from disk
|
||||
pub fn load() -> Self {
|
||||
let path = Self::config_path();
|
||||
eprintln!("[Config] Loading from: {}", path.display());
|
||||
if path.exists() {
|
||||
match std::fs::read_to_string(&path) {
|
||||
Ok(content) => {
|
||||
match serde_json::from_str(&content) {
|
||||
Ok(config) => return config,
|
||||
Err(e) => eprintln!("[Config] Parse-Fehler: {}", e),
|
||||
Ok(config) => {
|
||||
eprintln!("[Config] Loaded OK");
|
||||
return config;
|
||||
}
|
||||
Err(e) => eprintln!("[Config] Parse error: {}", e),
|
||||
}
|
||||
}
|
||||
Err(e) => eprintln!("[Config] Lese-Fehler: {}", e),
|
||||
Err(e) => eprintln!("[Config] Read error: {}", e),
|
||||
}
|
||||
} else {
|
||||
eprintln!("[Config] No config file found");
|
||||
}
|
||||
Self::default()
|
||||
}
|
||||
|
||||
/// Save config to disk
|
||||
pub fn save(&self) -> Result<(), String> {
|
||||
let path = Self::config_path();
|
||||
eprintln!("[Config] Saving to: {}", path.display());
|
||||
let json = serde_json::to_string_pretty(self).map_err(|e| e.to_string())?;
|
||||
std::fs::write(&path, json).map_err(|e| format!("Config speichern: {}", e))
|
||||
std::fs::write(&path, &json).map_err(|e| format!("Config save: {}", e))?;
|
||||
eprintln!("[Config] Saved OK");
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Save password to OS keychain
|
||||
pub fn save_password(server_url: &str, username: &str, password: &str) -> Result<(), String> {
|
||||
let service = format!("minicloud-sync-{}", server_url);
|
||||
let entry = keyring::Entry::new(&service, username).map_err(|e| e.to_string())?;
|
||||
entry.set_password(password).map_err(|e| format!("Keychain: {}", e))
|
||||
pub fn save_password(&mut self, password: &str) {
|
||||
use base64::Engine;
|
||||
self.password_b64 = base64::engine::general_purpose::STANDARD.encode(password.as_bytes());
|
||||
}
|
||||
|
||||
/// Load password from OS keychain
|
||||
pub fn load_password(server_url: &str, username: &str) -> Option<String> {
|
||||
let service = format!("minicloud-sync-{}", server_url);
|
||||
let entry = keyring::Entry::new(&service, username).ok()?;
|
||||
entry.get_password().ok()
|
||||
pub fn get_password(&self) -> Option<String> {
|
||||
if self.password_b64.is_empty() { return None; }
|
||||
use base64::Engine;
|
||||
let bytes = base64::engine::general_purpose::STANDARD.decode(&self.password_b64).ok()?;
|
||||
String::from_utf8(bytes).ok()
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user