using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Text.Json; using AxCopilot.Models; namespace AxCopilot.Services; public class SettingsService { private static readonly string AppDataDir = InitAppDataDir(); public static readonly string SettingsPath = Path.Combine(AppDataDir, "settings.dat"); private static readonly string LegacyJsonPath = Path.Combine(AppDataDir, "settings.json"); private static readonly JsonSerializerOptions JsonOptions = new JsonSerializerOptions { WriteIndented = true, PropertyNameCaseInsensitive = true }; private AppSettings _settings = new AppSettings(); private const string CurrentSettingsVersion = "1.2"; public AppSettings Settings => _settings; public string? MigrationSummary { get; private set; } public event EventHandler? SettingsChanged; private static string InitAppDataDir() { string folderPath = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData); string text = Path.Combine(folderPath, "AxCopilot"); string text2 = Path.Combine(folderPath, "AxCommander"); if (!Directory.Exists(text) && Directory.Exists(text2)) { try { Directory.Move(text2, text); } catch { } } return text; } public void Load() { EnsureDirectories(); if (File.Exists(SettingsPath)) { try { string @base = File.ReadAllText(SettingsPath); string text = CryptoService.PortableDecrypt(@base); if (string.IsNullOrEmpty(text)) { throw new InvalidOperationException("복호화 결과가 비어 있습니다."); } _settings = JsonSerializer.Deserialize(text, JsonOptions) ?? new AppSettings(); MigrateIfNeeded(); return; } catch (Exception ex) { string destFileName = SettingsPath + ".bak"; try { File.Copy(SettingsPath, destFileName, overwrite: true); } catch (Exception ex2) { LogService.Warn("settings.dat 백업 실패: " + ex2.Message); } LogService.Error("settings.dat 복호화/파싱 실패, 기본값으로 복구: " + ex.Message); } } if (File.Exists(LegacyJsonPath)) { try { string json = File.ReadAllText(LegacyJsonPath); _settings = JsonSerializer.Deserialize(json, JsonOptions) ?? new AppSettings(); MigrateIfNeeded(); Save(); try { File.Delete(LegacyJsonPath); } catch (Exception ex3) { LogService.Warn("레거시 settings.json 삭제 실패: " + ex3.Message); } LogService.Info("설정 마이그레이션: settings.json → settings.dat (암호화 적용)"); return; } catch (Exception ex4) { LogService.Error("레거시 settings.json 파싱 실패: " + ex4.Message); } } _settings = CreateDefaults(); _settings.Version = "1.2"; Save(); } private void MigrateIfNeeded() { string current = _settings.Version ?? "1.0"; bool flag = false; if (IsVersionLessThan(current, "1.1")) { foreach (AliasEntry item in _settings.Aliases.Where((AliasEntry a) => a.Type == "folder")) { if (item.Key.StartsWith("~")) { item.Key = "cd " + item.Key.TrimStart('~'); } } LogService.Info("설정 마이그레이션: 1.0 → 1.1 (폴더 별칭 prefix ~ → cd)"); current = "1.1"; flag = true; } if (IsVersionLessThan(current, "1.2")) { _settings.Launcher.EnableFileDialogIntegration = false; LogService.Info("설정 마이그레이션: 1.1 → 1.2 (파일 대화상자 연동 비활성화)"); current = "1.2"; flag = true; } if (flag) { _settings.Version = "1.2"; Save(); MigrationSummary = "설정이 v1.2으로 업데이트되었습니다.\n\n• 파일 대화상자 연동 기능이 비활성화되었습니다.\n 웹 브라우저 파일 업로드 시 런처가 열리는 문제를 방지합니다.\n 필요 시 설정 → 기능 → AI 기능에서 다시 활성화할 수 있습니다."; LogService.Info("설정 파일 마이그레이션 완료 → v1.2"); } } private static bool IsVersionLessThan(string current, string target) { if (Version.TryParse(current, out Version result) && Version.TryParse(target, out Version result2)) { return result < result2; } return string.Compare(current, target, StringComparison.Ordinal) < 0; } public void Save() { EnsureDirectories(); string plainText = JsonSerializer.Serialize(_settings, JsonOptions); string contents = CryptoService.PortableEncrypt(plainText); File.WriteAllText(SettingsPath, contents); this.SettingsChanged?.Invoke(this, EventArgs.Empty); } private static void EnsureDirectories() { Directory.CreateDirectory(AppDataDir); Directory.CreateDirectory(Path.Combine(AppDataDir, "logs")); Directory.CreateDirectory(Path.Combine(AppDataDir, "crashes")); Directory.CreateDirectory(Path.Combine(AppDataDir, "skills")); } private static AppSettings CreateDefaults() { List list = new List { "%USERPROFILE%\\Desktop", "%APPDATA%\\Microsoft\\Windows\\Start Menu" }; if (Directory.Exists("D:\\Non_Documents")) { list.Add("D:\\Non_Documents"); } return new AppSettings { IndexPaths = list, Aliases = new List { new AliasEntry { Key = "@blog", Type = "url", Target = "https://example.com", Description = "내 블로그" }, new AliasEntry { Key = "~home", Type = "folder", Target = "%USERPROFILE%", Description = "홈 폴더" } }, Launcher = new LauncherSettings { CustomTheme = new CustomThemeColors() } }; } }