Config.cs 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125
  1. using System.Collections.Concurrent;
  2. using System.Security.Cryptography;
  3. namespace MeloongCore;
  4. public interface IConfigProvider {
  5. /// <summary>
  6. /// 将原始数据存入缓存。
  7. /// </summary>
  8. void Set(string key, string value, bool encrypted = false);
  9. string? Read(string key, string? defaultValue = null, bool encrypted = false);
  10. bool HasValue(string key);
  11. void Remove(string key);
  12. void Save();
  13. void ClearCache();
  14. }
  15. public class JsonConfigProvider : IConfigProvider {
  16. private readonly string filePath;
  17. public readonly ConcurrentDictionary<string, string?> DecryptedContent = new();
  18. public JsonConfigProvider(string filePath) {
  19. this.filePath = filePath;
  20. ResetContent();
  21. }
  22. /// <summary>
  23. /// 读取到的原始内容。
  24. /// </summary>
  25. public Lazy<JObject> Content { get; private set; }
  26. private void ResetContent() {
  27. Content = new(() => {
  28. try {
  29. return FileUtils.Exists(filePath) ? ((JObject?) FileUtils.ReadAsJson(filePath) ?? []) : [];
  30. } catch (Exception ex) {
  31. Logger.Error(ex, $"读取配置文件失败:{filePath}", LogBehavior.Toast);
  32. return [];
  33. }
  34. });
  35. }
  36. public void Set(string key, string? value, bool encrypted = false) {
  37. if (encrypted) {
  38. Content.Value[key] = CryptographyUtils.AesEncrypt(value); // UNDONE: 改为识别码
  39. DecryptedContent[key] = value;
  40. } else {
  41. Content.Value[key] = value;
  42. }
  43. }
  44. public string? Read(string key, string? defaultValue = null, bool encrypted = false) {
  45. if (!Content.Value.ContainsKey(key)) return defaultValue;
  46. if (encrypted && DecryptedContent.TryGetValue(key, out var cachedValue)) return cachedValue; // 读取缓存中已经解密的值
  47. // 读取值
  48. string value = Content.Value[key]!.ToString();
  49. if (!encrypted) return value;
  50. // 解密读取的数据
  51. try {
  52. return DecryptedContent.GetOrAdd(key, _ => CryptographyUtils.AesDecrypt(value));
  53. } catch (CryptographicException ex) {
  54. Logger.Error(ex, $"解密设置失败,该设置将被重置:{key}", LogBehavior.Toast);
  55. Remove(key);
  56. Save();
  57. return defaultValue;
  58. }
  59. }
  60. public bool HasValue(string key)
  61. => Content.Value.ContainsKey(key);
  62. public void Remove(string key) {
  63. Content.Value.Remove(key);
  64. DecryptedContent.TryRemove(key, out _);
  65. }
  66. public void Save()
  67. => FileUtils.Write(filePath, Content.Value.ToString(Formatting.Indented));
  68. public void ClearCache() {
  69. ResetContent();
  70. DecryptedContent.Clear();
  71. }
  72. }
  73. public static class Configs {
  74. public static readonly JsonConfigProvider AppData = new(Path.Combine(Paths.AppDataThenName, "config.json"));
  75. /// <summary>
  76. /// 从 secret.json 中读取特定密钥,如果未找到对应密钥则抛出异常。
  77. /// </summary>
  78. public static string GetSecret(string key) => secret.Read(key)?.ToString() ?? throw new KeyNotFoundException($"Secret not found: {key}");
  79. private static readonly JsonConfigProvider secret = new(Path.Combine(Paths.AppData, "secret.json"));
  80. }
  81. public class ConfigEntry<T>(string key, T? defaultValue, IConfigProvider? defaultProvider = null, bool encrypted = false) {
  82. private IConfigProvider GetProvider(IConfigProvider? provider)
  83. => provider ?? defaultProvider ?? Configs.AppData;
  84. /// <summary>
  85. /// 当设置项的值实际被改变时触发,参数为新值。
  86. /// </summary>
  87. public event Action<T?>? OnChanged;
  88. /*public T? Value {
  89. get {
  90. return GetProvider(null).Read(key, JToken.FromObject(defaultValue))?.ToObject<T>();
  91. }
  92. set {
  93. var provider = GetProvider(null);
  94. var oldValue = Value;
  95. if (!Equals(oldValue, value)) {
  96. provider.Write(key, JToken.FromObject(value));
  97. OnChanged?.Invoke(value);
  98. }
  99. }
  100. }
  101. public T? GetValue(IConfigProvider? provider = null) {
  102. return GetProvider(provider).Read(key, JToken.FromObject(defaultValue))?.ToObject<T>();
  103. }*/
  104. public bool HasValue(IConfigProvider? provider = null)
  105. => GetProvider(provider).HasValue(key);
  106. public void Remove(IConfigProvider? provider = null)
  107. => GetProvider(provider).Remove(key);
  108. public void Save(IConfigProvider? provider = null)
  109. => GetProvider(provider).Save();
  110. }