using System.Data.Common;
using System.Security.Cryptography;
using System.Text;
namespace Admin.NET.Plugin.AiDOP.Service.S8.Rules;
///
/// S8-DYNAMIC-SQLSUGAR-SCOPE-FACTORY-1:S8 data_source endpoint 规范化与 hash 派生。
/// 纯字符串变换:
/// 1) 用 DbConnectionStringBuilder 解析 raw endpoint 的 key/value(大小写无关);
/// 2) 按 OrdinalIgnoreCase 全部 key lower-case 化,避免不同输入 case 导致 hash 漂移;
/// 3) 补齐缺失参数(已有 key 不覆盖),与主库 Database.json 池层参数对齐:
/// AllowPublicKeyRetrieval / Pooling / Minimum Pool Size / Maximum Pool Size /
/// Connection Timeout / Connection Idle Timeout / Connection LifeTime;
/// 4) 按 key 字母序输出 normalized canonical connection string;
/// 5) 对 normalized 做 SHA256 → 64 字符小写 hex EndpointHash。
/// 不访问数据库、不读写文件、不读 env、不输出连接串原文、不输出密码。
/// 输入为空或空白时抛 ArgumentException,message 不携带 endpoint 原文。
///
internal static class S8DataSourceEndpointNormalizer
{
public const string KeyAllowPublicKeyRetrieval = "allowpublickeyretrieval";
public const string KeyPooling = "pooling";
public const string KeyMinimumPoolSize = "minimum pool size";
public const string KeyMaximumPoolSize = "maximum pool size";
public const string KeyConnectionTimeout = "connection timeout";
public const string KeyConnectionIdleTimeout = "connection idle timeout";
public const string KeyConnectionLifeTime = "connection lifetime";
private static readonly (string Key, string Value)[] _normalizeDefaults =
{
(KeyAllowPublicKeyRetrieval, "True"),
(KeyPooling, "True"),
(KeyMinimumPoolSize, "0"),
(KeyMaximumPoolSize, "20"),
(KeyConnectionTimeout, "10"),
(KeyConnectionIdleTimeout, "180"),
(KeyConnectionLifeTime, "300"),
};
public static S8DataSourceEndpointNormalizeResult Normalize(string rawEndpoint)
{
if (string.IsNullOrWhiteSpace(rawEndpoint))
throw new ArgumentException("S8 data_source endpoint 不能为空或空白", nameof(rawEndpoint));
var parsed = new DbConnectionStringBuilder { ConnectionString = rawEndpoint };
// SortedDictionary(Ordinal) + 全部 key 小写化 → 相同语义 endpoint 落到相同 canonical 顺序,hash 稳定。
var canonical = new SortedDictionary(StringComparer.Ordinal);
foreach (string key in parsed.Keys)
{
var canonicalKey = key.ToLowerInvariant();
canonical[canonicalKey] = parsed[key]?.ToString() ?? string.Empty;
}
// 补齐缺失默认值;已有 key(如自定义 Maximum Pool Size)保留原值,不覆盖。
foreach (var (defaultKey, defaultValue) in _normalizeDefaults)
{
if (!canonical.ContainsKey(defaultKey))
canonical[defaultKey] = defaultValue;
}
// 用 DbConnectionStringBuilder 按 sorted key 顺序重组;其 ToString 自动处理 value quoting。
var normalizedBuilder = new DbConnectionStringBuilder();
foreach (var kv in canonical)
normalizedBuilder[kv.Key] = kv.Value;
var normalized = normalizedBuilder.ConnectionString;
var bytes = SHA256.HashData(Encoding.UTF8.GetBytes(normalized));
var hashHex = Convert.ToHexString(bytes).ToLowerInvariant();
return new S8DataSourceEndpointNormalizeResult(normalized, hashHex);
}
}
///
/// S8 data_source endpoint 规范化结果。NormalizedConnectionString 用于建 SqlSugarScope;
/// EndpointHash 是 SHA256(normalized) 的 64 字符小写 hex,调用方可取前 N 位派生 ConfigId。
///
public readonly record struct S8DataSourceEndpointNormalizeResult(string NormalizedConnectionString, string EndpointHash);