| | using System; |
| | using System.IO; |
| | using System.Threading.Tasks; |
| | using Microsoft.Win32; |
| |
|
| | namespace KSTools.Licensing |
| | { |
| | |
| | |
| | |
| | |
| | public class LicenseManager |
| | { |
| | private const string REGISTRY_KEY_PATH = @"HKEY_CURRENT_USER\Software\KSTools\RevitPlugin"; |
| | private const string LICENSE_CODE_KEY = "LicenseCode"; |
| | private const string LICENSE_CACHE_KEY = "LicenseCache"; |
| | private const string LAST_VALIDATION_KEY = "LastValidation"; |
| | private const int VALIDATION_CACHE_HOURS = 1; |
| | |
| | private readonly ApiClient _apiClient; |
| | private readonly string _hardwareId; |
| | |
| | |
| | |
| | |
| | |
| | public LicenseManager(string apiBaseUrl = null) |
| | { |
| | _apiClient = new ApiClient(apiBaseUrl); |
| | _hardwareId = HardwareFingerprint.GetHardwareId(); |
| | } |
| | |
| | |
| | |
| | |
| | |
| | public string GetHardwareId() |
| | { |
| | return _hardwareId; |
| | } |
| | |
| | |
| | |
| | |
| | |
| | public string GetHardwareInfo() |
| | { |
| | return HardwareFingerprint.GetHardwareInfo(); |
| | } |
| | |
| | |
| | |
| | |
| | |
| | |
| | public async Task<ActivationResult> ActivateLicenseAsync(string licenseCode) |
| | { |
| | try |
| | { |
| | var result = await _apiClient.ActivateLicenseAsync(licenseCode, _hardwareId); |
| | |
| | if (result.Success) |
| | { |
| | |
| | StoreLicenseCode(licenseCode); |
| | |
| | |
| | StoreLicenseCache(result); |
| | |
| | |
| | StoreLastValidation(); |
| | } |
| | |
| | return result; |
| | } |
| | catch (Exception ex) |
| | { |
| | return new ActivationResult |
| | { |
| | Success = false, |
| | Message = $"啟用過程發生錯誤: {ex.Message}" |
| | }; |
| | } |
| | } |
| | |
| | |
| | |
| | |
| | |
| | |
| | public async Task<ValidationResult> ValidateLicenseAsync(bool forceOnlineCheck = false) |
| | { |
| | try |
| | { |
| | string licenseCode = GetStoredLicenseCode(); |
| | |
| | if (string.IsNullOrEmpty(licenseCode)) |
| | { |
| | return new ValidationResult |
| | { |
| | Valid = false, |
| | Message = "尚未啟用授權,請先輸入授權碼" |
| | }; |
| | } |
| | |
| | |
| | if (!forceOnlineCheck) |
| | { |
| | var cachedResult = GetCachedValidation(); |
| | if (cachedResult != null) |
| | { |
| | return cachedResult; |
| | } |
| | } |
| | |
| | |
| | var result = await _apiClient.ValidateLicenseAsync(licenseCode, _hardwareId); |
| | |
| | if (result.Valid) |
| | { |
| | |
| | StoreLicenseCache(new ActivationResult |
| | { |
| | Success = true, |
| | Message = result.Message, |
| | ExpiresAt = result.ExpiresAt, |
| | UserName = result.UserName, |
| | HardwareId = result.HardwareId |
| | }); |
| | |
| | StoreLastValidation(); |
| | } |
| | else |
| | { |
| | |
| | ClearLicenseCache(); |
| | } |
| | |
| | return result; |
| | } |
| | catch (Exception ex) |
| | { |
| | |
| | var cachedResult = GetCachedValidation(); |
| | if (cachedResult != null) |
| | { |
| | cachedResult.Message += " (離線模式)"; |
| | return cachedResult; |
| | } |
| | |
| | return new ValidationResult |
| | { |
| | Valid = false, |
| | Message = $"驗證過程發生錯誤: {ex.Message}" |
| | }; |
| | } |
| | } |
| | |
| | |
| | |
| | |
| | |
| | public bool HasValidLicense() |
| | { |
| | try |
| | { |
| | string licenseCode = GetStoredLicenseCode(); |
| | if (string.IsNullOrEmpty(licenseCode)) |
| | return false; |
| | |
| | var cachedResult = GetCachedValidation(); |
| | return cachedResult?.Valid == true; |
| | } |
| | catch |
| | { |
| | return false; |
| | } |
| | } |
| | |
| | |
| | |
| | |
| | |
| | public LicenseInfo GetLicenseInfo() |
| | { |
| | try |
| | { |
| | string licenseCode = GetStoredLicenseCode(); |
| | if (string.IsNullOrEmpty(licenseCode)) |
| | return null; |
| | |
| | var cachedData = GetRegistryValue(LICENSE_CACHE_KEY); |
| | if (!string.IsNullOrEmpty(cachedData)) |
| | { |
| | var parts = cachedData.Split('|'); |
| | if (parts.Length >= 4) |
| | { |
| | return new LicenseInfo |
| | { |
| | LicenseCode = licenseCode, |
| | UserName = parts[0], |
| | ExpiresAt = DateTime.TryParse(parts[1], out var expires) ? expires : (DateTime?)null, |
| | HardwareId = parts[2], |
| | LastValidation = DateTime.TryParse(parts[3], out var lastVal) ? lastVal : (DateTime?)null |
| | }; |
| | } |
| | } |
| | |
| | return new LicenseInfo |
| | { |
| | LicenseCode = licenseCode, |
| | HardwareId = _hardwareId |
| | }; |
| | } |
| | catch |
| | { |
| | return null; |
| | } |
| | } |
| | |
| | |
| | |
| | |
| | public void ClearLicense() |
| | { |
| | try |
| | { |
| | Registry.SetValue(REGISTRY_KEY_PATH, LICENSE_CODE_KEY, ""); |
| | ClearLicenseCache(); |
| | } |
| | catch |
| | { |
| | |
| | } |
| | } |
| | |
| | |
| | |
| | |
| | |
| | public async Task<bool> TestApiConnectionAsync() |
| | { |
| | return await _apiClient.TestConnectionAsync(); |
| | } |
| | |
| | #region 私有方法 |
| | |
| | |
| | |
| | |
| | |
| | private void StoreLicenseCode(string licenseCode) |
| | { |
| | Registry.SetValue(REGISTRY_KEY_PATH, LICENSE_CODE_KEY, licenseCode); |
| | } |
| | |
| | |
| | |
| | |
| | |
| | private string GetStoredLicenseCode() |
| | { |
| | return GetRegistryValue(LICENSE_CODE_KEY); |
| | } |
| | |
| | |
| | |
| | |
| | |
| | private void StoreLicenseCache(ActivationResult result) |
| | { |
| | string cacheData = $"{result.UserName}|{result.ExpiresAt}|{result.HardwareId}|{DateTime.Now}"; |
| | Registry.SetValue(REGISTRY_KEY_PATH, LICENSE_CACHE_KEY, cacheData); |
| | } |
| | |
| | |
| | |
| | |
| | private void StoreLastValidation() |
| | { |
| | Registry.SetValue(REGISTRY_KEY_PATH, LAST_VALIDATION_KEY, DateTime.Now.ToString()); |
| | } |
| | |
| | |
| | |
| | |
| | |
| | private ValidationResult GetCachedValidation() |
| | { |
| | try |
| | { |
| | string lastValidationStr = GetRegistryValue(LAST_VALIDATION_KEY); |
| | if (string.IsNullOrEmpty(lastValidationStr)) |
| | return null; |
| | |
| | if (!DateTime.TryParse(lastValidationStr, out var lastValidation)) |
| | return null; |
| | |
| | |
| | if (DateTime.Now - lastValidation > TimeSpan.FromHours(VALIDATION_CACHE_HOURS)) |
| | return null; |
| | |
| | string cacheData = GetRegistryValue(LICENSE_CACHE_KEY); |
| | if (string.IsNullOrEmpty(cacheData)) |
| | return null; |
| | |
| | var parts = cacheData.Split('|'); |
| | if (parts.Length < 4) |
| | return null; |
| | |
| | DateTime.TryParse(parts[1], out var expiresAt); |
| | |
| | |
| | if (expiresAt > DateTime.MinValue && expiresAt < DateTime.Now) |
| | { |
| | return new ValidationResult |
| | { |
| | Valid = false, |
| | Message = "授權已過期" |
| | }; |
| | } |
| | |
| | return new ValidationResult |
| | { |
| | Valid = true, |
| | Message = "授權有效 (快取)", |
| | ExpiresAt = expiresAt > DateTime.MinValue ? expiresAt : (DateTime?)null, |
| | UserName = parts[0], |
| | HardwareId = parts[2] |
| | }; |
| | } |
| | catch |
| | { |
| | return null; |
| | } |
| | } |
| | |
| | |
| | |
| | |
| | private void ClearLicenseCache() |
| | { |
| | try |
| | { |
| | Registry.SetValue(REGISTRY_KEY_PATH, LICENSE_CACHE_KEY, ""); |
| | Registry.SetValue(REGISTRY_KEY_PATH, LAST_VALIDATION_KEY, ""); |
| | } |
| | catch |
| | { |
| | |
| | } |
| | } |
| | |
| | |
| | |
| | |
| | |
| | |
| | private string GetRegistryValue(string keyName) |
| | { |
| | try |
| | { |
| | return Registry.GetValue(REGISTRY_KEY_PATH, keyName, "")?.ToString() ?? ""; |
| | } |
| | catch |
| | { |
| | return ""; |
| | } |
| | } |
| | |
| | #endregion |
| | } |
| | |
| | |
| | |
| | |
| | public class LicenseInfo |
| | { |
| | |
| | |
| | |
| | public string LicenseCode { get; set; } |
| | |
| | |
| | |
| | |
| | public string UserName { get; set; } |
| | |
| | |
| | |
| | |
| | public DateTime? ExpiresAt { get; set; } |
| | |
| | |
| | |
| | |
| | public string HardwareId { get; set; } |
| | |
| | |
| | |
| | |
| | public DateTime? LastValidation { get; set; } |
| | |
| | |
| | |
| | |
| | public bool IsExpiringSoon => ExpiresAt.HasValue && ExpiresAt.Value.Subtract(DateTime.Now).TotalDays <= 7; |
| | |
| | |
| | |
| | |
| | public int DaysRemaining => ExpiresAt.HasValue ? Math.Max(0, (int)ExpiresAt.Value.Subtract(DateTime.Now).TotalDays) : 0; |
| | } |
| | } |