Browse Source

Config 序列化调整

龙腾猫跃 4 months ago
parent
commit
2515eb5781
2 changed files with 32 additions and 72 deletions
  1. 32 36
      Shared/General/Config.cs
  2. 0 36
      Tests/General/ConfigTest.cs

+ 32 - 36
Shared/General/Config.cs

@@ -8,15 +8,15 @@ public interface IConfigProvider {
     /// 将数据写入缓存。
     /// <para/> 这不会写入数据,还需要调用 <see cref="Save"/> 以保存。
     /// </summary>
-    void SetToCache(string key, string? value, bool encrypted = false);
+    void SetToCache(string key, string? value, bool encrypted);
     /// <summary>
     /// 将指定数据从缓存中移除。
     /// <para/> 这不会写入数据,还需要调用 <see cref="Save"/> 以保存。
     /// </summary>
     void RemoveFromCache(string key);
-    string? Read(string key, string? defaultValue = null, bool encrypted = false);
+    string? Read(string key, string? defaultValue, bool encrypted);
     bool HasValue(string key);
-    void ClearCache();
+    void DiscardCache();
     void Save();
 }
 
@@ -29,40 +29,37 @@ public class JsonConfigProvider : IConfigProvider {
         ResetContent();
     }
 
-    /// <summary>
-    /// 读取到的原始内容。
-    /// </summary>
-    public Lazy<JObject> Content { get; private set; }
+    private Lazy<JObject> content;
     private void ResetContent() {
-        Content = new(() => {
+        content = new(() => {
             try {
                 return FileUtils.Exists(filePath) ? ((JObject?) FileUtils.ReadAsJson(filePath) ?? []) : [];
             } catch (Exception ex) {
-                Logger.Error(ex, $"读取配置文件失败({filePath})", LogBehavior.Toast);
+                Logger.Error(ex, $"读取配置文件失败({filePath})", LogBehavior.Alert);
                 return [];
             }
         });
     }
 
-    public void SetToCache(string key, string? value, bool encrypted = false) {
+    public void SetToCache(string key, string? value, bool encrypted) {
         if (encrypted) {
-            Content.Value[key] = CryptographyUtils.AesEncrypt(value); // UNDONE: 改为识别码
+            content.Value[key] = CryptographyUtils.AesEncrypt(value); // UNDONE: 改为识别码
             decryptedContentCache[key] = value;
         } else {
-            Content.Value[key] = value;
+            content.Value[key] = value;
         }
     }
-    public string? Read(string key, string? defaultValue = null, bool encrypted = false) {
-        if (!Content.Value.ContainsKey(key)) return defaultValue;
+    public string? Read(string key, string? defaultValue, bool encrypted) {
         if (encrypted && decryptedContentCache.TryGetValue(key, out var cachedValue)) return cachedValue; // 读取缓存中已经解密的值
-        // 读取值
-        string? value = Content.Value[key]?.ToString();
-        if (!encrypted) return value;
-        // 解密读取的数据
         try {
-            return decryptedContentCache.GetOrAdd(key, _ => CryptographyUtils.AesDecrypt(value));
+            if (!content.Value.TryGetValue(key, out var result)) return defaultValue;
+            if (encrypted) { // 解密
+                result = CryptographyUtils.AesDecrypt(result?.ToString());
+                decryptedContentCache.TryAdd(key, result?.ToString());
+            }
+            return result?.ToString();
         } catch (CryptographicException ex) {
-            Logger.Error(ex, $"解密配置失败({key})", LogBehavior.Toast);
+            Logger.Error(ex, $"解密配置失败,该配置将被重置({key})", LogBehavior.Alert);
             RemoveFromCache(key);
             Save();
             return defaultValue;
@@ -70,37 +67,38 @@ public class JsonConfigProvider : IConfigProvider {
     }
 
     public bool HasValue(string key)
-        => Content.Value.ContainsKey(key);
+        => content.Value.ContainsKey(key);
     public void RemoveFromCache(string key) {
-        Content.Value.Remove(key);
+        content.Value.Remove(key);
         decryptedContentCache.TryRemove(key, out _);
     }
-    public void ClearCache() {
+    public void DiscardCache() {
         ResetContent();
         decryptedContentCache.Clear();
     }
     public void Save()
-        => FileUtils.Write(filePath, Content.Value.ToString(Formatting.Indented));
+        => FileUtils.Write(filePath, content.Value.ToString(Formatting.Indented));
 }
 
-public static class Configs {
+public static class ConfigUtils {
     public static readonly JsonConfigProvider AppData = new(Path.Combine(Paths.AppDataThenName, "config.json"));
 
     /// <summary>
     /// 从 secret.json 中读取特定密钥,如果未找到对应密钥则抛出异常。
     /// </summary>
-    public static string GetSecret(string key) => secret.Read(key)?.ToString() ?? throw new KeyNotFoundException($"Secret not found: {key}");
+    public static string GetSecret(string key) 
+        => secret.Read(key, null, false)?.ToString() ?? throw new KeyNotFoundException($"Secret not found: {key}");
     private static readonly JsonConfigProvider secret = new(Path.Combine(Paths.AppData, "secret.json"));
 }
 
-public class ConfigEntry<T>(string key, T? defaultValue, IConfigProvider? defaultProvider = null, bool encrypted = false) where T : IEquatable<T> {
+public class ConfigEntry<T>(string key, T? defaultValue, IConfigProvider? defaultProvider = null, bool encrypted = false) {
     private IConfigProvider GetProvider(IConfigProvider? provider)
-        => provider ?? defaultProvider ?? Configs.AppData;
+        => provider ?? defaultProvider ?? ConfigUtils.AppData;
 
     /// <summary>
     /// 当设置项的值实际被改变时触发,参数为新值。
     /// </summary>
-    public event Action<T?>? OnChanged;
+    public event Action<T?>? Changed;
 
     public T? Get(IConfigProvider? providerOverride = null) {
         var provider = GetProvider(providerOverride);
@@ -108,10 +106,7 @@ public class ConfigEntry<T>(string key, T? defaultValue, IConfigProvider? defaul
         string? value = provider.Read(key, defaultValue?.ToString(), encrypted);
         try {
             if (value is null) return default;
-            var type = typeof(T);
-            if (type == typeof(string)) return (T) (object) value;
-            if (typeof(JToken).IsAssignableFrom(type)) return (T) (object) value.DeserializeJson()!;
-            return (T) Convert.ChangeType(value, type);
+            return typeof(T) == typeof(string) ? ((T) (object) value) : JsonConvert.DeserializeObject<T>(value);
         } catch (Exception ex) {
             Logger.Error(ex, $"读取配置项失败({key})", LogBehavior.Toast);
             return defaultValue;
@@ -121,9 +116,10 @@ public class ConfigEntry<T>(string key, T? defaultValue, IConfigProvider? defaul
         var provider = GetProvider(providerOverride);
         T? current = Get(provider);
         if ((current is null && value is null) || (current is not null && value is not null && current.Equals(value))) return; // 值未改变,无需更新
-        provider.SetToCache(key, value?.ToString(), encrypted);
+        provider.SetToCache(key, 
+            typeof(T) == typeof(string) ? (value as string) : JsonConvert.SerializeObject(value), encrypted);
         provider.Save();
-        OnChanged?.Invoke(value);
+        Changed?.Invoke(value);
     }
     public bool HasValue(IConfigProvider? providerOverride = null) {
         var provider = GetProvider(providerOverride);
@@ -134,7 +130,7 @@ public class ConfigEntry<T>(string key, T? defaultValue, IConfigProvider? defaul
         if (!provider.HasValue(key)) return; // 值未设置,无需重置
         provider.RemoveFromCache(key);
         provider.Save();
-        OnChanged?.Invoke(defaultValue);
+        Changed?.Invoke(defaultValue);
     }
     public void Save(IConfigProvider? providerOverride = null) {
         var provider = GetProvider(providerOverride);

+ 0 - 36
Tests/General/ConfigTest.cs

@@ -1,36 +0,0 @@
-namespace MeloongCore.Tests;
-
-public class ConfigTest : TestWithFolder {
-
-    [Test]
-    public async Task JsonConfigProvider_EncryptedReadWrite() {
-        var filePath = Path.Combine(tempFolder, "config.json");
-        var provider = new JsonConfigProvider(filePath);
-
-        provider.SetToCache("token", "plain token", encrypted: true);
-        provider.Save();
-
-        await Assert.That(FileUtils.ReadAsString(filePath).Contains("plain token")).IsFalse();
-        await Assert.That(provider.Read("token", encrypted: true)).IsEqualTo("plain token");
-        await Assert.That(provider.DecryptedContent.TryGetValue("token", out var decrypted) && decrypted == "plain token").IsTrue();
-    }
-
-    [Test]
-    public async Task JsonConfigProvider_ClearCache() {
-        var filePath = Path.Combine(tempFolder, "config.json");
-        var provider = new JsonConfigProvider(filePath);
-
-        provider.SetToCache("token", "old token", encrypted: true);
-        provider.Save();
-        await Assert.That(provider.Read("token", encrypted: true)).IsEqualTo("old token");
-
-        var secondProvider = new JsonConfigProvider(filePath);
-        secondProvider.SetToCache("token", "new token", encrypted: true);
-        secondProvider.Save();
-        provider.ClearCache();
-
-        await Assert.That(provider.DecryptedContent.IsEmpty).IsTrue();
-        await Assert.That(provider.Read("token", encrypted: true)).IsEqualTo("new token");
-    }
-
-}