From 50146a0f34098570694f7433e1b5e585e3edb8a1 Mon Sep 17 00:00:00 2001 From: JusterZhu Date: Sun, 24 May 2026 16:05:06 +0800 Subject: [PATCH] chore: clean up residual directories, add Core references, trim readiness MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Deleted src/c#/GeneralUpdate.Common/ (dead code after project merge) - Deleted src/c#/GeneralUpdate.ClientCore/ (dead code after project merge) - Updated Bowl, Drivelution, Extension to reference Core directly (removed file-link compilation to deleted Common directory) - Fixed namespace references: GeneralUpdate.Common → GeneralUpdate.Core in Bowl, Drivelution, Extension source files - Added IsTrimmable + EnableTrimAnalyzer to Core.csproj - Upgraded Extension Microsoft.Extensions packages to 10.0.1 Closes #348 --- src/c#/GeneralUpdate.Bowl/Bowl.cs | 10 +- .../GeneralUpdate.Bowl.csproj | 72 +-- .../Internal/CrashReporter.cs | 4 +- .../Internal/EnvironmentProvider.cs | 2 +- .../Internal/WindowsSystemInfoProvider.cs | 2 +- .../Strategies/LinuxBowlStrategy.cs | 2 +- .../Strategies/MacBowlStrategy.cs | 2 +- .../Strategies/ProcessRunner.cs | 2 +- .../Strategies/WindowsBowlStrategy.cs | 4 +- .../Strategys/AbstractStrategy.cs | 6 +- .../Strategys/LinuxStrategy.cs | 4 +- .../Strategys/WindowStrategy.cs | 8 +- .../GeneralUpdate.ClientCore/Common/git.keep | 0 .../GeneralClientBootstrap.cs | 467 ------------------ .../GeneralClientOSS.cs | 102 ---- .../GeneralUpdate.ico | Bin 16958 -> 0 bytes .../GeneralUpdate.png | Bin 16305 -> 0 bytes .../Hubs/IUpgradeHubService.cs | 47 -- .../Hubs/RandomRetryPolicy.cs | 19 - .../Hubs/UpgradeHubService.cs | 97 ---- .../Pipeline/CompressMiddleware.cs | 36 -- .../Pipeline/HashMiddleware.cs | 47 -- .../Pipeline/PatchMiddleware.cs | 27 - .../Properties/launchSettings.json | 12 - .../SilentUpdateMode.cs | 227 --------- .../Strategys/LinuxStrategy.cs | 75 --- .../Strategys/WindowsStrategy.cs | 73 --- .../Compress/CompressProvider.cs | 30 -- .../Compress/ICompressionStrategy.cs | 9 - .../Compress/ZipCompressionStrategy.cs | 196 -------- .../Download/DownloadManager.cs | 78 --- .../Download/DownloadTask.cs | 305 ------------ .../MutiAllDownloadCompletedEventArgs.cs | 18 - .../MutiDownloadCompletedEventArgs.cs | 11 - .../MutiDownloadErrorEventArgs.cs | 17 - .../MutiDownloadStatisticsEventArgs.cs | 23 - .../MultiEventArgs/UpdateInfoEventArgs.cs | 9 - .../FileBasic/BlackListManager.cs | 139 ------ .../FileBasic/ComparisonResult.cs | 41 -- .../FileBasic/FileNode.cs | 150 ------ .../FileBasic/FileTree.cs | 172 ------- .../FileBasic/StorageManager.cs | 268 ---------- .../GeneralUpdate.Common/GeneralUpdate128.png | Bin 19572 -> 0 bytes .../HashAlgorithms/HashAlgorithmBase.cs | 37 -- .../HashAlgorithms/Sha256HashAlgorithm.cs | 9 - .../Internal/Bootstrap/AbstractBootstrap.cs | 66 --- .../Internal/Bootstrap/Environments.cs | 24 - .../Internal/Bootstrap/UpdateOption.cs | 240 --------- .../Internal/Bootstrap/UpdateOptionValue.cs | 29 -- .../Internal/Event/EventManager.cs | 107 ---- .../Internal/Event/IEventManager.cs | 37 -- .../Internal/Exception/ExceptionEventArgs.cs | 9 - .../JsonContext/FileNodesJsonContext.cs | 8 - .../GlobalConfigInfoOSSJsonContext.cs | 7 - .../JsonContext/HttpParameterJsonContext.cs | 12 - .../Internal/JsonContext/PacketJsonContext.cs | 7 - .../JsonContext/ProcessInfoJsonContext.cs | 7 - .../JsonContext/ReportRespJsonContext.cs | 7 - .../JsonContext/VersionOSSJsonContext.cs | 8 - .../JsonContext/VersionRespJsonContext.cs | 7 - .../Internal/Pipeline/IMiddleware.cs | 12 - .../Internal/Pipeline/PipelineBuilder.cs | 43 -- .../Internal/Pipeline/PipelineContext.cs | 32 -- .../Internal/Strategy/AbstractStrategy.cs | 131 ----- .../Internal/Strategy/IStrategy.cs | 31 -- .../Shared/Object/BaseConfigInfo.cs | 105 ---- .../Shared/Object/Configinfo.cs | 55 --- .../Object/ConfiginfoBuilder-Example.cs | 118 ----- .../Shared/Object/ConfiginfoBuilder-Usage.md | 366 -------------- .../Shared/Object/ConfiginfoBuilder.cs | 413 ---------------- .../Shared/Object/ConfigurationMapper.cs | 135 ----- .../Shared/Object/DTO/BaseResponseDTO.cs | 16 - .../Shared/Object/DTO/VersionRespDTO.cs | 4 - .../Shared/Object/Enum/AppType.cs | 15 - .../Shared/Object/Enum/Format.cs | 7 - .../Shared/Object/Enum/PlatformType.cs | 8 - .../Shared/Object/Enum/ReportType.cs | 10 - .../Shared/Object/Enum/UpdateMode.cs | 7 - .../Shared/Object/GlobalConfigInfo.cs | 120 ----- .../Shared/Object/GlobalConfigInfoOSS.cs | 35 -- .../Shared/Object/ObjectTranslator.cs | 7 - .../Shared/Object/Packet.cs | 40 -- .../Shared/Object/ProcessInfo.cs | 243 --------- .../Shared/Object/README-ConfiginfoBuilder.md | 263 ---------- .../Shared/Object/VersionInfo.cs | 61 --- .../Shared/Object/VersionOSS.cs | 42 -- .../Shared/Object/update_config.example.json | 31 -- .../Shared/Service/VersionService.cs | 116 ----- .../Shared/Trace/GeneralTracer.cs | 163 ------ .../Shared/Trace/TextTraceListener.cs | 63 --- .../Trace/WindowsOutputDebugListener.cs | 31 -- .../GeneralUpdate.Core.csproj | 3 + .../Core/DriverUpdaterFactory.cs | 2 +- .../Core/Execution/CommandRunner.cs | 2 +- .../Core/Pipeline/BaseDriverUpdater.cs | 2 +- .../Core/Pipeline/DefaultPipelineSteps.cs | 2 +- .../GeneralDrivelution.cs | 2 +- .../GeneralUpdate.Drivelution.csproj | 9 +- .../Linux/Implementation/LinuxDriverBackup.cs | 2 +- .../Implementation/LinuxDriverValidator.cs | 2 +- .../Implementation/LinuxGeneralDrivelution.cs | 2 +- .../Implementation/MacOsGeneralDrivelution.cs | 2 +- .../Implementation/WindowsDriverBackup.cs | 2 +- .../Implementation/WindowsDriverValidator.cs | 2 +- .../WindowsGeneralDrivelution.cs | 2 +- .../Core/GeneralExtensionHost.cs | 2 +- .../Download/DownloadQueueManager.cs | 2 +- .../GeneralUpdate.Extension.csproj | 17 +- 108 files changed, 60 insertions(+), 6154 deletions(-) delete mode 100644 src/c#/GeneralUpdate.ClientCore/Common/git.keep delete mode 100644 src/c#/GeneralUpdate.ClientCore/GeneralClientBootstrap.cs delete mode 100644 src/c#/GeneralUpdate.ClientCore/GeneralClientOSS.cs delete mode 100644 src/c#/GeneralUpdate.ClientCore/GeneralUpdate.ico delete mode 100644 src/c#/GeneralUpdate.ClientCore/GeneralUpdate.png delete mode 100644 src/c#/GeneralUpdate.ClientCore/Hubs/IUpgradeHubService.cs delete mode 100644 src/c#/GeneralUpdate.ClientCore/Hubs/RandomRetryPolicy.cs delete mode 100644 src/c#/GeneralUpdate.ClientCore/Hubs/UpgradeHubService.cs delete mode 100644 src/c#/GeneralUpdate.ClientCore/Pipeline/CompressMiddleware.cs delete mode 100644 src/c#/GeneralUpdate.ClientCore/Pipeline/HashMiddleware.cs delete mode 100644 src/c#/GeneralUpdate.ClientCore/Pipeline/PatchMiddleware.cs delete mode 100644 src/c#/GeneralUpdate.ClientCore/Properties/launchSettings.json delete mode 100644 src/c#/GeneralUpdate.ClientCore/SilentUpdateMode.cs delete mode 100644 src/c#/GeneralUpdate.ClientCore/Strategys/LinuxStrategy.cs delete mode 100644 src/c#/GeneralUpdate.ClientCore/Strategys/WindowsStrategy.cs delete mode 100644 src/c#/GeneralUpdate.Common/Compress/CompressProvider.cs delete mode 100644 src/c#/GeneralUpdate.Common/Compress/ICompressionStrategy.cs delete mode 100644 src/c#/GeneralUpdate.Common/Compress/ZipCompressionStrategy.cs delete mode 100644 src/c#/GeneralUpdate.Common/Download/DownloadManager.cs delete mode 100644 src/c#/GeneralUpdate.Common/Download/DownloadTask.cs delete mode 100644 src/c#/GeneralUpdate.Common/Download/MultiEventArgs/MutiAllDownloadCompletedEventArgs.cs delete mode 100644 src/c#/GeneralUpdate.Common/Download/MultiEventArgs/MutiDownloadCompletedEventArgs.cs delete mode 100644 src/c#/GeneralUpdate.Common/Download/MultiEventArgs/MutiDownloadErrorEventArgs.cs delete mode 100644 src/c#/GeneralUpdate.Common/Download/MultiEventArgs/MutiDownloadStatisticsEventArgs.cs delete mode 100644 src/c#/GeneralUpdate.Common/Download/MultiEventArgs/UpdateInfoEventArgs.cs delete mode 100644 src/c#/GeneralUpdate.Common/FileBasic/BlackListManager.cs delete mode 100644 src/c#/GeneralUpdate.Common/FileBasic/ComparisonResult.cs delete mode 100644 src/c#/GeneralUpdate.Common/FileBasic/FileNode.cs delete mode 100644 src/c#/GeneralUpdate.Common/FileBasic/FileTree.cs delete mode 100644 src/c#/GeneralUpdate.Common/FileBasic/StorageManager.cs delete mode 100644 src/c#/GeneralUpdate.Common/GeneralUpdate128.png delete mode 100644 src/c#/GeneralUpdate.Common/HashAlgorithms/HashAlgorithmBase.cs delete mode 100644 src/c#/GeneralUpdate.Common/HashAlgorithms/Sha256HashAlgorithm.cs delete mode 100644 src/c#/GeneralUpdate.Common/Internal/Bootstrap/AbstractBootstrap.cs delete mode 100644 src/c#/GeneralUpdate.Common/Internal/Bootstrap/Environments.cs delete mode 100644 src/c#/GeneralUpdate.Common/Internal/Bootstrap/UpdateOption.cs delete mode 100644 src/c#/GeneralUpdate.Common/Internal/Bootstrap/UpdateOptionValue.cs delete mode 100644 src/c#/GeneralUpdate.Common/Internal/Event/EventManager.cs delete mode 100644 src/c#/GeneralUpdate.Common/Internal/Event/IEventManager.cs delete mode 100644 src/c#/GeneralUpdate.Common/Internal/Exception/ExceptionEventArgs.cs delete mode 100644 src/c#/GeneralUpdate.Common/Internal/JsonContext/FileNodesJsonContext.cs delete mode 100644 src/c#/GeneralUpdate.Common/Internal/JsonContext/GlobalConfigInfoOSSJsonContext.cs delete mode 100644 src/c#/GeneralUpdate.Common/Internal/JsonContext/HttpParameterJsonContext.cs delete mode 100644 src/c#/GeneralUpdate.Common/Internal/JsonContext/PacketJsonContext.cs delete mode 100644 src/c#/GeneralUpdate.Common/Internal/JsonContext/ProcessInfoJsonContext.cs delete mode 100644 src/c#/GeneralUpdate.Common/Internal/JsonContext/ReportRespJsonContext.cs delete mode 100644 src/c#/GeneralUpdate.Common/Internal/JsonContext/VersionOSSJsonContext.cs delete mode 100644 src/c#/GeneralUpdate.Common/Internal/JsonContext/VersionRespJsonContext.cs delete mode 100644 src/c#/GeneralUpdate.Common/Internal/Pipeline/IMiddleware.cs delete mode 100644 src/c#/GeneralUpdate.Common/Internal/Pipeline/PipelineBuilder.cs delete mode 100644 src/c#/GeneralUpdate.Common/Internal/Pipeline/PipelineContext.cs delete mode 100644 src/c#/GeneralUpdate.Common/Internal/Strategy/AbstractStrategy.cs delete mode 100644 src/c#/GeneralUpdate.Common/Internal/Strategy/IStrategy.cs delete mode 100644 src/c#/GeneralUpdate.Common/Shared/Object/BaseConfigInfo.cs delete mode 100644 src/c#/GeneralUpdate.Common/Shared/Object/Configinfo.cs delete mode 100644 src/c#/GeneralUpdate.Common/Shared/Object/ConfiginfoBuilder-Example.cs delete mode 100644 src/c#/GeneralUpdate.Common/Shared/Object/ConfiginfoBuilder-Usage.md delete mode 100644 src/c#/GeneralUpdate.Common/Shared/Object/ConfiginfoBuilder.cs delete mode 100644 src/c#/GeneralUpdate.Common/Shared/Object/ConfigurationMapper.cs delete mode 100644 src/c#/GeneralUpdate.Common/Shared/Object/DTO/BaseResponseDTO.cs delete mode 100644 src/c#/GeneralUpdate.Common/Shared/Object/DTO/VersionRespDTO.cs delete mode 100644 src/c#/GeneralUpdate.Common/Shared/Object/Enum/AppType.cs delete mode 100644 src/c#/GeneralUpdate.Common/Shared/Object/Enum/Format.cs delete mode 100644 src/c#/GeneralUpdate.Common/Shared/Object/Enum/PlatformType.cs delete mode 100644 src/c#/GeneralUpdate.Common/Shared/Object/Enum/ReportType.cs delete mode 100644 src/c#/GeneralUpdate.Common/Shared/Object/Enum/UpdateMode.cs delete mode 100644 src/c#/GeneralUpdate.Common/Shared/Object/GlobalConfigInfo.cs delete mode 100644 src/c#/GeneralUpdate.Common/Shared/Object/GlobalConfigInfoOSS.cs delete mode 100644 src/c#/GeneralUpdate.Common/Shared/Object/ObjectTranslator.cs delete mode 100644 src/c#/GeneralUpdate.Common/Shared/Object/Packet.cs delete mode 100644 src/c#/GeneralUpdate.Common/Shared/Object/ProcessInfo.cs delete mode 100644 src/c#/GeneralUpdate.Common/Shared/Object/README-ConfiginfoBuilder.md delete mode 100644 src/c#/GeneralUpdate.Common/Shared/Object/VersionInfo.cs delete mode 100644 src/c#/GeneralUpdate.Common/Shared/Object/VersionOSS.cs delete mode 100644 src/c#/GeneralUpdate.Common/Shared/Object/update_config.example.json delete mode 100644 src/c#/GeneralUpdate.Common/Shared/Service/VersionService.cs delete mode 100644 src/c#/GeneralUpdate.Common/Shared/Trace/GeneralTracer.cs delete mode 100644 src/c#/GeneralUpdate.Common/Shared/Trace/TextTraceListener.cs delete mode 100644 src/c#/GeneralUpdate.Common/Shared/Trace/WindowsOutputDebugListener.cs diff --git a/src/c#/GeneralUpdate.Bowl/Bowl.cs b/src/c#/GeneralUpdate.Bowl/Bowl.cs index 2b308a37..a2737421 100644 --- a/src/c#/GeneralUpdate.Bowl/Bowl.cs +++ b/src/c#/GeneralUpdate.Bowl/Bowl.cs @@ -6,11 +6,11 @@ using GeneralUpdate.Bowl.Internal; using GeneralUpdate.Bowl.Strategies; using GeneralUpdate.Bowl.Strategys; -using GeneralUpdate.Common.FileBasic; -using GeneralUpdate.Common.Internal.Bootstrap; -using GeneralUpdate.Common.Internal.JsonContext; -using GeneralUpdate.Common.Shared; -using GeneralUpdate.Common.Shared.Object; +using GeneralUpdate.Core.FileSystem; +using GeneralUpdate.Core.Configuration; +using GeneralUpdate.Core.JsonContext; +using GeneralUpdate.Core; +using GeneralUpdate.Core.Configuration; namespace GeneralUpdate.Bowl; diff --git a/src/c#/GeneralUpdate.Bowl/GeneralUpdate.Bowl.csproj b/src/c#/GeneralUpdate.Bowl/GeneralUpdate.Bowl.csproj index 23557576..2379360a 100644 --- a/src/c#/GeneralUpdate.Bowl/GeneralUpdate.Bowl.csproj +++ b/src/c#/GeneralUpdate.Bowl/GeneralUpdate.Bowl.csproj @@ -1,4 +1,4 @@ - + default enable @@ -17,81 +17,27 @@ bowl.jpeg netstandard2.0; + - - + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + PreserveNewest true - - - + + True diff --git a/src/c#/GeneralUpdate.Bowl/Internal/CrashReporter.cs b/src/c#/GeneralUpdate.Bowl/Internal/CrashReporter.cs index 2347a0de..2aba8195 100644 --- a/src/c#/GeneralUpdate.Bowl/Internal/CrashReporter.cs +++ b/src/c#/GeneralUpdate.Bowl/Internal/CrashReporter.cs @@ -3,8 +3,8 @@ using System.IO; using System.Threading; using System.Threading.Tasks; -using GeneralUpdate.Common.FileBasic; -using GeneralUpdate.Common.Shared; +using GeneralUpdate.Core.FileSystem; +using GeneralUpdate.Core; namespace GeneralUpdate.Bowl.Internal; diff --git a/src/c#/GeneralUpdate.Bowl/Internal/EnvironmentProvider.cs b/src/c#/GeneralUpdate.Bowl/Internal/EnvironmentProvider.cs index 661ea018..fad2ae8a 100644 --- a/src/c#/GeneralUpdate.Bowl/Internal/EnvironmentProvider.cs +++ b/src/c#/GeneralUpdate.Bowl/Internal/EnvironmentProvider.cs @@ -1,6 +1,6 @@ using System.Threading; using System.Threading.Tasks; -using GeneralUpdate.Common.Internal.Bootstrap; +using GeneralUpdate.Core.Configuration; namespace GeneralUpdate.Bowl.Internal; diff --git a/src/c#/GeneralUpdate.Bowl/Internal/WindowsSystemInfoProvider.cs b/src/c#/GeneralUpdate.Bowl/Internal/WindowsSystemInfoProvider.cs index d4c8de07..c5fcd292 100644 --- a/src/c#/GeneralUpdate.Bowl/Internal/WindowsSystemInfoProvider.cs +++ b/src/c#/GeneralUpdate.Bowl/Internal/WindowsSystemInfoProvider.cs @@ -3,7 +3,7 @@ using System.IO; using System.Threading; using System.Threading.Tasks; -using GeneralUpdate.Common.Shared; +using GeneralUpdate.Core; namespace GeneralUpdate.Bowl.Internal; diff --git a/src/c#/GeneralUpdate.Bowl/Strategies/LinuxBowlStrategy.cs b/src/c#/GeneralUpdate.Bowl/Strategies/LinuxBowlStrategy.cs index b0f97827..6a48f1e1 100644 --- a/src/c#/GeneralUpdate.Bowl/Strategies/LinuxBowlStrategy.cs +++ b/src/c#/GeneralUpdate.Bowl/Strategies/LinuxBowlStrategy.cs @@ -5,7 +5,7 @@ using System.Threading; using System.Threading.Tasks; using GeneralUpdate.Bowl.Internal; -using GeneralUpdate.Common.Shared; +using GeneralUpdate.Core; namespace GeneralUpdate.Bowl.Strategies; diff --git a/src/c#/GeneralUpdate.Bowl/Strategies/MacBowlStrategy.cs b/src/c#/GeneralUpdate.Bowl/Strategies/MacBowlStrategy.cs index 92411947..9d5f3198 100644 --- a/src/c#/GeneralUpdate.Bowl/Strategies/MacBowlStrategy.cs +++ b/src/c#/GeneralUpdate.Bowl/Strategies/MacBowlStrategy.cs @@ -3,7 +3,7 @@ using System.IO; using System.Threading; using System.Threading.Tasks; -using GeneralUpdate.Common.Shared; +using GeneralUpdate.Core; namespace GeneralUpdate.Bowl.Strategies; diff --git a/src/c#/GeneralUpdate.Bowl/Strategies/ProcessRunner.cs b/src/c#/GeneralUpdate.Bowl/Strategies/ProcessRunner.cs index 356113ac..2b679897 100644 --- a/src/c#/GeneralUpdate.Bowl/Strategies/ProcessRunner.cs +++ b/src/c#/GeneralUpdate.Bowl/Strategies/ProcessRunner.cs @@ -3,7 +3,7 @@ using System.Diagnostics; using System.Threading; using System.Threading.Tasks; -using GeneralUpdate.Common.Shared; +using GeneralUpdate.Core; namespace GeneralUpdate.Bowl.Strategies; diff --git a/src/c#/GeneralUpdate.Bowl/Strategies/WindowsBowlStrategy.cs b/src/c#/GeneralUpdate.Bowl/Strategies/WindowsBowlStrategy.cs index b7e4cc88..dd1c2bfe 100644 --- a/src/c#/GeneralUpdate.Bowl/Strategies/WindowsBowlStrategy.cs +++ b/src/c#/GeneralUpdate.Bowl/Strategies/WindowsBowlStrategy.cs @@ -4,8 +4,8 @@ using System.Runtime.InteropServices; using System.Threading; using System.Threading.Tasks; -using GeneralUpdate.Common.FileBasic; -using GeneralUpdate.Common.Shared; +using GeneralUpdate.Core.FileSystem; +using GeneralUpdate.Core; namespace GeneralUpdate.Bowl.Strategies; diff --git a/src/c#/GeneralUpdate.Bowl/Strategys/AbstractStrategy.cs b/src/c#/GeneralUpdate.Bowl/Strategys/AbstractStrategy.cs index cf7ad3c0..b207b8a1 100644 --- a/src/c#/GeneralUpdate.Bowl/Strategys/AbstractStrategy.cs +++ b/src/c#/GeneralUpdate.Bowl/Strategys/AbstractStrategy.cs @@ -1,8 +1,8 @@ -using System.Collections.Generic; +using System.Collections.Generic; using System.Diagnostics; using System.IO; -using GeneralUpdate.Common.FileBasic; -using GeneralUpdate.Common.Shared; +using GeneralUpdate.Core.FileSystem; +using GeneralUpdate.Core; namespace GeneralUpdate.Bowl.Strategys; diff --git a/src/c#/GeneralUpdate.Bowl/Strategys/LinuxStrategy.cs b/src/c#/GeneralUpdate.Bowl/Strategys/LinuxStrategy.cs index 24517ae9..03af8d7e 100644 --- a/src/c#/GeneralUpdate.Bowl/Strategys/LinuxStrategy.cs +++ b/src/c#/GeneralUpdate.Bowl/Strategys/LinuxStrategy.cs @@ -1,10 +1,10 @@ -using System; +using System; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Linq; using GeneralUpdate.Bowl.Internal; -using GeneralUpdate.Common.Shared; +using GeneralUpdate.Core; namespace GeneralUpdate.Bowl.Strategys; diff --git a/src/c#/GeneralUpdate.Bowl/Strategys/WindowStrategy.cs b/src/c#/GeneralUpdate.Bowl/Strategys/WindowStrategy.cs index 9e06b56b..dd8a2130 100644 --- a/src/c#/GeneralUpdate.Bowl/Strategys/WindowStrategy.cs +++ b/src/c#/GeneralUpdate.Bowl/Strategys/WindowStrategy.cs @@ -1,12 +1,12 @@ -using System; +using System; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Runtime.InteropServices; using GeneralUpdate.Bowl.Internal; -using GeneralUpdate.Common.FileBasic; -using GeneralUpdate.Common.Internal.Bootstrap; -using GeneralUpdate.Common.Shared; +using GeneralUpdate.Core.FileSystem; +using GeneralUpdate.Core.Configuration; +using GeneralUpdate.Core; namespace GeneralUpdate.Bowl.Strategys; diff --git a/src/c#/GeneralUpdate.ClientCore/Common/git.keep b/src/c#/GeneralUpdate.ClientCore/Common/git.keep deleted file mode 100644 index e69de29b..00000000 diff --git a/src/c#/GeneralUpdate.ClientCore/GeneralClientBootstrap.cs b/src/c#/GeneralUpdate.ClientCore/GeneralClientBootstrap.cs deleted file mode 100644 index 1843613f..00000000 --- a/src/c#/GeneralUpdate.ClientCore/GeneralClientBootstrap.cs +++ /dev/null @@ -1,467 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Diagnostics; -using System.IO; -using System.Linq; -using System.Runtime.InteropServices; -using System.Text; -using System.Text.Json; -using System.Threading.Tasks; -using GeneralUpdate.ClientCore.Strategys; -using GeneralUpdate.Common.FileBasic; -using GeneralUpdate.Common.Download; -using GeneralUpdate.Common.Internal; -using GeneralUpdate.Common.Internal.Bootstrap; -using GeneralUpdate.Common.Internal.Event; -using GeneralUpdate.Common.Internal.JsonContext; -using GeneralUpdate.Common.Internal.Strategy; -using GeneralUpdate.Common.Shared; -using GeneralUpdate.Common.Shared.Object; -using GeneralUpdate.Common.Shared.Object.Enum; -using GeneralUpdate.Common.Shared.Service; - -namespace GeneralUpdate.ClientCore; - -/// -/// This component is used only for client application bootstrapping classes. -/// -public class GeneralClientBootstrap : AbstractBootstrap -{ - /// - /// All update actions of the core object for automatic upgrades will be related to the packet object. - /// - private GlobalConfigInfo? _configInfo; - private IStrategy? _strategy; - private Func? _updatePrecheck; - private readonly List> _customOptions = new(); - - #region Public Methods - - /// - /// Main function for booting the update startup. - /// - /// - public override async Task LaunchAsync() - { - try - { - GeneralTracer.Debug("GeneralClientBootstrap Launch."); - CallSmallBowlHome(_configInfo.Bowl); - ExecuteCustomOptions(); - await ExecuteWorkflowAsync(); - } - catch (Exception exception) - { - GeneralTracer.Error("The LaunchAsync method in the GeneralClientBootstrap class throws an exception." , exception); - EventManager.Instance.Dispatch(this, new ExceptionEventArgs(exception, exception.Message)); - } - return this; - } - - /// - /// Configure server address (Recommended Windows,Linux,Mac). - /// - public GeneralClientBootstrap SetConfig(Configinfo configInfo) - { - Debug.Assert(configInfo != null, "configInfo should not be null"); - configInfo?.Validate(); - - // Use ConfigurationMapper instead of manual field mapping - // This ensures all fields are consistently mapped and reduces maintenance burden - _configInfo = ConfigurationMapper.MapToGlobalConfigInfo(configInfo); - - return this; - } - - /// - /// Registers a callback that is invoked when update information is available. - /// The callback receives the full and returns - /// true to skip the update or false to proceed with the automatic upgrade. - /// Built-in forced-update protection is still applied; if any version is marked as - /// forcibly required the callback return value is ignored and the update proceeds. - /// - /// - /// A function that receives containing complete update - /// details and returns true to skip, or false to proceed with the update. - /// - /// The current bootstrap instance for fluent method chaining. - public GeneralClientBootstrap AddListenerUpdatePrecheck(Func func) - { - if (func == null) throw new ArgumentNullException(nameof(func)); - _updatePrecheck = func; - return this; - } - - /// - /// Add an asynchronous custom operation. - /// In theory, any custom operation can be done. It is recommended to register the environment check method to ensure - /// that there are normal dependencies and environments after the update is completed. - /// - public GeneralClientBootstrap AddCustomOption(List> funcList) - { - Debug.Assert(funcList != null && funcList.Any()); - _customOptions.AddRange(funcList); - return this; - } - - public GeneralClientBootstrap AddListenerMultiAllDownloadCompleted( - Action callbackAction) - => AddListener(callbackAction); - - public GeneralClientBootstrap AddListenerMultiDownloadCompleted( - Action callbackAction) - => AddListener(callbackAction); - - public GeneralClientBootstrap AddListenerMultiDownloadError( - Action callbackAction) - => AddListener(callbackAction); - - public GeneralClientBootstrap AddListenerMultiDownloadStatistics( - Action callbackAction) - => AddListener(callbackAction); - - public GeneralClientBootstrap AddListenerException(Action callbackAction) - => AddListener(callbackAction); - - public GeneralClientBootstrap AddListenerUpdateInfo(Action callbackAction) - => AddListener(callbackAction); - - #endregion Public Methods - - #region Private Methods - - private async Task ExecuteWorkflowAsync() - { - try - { - Debug.Assert(_configInfo != null); - if (GetOption(UpdateOption.EnableSilentUpdate)) - { - GeneralTracer.Info("GeneralClientBootstrap.ExecuteWorkflowAsync: silent update mode enabled, delegating to SilentUpdateMode."); - await new SilentUpdateMode( - _configInfo, - GetOption(UpdateOption.Encoding) ?? Encoding.Default, - GetOption(UpdateOption.Format) ?? Format.ZIP, - GetOption(UpdateOption.DownloadTimeOut) ?? 60, - GetOption(UpdateOption.Patch) ?? true, - GetOption(UpdateOption.BackUp) ?? true).StartAsync(); - return; - } - //Request the upgrade information needed by the client and upgrade end, and determine if an upgrade is necessary. - GeneralTracer.Info($"GeneralClientBootstrap.ExecuteWorkflowAsync: validating client version={_configInfo.ClientVersion} and upgrade version={_configInfo.UpgradeClientVersion}"); - var mainResp = await VersionService.Validate(_configInfo.UpdateUrl - , _configInfo.ClientVersion - , AppType.ClientApp - , _configInfo.AppSecretKey - , GetPlatform() - , _configInfo.ProductId - , _configInfo.Scheme - , _configInfo.Token); - - var upgradeResp = await VersionService.Validate(_configInfo.UpdateUrl - , _configInfo.UpgradeClientVersion - , AppType.UpgradeApp - , _configInfo.AppSecretKey - , GetPlatform() - , _configInfo.ProductId - , _configInfo.Scheme - , _configInfo.Token); - - _configInfo.IsUpgradeUpdate = CheckUpgrade(upgradeResp); - _configInfo.IsMainUpdate = CheckUpgrade(mainResp); - GeneralTracer.Info($"GeneralClientBootstrap.ExecuteWorkflowAsync: version validation completed. IsMainUpdate={_configInfo.IsMainUpdate}, IsUpgradeUpdate={_configInfo.IsUpgradeUpdate}"); - - var updateInfoArgs = new UpdateInfoEventArgs(mainResp); - EventManager.Instance.Dispatch(this, updateInfoArgs); - - //If the main program needs to be forced to update, the skip will not take effect. - var isForcibly = CheckForcibly(mainResp.Body) || CheckForcibly(upgradeResp.Body); - if (CanSkip(isForcibly, updateInfoArgs)) - { - GeneralTracer.Info("GeneralClientBootstrap.ExecuteWorkflowAsync: update skipped by precheck callback."); - return; - } - - //black list initialization. - BlackListManager.Instance?.AddBlackFiles(_configInfo.BlackFiles); - BlackListManager.Instance?.AddBlackFileFormats(_configInfo.BlackFormats); - BlackListManager.Instance?.AddSkipDirectorys(_configInfo.SkipDirectorys); - - _configInfo.Encoding = GetOption(UpdateOption.Encoding) ?? Encoding.Default; - _configInfo.Format = GetOption(UpdateOption.Format) ?? Format.ZIP; - _configInfo.DownloadTimeOut = GetOption(UpdateOption.DownloadTimeOut) ?? 60; - _configInfo.DriveEnabled = GetOption(UpdateOption.Drive) ?? false; - _configInfo.PatchEnabled = GetOption(UpdateOption.Patch) ?? true; - _configInfo.TempPath = StorageManager.GetTempDirectory("main_temp"); - _configInfo.BackupDirectory = Path.Combine(_configInfo.InstallPath, - $"{StorageManager.DirectoryName}{_configInfo.ClientVersion}"); - - _configInfo.UpdateVersions = _configInfo.IsUpgradeUpdate ? upgradeResp.Body.OrderBy(x => x.ReleaseDate).ToList() : []; - - if (_configInfo.IsMainUpdate) - { - _configInfo.LastVersion = mainResp.Body.OrderBy(x => x.ReleaseDate).Last().Version; - GeneralTracer.Info($"GeneralClientBootstrap.ExecuteWorkflowAsync: main update required, LastVersion={_configInfo.LastVersion}"); - - var failed = CheckFail(_configInfo.LastVersion); - if (failed) - { - GeneralTracer.Warn($"GeneralClientBootstrap.ExecuteWorkflowAsync: version {_configInfo.LastVersion} matches or precedes a known failed upgrade, aborting."); - return; - } - - // Use ConfigurationMapper to create ProcessInfo instead of manual parameter passing - // This centralizes the complex parameter mapping logic and reduces errors - var processInfo = ConfigurationMapper.MapToProcessInfo( - _configInfo, - mainResp.Body, - BlackListManager.Instance.BlackFileFormats.ToList(), - BlackListManager.Instance.BlackFiles.ToList(), - BlackListManager.Instance.SkipDirectorys.ToList()); - - _configInfo.ProcessInfo = - JsonSerializer.Serialize(processInfo, ProcessInfoJsonContext.Default.ProcessInfo); - } - - if (GetOption(UpdateOption.BackUp) ?? true) - { - GeneralTracer.Info($"GeneralClientBootstrap.ExecuteWorkflowAsync: backing up from {_configInfo.InstallPath} to {_configInfo.BackupDirectory}"); - StorageManager.Backup(_configInfo.InstallPath - , _configInfo.BackupDirectory - , BlackListManager.Instance.SkipDirectorys); - GeneralTracer.Info("GeneralClientBootstrap.ExecuteWorkflowAsync: backup completed."); - } - - StrategyFactory(); - GeneralTracer.Info($"GeneralClientBootstrap.ExecuteWorkflowAsync: executing update scenario. IsUpgradeUpdate={_configInfo.IsUpgradeUpdate}, IsMainUpdate={_configInfo.IsMainUpdate}"); - switch (_configInfo.IsUpgradeUpdate) - { - case true when _configInfo.IsMainUpdate: - //Both upgrade and main program update. - GeneralTracer.Info("GeneralClientBootstrap.ExecuteWorkflowAsync: both upgrade and main update required - downloading and executing."); - await Download(); - await _strategy?.ExecuteAsync()!; - _strategy?.StartApp(); - break; - case true when !_configInfo.IsMainUpdate: - //Upgrade program update. - GeneralTracer.Info("GeneralClientBootstrap.ExecuteWorkflowAsync: upgrade-only update - downloading and executing."); - await Download(); - await _strategy?.ExecuteAsync()!; - break; - case false when _configInfo.IsMainUpdate: - //Main program update. - GeneralTracer.Info("GeneralClientBootstrap.ExecuteWorkflowAsync: main update only - starting application (updater will handle it)."); - _strategy?.StartApp(); - break; - } - } - catch (Exception exception) - { - GeneralTracer.Error("The ExecuteWorkflowAsync method in the GeneralClientBootstrap class throws an exception." , exception); - EventManager.Instance.Dispatch(this, new ExceptionEventArgs(exception, exception.Message)); - } - } - - private async Task Download() - { - var manager = new DownloadManager(_configInfo.TempPath, _configInfo.Format, _configInfo.DownloadTimeOut); - manager.MultiAllDownloadCompleted += OnMultiAllDownloadCompleted; - manager.MultiDownloadCompleted += OnMultiDownloadCompleted; - manager.MultiDownloadError += OnMultiDownloadError; - manager.MultiDownloadStatistics += OnMultiDownloadStatistics; - foreach (var versionInfo in _configInfo.UpdateVersions) - { - manager.Add(new DownloadTask(manager, versionInfo)); - } - - await manager.LaunchTasksAsync(); - } - - private int GetPlatform() - { - if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) - { - return PlatformType.Windows; - } - - if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux)) - { - return PlatformType.Linux; - } - - return -1; - } - - /// - /// Check if there has been a recent update failure.(only windows) - /// - /// - /// - private bool CheckFail(string version) - { - /* - Read the version number of the last failed upgrade from the system environment variables, then compare it with the version number of the current request. - If it is less than or equal to the failed version number, do not perform the update. - */ - var fail = Environments.GetEnvironmentVariable("UpgradeFail"); - if (string.IsNullOrEmpty(fail) || string.IsNullOrEmpty(version)) - return false; - - var failVersion = new Version(fail); - var lastVersion = new Version(version); - return failVersion >= lastVersion; - } - - /// - /// Determine whether the current version verification result indicates that an update is needed. - /// - /// - /// - private bool CheckUpgrade(VersionRespDTO? response) - { - if (response is null) - return false; - - if (response.Body is null) - return false; - - if (response.Code == 200) - return response.Body.Count > 0; - - return false; - } - - /// - /// During the iteration process, if any version requires a mandatory update, all the update content from this request should be updated. - /// - /// - /// - private bool CheckForcibly(List? versions) - { - if (versions == null) - return false; - - foreach (var item in versions) - { - if (item.IsForcibly == true) - { - return true; - } - } - - return false; - } - - /// - /// User decides if update is required. - /// - /// Whether the update is forcibly required; if true, skip is never allowed. - /// Update information passed to the precheck callback. - /// true to skip (abort) the update; false to continue execution. - private bool CanSkip(bool isForcibly, UpdateInfoEventArgs updateInfo) - { - if (isForcibly) return false; - return _updatePrecheck?.Invoke(updateInfo) == true; - } - - private void CallSmallBowlHome(string processName) - { - if (string.IsNullOrWhiteSpace(processName)) - return; - - try - { - var processes = Process.GetProcessesByName(processName); - if (processes.Length == 0) - { - GeneralTracer.Info($"No process named {processName} found."); - return; - } - - foreach (var process in processes) - { - GeneralTracer.Info($"Killing process {process.ProcessName} (ID: {process.Id})"); - process.Kill(); - } - } - catch (Exception ex) - { - GeneralTracer.Error("The CallSmallBowlHome method in the GeneralClientBootstrap class throws an exception.", ex); - EventManager.Instance.Dispatch(this, new ExceptionEventArgs(ex, ex.Message)); - } - } - - /// - /// Performs all injected custom operations. - /// - /// - private void ExecuteCustomOptions() - { - if (!_customOptions.Any()) return; - - foreach (var option in _customOptions) - { - if (!option.Invoke()) - { - var exception = new Exception($"{nameof(option)}Execution failure!"); - var args = new ExceptionEventArgs(exception, exception.Message); - GeneralTracer.Error("The ExecuteCustomOptions method in the GeneralClientBootstrap class throws an exception.", exception); - EventManager.Instance.Dispatch(this, args); - } - } - } - - protected override void ExecuteStrategy() => throw new NotImplementedException(); - - protected override Task ExecuteStrategyAsync() => throw new NotImplementedException(); - - protected override GeneralClientBootstrap StrategyFactory() - { - if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) - _strategy = new WindowsStrategy(); - else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux)) - _strategy = new LinuxStrategy(); - else - throw new PlatformNotSupportedException("The current operating system is not supported!"); - - _strategy?.Create(_configInfo!); - return this; - } - - private GeneralClientBootstrap AddListener(Action callbackAction) where TArgs : EventArgs - { - Debug.Assert(callbackAction != null); - EventManager.Instance.AddListener(callbackAction); - return this; - } - - private void OnMultiDownloadStatistics(object sender, MultiDownloadStatisticsEventArgs e) - { - var message = ObjectTranslator.GetPacketHash(e.Version); - GeneralTracer.Info($"Multi download statistics, {message}[BytesReceived]:{e.BytesReceived} [ProgressPercentage]:{e.ProgressPercentage} [Remaining]:{e.Remaining} [TotalBytesToReceive]:{e.TotalBytesToReceive} [Speed]:{e.Speed}"); - EventManager.Instance.Dispatch(sender, e); - } - - private void OnMultiDownloadCompleted(object sender, MultiDownloadCompletedEventArgs e) - { - var message = ObjectTranslator.GetPacketHash(e.Version); - GeneralTracer.Info($"Multi download completed, {message}[IsComplated]:{e.IsComplated}."); - EventManager.Instance.Dispatch(sender, e); - } - - private void OnMultiDownloadError(object sender, MultiDownloadErrorEventArgs e) - { - var message = ObjectTranslator.GetPacketHash(e.Version); - GeneralTracer.Error($"Multi download error {message}.", e.Exception); - EventManager.Instance.Dispatch(sender, e); - } - - private void OnMultiAllDownloadCompleted(object sender, MultiAllDownloadCompletedEventArgs e) - { - GeneralTracer.Info($"Multi all download completed {e.IsAllDownloadCompleted}."); - EventManager.Instance.Dispatch(sender, e); - } - - #endregion Private Methods -} diff --git a/src/c#/GeneralUpdate.ClientCore/GeneralClientOSS.cs b/src/c#/GeneralUpdate.ClientCore/GeneralClientOSS.cs deleted file mode 100644 index 20e112a0..00000000 --- a/src/c#/GeneralUpdate.ClientCore/GeneralClientOSS.cs +++ /dev/null @@ -1,102 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Diagnostics; -using System.IO; -using System.Linq; -using System.Net; -using System.Text.Json; -using System.Threading; -using System.Threading.Tasks; -using GeneralUpdate.Common.FileBasic; -using GeneralUpdate.Common.Shared; -using GeneralUpdate.Common.Internal.Bootstrap; -using GeneralUpdate.Common.Internal.JsonContext; -using GeneralUpdate.Common.Shared.Object; - -namespace GeneralUpdate.ClientCore; - -public sealed class GeneralClientOSS -{ - private GeneralClientOSS() { } - - /// - /// Starting an OSS update for windows platform. - /// - public static async Task Start(GlobalConfigInfoOSS configGlobalConfigInfo, string upgradeAppName = "GeneralUpdate.Upgrade.exe") - { - await Task.Run(() => - { - try - { - GeneralTracer.Debug("1.Starting OSS upgrade mode."); - var basePath = Thread.GetDomain().BaseDirectory; - //Download the version information file from OSS to be updated.(JSON) - GeneralTracer.Debug("2.Download the version information file from OSS to be updated.(JSON)"); - var versionsFilePath = Path.Combine(basePath, configGlobalConfigInfo.VersionFileName); - DownloadFile(configGlobalConfigInfo.Url, versionsFilePath); - if (!File.Exists(versionsFilePath)) return; - var versions = StorageManager.GetJson>(versionsFilePath, - VersionOSSJsonContext.Default.ListVersionOSS); - if (versions == null || versions.Count == 0) return; - versions = versions.OrderByDescending(x => x.PubTime).ToList(); - var newVersion = versions.First(); - //Determine whether the current client version needs to be upgraded. - GeneralTracer.Debug("3.Determine whether the current client version needs to be upgraded."); - if (!IsUpgrade(configGlobalConfigInfo.CurrentVersion, newVersion.Version)) - return; - - //If you confirm that an update is required, start the upgrade application. - var appPath = Path.Combine(basePath, $"{upgradeAppName}"); - if (!File.Exists(appPath)) - throw new Exception($"The application does not exist {upgradeAppName} !"); - - GeneralTracer.Debug("4.Start upgrade app."); - var json = JsonSerializer.Serialize(configGlobalConfigInfo, - GlobalConfigInfoOSSJsonContext.Default.GlobalConfigInfoOSS); - Environments.SetEnvironmentVariable("GlobalConfigInfoOSS", json); - Process.Start(appPath); - Process.GetCurrentProcess().Kill(); - } - catch (Exception exception) - { - GeneralTracer.Error("The BaseStart method in the GeneralClientOSS class throws an exception.", - exception); - throw exception; - } - finally - { - GeneralTracer.Dispose(); - } - }); - } - - /// - /// Determine whether the current client version needs to be upgraded. - /// - /// - /// - /// true: Upgrade required , false: No upgrade is required - private static bool IsUpgrade(string clientVersion, string serverVersion) - { - if (string.IsNullOrWhiteSpace(clientVersion) || string.IsNullOrWhiteSpace(serverVersion)) - return false; - - var isParseClientVersion = Version.TryParse(clientVersion, out var currentClientVersion); - var isParseServerVersion = Version.TryParse(serverVersion, out var currentServerVersion); - if (!isParseClientVersion || !isParseServerVersion) return false; - if (currentClientVersion < currentServerVersion) return true; - return false; - } - - private static void DownloadFile(string url, string path) - { - if (File.Exists(path)) - { - File.SetAttributes(path, FileAttributes.Normal); - File.Delete(path); - } - - using var webClient = new WebClient(); - webClient.DownloadFile(new Uri(url), path); - } -} \ No newline at end of file diff --git a/src/c#/GeneralUpdate.ClientCore/GeneralUpdate.ico b/src/c#/GeneralUpdate.ClientCore/GeneralUpdate.ico deleted file mode 100644 index 7cb4a86c1bbf84baf7b783ab21b4a7e1cf700455..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 16958 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 diff --git a/src/c#/GeneralUpdate.ClientCore/GeneralUpdate.png b/src/c#/GeneralUpdate.ClientCore/GeneralUpdate.png deleted file mode 100644 index c8a51ff692fb434aea351d66ef66fb16cb35ebfe..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 16305 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 diff --git a/src/c#/GeneralUpdate.ClientCore/Hubs/IUpgradeHubService.cs b/src/c#/GeneralUpdate.ClientCore/Hubs/IUpgradeHubService.cs deleted file mode 100644 index ee29b399..00000000 --- a/src/c#/GeneralUpdate.ClientCore/Hubs/IUpgradeHubService.cs +++ /dev/null @@ -1,47 +0,0 @@ -using System; -using System.Threading.Tasks; - -namespace GeneralUpdate.ClientCore.Hubs; - -public interface IUpgradeHubService -{ - ///

- /// Add a listener to receive upgrade information pushed from the server. - /// - /// string : group name , string : received message content. - public void AddListenerReceive(Action receiveMessageCallback); - - /// - /// Add a listener to receive online and offline notifications. - /// - /// string : Offline or online information. - public void AddListenerOnline(Action onlineMessageCallback); - - /// - /// Add a listener to receive reconnection notifications. - /// - /// string? : Reconnection information. - public void AddListenerReconnected(Func? reconnectedCallback); - - /// - /// Add a listener to receive disconnection notifications. - /// - /// Exception? : Offline exception information. - public void AddListenerClosed(Func closeCallback); - - /// - /// Start subscribing to upgrade push notifications, and the content of the notifications should be agreed upon independently (it is recommended to use JSON data format). - /// - public Task StartAsync(); - - /// - /// When closing the connection, any ongoing message processing will be completed, but no new messages will be accepted. - /// This should be called before the application closes or goes to sleep, so it can reconnect when it resumes next time. - /// - public Task StopAsync(); - - /// - /// The Hub instance will be completely disposed of and cannot be used for reconnection. - /// - public Task DisposeAsync(); -} \ No newline at end of file diff --git a/src/c#/GeneralUpdate.ClientCore/Hubs/RandomRetryPolicy.cs b/src/c#/GeneralUpdate.ClientCore/Hubs/RandomRetryPolicy.cs deleted file mode 100644 index 93ef549c..00000000 --- a/src/c#/GeneralUpdate.ClientCore/Hubs/RandomRetryPolicy.cs +++ /dev/null @@ -1,19 +0,0 @@ -using System; -using Microsoft.AspNetCore.SignalR.Client; - -namespace GeneralUpdate.ClientCore.Hubs; - -public class RandomRetryPolicy : IRetryPolicy -{ - private readonly Random _random = new(); - - public TimeSpan? NextRetryDelay(RetryContext retryContext) - { - // If we've been reconnecting for less than 60 seconds so far, - // wait between 0 and 10 seconds before the next reconnect attempt. - if (retryContext.ElapsedTime < TimeSpan.FromSeconds(60)) - return TimeSpan.FromSeconds(_random.NextDouble() * 10); - // If we've been reconnecting for more than 60 seconds so far, stop reconnecting. - return null; - } -} \ No newline at end of file diff --git a/src/c#/GeneralUpdate.ClientCore/Hubs/UpgradeHubService.cs b/src/c#/GeneralUpdate.ClientCore/Hubs/UpgradeHubService.cs deleted file mode 100644 index 35b60e01..00000000 --- a/src/c#/GeneralUpdate.ClientCore/Hubs/UpgradeHubService.cs +++ /dev/null @@ -1,97 +0,0 @@ -using System; -using System.Threading.Tasks; -using GeneralUpdate.Common.Shared; -using GeneralUpdate.Common.Internal.JsonContext; -using Microsoft.AspNetCore.SignalR; -using Microsoft.AspNetCore.SignalR.Client; -using Microsoft.Extensions.DependencyInjection; - -namespace GeneralUpdate.ClientCore.Hubs; - -/// -/// Upgrade the push notification service. -/// -/// Subscription address, for example: http://127.0.0.1/UpgradeHub -/// ID4 authentication token string. -/// Parameters to be sent to the server upon connection (recommended as a JSON string). -public class UpgradeHubService : IUpgradeHubService -{ - private const string Onlineflag = "Online"; - private const string ReceiveMessageflag = "ReceiveMessage"; - private HubConnection? _connection; - - public UpgradeHubService(string url, string? token = null, string? appkey = null) - => _connection = BuildHubConnection(url, token, appkey); - - private HubConnection BuildHubConnection(string url, string? token = null, string? appkey = null) - { - var builder = new HubConnectionBuilder() - .WithUrl(url, config => - { - if (!string.IsNullOrWhiteSpace(token)) - config.AccessTokenProvider = () => Task.FromResult(token); - - if (!string.IsNullOrWhiteSpace(appkey)) - config.Headers.Add("appkey", appkey); - }).WithAutomaticReconnect(new RandomRetryPolicy()); - builder.Services.Configure(o => - { - o.PayloadSerializerOptions.TypeInfoResolverChain.Insert(0, PacketJsonContext.Default); - }); - return builder.Build(); - } - - public void AddListenerReceive(Action receiveMessageCallback) - => _connection?.On(ReceiveMessageflag, receiveMessageCallback); - - public void AddListenerOnline(Action onlineMessageCallback) - => _connection?.On(Onlineflag, onlineMessageCallback); - - public void AddListenerReconnected(Func? reconnectedCallback) - => _connection!.Reconnected += reconnectedCallback; - - public void AddListenerClosed(Func closeCallback) - => _connection!.Closed += closeCallback; - - public async Task StartAsync() - { - try - { - GeneralTracer.Info($"UpgradeHubService.StartAsync: connecting to SignalR hub. State={_connection?.State}"); - await _connection!.StartAsync(); - GeneralTracer.Info($"UpgradeHubService.StartAsync: SignalR hub connection established. State={_connection?.State}"); - } - catch (Exception e) - { - GeneralTracer.Error("The StartAsync method in the UpgradeHubService class throws an exception." , e); - } - } - - public async Task StopAsync() - { - try - { - GeneralTracer.Info($"UpgradeHubService.StopAsync: stopping SignalR hub connection. State={_connection?.State}"); - await _connection!.StopAsync(); - GeneralTracer.Info("UpgradeHubService.StopAsync: SignalR hub connection stopped."); - } - catch (Exception e) - { - GeneralTracer.Error("The StopAsync method in the UpgradeHubService class throws an exception." , e); - } - } - - public async Task DisposeAsync() - { - try - { - GeneralTracer.Info("UpgradeHubService.DisposeAsync: disposing SignalR hub connection and releasing resources."); - await _connection!.DisposeAsync(); - GeneralTracer.Info("UpgradeHubService.DisposeAsync: SignalR hub connection disposed."); - } - catch (Exception e) - { - GeneralTracer.Error("The DisposeAsync method in the UpgradeHubService class throws an exception." , e); - } - } -} \ No newline at end of file diff --git a/src/c#/GeneralUpdate.ClientCore/Pipeline/CompressMiddleware.cs b/src/c#/GeneralUpdate.ClientCore/Pipeline/CompressMiddleware.cs deleted file mode 100644 index e11802b6..00000000 --- a/src/c#/GeneralUpdate.ClientCore/Pipeline/CompressMiddleware.cs +++ /dev/null @@ -1,36 +0,0 @@ -using System; -using System.Text; -using System.Threading.Tasks; -using GeneralUpdate.Common.Compress; -using GeneralUpdate.Common.Internal.Pipeline; -using GeneralUpdate.Common.Shared; - -namespace GeneralUpdate.ClientCore.Pipeline; - -public class CompressMiddleware : IMiddleware -{ - public Task InvokeAsync(PipelineContext? context) - { - return Task.Run(() => - { - var format = context.Get("Format"); - var sourcePath = context.Get("ZipFilePath"); - var patchPath = context.Get("PatchPath"); - var encoding = context.Get("Encoding"); - var appPath = context.Get("SourcePath"); - var patchEnabled = context.Get("PatchEnabled"); - var targetPath = patchEnabled == false ? appPath : patchPath; - GeneralTracer.Info($"ClientCore.CompressMiddleware.InvokeAsync: decompressing package. Format={format}, Source={sourcePath}, Target={targetPath}, PatchEnabled={patchEnabled}"); - try - { - CompressProvider.Decompress(format, sourcePath, targetPath, encoding); - GeneralTracer.Info("ClientCore.CompressMiddleware.InvokeAsync: decompression completed successfully."); - } - catch (Exception ex) - { - GeneralTracer.Error("ClientCore.CompressMiddleware.InvokeAsync: decompression failed.", ex); - throw; - } - }); - } -} \ No newline at end of file diff --git a/src/c#/GeneralUpdate.ClientCore/Pipeline/HashMiddleware.cs b/src/c#/GeneralUpdate.ClientCore/Pipeline/HashMiddleware.cs deleted file mode 100644 index 7e437d9f..00000000 --- a/src/c#/GeneralUpdate.ClientCore/Pipeline/HashMiddleware.cs +++ /dev/null @@ -1,47 +0,0 @@ -using System; -using System.Security.Cryptography; -using System.Threading.Tasks; -using GeneralUpdate.Common.HashAlgorithms; -using GeneralUpdate.Common.Internal.Pipeline; -using GeneralUpdate.Common.Shared; - -namespace GeneralUpdate.ClientCore.Pipeline; - -public class HashMiddleware : IMiddleware -{ - public async Task InvokeAsync(PipelineContext context) - { - var path = context.Get("ZipFilePath"); - var hash = context.Get("Hash"); - GeneralTracer.Info($"ClientCore.HashMiddleware.InvokeAsync: verifying hash for file={path}, expectedHash={hash}"); - try - { - var isVerify = await VerifyFileHash(path, hash); - if (!isVerify) - { - GeneralTracer.Error($"ClientCore.HashMiddleware.InvokeAsync: hash verification failed for file={path}."); - throw new CryptographicException("Hash verification failed ."); - } - GeneralTracer.Info("ClientCore.HashMiddleware.InvokeAsync: hash verification passed."); - } - catch (CryptographicException) - { - throw; - } - catch (Exception ex) - { - GeneralTracer.Error("ClientCore.HashMiddleware.InvokeAsync: unexpected exception during hash verification.", ex); - throw; - } - } - - private Task VerifyFileHash(string path, string hash) - { - return Task.Run(() => - { - var hashAlgorithm = new Sha256HashAlgorithm(); - var hashSha256 = hashAlgorithm.ComputeHash(path); - return string.Equals(hash, hashSha256, StringComparison.OrdinalIgnoreCase); - }); - } -} \ No newline at end of file diff --git a/src/c#/GeneralUpdate.ClientCore/Pipeline/PatchMiddleware.cs b/src/c#/GeneralUpdate.ClientCore/Pipeline/PatchMiddleware.cs deleted file mode 100644 index adba119b..00000000 --- a/src/c#/GeneralUpdate.ClientCore/Pipeline/PatchMiddleware.cs +++ /dev/null @@ -1,27 +0,0 @@ -using System; -using System.Threading.Tasks; -using GeneralUpdate.Common.Internal.Pipeline; -using GeneralUpdate.Common.Shared; -using GeneralUpdate.Differential; - -namespace GeneralUpdate.ClientCore.Pipeline; - -public class PatchMiddleware : IMiddleware -{ - public async Task InvokeAsync(PipelineContext context) - { - var sourcePath = context.Get("SourcePath"); - var targetPath = context.Get("PatchPath"); - GeneralTracer.Info($"ClientCore.PatchMiddleware.InvokeAsync: applying differential patch. SourcePath={sourcePath}, PatchPath={targetPath}"); - try - { - await DifferentialCore.Dirty(sourcePath, targetPath); - GeneralTracer.Info("ClientCore.PatchMiddleware.InvokeAsync: differential patch applied successfully."); - } - catch (Exception ex) - { - GeneralTracer.Error("ClientCore.PatchMiddleware.InvokeAsync: failed to apply differential patch.", ex); - throw; - } - } -} \ No newline at end of file diff --git a/src/c#/GeneralUpdate.ClientCore/Properties/launchSettings.json b/src/c#/GeneralUpdate.ClientCore/Properties/launchSettings.json deleted file mode 100644 index cb2633b8..00000000 --- a/src/c#/GeneralUpdate.ClientCore/Properties/launchSettings.json +++ /dev/null @@ -1,12 +0,0 @@ -{ - "profiles": { - "GeneralUpdate.ClientCore": { - "commandName": "Project", - "launchBrowser": true, - "environmentVariables": { - "ASPNETCORE_ENVIRONMENT": "Development" - }, - "applicationUrl": "https://localhost:65352;http://localhost:65362" - } - } -} \ No newline at end of file diff --git a/src/c#/GeneralUpdate.ClientCore/SilentUpdateMode.cs b/src/c#/GeneralUpdate.ClientCore/SilentUpdateMode.cs deleted file mode 100644 index c0bbc06e..00000000 --- a/src/c#/GeneralUpdate.ClientCore/SilentUpdateMode.cs +++ /dev/null @@ -1,227 +0,0 @@ -using System; -using System.Diagnostics; -using System.IO; -using System.Linq; -using System.Runtime.InteropServices; -using System.Text; -using System.Text.Json; -using System.Threading; -using System.Threading.Tasks; -using GeneralUpdate.ClientCore.Strategys; -using GeneralUpdate.Common.Download; -using GeneralUpdate.Common.FileBasic; -using GeneralUpdate.Common.Internal; -using GeneralUpdate.Common.Internal.Bootstrap; -using GeneralUpdate.Common.Internal.JsonContext; -using GeneralUpdate.Common.Internal.Strategy; -using GeneralUpdate.Common.Shared; -using GeneralUpdate.Common.Shared.Object; -using GeneralUpdate.Common.Shared.Object.Enum; -using GeneralUpdate.Common.Shared.Service; - -namespace GeneralUpdate.ClientCore; - -internal sealed class SilentUpdateMode -{ - private const string ProcessInfoEnvironmentKey = "ProcessInfo"; - private static readonly TimeSpan PollingInterval = TimeSpan.FromMinutes(20); - private readonly GlobalConfigInfo _configInfo; - private readonly Encoding _encoding; - private readonly string _format; - private readonly int _downloadTimeOut; - private readonly bool _patchEnabled; - private readonly bool _backupEnabled; - private Task? _pollingTask; - private int _prepared; - private int _updaterStarted; - - public SilentUpdateMode(GlobalConfigInfo configInfo, Encoding encoding, string format, int downloadTimeOut, bool patchEnabled, bool backupEnabled) - { - _configInfo = configInfo; - _encoding = encoding; - _format = format; - _downloadTimeOut = downloadTimeOut; - _patchEnabled = patchEnabled; - _backupEnabled = backupEnabled; - } - - public Task StartAsync() - { - GeneralTracer.Info($"SilentUpdateMode.StartAsync: initializing silent update mode. PollingInterval={PollingInterval.TotalMinutes}min, BackupEnabled={_backupEnabled}, PatchEnabled={_patchEnabled}"); - AppDomain.CurrentDomain.ProcessExit += OnProcessExit; - _pollingTask = Task.Run(PollLoopAsync); - _pollingTask.ContinueWith(task => - { - if (task.Exception != null) - { - GeneralTracer.Error("The StartAsync method in SilentUpdateMode captured a polling exception.", task.Exception); - } - }, TaskContinuationOptions.OnlyOnFaulted); - GeneralTracer.Info("SilentUpdateMode.StartAsync: polling loop started, returning to caller."); - return Task.CompletedTask; - } - - private async Task PollLoopAsync() - { - GeneralTracer.Info("SilentUpdateMode.PollLoopAsync: entering silent update polling loop."); - while (Volatile.Read(ref _prepared) == 0) - { - try - { - GeneralTracer.Info("SilentUpdateMode.PollLoopAsync: polling for available updates."); - await PrepareUpdateIfNeededAsync(); - } - catch (Exception exception) - { - GeneralTracer.Error("The PollLoopAsync method in SilentUpdateMode throws an exception.", exception); - } - - if (Volatile.Read(ref _prepared) == 1) - { - GeneralTracer.Info("SilentUpdateMode.PollLoopAsync: update preparation completed, exiting poll loop."); - break; - } - - GeneralTracer.Info($"SilentUpdateMode.PollLoopAsync: no update prepared this cycle, waiting {PollingInterval.TotalMinutes}min before next poll."); - await Task.Delay(PollingInterval); - } - } - - private async Task PrepareUpdateIfNeededAsync() - { - GeneralTracer.Info($"SilentUpdateMode.PrepareUpdateIfNeededAsync: validating version. UpdateUrl={_configInfo.UpdateUrl}, ClientVersion={_configInfo.ClientVersion}"); - var mainResp = await VersionService.Validate(_configInfo.UpdateUrl - , _configInfo.ClientVersion - , AppType.ClientApp - , _configInfo.AppSecretKey - , GetPlatform() - , _configInfo.ProductId - , _configInfo.Scheme - , _configInfo.Token); - - if (mainResp?.Code != 200 || mainResp.Body == null || mainResp.Body.Count == 0) - { - GeneralTracer.Info($"SilentUpdateMode.PrepareUpdateIfNeededAsync: no update available. ResponseCode={mainResp?.Code}, BodyCount={mainResp?.Body?.Count ?? 0}"); - return; - } - - var versions = mainResp.Body.OrderBy(x => x.ReleaseDate).ToList(); - var latestVersion = versions.Last().Version; - GeneralTracer.Info($"SilentUpdateMode.PrepareUpdateIfNeededAsync: {versions.Count} version(s) available. LatestVersion={latestVersion}"); - - if (CheckFail(latestVersion)) - { - GeneralTracer.Warn($"SilentUpdateMode.PrepareUpdateIfNeededAsync: latest version {latestVersion} matches or precedes a known failed upgrade, skipping."); - return; - } - - BlackListManager.Instance?.AddBlackFiles(_configInfo.BlackFiles); - BlackListManager.Instance?.AddBlackFileFormats(_configInfo.BlackFormats); - BlackListManager.Instance?.AddSkipDirectorys(_configInfo.SkipDirectorys); - - _configInfo.Encoding = _encoding; - _configInfo.Format = _format; - _configInfo.DownloadTimeOut = _downloadTimeOut; - _configInfo.PatchEnabled = _patchEnabled; - _configInfo.IsMainUpdate = true; - _configInfo.LastVersion = latestVersion; - _configInfo.UpdateVersions = versions; - _configInfo.TempPath = StorageManager.GetTempDirectory("main_temp"); - _configInfo.BackupDirectory = Path.Combine(_configInfo.InstallPath, $"{StorageManager.DirectoryName}{_configInfo.ClientVersion}"); - - if (_backupEnabled) - { - GeneralTracer.Info($"SilentUpdateMode.PrepareUpdateIfNeededAsync: backing up from {_configInfo.InstallPath} to {_configInfo.BackupDirectory}"); - StorageManager.Backup(_configInfo.InstallPath, _configInfo.BackupDirectory, BlackListManager.Instance.SkipDirectorys); - GeneralTracer.Info("SilentUpdateMode.PrepareUpdateIfNeededAsync: backup completed."); - } - - var processInfo = ConfigurationMapper.MapToProcessInfo( - _configInfo, - versions, - BlackListManager.Instance.BlackFileFormats.ToList(), - BlackListManager.Instance.BlackFiles.ToList(), - BlackListManager.Instance.SkipDirectorys.ToList()); - _configInfo.ProcessInfo = JsonSerializer.Serialize(processInfo, ProcessInfoJsonContext.Default.ProcessInfo); - - GeneralTracer.Info($"SilentUpdateMode.PrepareUpdateIfNeededAsync: starting download of {versions.Count} package(s)."); - var manager = new DownloadManager(_configInfo.TempPath, _configInfo.Format, _configInfo.DownloadTimeOut); - foreach (var versionInfo in _configInfo.UpdateVersions) - { - manager.Add(new DownloadTask(manager, versionInfo)); - } - await manager.LaunchTasksAsync(); - GeneralTracer.Info("SilentUpdateMode.PrepareUpdateIfNeededAsync: all packages downloaded."); - - GeneralTracer.Info("SilentUpdateMode.PrepareUpdateIfNeededAsync: executing update strategy."); - var strategy = CreateStrategy(); - strategy.Create(_configInfo); - await strategy.ExecuteAsync(); - GeneralTracer.Info("SilentUpdateMode.PrepareUpdateIfNeededAsync: update strategy executed, marking as prepared."); - - Interlocked.Exchange(ref _prepared, 1); - } - - private void OnProcessExit(object? sender, EventArgs e) - { - GeneralTracer.Info($"SilentUpdateMode.OnProcessExit: process exit detected. Prepared={Volatile.Read(ref _prepared)}, UpdaterStarted={Volatile.Read(ref _updaterStarted)}"); - if (Volatile.Read(ref _prepared) != 1 || Interlocked.Exchange(ref _updaterStarted, 1) == 1) - { - GeneralTracer.Info("SilentUpdateMode.OnProcessExit: silent updater launch skipped (not prepared or already started)."); - return; - } - - try - { - Environments.SetEnvironmentVariable(ProcessInfoEnvironmentKey, _configInfo.ProcessInfo); - var updaterPath = Path.Combine(_configInfo.InstallPath, _configInfo.AppName); - GeneralTracer.Info($"SilentUpdateMode.OnProcessExit: launching silent updater. Path={updaterPath}"); - if (File.Exists(updaterPath)) - { - Process.Start(new ProcessStartInfo - { - UseShellExecute = true, - FileName = updaterPath - }); - GeneralTracer.Info("SilentUpdateMode.OnProcessExit: silent updater launched successfully."); - } - else - { - GeneralTracer.Warn($"SilentUpdateMode.OnProcessExit: updater not found at {updaterPath}, cannot launch."); - } - } - catch (Exception exception) - { - GeneralTracer.Error("The OnProcessExit method in SilentUpdateMode throws an exception.", exception); - } - } - - private IStrategy CreateStrategy() - { - if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) - return new WindowsStrategy(); - if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux)) - return new LinuxStrategy(); - throw new PlatformNotSupportedException("The current operating system is not supported!"); - } - - private static int GetPlatform() - { - if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) - return PlatformType.Windows; - if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux)) - return PlatformType.Linux; - return -1; - } - - private static bool CheckFail(string version) - { - var fail = Environments.GetEnvironmentVariable("UpgradeFail"); - if (string.IsNullOrEmpty(fail) || string.IsNullOrEmpty(version)) - return false; - - var failVersion = new Version(fail); - var latestVersion = new Version(version); - return failVersion >= latestVersion; - } -} diff --git a/src/c#/GeneralUpdate.ClientCore/Strategys/LinuxStrategy.cs b/src/c#/GeneralUpdate.ClientCore/Strategys/LinuxStrategy.cs deleted file mode 100644 index df6cd9c4..00000000 --- a/src/c#/GeneralUpdate.ClientCore/Strategys/LinuxStrategy.cs +++ /dev/null @@ -1,75 +0,0 @@ -using System; -using System.Diagnostics; -using System.IO; -using GeneralUpdate.ClientCore.Pipeline; -using GeneralUpdate.Common.Internal; -using GeneralUpdate.Common.Internal.Bootstrap; -using GeneralUpdate.Common.Internal.Event; -using GeneralUpdate.Common.Internal.Pipeline; -using GeneralUpdate.Common.Internal.Strategy; -using GeneralUpdate.Common.Shared; -using GeneralUpdate.Common.Shared.Object; - -namespace GeneralUpdate.ClientCore.Strategys; - -/// -/// Update policy based on the linux platform. -/// -public class LinuxStrategy : AbstractStrategy -{ - protected override PipelineContext CreatePipelineContext(VersionInfo version, string patchPath) - { - GeneralTracer.Info($"GeneralUpdate.ClientCore.LinuxStrategy.CreatePipelineContext: building context for version={version.Version}, patchPath={patchPath}"); - var context = base.CreatePipelineContext(version, patchPath); - - // Add ClientCore-specific context items (blacklists for Linux) - context.Add("BlackFiles", _configinfo.BlackFiles); - context.Add("BlackFileFormats", _configinfo.BlackFormats); - context.Add("SkipDirectorys", _configinfo.SkipDirectorys); - - return context; - } - - protected override PipelineBuilder BuildPipeline(PipelineContext context) - { - GeneralTracer.Info($"GeneralUpdate.ClientCore.LinuxStrategy.BuildPipeline: assembling middleware pipeline. PatchEnabled={_configinfo.PatchEnabled}"); - return new PipelineBuilder(context) - .UseMiddlewareIf(_configinfo.PatchEnabled) - .UseMiddleware() - .UseMiddleware(); - } - - public override void StartApp() - { - try - { - GeneralTracer.Info($"GeneralUpdate.ClientCore.LinuxStrategy.StartApp: setting ProcessInfo environment variable and launching updater app={_configinfo.AppName}"); - Environments.SetEnvironmentVariable("ProcessInfo", _configinfo.ProcessInfo); - var appPath = Path.Combine(_configinfo.InstallPath, _configinfo.AppName); - if (File.Exists(appPath)) - { - Process.Start(new ProcessStartInfo - { - UseShellExecute = true, - FileName = appPath - }); - GeneralTracer.Info($"GeneralUpdate.ClientCore.LinuxStrategy.StartApp: updater process launched successfully. Path={appPath}"); - } - else - { - GeneralTracer.Warn($"GeneralUpdate.ClientCore.LinuxStrategy.StartApp: updater app not found at path={appPath}, skipping launch."); - } - } - catch (Exception e) - { - GeneralTracer.Error("The StartApp method in the GeneralUpdate.ClientCore.LinuxStrategy class throws an exception." , e); - EventManager.Instance.Dispatch(this, new ExceptionEventArgs(e, e.Message)); - } - finally - { - GeneralTracer.Info("GeneralUpdate.ClientCore.LinuxStrategy.StartApp: releasing tracer and terminating client process."); - GeneralTracer.Dispose(); - Process.GetCurrentProcess().Kill(); - } - } -} \ No newline at end of file diff --git a/src/c#/GeneralUpdate.ClientCore/Strategys/WindowsStrategy.cs b/src/c#/GeneralUpdate.ClientCore/Strategys/WindowsStrategy.cs deleted file mode 100644 index bc607ef0..00000000 --- a/src/c#/GeneralUpdate.ClientCore/Strategys/WindowsStrategy.cs +++ /dev/null @@ -1,73 +0,0 @@ -using System; -using System.Diagnostics; -using System.IO; -using GeneralUpdate.ClientCore.Pipeline; -using GeneralUpdate.Common.Internal; -using GeneralUpdate.Common.Internal.Bootstrap; -using GeneralUpdate.Common.Internal.Event; -using GeneralUpdate.Common.Internal.Pipeline; -using GeneralUpdate.Common.Internal.Strategy; -using GeneralUpdate.Common.Shared; -using GeneralUpdate.Common.Shared.Object; - -namespace GeneralUpdate.ClientCore.Strategys; - -/// -/// Update policy based on the Windows platform. -/// -public class WindowsStrategy : AbstractStrategy -{ - protected override PipelineContext CreatePipelineContext(VersionInfo version, string patchPath) - { - GeneralTracer.Info($"GeneralUpdate.ClientCore.WindowsStrategy.CreatePipelineContext: building context for version={version.Version}, patchPath={patchPath}"); - var context = base.CreatePipelineContext(version, patchPath); - - // Add ClientCore-specific context items (blacklists are not needed for Windows in Core) - // Keeping this override to maintain extensibility - - return context; - } - - protected override PipelineBuilder BuildPipeline(PipelineContext context) - { - GeneralTracer.Info($"GeneralUpdate.ClientCore.WindowsStrategy.BuildPipeline: assembling middleware pipeline. PatchEnabled={_configinfo.PatchEnabled}"); - return new PipelineBuilder(context) - .UseMiddlewareIf(_configinfo.PatchEnabled) - .UseMiddleware() - .UseMiddleware(); - } - - public override void StartApp() - { - try - { - GeneralTracer.Info($"GeneralUpdate.ClientCore.WindowsStrategy.StartApp: setting ProcessInfo environment variable and launching updater app={_configinfo.AppName}"); - Environments.SetEnvironmentVariable("ProcessInfo", _configinfo.ProcessInfo); - var appPath = Path.Combine(_configinfo.InstallPath, _configinfo.AppName); - if (File.Exists(appPath)) - { - Process.Start(new ProcessStartInfo - { - UseShellExecute = true, - FileName = appPath - }); - GeneralTracer.Info($"GeneralUpdate.ClientCore.WindowsStrategy.StartApp: updater process launched successfully. Path={appPath}"); - } - else - { - GeneralTracer.Warn($"GeneralUpdate.ClientCore.WindowsStrategy.StartApp: updater app not found at path={appPath}, skipping launch."); - } - } - catch (Exception e) - { - GeneralTracer.Error("The StartApp method in the GeneralUpdate.ClientCore.WindowsStrategy class throws an exception." , e); - EventManager.Instance.Dispatch(this, new ExceptionEventArgs(e, e.Message)); - } - finally - { - GeneralTracer.Info("GeneralUpdate.ClientCore.WindowsStrategy.StartApp: releasing tracer and terminating client process."); - GeneralTracer.Dispose(); - Process.GetCurrentProcess().Kill(); - } - } -} \ No newline at end of file diff --git a/src/c#/GeneralUpdate.Common/Compress/CompressProvider.cs b/src/c#/GeneralUpdate.Common/Compress/CompressProvider.cs deleted file mode 100644 index 06f9fa75..00000000 --- a/src/c#/GeneralUpdate.Common/Compress/CompressProvider.cs +++ /dev/null @@ -1,30 +0,0 @@ -using System; -using System.Text; -using GeneralUpdate.Common.Shared.Object; - -namespace GeneralUpdate.Common.Compress; - -public class CompressProvider -{ - private static ICompressionStrategy _compressionStrategy; - - private CompressProvider() { } - - public static void Compress(string compressType,string sourcePath, string destinationPath, bool includeRootDirectory, Encoding encoding) - { - _compressionStrategy = GetCompressionStrategy(compressType); - _compressionStrategy.Compress(sourcePath, destinationPath, includeRootDirectory, encoding); - } - - public static void Decompress(string compressType, string archivePath, string destinationPath, Encoding encoding) - { - _compressionStrategy = GetCompressionStrategy(compressType); - _compressionStrategy.Decompress(archivePath, destinationPath, encoding); - } - - private static ICompressionStrategy GetCompressionStrategy(string compressType) => compressType switch - { - Format.ZIP => new ZipCompressionStrategy(), - _ => throw new ArgumentException("Compression format is not supported!") - }; -} \ No newline at end of file diff --git a/src/c#/GeneralUpdate.Common/Compress/ICompressionStrategy.cs b/src/c#/GeneralUpdate.Common/Compress/ICompressionStrategy.cs deleted file mode 100644 index be4f22e5..00000000 --- a/src/c#/GeneralUpdate.Common/Compress/ICompressionStrategy.cs +++ /dev/null @@ -1,9 +0,0 @@ -using System.Text; - -namespace GeneralUpdate.Common.Compress; - -public interface ICompressionStrategy -{ - void Compress(string sourcePath, string destinationPath, bool includeRootDirectory, Encoding encoding); - void Decompress(string archivePath, string destinationPath, Encoding encoding); -} \ No newline at end of file diff --git a/src/c#/GeneralUpdate.Common/Compress/ZipCompressionStrategy.cs b/src/c#/GeneralUpdate.Common/Compress/ZipCompressionStrategy.cs deleted file mode 100644 index a0cecdb7..00000000 --- a/src/c#/GeneralUpdate.Common/Compress/ZipCompressionStrategy.cs +++ /dev/null @@ -1,196 +0,0 @@ -using System; -using System.Collections.Generic; -using System.IO; -using System.IO.Compression; -using System.Linq; -using System.Text; -using System.Text.RegularExpressions; -using GeneralUpdate.Common.Shared; - -namespace GeneralUpdate.Common.Compress; - -public class ZipCompressionStrategy : ICompressionStrategy -{ - /// - /// Creates a zip archive containing the files and subdirectories of the specified directory. - /// - /// The path of the file directory to be compressed and archived, which can be a relative path or an absolute path. A relative path is a path relative to the current working directory. - /// The archive path of the compressed package to be generated, which can be a relative path or an absolute path. A relative path is a path relative to the current working directory. - /// Enumeration value indicating whether the compression operation emphasizes speed or compression size . - /// Whether the archive contains the parent directory . - public void Compress(string sourceDirectoryName - , string destinationArchiveFileName - , bool includeBaseDirectory - , Encoding encoding) - { - try - { - var compressionLevel = CompressionLevel.Optimal; - if (Directory.Exists(sourceDirectoryName)) - { - if (!File.Exists(destinationArchiveFileName)) - { - ZipFile.CreateFromDirectory(sourceDirectoryName - , destinationArchiveFileName - , compressionLevel - , includeBaseDirectory - , encoding); - } - else - { - var toZipFileDictionaryList = GetAllDirList(sourceDirectoryName, includeBaseDirectory); - using var archive = ZipFile.Open(destinationArchiveFileName, ZipArchiveMode.Update, encoding); - foreach (var toZipFileKey in toZipFileDictionaryList.Keys) - { - if (toZipFileKey == destinationArchiveFileName) continue; - - var toZipedFileName = Path.GetFileName(toZipFileKey); - var toDelArchives = new List(); - foreach (var zipArchiveEntry in archive.Entries) - { - if (toZipedFileName != null && - (zipArchiveEntry.FullName.StartsWith(toZipedFileName) || toZipedFileName.StartsWith(zipArchiveEntry.FullName))) - { - toDelArchives.Add(zipArchiveEntry); - } - } - - foreach (var zipArchiveEntry in toDelArchives) - { - zipArchiveEntry.Delete(); - } - - archive.CreateEntryFromFile(toZipFileKey, toZipFileDictionaryList[toZipFileKey], compressionLevel); - } - } - } - else if (File.Exists(sourceDirectoryName)) - { - if (!File.Exists(destinationArchiveFileName)) - { - ZipFile.CreateFromDirectory(sourceDirectoryName - , destinationArchiveFileName - , compressionLevel - , false - , encoding); - } - else - { - using var archive = ZipFile.Open(destinationArchiveFileName, ZipArchiveMode.Update, encoding); - if (sourceDirectoryName != destinationArchiveFileName) - { - var toZipedFileName = Path.GetFileName(sourceDirectoryName); - var toDelArchives = new List(); - foreach (var zipArchiveEntry in archive.Entries) - { - if (toZipedFileName != null - && (zipArchiveEntry.FullName.StartsWith(toZipedFileName) || toZipedFileName.StartsWith(zipArchiveEntry.FullName))) - { - toDelArchives.Add(zipArchiveEntry); - } - } - - foreach (var zipArchiveEntry in toDelArchives) - { - zipArchiveEntry.Delete(); - } - - archive.CreateEntryFromFile(sourceDirectoryName, toZipedFileName, compressionLevel); - } - } - } - } - catch (Exception exception) - { - GeneralTracer.Error("The Compress method in the ZipCompressionStrategy class throws an exception." , exception); - throw new Exception($"Failed to compress archive: {exception.Message}"); - } - } - - /// - /// Unzip the Zip file and save it to the specified target path folder . - /// - /// - /// - /// - public void Decompress(string zipFilePath, string unZipDir, Encoding encoding) - { - try - { - var dirSeparatorChar = Path.DirectorySeparatorChar.ToString(); - unZipDir = unZipDir.EndsWith(dirSeparatorChar) ? unZipDir : unZipDir + dirSeparatorChar; - - var directoryInfo = new DirectoryInfo(unZipDir); - if (!directoryInfo.Exists) - { - directoryInfo.Create(); - } - - var fileInfo = new FileInfo(zipFilePath); - if (!fileInfo.Exists) - { - return; - } - - using var zipToOpen = new FileStream(zipFilePath, FileMode.Open, FileAccess.ReadWrite, FileShare.Read); - using var archive = new ZipArchive(zipToOpen, ZipArchiveMode.Read, false, encoding); - for (int i = 0; i < archive.Entries.Count; i++) - { - var entries = archive.Entries[i]; - var pattern = $"^{dirSeparatorChar}*"; - var entryFilePath = Regex.Replace(entries.FullName.Replace("/", dirSeparatorChar), pattern, - ""); - if (entryFilePath.EndsWith(dirSeparatorChar)) - { - continue; - } - - var filePath = directoryInfo + entryFilePath; - var greatFolder = Directory.GetParent(filePath); - if (greatFolder is not null && !greatFolder.Exists) - { - greatFolder.Create(); - } - - if (File.Exists(filePath)) - { - File.SetAttributes(filePath, FileAttributes.Normal); - File.Delete(filePath); - } - - entries.ExtractToFile(filePath); - } - } - catch (Exception exception) - { - GeneralTracer.Error("The Decompress method in the ZipCompressionStrategy class throws an exception." , exception); - throw new Exception($"Failed to decompress archive: {exception.Message}"); - } - } - - /// - /// Recursively get the set of all files in the specified directory on the disk, the return type is: dictionary [file name, relative file name to be compressed] - /// - /// - /// - /// - /// - private Dictionary GetAllDirList(string strBaseDir - , bool includeBaseDirectory = false - , string namePrefix = "") - { - var resultDictionary = new Dictionary(); - var directoryInfo = new DirectoryInfo(strBaseDir); - var directories = directoryInfo.GetDirectories(); - var fileInfos = directoryInfo.GetFiles(); - if (includeBaseDirectory) - namePrefix += directoryInfo.Name + "\\"; - foreach (var directory in directories) - resultDictionary = resultDictionary.Concat(GetAllDirList(directory.FullName, true, namePrefix)) - .ToDictionary(k => k.Key, k => k.Value); - foreach (var fileInfo in fileInfos) - if (!resultDictionary.ContainsKey(fileInfo.FullName)) - resultDictionary.Add(fileInfo.FullName, namePrefix + fileInfo.Name); - return resultDictionary; - } -} \ No newline at end of file diff --git a/src/c#/GeneralUpdate.Common/Download/DownloadManager.cs b/src/c#/GeneralUpdate.Common/Download/DownloadManager.cs deleted file mode 100644 index c474923d..00000000 --- a/src/c#/GeneralUpdate.Common/Download/DownloadManager.cs +++ /dev/null @@ -1,78 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Threading.Tasks; -using System.Collections.Immutable; -using System.Diagnostics; -using System.Linq; - -namespace GeneralUpdate.Common.Download -{ - public class DownloadManager(string path, string format, int timeOut) - { - #region Private Members - - private readonly ImmutableList.Builder _downloadTasksBuilder = ImmutableList.Create().ToBuilder(); - private ImmutableList _downloadTasks; - - #endregion Private Members - - #region Public Properties - - public List<(object, string)> FailedVersions { get; } = new(); - - public string Path => path; - - public string Format => format; - - public int TimeOut => timeOut; - - private ImmutableList DownloadTasks => _downloadTasks ?? _downloadTasksBuilder.ToImmutable(); - - public event EventHandler MultiAllDownloadCompleted; - public event EventHandler MultiDownloadCompleted; - public event EventHandler MultiDownloadError; - public event EventHandler MultiDownloadStatistics; - - #endregion Public Properties - - #region Public Methods - - public async Task LaunchTasksAsync() - { - try - { - var downloadTasks = DownloadTasks.Select(task => task.LaunchAsync()).ToList(); - await Task.WhenAll(downloadTasks); - MultiAllDownloadCompleted?.Invoke(this, new MultiAllDownloadCompletedEventArgs(true, FailedVersions)); - } - catch (Exception ex) - { - MultiAllDownloadCompleted?.Invoke(this, new MultiAllDownloadCompletedEventArgs(false, FailedVersions)); - throw new Exception($"Download manager error: {ex.Message}", ex); - } - } - - public void OnMultiDownloadStatistics(object sender, MultiDownloadStatisticsEventArgs e) - => MultiDownloadStatistics?.Invoke(this, e); - - public void OnMultiAsyncCompleted(object sender, MultiDownloadCompletedEventArgs e) - => MultiDownloadCompleted?.Invoke(this, e); - - public void OnMultiDownloadError(object sender, MultiDownloadErrorEventArgs e) - { - MultiDownloadError?.Invoke(this, e); - FailedVersions.Add((e.Version, e.Exception.Message)); - } - - public void Add(DownloadTask task) - { - Debug.Assert(task != null); - if (!_downloadTasksBuilder.Contains(task)) - { - _downloadTasksBuilder.Add(task); - } - } - - #endregion Public Methods - } -} \ No newline at end of file diff --git a/src/c#/GeneralUpdate.Common/Download/DownloadTask.cs b/src/c#/GeneralUpdate.Common/Download/DownloadTask.cs deleted file mode 100644 index 40de3d61..00000000 --- a/src/c#/GeneralUpdate.Common/Download/DownloadTask.cs +++ /dev/null @@ -1,305 +0,0 @@ -using System; -using System.Collections.Generic; -using System.IO; -using System.Net.Http; -using System.Net.Http.Headers; -using System.Threading; -using System.Threading.Tasks; -using GeneralUpdate.Common.Shared; -using GeneralUpdate.Common.Shared.Object; - -namespace GeneralUpdate.Common.Download -{ - public class DownloadTask - { - #region Private Members - - private readonly HttpClient _httpClient; - private readonly DownloadManager _manager; - private readonly VersionInfo? _version; - private Timer? _timer; - private DateTime _startTime; - private long _receivedBytes; - private long _totalBytes; - private long _currentBytes; - - #endregion Private Members - - public DownloadTask(DownloadManager manager, VersionInfo version) - { - _manager = manager; - _version = version; - _httpClient = new HttpClient { Timeout = TimeSpan.FromSeconds(_manager.TimeOut) }; - - // Set authentication headers if provided - if (!string.IsNullOrEmpty(version?.AuthScheme) && !string.IsNullOrEmpty(version?.AuthToken)) - { - _httpClient.DefaultRequestHeaders.Authorization = - new AuthenticationHeaderValue(version.AuthScheme, version.AuthToken); - } - - _timer = new Timer(_=> Statistics(), null, 0, 1000); - } - - public async Task LaunchAsync() - { - try - { - var path = Path.Combine(_manager.Path, $"{_version?.Name}{_manager.Format}"); - await DownloadFileRangeAsync(_version.Url, path); - } - catch (Exception exception) - { - GeneralTracer.Error("The LaunchAsync method in the DownloadTask class throws an exception." , exception); - _manager.OnMultiDownloadError(this, new MultiDownloadErrorEventArgs(exception, _version)); - } - } - - #region Private Methods - - private async Task DownloadFileRangeAsync(string url, string path) - { - try - { - var tempPath = path + ".temp"; - var startPos = CheckFile(tempPath); - - var requestMessage = new HttpRequestMessage(HttpMethod.Get, url); - if (startPos > 0) - { - requestMessage.Headers.Range = new RangeHeaderValue(startPos, null); - } - - using var response = await _httpClient.SendAsync(requestMessage, HttpCompletionOption.ResponseHeadersRead); - if (!response.IsSuccessStatusCode) - throw new HttpRequestException($"Failed to download file: {response.ReasonPhrase}"); - - var totalBytes = response.Content.Headers.ContentLength ?? 0; - Interlocked.Exchange(ref _totalBytes, totalBytes); - - if (startPos >= totalBytes) - { - if (File.Exists(path)) - { - File.SetAttributes(path, FileAttributes.Normal); - File.Delete(path); - } - - File.Move(tempPath, path); - OnDownloadCompleted(true); - return; - } - - await foreach (var chunk in DownloadChunksAsync(response)) - { - await WriteFileAsync(tempPath, chunk, totalBytes); - } - } - catch (Exception exception) - { - OnDownloadCompleted(false); - GeneralTracer.Error("The DownloadFileRangeAsync method in the DownloadTask class throws an exception." , exception); - _manager.OnMultiDownloadError(this, new MultiDownloadErrorEventArgs(exception, _version)); - } - } - - private async IAsyncEnumerable DownloadChunksAsync(HttpResponseMessage response) - { - using var responseStream = await response.Content.ReadAsStreamAsync(); - var buffer = new byte[8192]; - int bytesRead; - while ((bytesRead = await responseStream.ReadAsync(buffer, 0, buffer.Length)) > 0) - { - var chunk = new byte[bytesRead]; - Buffer.BlockCopy(buffer, 0, chunk, 0, bytesRead); - yield return chunk; - } - } - - private async Task WriteFileAsync(string tempPath, byte[] chunk, long totalBytes) - { - try - { - using var fileStream = new FileStream(tempPath, FileMode.Append, FileAccess.Write, FileShare.None); - await fileStream.WriteAsync(chunk, 0, chunk.Length); - Interlocked.Add(ref _receivedBytes, chunk.Length); - if (_receivedBytes >= totalBytes) - { - fileStream.Close(); - var path = tempPath.Replace(".temp", ""); - if (File.Exists(path)) - { - File.SetAttributes(path, FileAttributes.Normal); - File.Delete(path); - } - - File.Move(tempPath, path); - OnDownloadCompleted(true); - } - } - catch (Exception exception) - { - OnDownloadCompleted(false); - GeneralTracer.Error("The WriteFileAsync method in the DownloadTask class throws an exception." , exception); - _manager.OnMultiDownloadError(this, new MultiDownloadErrorEventArgs(exception, _version)); - } - } - - private void Statistics() - { - try - { - var interval = DateTime.Now - _startTime; - var tempTotalBytes = Interlocked.Read(ref _totalBytes); - //Accumulate the downloaded size. - var tempReceivedBytes = Interlocked.Read(ref _receivedBytes); - //Current downloaded size. - var tempCurrentBytes = tempReceivedBytes - Interlocked.Read(ref _currentBytes); - var speed = CalculateDownloadSpeed(tempCurrentBytes, interval); - var formatSpeed = FormatDownloadSpeed(speed); - var remainingTime = CalculateRemainingTime(tempTotalBytes, tempReceivedBytes, speed); - var progress = CalculateDownloadProgress(tempTotalBytes, tempReceivedBytes); - - var args = new MultiDownloadStatisticsEventArgs(_version - , remainingTime - , formatSpeed - , tempTotalBytes - , tempReceivedBytes - , progress); - _manager.OnMultiDownloadStatistics(this, args); - Interlocked.Exchange(ref _currentBytes, tempReceivedBytes); - _startTime = DateTime.Now; - } - catch (Exception exception) - { - _manager.OnMultiDownloadError(this, new MultiDownloadErrorEventArgs(exception, _version)); - } - } - - private void OnDownloadCompleted(bool isComplated) - { - try - { - DisposeTimer(); - var eventArgs = new MultiDownloadCompletedEventArgs(_version, isComplated); - _manager.OnMultiAsyncCompleted(this, eventArgs); - } - catch (Exception exception) - { - GeneralTracer.Error(exception.Message, exception); - _manager.OnMultiDownloadError(this, new MultiDownloadErrorEventArgs(exception, _version)); - } - } - - /// - /// Calculate the remaining download time (in seconds). - /// - /// - /// - /// - /// - private static TimeSpan CalculateRemainingTime(long totalBytes, long bytesReceived, double downloadSpeed) - { - if (downloadSpeed == 0) - { - return new TimeSpan(0, 0, 0, 0); - } - - var bytesRemaining = totalBytes - bytesReceived; - var secondsRemaining = bytesRemaining / downloadSpeed; - return TimeSpan.FromSeconds(secondsRemaining); - } - - /// - /// Calculate the download speed (in bytes per second). - /// - /// - /// - /// - private static double CalculateDownloadSpeed(long bytesReceived, TimeSpan elapsedTime) - { - if (elapsedTime.TotalSeconds == 0) - return 0; - - return bytesReceived / elapsedTime.TotalSeconds; - } - - /// - /// Calculate the download progress (percentage %). - /// - /// - /// - /// - private static double CalculateDownloadProgress(long totalBytes, long bytesReceived) - { - if (totalBytes == 0) - return 0; - - return (double)bytesReceived / totalBytes * 100; - } - - /// - /// Convert the download speed to an appropriate unit (B, KB, MB, GB). - /// - /// - /// - private static string FormatDownloadSpeed(double speedInBytesPerSecond) - { - const double kiloByte = 1024; - const double megaByte = kiloByte * 1024; - const double gigaByte = megaByte * 1024; - - return speedInBytesPerSecond switch - { - >= gigaByte => $"{speedInBytesPerSecond / gigaByte:F2} GB/s", - >= megaByte => $"{speedInBytesPerSecond / megaByte:F2} MB/s", - _ => speedInBytesPerSecond >= kiloByte - ? $"{speedInBytesPerSecond / kiloByte:F2} KB/s" - : $"{speedInBytesPerSecond:F2} B/s" - }; - } - - /// - /// Get the size of the downloaded file for resuming interrupted downloads. - /// - /// - /// - private static long CheckFile(string tempPath) - { - long startPos = 0; - if (!File.Exists(tempPath)) return startPos; - using var fileStream = File.OpenWrite(tempPath); - startPos = fileStream.Length; - fileStream.Seek(startPos, SeekOrigin.Current); - return startPos; - } - - private void DisposeTimer() - { - if (_timer == null) return; - try - { - _timer.Change(Timeout.Infinite, Timeout.Infinite); - using var waitHandle = new ManualResetEvent(false); - _timer.Dispose(waitHandle); - waitHandle.WaitOne(); - } - catch (ObjectDisposedException exception) - { - GeneralTracer.Error("Timer has already been disposed: " + exception.Message); - _manager.OnMultiDownloadError(this, new MultiDownloadErrorEventArgs(exception, _version)); - } - catch (Exception exception) - { - GeneralTracer.Error("An error occurred while disposing the timer.", exception); - _manager.OnMultiDownloadError(this, new MultiDownloadErrorEventArgs(exception, _version)); - } - finally - { - _timer = null; - } - } - - #endregion Private Methods - } -} diff --git a/src/c#/GeneralUpdate.Common/Download/MultiEventArgs/MutiAllDownloadCompletedEventArgs.cs b/src/c#/GeneralUpdate.Common/Download/MultiEventArgs/MutiAllDownloadCompletedEventArgs.cs deleted file mode 100644 index aeec9ee6..00000000 --- a/src/c#/GeneralUpdate.Common/Download/MultiEventArgs/MutiAllDownloadCompletedEventArgs.cs +++ /dev/null @@ -1,18 +0,0 @@ -using System; -using System.Collections.Generic; - -namespace GeneralUpdate.Common.Download -{ - public class MultiAllDownloadCompletedEventArgs : EventArgs - { - public MultiAllDownloadCompletedEventArgs(bool isAllDownloadCompleted, IList<(object, string)> failedVersions) - { - IsAllDownloadCompleted = isAllDownloadCompleted; - FailedVersions = failedVersions; - } - - public bool IsAllDownloadCompleted { get; set; } - - public IList> FailedVersions { get; set; } - } -} \ No newline at end of file diff --git a/src/c#/GeneralUpdate.Common/Download/MultiEventArgs/MutiDownloadCompletedEventArgs.cs b/src/c#/GeneralUpdate.Common/Download/MultiEventArgs/MutiDownloadCompletedEventArgs.cs deleted file mode 100644 index 3f684162..00000000 --- a/src/c#/GeneralUpdate.Common/Download/MultiEventArgs/MutiDownloadCompletedEventArgs.cs +++ /dev/null @@ -1,11 +0,0 @@ -using System; - -namespace GeneralUpdate.Common.Download -{ - public class MultiDownloadCompletedEventArgs(object version, bool isComplated) : EventArgs - { - public object Version { get; private set; } = version; - - public bool IsComplated { get; private set; } = isComplated; - } -} \ No newline at end of file diff --git a/src/c#/GeneralUpdate.Common/Download/MultiEventArgs/MutiDownloadErrorEventArgs.cs b/src/c#/GeneralUpdate.Common/Download/MultiEventArgs/MutiDownloadErrorEventArgs.cs deleted file mode 100644 index 050db181..00000000 --- a/src/c#/GeneralUpdate.Common/Download/MultiEventArgs/MutiDownloadErrorEventArgs.cs +++ /dev/null @@ -1,17 +0,0 @@ -using System; - -namespace GeneralUpdate.Common.Download -{ - public class MultiDownloadErrorEventArgs : EventArgs - { - public MultiDownloadErrorEventArgs(Exception exception, object version) - { - Exception = exception; - Version = version; - } - - public Exception Exception { get; set; } - - public object Version { get; set; } - } -} \ No newline at end of file diff --git a/src/c#/GeneralUpdate.Common/Download/MultiEventArgs/MutiDownloadStatisticsEventArgs.cs b/src/c#/GeneralUpdate.Common/Download/MultiEventArgs/MutiDownloadStatisticsEventArgs.cs deleted file mode 100644 index 4dad6104..00000000 --- a/src/c#/GeneralUpdate.Common/Download/MultiEventArgs/MutiDownloadStatisticsEventArgs.cs +++ /dev/null @@ -1,23 +0,0 @@ -using System; - -namespace GeneralUpdate.Common.Download; - -public class MultiDownloadStatisticsEventArgs(object version - , TimeSpan remaining - , string speed - , long totalBytes - , long bytesReceived - , double progressPercentage) : EventArgs -{ - public object Version { get; private set; } = version; - - public TimeSpan Remaining { get; private set; } = remaining; - - public string Speed { get; private set; } = speed; - - public long TotalBytesToReceive { get; private set; } = totalBytes; - - public long BytesReceived { get; private set; } = bytesReceived; - - public double ProgressPercentage { get; private set; } = progressPercentage; -} \ No newline at end of file diff --git a/src/c#/GeneralUpdate.Common/Download/MultiEventArgs/UpdateInfoEventArgs.cs b/src/c#/GeneralUpdate.Common/Download/MultiEventArgs/UpdateInfoEventArgs.cs deleted file mode 100644 index ca835da8..00000000 --- a/src/c#/GeneralUpdate.Common/Download/MultiEventArgs/UpdateInfoEventArgs.cs +++ /dev/null @@ -1,9 +0,0 @@ -using System; -using GeneralUpdate.Common.Shared.Object; - -namespace GeneralUpdate.Common.Download; - -public class UpdateInfoEventArgs(VersionRespDTO info) : EventArgs -{ - public VersionRespDTO Info { get; private set; } = info; -} diff --git a/src/c#/GeneralUpdate.Common/FileBasic/BlackListManager.cs b/src/c#/GeneralUpdate.Common/FileBasic/BlackListManager.cs deleted file mode 100644 index ed8af84b..00000000 --- a/src/c#/GeneralUpdate.Common/FileBasic/BlackListManager.cs +++ /dev/null @@ -1,139 +0,0 @@ -using System.Collections.Generic; -using System.IO; -using System.Linq; -using GeneralUpdate.Common.Shared.Object; - -namespace GeneralUpdate.Common.FileBasic; - -public class BlackListManager -{ - private static readonly object LockObject = new object(); - private static BlackListManager? _instance; - - private static readonly List _blackFileFormats = - [ - ".patch", - Format.ZIP, - ".rar", - ".tar", - ".json", - ".pdb" - ]; - - private static readonly List _blackFiles = - [ - "Microsoft.Bcl.AsyncInterfaces.dll", - "System.Collections.Immutable.dll", - "System.IO.Pipelines.dll", - "System.Text.Encodings.Web.dll", - "System.Text.Json.dll" - ]; - - private static readonly List _skipDirectorys = ["app-", "fail"]; - - private BlackListManager() { } - - public static BlackListManager? Instance - { - get - { - if (_instance == null) - { - lock (LockObject) - { - if (_instance == null) - { - _instance = new BlackListManager(); - } - } - } - - return _instance; - } - } - - public IReadOnlyList BlackFileFormats => _blackFileFormats.AsReadOnly(); - public IReadOnlyList BlackFiles => _blackFiles.AsReadOnly(); - - public IReadOnlyList SkipDirectorys = _skipDirectorys.AsReadOnly(); - - public void AddBlackFileFormats(List? formats) - { - if (formats == null) - return; - - foreach (var format in formats) - { - AddBlackFileFormat(format); - } - } - - public void AddBlackFileFormat(string format) - { - if (string.IsNullOrWhiteSpace(format)) - return; - - if (!_blackFileFormats.Contains(format)) - { - _blackFileFormats.Add(format); - } - } - - public void AddBlackFiles(List? fileNames) - { - if (fileNames == null) - return; - - foreach (var fileName in fileNames) - { - AddBlackFile(fileName); - } - } - - public void AddBlackFile(string fileName) - { - if (string.IsNullOrWhiteSpace(fileName)) - return; - - if (!_blackFiles.Contains(fileName)) - { - _blackFiles.Add(fileName); - } - } - - public void AddSkipDirectorys(List? directorys) - { - if (directorys == null) - return; - - foreach (var directory in directorys) - { - AddSkipDirectory(directory); - } - } - - public void AddSkipDirectory(string directory) - { - if (string.IsNullOrWhiteSpace(directory)) - return; - - if (!_skipDirectorys.Contains(directory)) - { - _skipDirectorys.Add(directory); - } - } - - public bool IsBlacklisted(string relativeFilePath) - { - var fileName = Path.GetFileName(relativeFilePath); - var fileExtension = Path.GetExtension(relativeFilePath); - - return _blackFiles.Contains(fileName) || _blackFileFormats.Contains(fileExtension); - } - - public bool IsSkipDirectory(string directory) - { - var dirName = new DirectoryInfo(directory).Name; - return _skipDirectorys.Any(dirName.Contains); - } -} \ No newline at end of file diff --git a/src/c#/GeneralUpdate.Common/FileBasic/ComparisonResult.cs b/src/c#/GeneralUpdate.Common/FileBasic/ComparisonResult.cs deleted file mode 100644 index 41dbf234..00000000 --- a/src/c#/GeneralUpdate.Common/FileBasic/ComparisonResult.cs +++ /dev/null @@ -1,41 +0,0 @@ -using System.Collections.Generic; - -namespace GeneralUpdate.Common.FileBasic; - -/// -/// Result of a comparison between two directories. -/// -public class ComparisonResult -{ - private List _leftNodes; - private List _rightNodes; - private List _differentNodes; - - public ComparisonResult() - { - _leftNodes = new List(); - _rightNodes = new List(); - _differentNodes = new List(); - } - - /// - /// List of files that are unique to A. - /// - public IReadOnlyList LeftNodes => _leftNodes.AsReadOnly(); - - /// - /// List of files that are unique to B. - /// - public IReadOnlyList RightNodes => _rightNodes.AsReadOnly(); - - /// - /// List of files that are different between A and B. - /// - public IReadOnlyList DifferentNodes => _differentNodes.AsReadOnly(); - - public void AddToLeft(IEnumerable files) => _leftNodes.AddRange(files); - - public void AddToRight(IEnumerable files) => _rightNodes.AddRange(files); - - public void AddDifferent(IEnumerable files) => _differentNodes.AddRange(files); -} \ No newline at end of file diff --git a/src/c#/GeneralUpdate.Common/FileBasic/FileNode.cs b/src/c#/GeneralUpdate.Common/FileBasic/FileNode.cs deleted file mode 100644 index 90b3831a..00000000 --- a/src/c#/GeneralUpdate.Common/FileBasic/FileNode.cs +++ /dev/null @@ -1,150 +0,0 @@ -using System; - -namespace GeneralUpdate.Common.FileBasic; - -public class FileNode -{ - #region Public Properties - - public long Id { get; set; } - - public string Name { get; set; } - - public string FullName { get; set; } - - public string Path { get; set; } - - public string Hash { get; set; } - - public FileNode Left { get; set; } - - public FileNode Right { get; set; } - - public int LeftType { get; set; } - - public int RightType { get; set; } - - public string RelativePath { get; set; } - - #endregion Public Properties - - #region Constructors - - public FileNode() - { - } - - public FileNode(int id) - { - Id = id; - } - - #endregion Constructors - - #region Public Methods - - public void Add(FileNode node) - { - if (node == null) return; - - if (node.Id < Id) - { - if (Left == null) - { - Left = node; - } - else - { - Left.Add(node); - } - } - else - { - if (Right == null) - { - Right = node; - } - else - { - Right.Add(node); - } - } - } - - public void InfixOrder() - { - if (Left != null) - { - Left.InfixOrder(); - } - - if (Right != null) - { - Right.InfixOrder(); - } - } - - public FileNode Search(long id) - { - if (id == Id) - { - return this; - } - else if (id < Id) - { - if (Left == null) return null; - return Left.Search(id); - } - else - { - if (Right == null) return null; - return Right.Search(id); - } - } - - /// - /// Find the parent node of the node that you want to delete. - /// - /// - /// - public FileNode SearchParent(long id) - { - if (Left != null && Left.Id == id || Right != null && Right.Id == id) - { - return this; - } - else - { - if (id < Id && Left != null) - { - return Left.SearchParent(id); - } - else if (id >= Id && Right != null) - { - return Right.SearchParent(id); - } - else - { - return null; - } - } - } - - /// - /// Compare tree nodes equally by Hash and file names. - /// - /// - /// - public override bool Equals(object obj) - { - if (obj == null) return false; - var tempNode = obj as FileNode; - if (tempNode == null) throw new ArgumentException(nameof(tempNode)); - return string.Equals(Hash, tempNode.Hash, StringComparison.OrdinalIgnoreCase) && - string.Equals(Name, tempNode.Name, StringComparison.OrdinalIgnoreCase); - } - - public override int GetHashCode() => base.GetHashCode(); - - #endregion Public Methods -} diff --git a/src/c#/GeneralUpdate.Common/FileBasic/FileTree.cs b/src/c#/GeneralUpdate.Common/FileBasic/FileTree.cs deleted file mode 100644 index 02ea4cc8..00000000 --- a/src/c#/GeneralUpdate.Common/FileBasic/FileTree.cs +++ /dev/null @@ -1,172 +0,0 @@ -using System.Collections.Generic; -using GeneralUpdate.Common.Shared; - -namespace GeneralUpdate.Common.FileBasic; - - /// - /// Simple file binary tree. - /// - public class FileTree - { - #region Private Members - - private FileNode _root; - - #endregion Private Members - - #region Constructors - - public FileTree() - { } - - public FileTree(IEnumerable nodes) - { - foreach (var node in nodes) Add(node); - } - - #endregion Constructors - - #region Public Methods - - public void Add(FileNode node) - { - if (_root == null) - { - _root = node; - } - else - { - _root.Add(node); - } - } - - public void InfixOrder() - { - if (_root != null) - { - _root.InfixOrder(); - } - else - { - GeneralTracer.Info("The binary sort tree is empty and cannot be traversed!"); - } - } - - public FileNode Search(long id) => _root == null ? null : _root.Search(id); - - public FileNode SearchParent(long id) => _root == null ? null : _root.SearchParent(id); - - public long DelRightTreeMin(FileNode node) - { - FileNode target = node; - while (target.Left != null) - { - target = target.Left; - } - DelNode(target.Id); - return target.Id; - } - - public void DelNode(long id) - { - if (_root == null) - { - return; - } - else - { - FileNode targetNode = Search(id); - if (targetNode == null) - { - return; - } - if (_root.Left == null && _root.Right == null) - { - _root = null; - return; - } - - FileNode parent = SearchParent(id); - if (targetNode.Left == null && targetNode.Right == null) - { - if (parent.Left != null && parent.Left.Id == id) - { - parent.Left = null; - } - else if (parent.Right != null && parent.Right.Id == id) - { - parent.Right = null; - } - } - else if (targetNode.Left != null && targetNode.Right != null) - { - long minVal = DelRightTreeMin(targetNode.Right); - targetNode.Id = minVal; - } - else - { - if (targetNode.Left != null) - { - if (parent.Left.Id == id) - { - parent.Left = targetNode.Left; - } - else - { - parent.Right = targetNode.Left; - } - } - else - { - if (parent.Left.Id == id) - { - parent.Left = targetNode.Right; - } - else - { - parent.Right = targetNode.Right; - } - } - } - } - } - - /// - /// Starting from the root node, recursively compares two different child nodes of the binary tree and nodes that are not included. - /// - /// - /// - /// - public void Compare(FileNode node, FileNode node0, ref List nodes) - { - if (node != null && node.Left != null) - { - if (!node.Equals(node0) && node0 != null) nodes.Add(node0); - Compare(node.Left, node0.Left, ref nodes); - } - else if (node0 != null && node0.Left != null) - { - nodes.Add(node0); - Compare(node.Left, node0.Left, ref nodes); - } - - if (node != null && node.Right != null) - { - if (!node.Equals(node0) && node0 != null) nodes.Add(node0); - Compare(node.Right, node0 == null ? null : node0.Right, ref nodes); - } - else if (node0 != null && node0.Right != null) - { - nodes.Add(node0); - Compare(node == null ? null : node.Right, node0.Right, ref nodes); - } - else if (node0 != null) - { - nodes.Add(node0); - } - } - - public FileNode GetRoot() => _root; - - #endregion Public Methods - } \ No newline at end of file diff --git a/src/c#/GeneralUpdate.Common/FileBasic/StorageManager.cs b/src/c#/GeneralUpdate.Common/FileBasic/StorageManager.cs deleted file mode 100644 index 32970c82..00000000 --- a/src/c#/GeneralUpdate.Common/FileBasic/StorageManager.cs +++ /dev/null @@ -1,268 +0,0 @@ -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Text.Json; -using System.Text.Json.Serialization.Metadata; -using System.Threading; -using GeneralUpdate.Common.HashAlgorithms; - -namespace GeneralUpdate.Common.FileBasic -{ - public sealed class StorageManager - { - private long _fileCount = 0; - public const string DirectoryName = "app-"; - - private ComparisonResult ComparisonResult { get; set; } - - #region Public Methods - - /// - /// Using the list on the left as a baseline, find the set of differences between the two file lists. - /// - public IEnumerable? Except(string leftPath, string rightPath) - { - var leftFileNodes = ReadFileNode(leftPath); - var rightFileNodes = ReadFileNode(rightPath); - var rightNodeDic = rightFileNodes.ToDictionary(x => x.RelativePath); - return leftFileNodes.Where(f => !rightNodeDic.ContainsKey(f.RelativePath)).ToList(); - } - - /// - /// Compare two directories. - /// - /// - /// - public ComparisonResult Compare(string leftDir, string rightDir) - { - ResetId(); - ComparisonResult = new ComparisonResult(); - var leftFileNodes = ReadFileNode(leftDir); - var rightFileNodes = ReadFileNode(rightDir); - var leftTree = new FileTree(leftFileNodes); - var rightTree = new FileTree(rightFileNodes); - var differentTreeNode = new List(); - leftTree.Compare(leftTree.GetRoot(), rightTree.GetRoot(), ref differentTreeNode); - ComparisonResult.AddToLeft(leftFileNodes); - ComparisonResult.AddToRight(rightFileNodes); - ComparisonResult.AddDifferent(differentTreeNode); - return ComparisonResult; - } - - public static void CreateJson(string targetPath, T obj, JsonTypeInfo? typeInfo = null) where T : class - { - var folderPath = Path.GetDirectoryName(targetPath) ?? - throw new ArgumentException("invalid path", nameof(targetPath)); - - if (!Directory.Exists(folderPath)) - Directory.CreateDirectory(folderPath); - - var jsonString = typeInfo != null ? JsonSerializer.Serialize(obj, typeInfo) : JsonSerializer.Serialize(obj); - File.WriteAllText(targetPath, jsonString); - } - - public static T? GetJson(string path, JsonTypeInfo? typeInfo = null) where T : class - { - if (File.Exists(path)) - { - var json = File.ReadAllText(path); - if (typeInfo != null) - { - return JsonSerializer.Deserialize(json, typeInfo); - } - return JsonSerializer.Deserialize(json); - } - - return default; - } - - public static string GetTempDirectory(string name) - { - var path = $"generalupdate_{DateTime.Now:yyyy-MM-dd}_{name}"; - var tempDir = Path.Combine(Path.GetTempPath(), path); - if (!Directory.Exists(tempDir)) - { - Directory.CreateDirectory(tempDir); - } - - return tempDir; - } - - public static void DeleteDirectory(string targetDir) - { - foreach (var file in Directory.GetFiles(targetDir)) - { - File.SetAttributes(file, FileAttributes.Normal); - File.Delete(file); - } - - foreach (var dir in Directory.GetDirectories(targetDir)) - { - DeleteDirectory(dir); - } - - Directory.Delete(targetDir, false); - } - - public static List GetAllFiles(string path, List skipDirectorys) - { - try - { - var files = new List(); - files.AddRange(new DirectoryInfo(path).GetFiles()); - var tmpDir = new DirectoryInfo(path).GetDirectories(); - - foreach (var dic in tmpDir) - { - bool shouldSkip = false; - foreach (var notBackup in skipDirectorys) - { - if (dic.Name.Contains(notBackup)) - { - shouldSkip = true; - break; - } - } - - if (!shouldSkip) - files.AddRange(GetAllFiles(dic.FullName, skipDirectorys)); - } - - return files; - } - catch - { - return new List(); - } - } - - public static bool HashEquals(string leftPath, string rightPath) - { - var hashAlgorithm = new Sha256HashAlgorithm(); - var hashLeft = hashAlgorithm.ComputeHash(leftPath); - var hashRight = hashAlgorithm.ComputeHash(rightPath); - return hashLeft.SequenceEqual(hashRight); - } - - /// - /// Backup the all program. - /// - /// - /// - /// - public static void Backup(string sourcePath, string backupPath, IReadOnlyList directoryNames) - { - if (Directory.Exists(backupPath)) - { - DeleteDirectory(backupPath); - } - Directory.CreateDirectory(backupPath); - CopyDirectory(sourcePath, backupPath, directoryNames); - } - - private static void CopyDirectory(string sourceDir, string targetDir, IReadOnlyList directoryNames) - { - foreach (string dirPath in Directory.GetDirectories(sourceDir, "*", SearchOption.TopDirectoryOnly)) - { - if (!directoryNames.Any(name => Path.GetFileName(dirPath).Contains(name))) - { - string newTargetDir = Path.Combine(targetDir, Path.GetFileName(dirPath)); - Directory.CreateDirectory(newTargetDir); - CopyDirectory(dirPath, newTargetDir, directoryNames); - } - } - - foreach (string filePath in Directory.GetFiles(sourceDir, "*.*", SearchOption.TopDirectoryOnly)) - { - string newFilePath = Path.Combine(targetDir, Path.GetFileName(filePath)); - File.Copy(filePath, newFilePath, true); - } - } - - /// - /// Restore the all program. - /// - /// - /// - public static void Restore(string backupPath, string sourcePath) - { - if (!Directory.Exists(sourcePath)) - { - Directory.CreateDirectory(sourcePath); - } - - CopyDirectory(backupPath, sourcePath); - } - - private static void CopyDirectory(string sourceDir, string targetDir) - { - foreach (string dirPath in Directory.GetDirectories(sourceDir, "*", SearchOption.TopDirectoryOnly)) - { - string newTargetDir = Path.Combine(targetDir, Path.GetFileName(dirPath)); - Directory.CreateDirectory(newTargetDir); - CopyDirectory(dirPath, newTargetDir); - } - - foreach (string filePath in Directory.GetFiles(sourceDir, "*.*", SearchOption.TopDirectoryOnly)) - { - string newFilePath = Path.Combine(targetDir, Path.GetFileName(filePath)); - File.Copy(filePath, newFilePath, true); - } - } - - #endregion - - #region Private Methods - - /// - /// Recursively read all files in the folder path. - /// - private IEnumerable ReadFileNode(string path, string rootPath = null) - { - var resultFiles = new List(); - rootPath ??= path; - if (!rootPath.EndsWith("/")) - { - rootPath += "/"; - } - var rootUri = new Uri(rootPath); - - foreach (var subPath in Directory.EnumerateFiles(path)) - { - if (BlackListManager.Instance.IsBlacklisted(subPath)) continue; - - var hashAlgorithm = new Sha256HashAlgorithm(); - var hash = hashAlgorithm.ComputeHash(subPath); - var subFileInfo = new FileInfo(subPath); - var subUri = new Uri(subFileInfo.FullName); - resultFiles.Add(new FileNode - { - Id = GetId(), - Path = path, - Name = subFileInfo.Name, - Hash = hash, - FullName = subFileInfo.FullName, - RelativePath = rootUri.MakeRelativeUri(subUri).ToString() - }); - } - - foreach (var subPath in Directory.EnumerateDirectories(path)) - { - if (BlackListManager.Instance.IsSkipDirectory(subPath)) continue; - resultFiles.AddRange(ReadFileNode(subPath, rootPath)); - } - - return resultFiles; - } - - /// - /// Self-growing file tree node ID. - /// - private long GetId() => Interlocked.Increment(ref _fileCount); - - private void ResetId() => Interlocked.Exchange(ref _fileCount, 0); - - #endregion - } -} \ No newline at end of file diff --git a/src/c#/GeneralUpdate.Common/GeneralUpdate128.png b/src/c#/GeneralUpdate.Common/GeneralUpdate128.png deleted file mode 100644 index a50af55afcaa3c79c3f168c98b100613fcb7acff..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 19572 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 SHA256.Create(); - } -} \ No newline at end of file diff --git a/src/c#/GeneralUpdate.Common/Internal/Bootstrap/AbstractBootstrap.cs b/src/c#/GeneralUpdate.Common/Internal/Bootstrap/AbstractBootstrap.cs deleted file mode 100644 index 451031bc..00000000 --- a/src/c#/GeneralUpdate.Common/Internal/Bootstrap/AbstractBootstrap.cs +++ /dev/null @@ -1,66 +0,0 @@ -using System.Collections.Concurrent; -using System.Diagnostics; -using System.Threading.Tasks; -using GeneralUpdate.Common.Internal.Strategy; -using GeneralUpdate.Common.Shared; -using GeneralUpdate.Common.Shared.Object; - -namespace GeneralUpdate.Common.Internal.Bootstrap -{ - public abstract class AbstractBootstrap - where TBootstrap : AbstractBootstrap - where TStrategy : IStrategy - { - private readonly ConcurrentDictionary _options; - - protected internal AbstractBootstrap() => - _options = new ConcurrentDictionary(); - - /// - /// Launch async udpate. - /// - /// - public abstract Task LaunchAsync(); - - protected abstract void ExecuteStrategy(); - - protected abstract Task ExecuteStrategyAsync(); - - protected abstract TBootstrap StrategyFactory(); - - /// - /// Setting update configuration. - /// - /// - /// Configuration Action Enumeration. - /// Value - /// - public TBootstrap Option(UpdateOption option, T value) - { - if (value == null) - { - _options.TryRemove(option, out _); - } - else - { - _options[option] = new UpdateOptionValue(option, value); - } - return (TBootstrap)this; - } - - protected T? GetOption(UpdateOption? option) - { - try - { - Debug.Assert(option != null && _options.Count != 0); - var val = _options[option]; - if (val != null) return (T)val.GetValue(); - return default; - } - catch - { - return default; - } - } - } -} \ No newline at end of file diff --git a/src/c#/GeneralUpdate.Common/Internal/Bootstrap/Environments.cs b/src/c#/GeneralUpdate.Common/Internal/Bootstrap/Environments.cs deleted file mode 100644 index 0cb417e1..00000000 --- a/src/c#/GeneralUpdate.Common/Internal/Bootstrap/Environments.cs +++ /dev/null @@ -1,24 +0,0 @@ -using System.IO; - -namespace GeneralUpdate.Common.Internal.Bootstrap; - -public static class Environments -{ - public static void SetEnvironmentVariable(string key, string value) - { - var filePath = Path.Combine(Path.GetTempPath(), $"{key}.txt"); - File.WriteAllText(filePath, value); - } - - public static string GetEnvironmentVariable(string key) - { - var filePath = Path.Combine(Path.GetTempPath(), $"{key}.txt"); - if (!File.Exists(filePath)) - return string.Empty; - - var content = File.ReadAllText(filePath); - File.SetAttributes(filePath, FileAttributes.Normal); - File.Delete(filePath); - return content; - } -} \ No newline at end of file diff --git a/src/c#/GeneralUpdate.Common/Internal/Bootstrap/UpdateOption.cs b/src/c#/GeneralUpdate.Common/Internal/Bootstrap/UpdateOption.cs deleted file mode 100644 index 9fdb174d..00000000 --- a/src/c#/GeneralUpdate.Common/Internal/Bootstrap/UpdateOption.cs +++ /dev/null @@ -1,240 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Diagnostics.Contracts; -using System.Text; -using System.Threading; -using GeneralUpdate.Common.Shared.Object.Enum; - -namespace GeneralUpdate.Common.Internal.Bootstrap -{ - public abstract class UpdateOption : AbstractConstant - { - private class UpdateOptionPool : ConstantPool - { - protected override IConstant NewConstant(int id, string name) => new UpdateOption(id, name); - } - - private static readonly UpdateOptionPool Pool = new(); - - public static UpdateOption ValueOf(string name) => (UpdateOption)Pool.ValueOf(name); - - /// - /// Update the file format of the package. - /// - public static readonly UpdateOption Format = ValueOf("COMPRESSFORMAT"); - - /// - /// Compress encoding. - /// - public static readonly UpdateOption Encoding = ValueOf("COMPRESSENCODING"); - - /// - /// Timeout period (unit: second). If this parameter is not specified, the default timeout period is 30 seconds. - /// - public static readonly UpdateOption DownloadTimeOut = ValueOf("DOWNLOADTIMEOUT"); - - /// - /// Whether to enable the driver upgrade function. - /// - public static readonly UpdateOption Drive = ValueOf("DRIVE"); - - /// - /// Whether to enable the patch function. - /// - public static readonly UpdateOption Patch = ValueOf("PATCH"); - - /// - /// Whether to enable the backup function. - /// - public static readonly UpdateOption BackUp = ValueOf("BACKUP"); - - /// - /// Specifies the update execution mode. - /// - public static readonly UpdateOption Mode = ValueOf("MODE"); - - /// - /// Whether to enable silent update mode. - /// - public static readonly UpdateOption EnableSilentUpdate = ValueOf("ENABLESILENTUPDATE"); - - internal UpdateOption(int id, string name) - : base(id, name) { } - - public abstract bool Set(IUpdateConfiguration configuration, object value); - } - - public sealed class UpdateOption : UpdateOption - { - internal UpdateOption(int id, string name) - : base(id, name) - { - } - - public void Validate(T value) => Contract.Requires(value != null); - - public override bool Set(IUpdateConfiguration configuration, object value) => configuration.SetOption(this, (T)value); - } - - public abstract class ConstantPool - { - private readonly Dictionary constants = new Dictionary(); - private int nextId = 1; - - /// Shortcut of this.ValueOf(firstNameComponent.Name + "#" + secondNameComponent). - public IConstant ValueOf(Type firstNameComponent, string secondNameComponent) - { - Contract.Requires(firstNameComponent != null); - Contract.Requires(secondNameComponent != null); - return this.ValueOf(firstNameComponent.Name + '#' + secondNameComponent); - } - - /// - /// Returns the which is assigned to the specified name. - /// If there's no such , a new one will be created and returned. - /// Once created, the subsequent calls with the same name will always return the previously created one - /// (i.e. singleton.) - /// - /// the name of the - public IConstant ValueOf(string name) - { - IConstant constant; - lock (this.constants) - { - if (this.constants.TryGetValue(name, out constant)) - { - return constant; - } - else - { - constant = this.NewInstance0(name); - } - } - return constant; - } - - /// Returns true if a exists for the given name. - public bool Exists(string name) - { - CheckNotNullAndNotEmpty(name); - lock (this.constants) - return this.constants.ContainsKey(name); - } - - /// - /// Creates a new for the given name or fail with an - /// if a for the given name exists. - /// - public IConstant NewInstance(string name) - { - if (this.Exists(name)) throw new ArgumentException($"'{name}' is already in use"); - IConstant constant = this.NewInstance0(name); - return constant; - } - - // Be careful that this dose not check whether the argument is null or empty. - private IConstant NewInstance0(string name) - { - lock (this.constants) - { - IConstant constant = this.NewConstant(this.nextId, name); - this.constants[name] = constant; - this.nextId++; - return constant; - } - } - - private static void CheckNotNullAndNotEmpty(string name) => Contract.Requires(!string.IsNullOrEmpty(name)); - - protected abstract IConstant NewConstant(int id, string name); - - [Obsolete] - public int NextId() - { - lock (this.constants) - { - int id = this.nextId; - this.nextId++; - return id; - } - } - } - - public interface IConstant - { - /// Returns the unique number assigned to this . - int Id { get; } - - /// Returns the name of this . - string Name { get; } - } - - public interface IUpdateConfiguration - { - T GetOption(UpdateOption option); - - bool SetOption(UpdateOption option, object value); - - bool SetOption(UpdateOption option, T value); - } - - public abstract class AbstractConstant : IConstant - { - private static long nextUniquifier; - private long volatileUniquifier; - - protected AbstractConstant(int id, string name) - { - this.Id = id; - this.Name = name; - } - - public int Id { get; } - - public string Name { get; } - - public override sealed string ToString() => this.Name; - - protected long Uniquifier - { - get - { - long result; - if ((result = Volatile.Read(ref this.volatileUniquifier)) == 0) - { - result = Interlocked.Increment(ref nextUniquifier); - long previousUniquifier = Interlocked.CompareExchange(ref this.volatileUniquifier, result, 0); - if (previousUniquifier != 0) result = previousUniquifier; - } - return result; - } - } - } - - public abstract class AbstractConstant : AbstractConstant, IComparable, IEquatable - where T : AbstractConstant - { - /// Creates a new instance. - protected AbstractConstant(int id, string name) - : base(id, name) { } - - public override sealed int GetHashCode() => base.GetHashCode(); - - public override sealed bool Equals(object obj) => base.Equals(obj); - - public bool Equals(T other) => ReferenceEquals(this, other); - - public int CompareTo(T o) - { - if (ReferenceEquals(this, o)) return 0; - AbstractConstant other = o; - int returnCode = this.GetHashCode() - other.GetHashCode(); - if (returnCode != 0) return returnCode; - long thisUV = this.Uniquifier; - long otherUV = other.Uniquifier; - if (thisUV < otherUV) return -1; - if (thisUV > otherUV) return 1; - throw new System.Exception("failed to compare two different constants"); - } - } -} diff --git a/src/c#/GeneralUpdate.Common/Internal/Bootstrap/UpdateOptionValue.cs b/src/c#/GeneralUpdate.Common/Internal/Bootstrap/UpdateOptionValue.cs deleted file mode 100644 index 11c049e0..00000000 --- a/src/c#/GeneralUpdate.Common/Internal/Bootstrap/UpdateOptionValue.cs +++ /dev/null @@ -1,29 +0,0 @@ -namespace GeneralUpdate.Common.Internal.Bootstrap -{ - public abstract class UpdateOptionValue - { - public abstract UpdateOption Option { get; } - - public abstract bool Set(IUpdateConfiguration config); - - public abstract object GetValue(); - } - - public sealed class UpdateOptionValue : UpdateOptionValue - { - public override UpdateOption Option { get; } - private readonly T value; - - public UpdateOptionValue(UpdateOption option, T value) - { - this.Option = option; - this.value = value; - } - - public override object GetValue() => this.value; - - public override bool Set(IUpdateConfiguration config) => config.SetOption(this.Option, this.value); - - public override string ToString() => this.value.ToString(); - } -} \ No newline at end of file diff --git a/src/c#/GeneralUpdate.Common/Internal/Event/EventManager.cs b/src/c#/GeneralUpdate.Common/Internal/Event/EventManager.cs deleted file mode 100644 index 94f0a099..00000000 --- a/src/c#/GeneralUpdate.Common/Internal/Event/EventManager.cs +++ /dev/null @@ -1,107 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Diagnostics; -using GeneralUpdate.Common.Shared; - -namespace GeneralUpdate.Common.Internal.Event -{ - public class EventManager : IDisposable - { - private static readonly object _lockObj = new(); - private static EventManager _instance; - private Dictionary _dicDelegates = new(); - private bool _disposed = false; - - private EventManager() { } - - public static EventManager Instance - { - get - { - if (_instance == null) - { - lock (_lockObj) - { - if (_instance == null) - _instance = new EventManager(); - } - } - return _instance; - } - } - - public void AddListener(Action listener) where TEventArgs : EventArgs - { - try - { - if (listener == null) throw new ArgumentNullException(nameof(listener)); - var delegateType = typeof(Action); - if (_dicDelegates.ContainsKey(delegateType)) - { - _dicDelegates[delegateType] = Delegate.Combine(_dicDelegates[delegateType], listener); - } - else - { - _dicDelegates.Add(delegateType, listener); - } - } - catch (Exception e) - { - GeneralTracer.Error("The AddListener method in the EventManager class throws an exception.", e); - } - } - - public void RemoveListener(Action listener) where TEventArgs : EventArgs - { - try - { - if (listener == null) throw new ArgumentNullException(nameof(listener)); - var delegateType = typeof(Action); - if (_dicDelegates.TryGetValue(delegateType, out var existingDelegate)) - { - _dicDelegates[delegateType] = Delegate.Remove(existingDelegate, listener); - } - } - catch (Exception e) - { - GeneralTracer.Error("The RemoveListener method in the EventManager class throws an exception.", e); - } - } - - public void Dispatch(object sender, TEventArgs eventArgs) where TEventArgs : EventArgs - { - try - { - if (sender == null) throw new ArgumentNullException(nameof(sender)); - if (eventArgs == null) throw new ArgumentNullException(nameof(eventArgs)); - var delegateType = typeof(Action); - if (_dicDelegates.TryGetValue(delegateType, out var existingDelegate)) - { - ((Action)existingDelegate)?.Invoke(sender, eventArgs); - } - } - catch (Exception e) - { - GeneralTracer.Error("The Dispatch method in the EventManager class throws an exception.", e); - } - } - - public void Clear() => _dicDelegates.Clear(); - - public void Dispose() - { - try - { - if (!this._disposed) - { - _dicDelegates.Clear(); - _disposed = true; - } - } - catch (Exception e) - { - GeneralTracer.Error("The Dispose method in the EventManager class throws an exception.", e); - } - } - } -} \ No newline at end of file diff --git a/src/c#/GeneralUpdate.Common/Internal/Event/IEventManager.cs b/src/c#/GeneralUpdate.Common/Internal/Event/IEventManager.cs deleted file mode 100644 index 34e017c4..00000000 --- a/src/c#/GeneralUpdate.Common/Internal/Event/IEventManager.cs +++ /dev/null @@ -1,37 +0,0 @@ -using System; - -namespace GeneralUpdate.Common.Internal.Event -{ - /// - /// Event manager interface. - /// - public interface IEventManager - { - /// - /// Adding Event Listeners. - /// - /// Generic delegate. - /// New delegate that needs to be injected. - void AddListener(TDelegate newDelegate) where TDelegate : Delegate; - - /// - /// Removing Event Listening. - /// - /// Generic delegate. - /// Need to remove an existing delegate. - void RemoveListener(TDelegate oldDelegate) where TDelegate : Delegate; - - /// - /// Triggers notifications of the same event type based on the listening event type. - /// - /// generic delegate. - /// Event handler. - /// Event args. - void Dispatch(object sender, EventArgs eventArgs) where TDelegate : Delegate; - - /// - /// Remove all injected delegates. - /// - void Clear(); - } -} \ No newline at end of file diff --git a/src/c#/GeneralUpdate.Common/Internal/Exception/ExceptionEventArgs.cs b/src/c#/GeneralUpdate.Common/Internal/Exception/ExceptionEventArgs.cs deleted file mode 100644 index ab5634ad..00000000 --- a/src/c#/GeneralUpdate.Common/Internal/Exception/ExceptionEventArgs.cs +++ /dev/null @@ -1,9 +0,0 @@ -using System; - -namespace GeneralUpdate.Common.Internal; - -public class ExceptionEventArgs(Exception? exception = null, string? message = null) : EventArgs -{ - public Exception Exception { get; private set; } = exception; - public string Message { get; private set; } = message; -} \ No newline at end of file diff --git a/src/c#/GeneralUpdate.Common/Internal/JsonContext/FileNodesJsonContext.cs b/src/c#/GeneralUpdate.Common/Internal/JsonContext/FileNodesJsonContext.cs deleted file mode 100644 index b0ca8541..00000000 --- a/src/c#/GeneralUpdate.Common/Internal/JsonContext/FileNodesJsonContext.cs +++ /dev/null @@ -1,8 +0,0 @@ -using System.Collections.Generic; -using System.Text.Json.Serialization; -using GeneralUpdate.Common.FileBasic; - -namespace GeneralUpdate.Common.Internal.JsonContext; - -[JsonSerializable(typeof(List))] -public partial class FileNodesJsonContext : JsonSerializerContext; \ No newline at end of file diff --git a/src/c#/GeneralUpdate.Common/Internal/JsonContext/GlobalConfigInfoOSSJsonContext.cs b/src/c#/GeneralUpdate.Common/Internal/JsonContext/GlobalConfigInfoOSSJsonContext.cs deleted file mode 100644 index bcb9fbaa..00000000 --- a/src/c#/GeneralUpdate.Common/Internal/JsonContext/GlobalConfigInfoOSSJsonContext.cs +++ /dev/null @@ -1,7 +0,0 @@ -using System.Text.Json.Serialization; -using GeneralUpdate.Common.Shared.Object; - -namespace GeneralUpdate.Common.Internal.JsonContext; - -[JsonSerializable(typeof(GlobalConfigInfoOSS))] -public partial class GlobalConfigInfoOSSJsonContext : JsonSerializerContext; \ No newline at end of file diff --git a/src/c#/GeneralUpdate.Common/Internal/JsonContext/HttpParameterJsonContext.cs b/src/c#/GeneralUpdate.Common/Internal/JsonContext/HttpParameterJsonContext.cs deleted file mode 100644 index 2df040f0..00000000 --- a/src/c#/GeneralUpdate.Common/Internal/JsonContext/HttpParameterJsonContext.cs +++ /dev/null @@ -1,12 +0,0 @@ -using System.Collections.Generic; -using System.Text.Json.Serialization; - -namespace GeneralUpdate.Common.Internal.JsonContext; - -[JsonSerializable(typeof(bool))] -[JsonSerializable(typeof(bool?))] -[JsonSerializable(typeof(int))] -[JsonSerializable(typeof(int?))] -[JsonSerializable(typeof(string))] -[JsonSerializable(typeof(Dictionary))] -public partial class HttpParameterJsonContext: JsonSerializerContext; \ No newline at end of file diff --git a/src/c#/GeneralUpdate.Common/Internal/JsonContext/PacketJsonContext.cs b/src/c#/GeneralUpdate.Common/Internal/JsonContext/PacketJsonContext.cs deleted file mode 100644 index 7b64503d..00000000 --- a/src/c#/GeneralUpdate.Common/Internal/JsonContext/PacketJsonContext.cs +++ /dev/null @@ -1,7 +0,0 @@ -using System.Text.Json.Serialization; -using GeneralUpdate.Common.Shared.Object; - -namespace GeneralUpdate.Common.Internal.JsonContext; - -[JsonSerializable(typeof(Packet))] -public partial class PacketJsonContext : JsonSerializerContext; \ No newline at end of file diff --git a/src/c#/GeneralUpdate.Common/Internal/JsonContext/ProcessInfoJsonContext.cs b/src/c#/GeneralUpdate.Common/Internal/JsonContext/ProcessInfoJsonContext.cs deleted file mode 100644 index ccd4f493..00000000 --- a/src/c#/GeneralUpdate.Common/Internal/JsonContext/ProcessInfoJsonContext.cs +++ /dev/null @@ -1,7 +0,0 @@ -using System.Text.Json.Serialization; -using GeneralUpdate.Common.Shared.Object; - -namespace GeneralUpdate.Common.Internal.JsonContext; - -[JsonSerializable(typeof(ProcessInfo))] -public partial class ProcessInfoJsonContext : JsonSerializerContext; \ No newline at end of file diff --git a/src/c#/GeneralUpdate.Common/Internal/JsonContext/ReportRespJsonContext.cs b/src/c#/GeneralUpdate.Common/Internal/JsonContext/ReportRespJsonContext.cs deleted file mode 100644 index bb6eeb96..00000000 --- a/src/c#/GeneralUpdate.Common/Internal/JsonContext/ReportRespJsonContext.cs +++ /dev/null @@ -1,7 +0,0 @@ -using System.Text.Json.Serialization; -using GeneralUpdate.Common.Shared.Object; - -namespace GeneralUpdate.Common.Internal.JsonContext; - -[JsonSerializable(typeof(BaseResponseDTO))] -public partial class ReportRespJsonContext : JsonSerializerContext; \ No newline at end of file diff --git a/src/c#/GeneralUpdate.Common/Internal/JsonContext/VersionOSSJsonContext.cs b/src/c#/GeneralUpdate.Common/Internal/JsonContext/VersionOSSJsonContext.cs deleted file mode 100644 index bd908333..00000000 --- a/src/c#/GeneralUpdate.Common/Internal/JsonContext/VersionOSSJsonContext.cs +++ /dev/null @@ -1,8 +0,0 @@ -using System.Collections.Generic; -using System.Text.Json.Serialization; -using GeneralUpdate.Common.Shared.Object; - -namespace GeneralUpdate.Common.Internal.JsonContext; - -[JsonSerializable(typeof(List))] -public partial class VersionOSSJsonContext : JsonSerializerContext; \ No newline at end of file diff --git a/src/c#/GeneralUpdate.Common/Internal/JsonContext/VersionRespJsonContext.cs b/src/c#/GeneralUpdate.Common/Internal/JsonContext/VersionRespJsonContext.cs deleted file mode 100644 index c6aa0069..00000000 --- a/src/c#/GeneralUpdate.Common/Internal/JsonContext/VersionRespJsonContext.cs +++ /dev/null @@ -1,7 +0,0 @@ -using System.Text.Json.Serialization; -using GeneralUpdate.Common.Shared.Object; - -namespace GeneralUpdate.Common.Internal.JsonContext; - -[JsonSerializable(typeof(VersionRespDTO))] -public partial class VersionRespJsonContext : JsonSerializerContext; \ No newline at end of file diff --git a/src/c#/GeneralUpdate.Common/Internal/Pipeline/IMiddleware.cs b/src/c#/GeneralUpdate.Common/Internal/Pipeline/IMiddleware.cs deleted file mode 100644 index 606b1c7c..00000000 --- a/src/c#/GeneralUpdate.Common/Internal/Pipeline/IMiddleware.cs +++ /dev/null @@ -1,12 +0,0 @@ -using System.Threading.Tasks; - -namespace GeneralUpdate.Common.Internal.Pipeline -{ - /// - /// Pipeline middleware. - /// - public interface IMiddleware - { - Task InvokeAsync(PipelineContext context); - } -} \ No newline at end of file diff --git a/src/c#/GeneralUpdate.Common/Internal/Pipeline/PipelineBuilder.cs b/src/c#/GeneralUpdate.Common/Internal/Pipeline/PipelineBuilder.cs deleted file mode 100644 index f8e45ca5..00000000 --- a/src/c#/GeneralUpdate.Common/Internal/Pipeline/PipelineBuilder.cs +++ /dev/null @@ -1,43 +0,0 @@ -using System; -using System.Collections.Immutable; -using System.Threading.Tasks; - -namespace GeneralUpdate.Common.Internal.Pipeline -{ - /// - /// Pipeline builder. - /// - public sealed class PipelineBuilder(PipelineContext context) - { - /// - /// LIFO,Last In First Out. - /// - private ImmutableStack _middlewareStack = ImmutableStack.Empty; - - public PipelineBuilder UseMiddleware() where TMiddleware : IMiddleware, new() - { - var middleware = new TMiddleware(); - _middlewareStack = _middlewareStack.Push(middleware); - return this; - } - - public PipelineBuilder UseMiddlewareIf(bool? condition) - where TMiddleware : IMiddleware, new() - { - if (condition is null or false) - return this; - - var middleware = new TMiddleware(); - _middlewareStack = _middlewareStack.Push(middleware); - return this; - } - - public async Task Build() - { - foreach (var middleware in _middlewareStack) - { - await middleware.InvokeAsync(context); - } - } - } -} \ No newline at end of file diff --git a/src/c#/GeneralUpdate.Common/Internal/Pipeline/PipelineContext.cs b/src/c#/GeneralUpdate.Common/Internal/Pipeline/PipelineContext.cs deleted file mode 100644 index 8a85686f..00000000 --- a/src/c#/GeneralUpdate.Common/Internal/Pipeline/PipelineContext.cs +++ /dev/null @@ -1,32 +0,0 @@ -using System; -using System.Collections.Concurrent; - -namespace GeneralUpdate.Common.Internal.Pipeline; - -public class PipelineContext -{ - private ConcurrentDictionary _context = new(); - - public TValue? Get(string key) - { - if (_context.TryGetValue(key, out var value)) - { - return value is TValue typedValue ? typedValue : default; - } - return default; - } - - public void Add(string key, TValue? value) - { - if (string.IsNullOrWhiteSpace(key)) - { - throw new ArgumentException("Key cannot be null or whitespace.", nameof(key)); - } - - _context[key] = value; - } - - public bool Remove(string key) => _context.TryRemove(key, out _); - - public bool ContainsKey(string key) => _context.ContainsKey(key); -} \ No newline at end of file diff --git a/src/c#/GeneralUpdate.Common/Internal/Strategy/AbstractStrategy.cs b/src/c#/GeneralUpdate.Common/Internal/Strategy/AbstractStrategy.cs deleted file mode 100644 index dc52f927..00000000 --- a/src/c#/GeneralUpdate.Common/Internal/Strategy/AbstractStrategy.cs +++ /dev/null @@ -1,131 +0,0 @@ -using System; -using System.IO; -using System.Threading.Tasks; -using GeneralUpdate.Common.FileBasic; -using GeneralUpdate.Common.Internal.Event; -using GeneralUpdate.Common.Internal.Pipeline; -using GeneralUpdate.Common.Shared; -using GeneralUpdate.Common.Shared.Object; -using GeneralUpdate.Common.Shared.Object.Enum; -using GeneralUpdate.Common.Shared.Service; - -namespace GeneralUpdate.Common.Internal.Strategy -{ - public abstract class AbstractStrategy : IStrategy - { - private const string Patchs = "patchs"; - protected GlobalConfigInfo _configinfo = new(); - - public virtual void Execute() => throw new NotImplementedException(); - - public virtual void StartApp() => throw new NotImplementedException(); - - public virtual async Task ExecuteAsync() - { - try - { - var status = ReportType.None; - var patchPath = StorageManager.GetTempDirectory(Patchs); - foreach (var version in _configinfo.UpdateVersions) - { - try - { - var context = CreatePipelineContext(version, patchPath); - var pipelineBuilder = BuildPipeline(context); - await pipelineBuilder.Build(); - status = ReportType.Success; - } - catch (Exception e) - { - status = ReportType.Failure; - HandleExecuteException(e); - } - finally - { - await VersionService.Report(_configinfo.ReportUrl - , version.RecordId - , status - , version.AppType - , _configinfo.Scheme - , _configinfo.Token); - } - } - - Clear(patchPath); - Clear(_configinfo.TempPath); - OnExecuteComplete(); - } - catch (Exception e) - { - HandleExecuteException(e); - } - } - - public virtual void Create(GlobalConfigInfo parameter) => _configinfo = parameter; - - /// - /// Creates the pipeline context with common and platform-specific parameters. - /// Override this method to add platform-specific context parameters. - /// - protected virtual PipelineContext CreatePipelineContext(VersionInfo version, string patchPath) - { - var context = new PipelineContext(); - // Common parameters - context.Add("ZipFilePath", Path.Combine(_configinfo.TempPath, $"{version.Name}{_configinfo.Format}")); - // Hash middleware - context.Add("Hash", version.Hash); - // Zip middleware - context.Add("Format", _configinfo.Format); - context.Add("Name", version.Name); - context.Add("Encoding", _configinfo.Encoding); - // Patch middleware - context.Add("SourcePath", _configinfo.InstallPath); - context.Add("PatchPath", patchPath); - context.Add("PatchEnabled", _configinfo.PatchEnabled); - - return context; - } - - /// - /// Builds the pipeline with middleware components. - /// Override this method to customize the pipeline for specific platforms. - /// - protected abstract PipelineBuilder BuildPipeline(PipelineContext context); - - /// - /// Called after ExecuteAsync completes successfully. - /// Override this method to add platform-specific post-execution logic. - /// - protected virtual void OnExecuteComplete() - { - // Default implementation does nothing - } - - /// - /// Handles exceptions during execution. - /// Override this method to customize error handling. - /// - protected virtual void HandleExecuteException(Exception e) - { - GeneralTracer.Error($"Strategy execution exception.", e); - EventManager.Instance.Dispatch(this, new ExceptionEventArgs(e, e.Message)); - } - - /// - /// Checks if a file exists at the specified path. - /// - protected static string CheckPath(string path, string name) - { - if (string.IsNullOrWhiteSpace(path) || string.IsNullOrWhiteSpace(name)) - return string.Empty; - var tempPath = Path.Combine(path, name); - return File.Exists(tempPath) ? tempPath : string.Empty; - } - - private static void Clear(string path) - { - if (Directory.Exists(path)) - StorageManager.DeleteDirectory(path); - } - } -} \ No newline at end of file diff --git a/src/c#/GeneralUpdate.Common/Internal/Strategy/IStrategy.cs b/src/c#/GeneralUpdate.Common/Internal/Strategy/IStrategy.cs deleted file mode 100644 index bc583b71..00000000 --- a/src/c#/GeneralUpdate.Common/Internal/Strategy/IStrategy.cs +++ /dev/null @@ -1,31 +0,0 @@ -using System.Threading.Tasks; -using GeneralUpdate.Common.Shared.Object; - -namespace GeneralUpdate.Common.Internal.Strategy -{ - /// - /// Update the strategy, if you need to extend it, you need to inherit this interface. - /// - public interface IStrategy - { - /// - /// Execution strategy. - /// - void Execute(); - - /// - /// After the update is complete. - /// - void StartApp(); - - /// - /// Execution strategy. - /// - Task ExecuteAsync(); - - /// - /// Create a strategy. - /// - void Create(GlobalConfigInfo parameter); - } -} \ No newline at end of file diff --git a/src/c#/GeneralUpdate.Common/Shared/Object/BaseConfigInfo.cs b/src/c#/GeneralUpdate.Common/Shared/Object/BaseConfigInfo.cs deleted file mode 100644 index 681c6096..00000000 --- a/src/c#/GeneralUpdate.Common/Shared/Object/BaseConfigInfo.cs +++ /dev/null @@ -1,105 +0,0 @@ -using System; -using System.Collections.Generic; - -namespace GeneralUpdate.Common.Shared.Object -{ - /// - /// Base configuration class containing common fields shared across all configuration objects. - /// This class serves as the foundation for user-facing configuration (Configinfo), - /// internal runtime state (GlobalConfigInfo), and inter-process communication (ProcessInfo). - /// - public abstract class BaseConfigInfo - { - /// - /// The name of the application that needs to be started after update. - /// This is the executable name without extension (e.g., "MyApp" for MyApp.exe). - /// Default value is "Update.exe". - /// - public string AppName { get; set; } = "Update.exe"; - - /// - /// The name of the main application without file extension. - /// Used to identify the primary application process that will be updated. - /// - public string MainAppName { get; set; } - - /// - /// The installation path where application files are located. - /// This is the root directory used for update file operations. - /// Default value is the current program's running directory. - /// - public string InstallPath { get; set; } = AppDomain.CurrentDomain.BaseDirectory; - - /// - /// The URL address for the update log webpage. - /// Users can view detailed changelog information at this address. - /// - public string UpdateLogUrl { get; set; } - - /// - /// The application secret key used for authentication. - /// This key is validated when requesting update information from the server. - /// - public string AppSecretKey { get; set; } - - /// - /// The current version of the client application. - /// Format should follow semantic versioning (e.g., "1.0.0"). - /// - public string ClientVersion { get; set; } - - /// - /// List of specific files that should be excluded from the update process. - /// Files in this blacklist will be skipped during update operations. - /// - public List BlackFiles { get; set; } - - /// - /// List of file format extensions that should be excluded from the update process. - /// For example: [".log", ".tmp", ".cache"] will skip all files with these extensions. - /// - public List BlackFormats { get; set; } - - /// - /// List of directory paths that should be skipped during the update process. - /// Entire directories in this list will be ignored during update operations. - /// - public List SkipDirectorys { get; set; } - - /// - /// The API endpoint URL for reporting update status and results. - /// Update progress and completion status will be sent to this URL. - /// - public string ReportUrl { get; set; } - - /// - /// The process name that should be terminated before starting the update. - /// This is typically used to close conflicting processes (e.g., "Bowl" process). - /// - public string Bowl { get; set; } - - /// - /// The URL scheme used for update requests (e.g., "http" or "https"). - /// This determines the protocol used for server communication. - /// - public string Scheme { get; set; } - - /// - /// The authentication token used for API requests. - /// This token is included in HTTP headers when communicating with the update server. - /// - public string Token { get; set; } - - /// - /// Shell script content used to grant file permissions on Linux/Unix systems. - /// This script is executed after update to ensure proper file permissions. - /// - public string Script { get; set; } - - /// - /// The directory path containing driver files for driver update functionality. - /// Used when DriveEnabled is true to locate and install driver files during updates. - /// - public string DriverDirectory { get; set; } - } -} diff --git a/src/c#/GeneralUpdate.Common/Shared/Object/Configinfo.cs b/src/c#/GeneralUpdate.Common/Shared/Object/Configinfo.cs deleted file mode 100644 index 855d17d0..00000000 --- a/src/c#/GeneralUpdate.Common/Shared/Object/Configinfo.cs +++ /dev/null @@ -1,55 +0,0 @@ -using System; -using System.Collections.Generic; - -namespace GeneralUpdate.Common.Shared.Object -{ - /// - /// User-facing configuration class for update parameters. - /// This class is designed for external API consumers to configure update behavior. - /// Inherits common fields from BaseConfigInfo to reduce duplication and improve maintainability. - /// - public class Configinfo : BaseConfigInfo - { - /// - /// The API endpoint URL for checking available updates. - /// The client queries this URL to determine if new versions are available. - /// - public string UpdateUrl { get; set; } - - /// - /// The current version of the upgrade application (the updater itself). - /// This allows the updater tool to be updated separately from the main application. - /// - public string UpgradeClientVersion { get; set; } - - /// - /// The unique product identifier used for tracking and update management. - /// Multiple products can share the same update infrastructure using different IDs. - /// - public string ProductId { get; set; } - - public void Validate() - { - if (string.IsNullOrWhiteSpace(UpdateUrl) || !Uri.IsWellFormedUriString(UpdateUrl, UriKind.Absolute)) - throw new ArgumentException("Invalid UpdateUrl"); - - if (!string.IsNullOrWhiteSpace(UpdateLogUrl) && !Uri.IsWellFormedUriString(UpdateLogUrl, UriKind.Absolute)) - throw new ArgumentException("Invalid UpdateLogUrl"); - - if (string.IsNullOrWhiteSpace(AppName)) - throw new ArgumentException("AppName cannot be empty"); - - if (string.IsNullOrWhiteSpace(MainAppName)) - throw new ArgumentException("MainAppName cannot be empty"); - - if (string.IsNullOrWhiteSpace(AppSecretKey)) - throw new ArgumentException("AppSecretKey cannot be empty"); - - if (string.IsNullOrWhiteSpace(ClientVersion)) - throw new ArgumentException("ClientVersion cannot be empty"); - - if (string.IsNullOrWhiteSpace(InstallPath)) - throw new ArgumentException("InstallPath cannot be empty"); - } - } -} \ No newline at end of file diff --git a/src/c#/GeneralUpdate.Common/Shared/Object/ConfiginfoBuilder-Example.cs b/src/c#/GeneralUpdate.Common/Shared/Object/ConfiginfoBuilder-Example.cs deleted file mode 100644 index bda8fac9..00000000 --- a/src/c#/GeneralUpdate.Common/Shared/Object/ConfiginfoBuilder-Example.cs +++ /dev/null @@ -1,118 +0,0 @@ -using System; -using System.Collections.Generic; -using System.IO; -using GeneralUpdate.Common.Shared.Object; - -namespace ConfiginfoBuilderExample -{ - /// - /// Example demonstrating the ConfiginfoBuilder usage with JSON configuration - /// - class Program - { - static void Main(string[] args) - { - Console.WriteLine("=== ConfiginfoBuilder Usage Examples ===\n"); - - // Example 1: Load configuration from JSON file (recommended) - Console.WriteLine("Example 1: Loading from update_config.json file"); - Console.WriteLine("This example requires an update_config.json file in the running directory."); - Console.WriteLine("The configuration file has the highest priority and must contain all required settings.\n"); - - try - { - // Create update_config.json for demonstration - CreateExampleConfigFile(); - - // Simply call Create() with no parameters - it loads from update_config.json - var config = ConfiginfoBuilder.Create().Build(); - - Console.WriteLine($" UpdateUrl: {config.UpdateUrl}"); - Console.WriteLine($" Token: {config.Token}"); - Console.WriteLine($" Scheme: {config.Scheme}"); - Console.WriteLine($" InstallPath: {config.InstallPath}"); - Console.WriteLine($" AppName: {config.AppName}"); - Console.WriteLine($" ClientVersion: {config.ClientVersion}"); - Console.WriteLine(); - } - catch (FileNotFoundException ex) - { - Console.WriteLine($" Error: {ex.Message}"); - Console.WriteLine(" Please create update_config.json in the running directory."); - Console.WriteLine(); - } - finally - { - CleanupExampleConfigFile(); - } - - // Example 2: Customizing configuration after loading from file - Console.WriteLine("Example 2: Loading from JSON and customizing with method chaining"); - try - { - CreateExampleConfigFile(); - - var customConfig = ConfiginfoBuilder.Create() - .SetAppName("CustomApp.exe") - .SetInstallPath("/custom/path") - .Build(); - - Console.WriteLine($" AppName: {customConfig.AppName}"); - Console.WriteLine($" InstallPath: {customConfig.InstallPath}"); - Console.WriteLine(); - } - catch (FileNotFoundException ex) - { - Console.WriteLine($" Error: {ex.Message}"); - Console.WriteLine(); - } - finally - { - CleanupExampleConfigFile(); - } - - // Example 3: Error handling when config file is missing - Console.WriteLine("Example 3: Error Handling - Missing Configuration File"); - try - { - var config = ConfiginfoBuilder.Create().Build(); - } - catch (FileNotFoundException ex) - { - Console.WriteLine($" Caught expected error: {ex.Message}"); - Console.WriteLine(" This is expected when update_config.json doesn't exist."); - } - Console.WriteLine(); - - Console.WriteLine("\n=== All Examples Completed! ==="); - Console.WriteLine("\nNote: ConfiginfoBuilder now requires update_config.json file."); - Console.WriteLine("See update_config.example.json for a complete example."); - } - - private static void CreateExampleConfigFile() - { - var configPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "update_config.json"); - var exampleConfig = @"{ - ""UpdateUrl"": ""https://api.example.com/updates"", - ""Token"": ""example-auth-token"", - ""Scheme"": ""https"", - ""AppName"": ""Update.exe"", - ""MainAppName"": ""MyApplication.exe"", - ""ClientVersion"": ""1.0.0"", - ""UpgradeClientVersion"": ""1.0.0"", - ""AppSecretKey"": ""example-secret-key"", - ""ProductId"": ""example-product-id"" -}"; - File.WriteAllText(configPath, exampleConfig); - } - - private static void CleanupExampleConfigFile() - { - var configPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "update_config.json"); - if (File.Exists(configPath)) - { - File.Delete(configPath); - } - } - } -} diff --git a/src/c#/GeneralUpdate.Common/Shared/Object/ConfiginfoBuilder-Usage.md b/src/c#/GeneralUpdate.Common/Shared/Object/ConfiginfoBuilder-Usage.md deleted file mode 100644 index 9e8a0dfc..00000000 --- a/src/c#/GeneralUpdate.Common/Shared/Object/ConfiginfoBuilder-Usage.md +++ /dev/null @@ -1,366 +0,0 @@ -# ConfiginfoBuilder Usage Guide - -The `ConfiginfoBuilder` class provides a simple and convenient way to create `Configinfo` objects for the GeneralUpdate system. It supports both JSON-based configuration and programmatic configuration. - -**Design Philosophy**: Inspired by zero-configuration patterns from projects like [Velopack](https://github.com/velopack/velopack), this builder minimizes required configuration while maintaining flexibility through optional fluent setters. - -## Configuration Methods - -There are two main ways to configure the update system: - -### 1. JSON Configuration File (Recommended) - -Place an `update_config.json` file in your application's running directory. When this file exists, ConfiginfoBuilder automatically loads all settings from it, giving the configuration file the highest priority. - -**Example `update_config.json`:** -```json -{ - "UpdateUrl": "https://api.example.com/updates", - "Token": "your-authentication-token", - "Scheme": "https", - "AppName": "Update.exe", - "MainAppName": "MyApplication.exe", - "ClientVersion": "1.0.0", - "InstallPath": "/path/to/installation", - "BlackFormats": [".log", ".tmp", ".cache"], - "SkipDirectorys": ["/temp", "/logs"] -} -``` - -See [update_config.example.json](update_config.example.json) for a complete example with all available options. - -**Usage:** -```csharp -// The Create method will automatically load from update_config.json if it exists -var config = ConfiginfoBuilder - .Create("fallback-url", "fallback-token", "https") - .Build(); -// If update_config.json exists, all values come from the file -// Parameters are only used as fallback if file doesn't exist -``` - -### 2. Programmatic Configuration - -Configure the update system entirely through code using the fluent API: - -## Automatic Configuration Detection - -The ConfiginfoBuilder implements intelligent zero-configuration by automatically extracting information from your project: - -### Comprehensive Project Metadata Extraction - -The builder automatically attempts to read multiple fields from your project file (`.csproj`): - -1. **Application Name** (maps to `AppName`, `MainAppName`) - - **Extracts** the `` property if specified - - **Falls back** to the csproj file name if `AssemblyName` is not defined - - **Applies** platform-specific extensions (`.exe` on Windows, none on Linux/macOS) - -2. **Version Number** (maps to `ClientVersion`, `UpgradeClientVersion`) - - **Reads** the `` property from csproj - - **Falls back** to default "1.0.0" if not specified - -3. **Publisher Information** (maps to `ProductId`) - - **Prioritizes** the `` property - - **Falls back** to `` property if Company not specified - - **Uses** default value if neither is available - -**Example:** -```xml - - - - MyApplication - 2.1.5 - MyCompany - Development Team - - -``` - -When the builder runs, it will automatically extract: -- **App Name**: `MyApplication.exe` (Windows) or `MyApplication` (Linux/macOS) -- **Version**: `2.1.5` -- **Product ID**: `MyCompany` - -All automatic with zero configuration needed! - -## Basic Usage - -### Minimal Configuration - -The simplest way to create a `Configinfo` object is to provide just the three required parameters: - -```csharp -using GeneralUpdate.Common.Shared.Object; - -// Method 1: Direct constructor (traditional) -var config = new ConfiginfoBuilder( - updateUrl: "https://api.example.com/updates", - token: "your-auth-token", - scheme: "https" -).Build(); - -// Method 2: Factory method (recommended, more fluent) -var config2 = ConfiginfoBuilder - .Create("https://api.example.com/updates", "your-auth-token", "https") - .Build(); - -// The config object now has all necessary defaults set based on the platform -// Application name is automatically detected from your project file! -``` - -## Platform-Specific Defaults - -The builder automatically detects the runtime platform and sets appropriate defaults: - -### Windows Platform -- **Install Path**: Current application's base directory (via `AppDomain.CurrentDomain.BaseDirectory`) -- **App Names**: Auto-detected from csproj + `.exe` extension (e.g., `MyApp.exe`) -- **Script**: Empty (Windows doesn't typically need permission scripts) -- **Path Separator**: Backslash (`\`) - handled automatically by .NET -- **Black Formats**: `.log`, `.tmp` (from `ConfiginfoBuilder.DefaultBlackFormats`) - -### Linux Platform -- **Install Path**: Current application's base directory (via `AppDomain.CurrentDomain.BaseDirectory`) -- **App Names**: Auto-detected from csproj, no `.exe` extension (e.g., `myapp`) -- **Script**: Default chmod script for granting execution permissions -- **Path Separator**: Forward slash (`/`) - handled automatically by .NET -- **Black Formats**: `.log`, `.tmp` (from `ConfiginfoBuilder.DefaultBlackFormats`) - -### macOS Platform -- **Install Path**: Current application's base directory (via `AppDomain.CurrentDomain.BaseDirectory`) -- **App Names**: Auto-detected from csproj, no `.exe` extension (e.g., `myapp`) -- **Script**: Default chmod script for granting execution permissions -- **Path Separator**: Forward slash (`/`) - handled automatically by .NET -- **Black Formats**: `.log`, `.tmp` (from `ConfiginfoBuilder.DefaultBlackFormats`) - -**Note**: The install path defaults to the current application's running directory, which does not require administrator privileges and automatically extracts the location from the host program. - -## Customizing Configuration - -You can override any default value using the fluent API: - -```csharp -var config = new ConfiginfoBuilder( - "https://api.example.com/updates", - "your-auth-token", - "https" -) -.SetAppName("MyApplication.exe") -.SetMainAppName("MyApplication.exe") -.SetClientVersion("1.5.0") -.SetInstallPath("/opt/myapp") -.SetAppSecretKey("my-secret-key") -.SetProductId("product-123") -.Build(); -``` - -## Advanced Configuration - -### Setting Update URLs - -```csharp -var config = new ConfiginfoBuilder(updateUrl, token, scheme) - .SetUpdateLogUrl("https://example.com/changelog") - .SetReportUrl("https://api.example.com/report") - .Build(); -``` - -### Configuring File Filters - -You can customize which files should be excluded from updates. By default, `.log` and `.tmp` files are excluded: - -```csharp -// Use default black formats -var config1 = new ConfiginfoBuilder(updateUrl, token, scheme).Build(); -// config1.BlackFormats will contain ConfiginfoBuilder.DefaultBlackFormats (.log, .tmp) - -// Override with custom filters -var config2 = new ConfiginfoBuilder(updateUrl, token, scheme) - .SetBlackFiles(new List { "config.json", "user.dat" }) - .SetBlackFormats(new List { ".log", ".tmp", ".cache", ".bak" }) - .SetSkipDirectorys(new List { "/temp", "/logs" }) - .Build(); -``` - -### Setting Process and Script Options - -```csharp -var config = new ConfiginfoBuilder(updateUrl, token, scheme) - .SetBowl("Bowl.exe") // Process to terminate before update - .SetScript("#!/bin/bash\nchmod +x \"$1\"") // Custom permission script - .SetDriverDirectory("/opt/myapp/drivers") - .Build(); -``` - -## Complete Example - -Here's a comprehensive example showing all available options: - -```csharp -using GeneralUpdate.Common.Shared.Object; -using System.Collections.Generic; - -public class UpdateConfigExample -{ - public static Configinfo CreateUpdateConfig() - { - var config = new ConfiginfoBuilder( - updateUrl: "https://api.example.com/updates", - token: "Bearer abc123xyz", - scheme: "https" - ) - // Application Info - .SetAppName("MyApp.exe") - .SetMainAppName("MyApp.exe") - .SetClientVersion("2.1.0") - .SetUpgradeClientVersion("1.0.0") - .SetProductId("myapp-001") - .SetAppSecretKey("secure-secret-key-789") - - // Paths - .SetInstallPath("/opt/myapp") - .SetDriverDirectory("/opt/myapp/drivers") - - // URLs - .SetUpdateLogUrl("https://myapp.example.com/changelog") - .SetReportUrl("https://api.example.com/report") - - // File Filters - .SetBlackFiles(new List - { - "config.json", - "user-settings.dat" - }) - .SetBlackFormats(new List - { - ".log", - ".tmp", - ".cache", - ".bak" - }) - .SetSkipDirectorys(new List - { - "/temp", - "/logs", - "/cache" - }) - - // Process Options - .SetBowl("Bowl.exe") - .SetScript("#!/bin/bash\nchmod +x \"$1\"\n") - - .Build(); - - // Validate the configuration - config.Validate(); - - return config; - } -} -``` - -## Error Handling - -The builder performs validation at two stages: - -### Construction Time -The constructor validates the three required parameters: - -```csharp -try -{ - var builder = new ConfiginfoBuilder( - null, // Invalid: null UpdateUrl - "token", - "https" - ); -} -catch (ArgumentException ex) -{ - Console.WriteLine($"Error: {ex.Message}"); - // Output: UpdateUrl cannot be null or empty. -} -``` - -### Build Time -The `Build()` method validates the complete configuration: - -```csharp -try -{ - var config = new ConfiginfoBuilder(updateUrl, token, scheme) - .SetAppName("") // Invalid: empty app name - .Build(); -} -catch (InvalidOperationException ex) -{ - Console.WriteLine($"Build Error: {ex.Message}"); - // Output: Failed to build valid Configinfo: AppName cannot be empty -} -``` - -## Best Practices - -1. **Use Minimal Configuration**: Only override defaults when necessary -2. **Validate Early**: Let the builder validate parameters immediately -3. **Use Method Chaining**: Take advantage of the fluent API for clean code -4. **Platform Awareness**: Let the builder handle platform-specific defaults -5. **Secure Tokens**: Never hardcode authentication tokens in production code - -## Migration from Manual Construction - -### Before (Manual Construction) -```csharp -var config = new Configinfo -{ - UpdateUrl = "https://api.example.com/updates", - Token = "token", - Scheme = "https", - AppName = "App.exe", - MainAppName = "App.exe", - ClientVersion = "1.0.0", - UpgradeClientVersion = "1.0.0", - InstallPath = Thread.GetDomain().BaseDirectory, - AppSecretKey = "secret", - ProductId = "product", - BlackFiles = new List(), - BlackFormats = new List { ".log", ".tmp" }, - SkipDirectorys = new List() -}; -``` - -### After (Using Builder) -```csharp -var config = new ConfiginfoBuilder( - "https://api.example.com/updates", - "token", - "https" -).Build(); // All defaults are set automatically! -``` - -## Platform Detection Example - -The builder automatically adapts to the runtime environment: - -```csharp -var config = new ConfiginfoBuilder(updateUrl, token, scheme).Build(); - -if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) -{ - Console.WriteLine($"Windows config: {config.InstallPath}"); - // Output: Windows config: C:\MyApp\ (current application directory) -} -else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux)) -{ - Console.WriteLine($"Linux config: {config.InstallPath}"); - // Output: Linux config: /opt/myapp/ (current application directory) -} -``` - -## See Also - -- `Configinfo` class documentation -- `BaseConfigInfo` class documentation -- GeneralUpdate main documentation diff --git a/src/c#/GeneralUpdate.Common/Shared/Object/ConfiginfoBuilder.cs b/src/c#/GeneralUpdate.Common/Shared/Object/ConfiginfoBuilder.cs deleted file mode 100644 index 924da8f5..00000000 --- a/src/c#/GeneralUpdate.Common/Shared/Object/ConfiginfoBuilder.cs +++ /dev/null @@ -1,413 +0,0 @@ -using System; -using System.Collections.Generic; -using System.IO; -using System.Text.Json; - -namespace GeneralUpdate.Common.Shared.Object -{ - /// - /// Universal ConfigInfo builder class that simplifies creation of update configurations. - /// Only requires three essential parameters (UpdateUrl, Token, Scheme) while automatically - /// generating platform-appropriate defaults for all other configuration items. - /// Inspired by zero-configuration design patterns from projects like Velopack. - /// - public class ConfiginfoBuilder - { - // Configurable default values - // Note: AppName and InstallPath defaults are set in Configinfo class itself - // These are ConfiginfoBuilder-specific defaults to support the builder pattern - private string _updateUrl; - private string _token; - private string _scheme; - private string _appName = "Update.exe"; - private string _mainAppName; - private string _clientVersion; - private string _upgradeClientVersion; - private string _appSecretKey; - private string _productId; - private string _installPath; - private string _updateLogUrl; - private string _reportUrl; - private string _bowl; - private string _script; - private string _driverDirectory; - private List _blackFiles; - private List _blackFormats; - private List _skipDirectorys; - - /// - /// Creates a new ConfiginfoBuilder instance by loading configuration from update_config.json file. - /// The configuration file must exist in the running directory and contain all required settings. - /// Configuration file has the highest priority - all settings must be specified in the JSON file. - /// - /// A new ConfiginfoBuilder instance with settings loaded from the configuration file. - /// Thrown when update_config.json is not found. - /// Thrown when the configuration file is invalid or cannot be loaded. - public static ConfiginfoBuilder Create() - { - // Try to load from configuration file - var configFromFile = LoadFromConfigFile(); - if (configFromFile != null) - { - // Configuration file loaded successfully - return configFromFile; - } - - // If no config file exists, throw an exception - throw new FileNotFoundException("Configuration file 'update_config.json' not found in the running directory. Please create this file with the required settings."); - } - - /// - /// Loads configuration from update_config.json file in the running directory. - /// - /// ConfiginfoBuilder with settings from file, or null if file doesn't exist or is invalid. - private static ConfiginfoBuilder LoadFromConfigFile() - { - try - { - var configPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "update_config.json"); - if (!File.Exists(configPath)) - { - return null; - } - - var json = File.ReadAllText(configPath); - var config = JsonSerializer.Deserialize(json, new JsonSerializerOptions - { - PropertyNameCaseInsensitive = true - }); - - if (config == null) - { - return null; - } - - // Create a builder with the loaded configuration - var builder = new ConfiginfoBuilder(); - - // Apply all loaded settings - if (!string.IsNullOrWhiteSpace(config.UpdateUrl)) - builder.SetUpdateUrl(config.UpdateUrl); - if (!string.IsNullOrWhiteSpace(config.Token)) - builder.SetToken(config.Token); - if (!string.IsNullOrWhiteSpace(config.Scheme)) - builder.SetScheme(config.Scheme); - if (!string.IsNullOrWhiteSpace(config.AppName)) - builder.SetAppName(config.AppName); - if (!string.IsNullOrWhiteSpace(config.MainAppName)) - builder.SetMainAppName(config.MainAppName); - if (!string.IsNullOrWhiteSpace(config.ClientVersion)) - builder.SetClientVersion(config.ClientVersion); - if (!string.IsNullOrWhiteSpace(config.UpgradeClientVersion)) - builder.SetUpgradeClientVersion(config.UpgradeClientVersion); - if (!string.IsNullOrWhiteSpace(config.AppSecretKey)) - builder.SetAppSecretKey(config.AppSecretKey); - if (!string.IsNullOrWhiteSpace(config.ProductId)) - builder.SetProductId(config.ProductId); - if (!string.IsNullOrWhiteSpace(config.InstallPath)) - builder.SetInstallPath(config.InstallPath); - if (!string.IsNullOrWhiteSpace(config.UpdateLogUrl)) - builder.SetUpdateLogUrl(config.UpdateLogUrl); - if (!string.IsNullOrWhiteSpace(config.ReportUrl)) - builder.SetReportUrl(config.ReportUrl); - if (!string.IsNullOrWhiteSpace(config.Bowl)) - builder.SetBowl(config.Bowl); - if (!string.IsNullOrWhiteSpace(config.Script)) - builder.SetScript(config.Script); - if (!string.IsNullOrWhiteSpace(config.DriverDirectory)) - builder.SetDriverDirectory(config.DriverDirectory); - if (config.BlackFiles != null) - builder.SetBlackFiles(config.BlackFiles); - if (config.BlackFormats != null) - builder.SetBlackFormats(config.BlackFormats); - if (config.SkipDirectorys != null) - builder.SetSkipDirectorys(config.SkipDirectorys); - - builder.SetInstallPath(string.IsNullOrWhiteSpace(config.InstallPath) ? AppDomain.CurrentDomain.BaseDirectory : config.InstallPath); - return builder; - } - catch (System.Text.Json.JsonException) - { - // Invalid JSON format, fall back to parameters - return null; - } - catch (IOException) - { - // File read error, fall back to parameters - return null; - } - catch (UnauthorizedAccessException) - { - // Permission denied, fall back to parameters - return null; - } - catch - { - // Any other unexpected error, fall back to parameters - return null; - } - } - - public ConfiginfoBuilder SetUpdateUrl(string updateUrl) - { - if (string.IsNullOrWhiteSpace(updateUrl)) - throw new ArgumentException("updateUrl cannot be null or empty.", nameof(updateUrl)); - - _updateUrl = updateUrl; - return this; - } - - public ConfiginfoBuilder SetToken(string token) - { - if (string.IsNullOrWhiteSpace(token)) - throw new ArgumentException("token cannot be null or empty.", nameof(token)); - - _token = token; - return this; - } - - public ConfiginfoBuilder SetScheme(string scheme) - { - if (string.IsNullOrWhiteSpace(scheme)) - throw new ArgumentException("scheme cannot be null or empty.", nameof(scheme)); - - _scheme = scheme; - return this; - } - - /// - /// Sets the application name (executable to start after update). - /// - /// The name of the application executable. - /// The current ConfiginfoBuilder instance for method chaining. - public ConfiginfoBuilder SetAppName(string appName) - { - if (string.IsNullOrWhiteSpace(appName)) - throw new ArgumentException("AppName cannot be null or empty.", nameof(appName)); - - _appName = appName; - return this; - } - - /// - /// Sets the main application name. - /// - /// The name of the main application without file extension. - /// The current ConfiginfoBuilder instance for method chaining. - public ConfiginfoBuilder SetMainAppName(string mainAppName) - { - if (string.IsNullOrWhiteSpace(mainAppName)) - throw new ArgumentException("MainAppName cannot be null or empty.", nameof(mainAppName)); - - _mainAppName = mainAppName; - return this; - } - - /// - /// Sets the client version. - /// - /// The current version of the client application. - /// The current ConfiginfoBuilder instance for method chaining. - public ConfiginfoBuilder SetClientVersion(string clientVersion) - { - if (string.IsNullOrWhiteSpace(clientVersion)) - throw new ArgumentException("ClientVersion cannot be null or empty.", nameof(clientVersion)); - - _clientVersion = clientVersion; - return this; - } - - /// - /// Sets the upgrade client version. - /// - /// The current version of the upgrade application. - /// The current ConfiginfoBuilder instance for method chaining. - public ConfiginfoBuilder SetUpgradeClientVersion(string upgradeClientVersion) - { - if (string.IsNullOrWhiteSpace(upgradeClientVersion)) - throw new ArgumentException("UpgradeClientVersion cannot be null or empty.", nameof(upgradeClientVersion)); - - _upgradeClientVersion = upgradeClientVersion; - return this; - } - - /// - /// Sets the application secret key. - /// - /// The secret key used for authentication. - /// The current ConfiginfoBuilder instance for method chaining. - public ConfiginfoBuilder SetAppSecretKey(string appSecretKey) - { - if (string.IsNullOrWhiteSpace(appSecretKey)) - throw new ArgumentException("AppSecretKey cannot be null or empty.", nameof(appSecretKey)); - - _appSecretKey = appSecretKey; - return this; - } - - /// - /// Sets the product identifier. - /// - /// The unique product identifier. - /// The current ConfiginfoBuilder instance for method chaining. - public ConfiginfoBuilder SetProductId(string productId) - { - if (string.IsNullOrWhiteSpace(productId)) - throw new ArgumentException("ProductId cannot be null or empty.", nameof(productId)); - - _productId = productId; - return this; - } - - /// - /// Sets the installation path. - /// - /// The installation path where application files are located. - /// The current ConfiginfoBuilder instance for method chaining. - public ConfiginfoBuilder SetInstallPath(string installPath) - { - if (string.IsNullOrWhiteSpace(installPath)) - throw new ArgumentException("InstallPath cannot be null or empty.", nameof(installPath)); - - _installPath = installPath; - return this; - } - - /// - /// Sets the update log URL. - /// - /// The URL address for the update log webpage. - /// The current ConfiginfoBuilder instance for method chaining. - public ConfiginfoBuilder SetUpdateLogUrl(string updateLogUrl) - { - if (!string.IsNullOrWhiteSpace(updateLogUrl) && !Uri.IsWellFormedUriString(updateLogUrl, UriKind.Absolute)) - throw new ArgumentException("UpdateLogUrl must be a valid absolute URI.", nameof(updateLogUrl)); - - _updateLogUrl = updateLogUrl; - return this; - } - - /// - /// Sets the report URL. - /// - /// The API endpoint URL for reporting update status and results. - /// The current ConfiginfoBuilder instance for method chaining. - public ConfiginfoBuilder SetReportUrl(string reportUrl) - { - if (!string.IsNullOrWhiteSpace(reportUrl) && !Uri.IsWellFormedUriString(reportUrl, UriKind.Absolute)) - throw new ArgumentException("ReportUrl must be a valid absolute URI.", nameof(reportUrl)); - - _reportUrl = reportUrl; - return this; - } - - /// - /// Sets the bowl process name. - /// - /// The process name that should be terminated before starting the update. - /// The current ConfiginfoBuilder instance for method chaining. - public ConfiginfoBuilder SetBowl(string bowl) - { - _bowl = bowl; - return this; - } - - /// - /// Sets the shell script content. - /// - /// Shell script content used to grant file permissions on Linux/Unix systems. - /// The current ConfiginfoBuilder instance for method chaining. - public ConfiginfoBuilder SetScript(string script) - { - _script = script; - return this; - } - - /// - /// Sets the driver directory. - /// - /// The directory path containing driver files for driver update functionality. - /// The current ConfiginfoBuilder instance for method chaining. - public ConfiginfoBuilder SetDriverDirectory(string driverDirectory) - { - _driverDirectory = driverDirectory; - return this; - } - - /// - /// Sets the list of blacklisted files. - /// - /// List of specific files that should be excluded from the update process. - /// The current ConfiginfoBuilder instance for method chaining. - public ConfiginfoBuilder SetBlackFiles(List blackFiles) - { - _blackFiles = blackFiles ?? new List(); - return this; - } - - /// - /// Sets the list of blacklisted file formats. - /// - /// List of file format extensions that should be excluded from the update process. - /// The current ConfiginfoBuilder instance for method chaining. - public ConfiginfoBuilder SetBlackFormats(List blackFormats) - { - _blackFormats = blackFormats ?? new List(); - return this; - } - - /// - /// Sets the list of directories to skip. - /// - /// List of directory paths that should be skipped during the update process. - /// The current ConfiginfoBuilder instance for method chaining. - public ConfiginfoBuilder SetSkipDirectorys(List skipDirectorys) - { - _skipDirectorys = skipDirectorys ?? new List(); - return this; - } - - /// - /// Builds and returns a complete Configinfo object with all configured and default values. - /// - /// A fully configured Configinfo instance. - /// Thrown when the builder is in an invalid state. - public Configinfo Build() - { - // Create the Configinfo object with all values - var configinfo = new Configinfo - { - UpdateUrl = _updateUrl, - Token = _token, - Scheme = _scheme, - AppName = _appName, - MainAppName = _mainAppName, - ClientVersion = _clientVersion, - UpgradeClientVersion = _upgradeClientVersion, - AppSecretKey = _appSecretKey, - ProductId = _productId, - InstallPath = _installPath, - UpdateLogUrl = _updateLogUrl, - ReportUrl = _reportUrl, - Bowl = _bowl, - Script = _script, - DriverDirectory = _driverDirectory, - BlackFiles = _blackFiles ?? new List(), - BlackFormats = _blackFormats ?? new List(), - SkipDirectorys = _skipDirectorys ?? new List() - }; - - // Validate the built configuration - try - { - configinfo.Validate(); - } - catch (ArgumentException ex) - { - throw new InvalidOperationException($"Failed to build valid Configinfo: {ex.Message}", ex); - } - - return configinfo; - } - } -} diff --git a/src/c#/GeneralUpdate.Common/Shared/Object/ConfigurationMapper.cs b/src/c#/GeneralUpdate.Common/Shared/Object/ConfigurationMapper.cs deleted file mode 100644 index 45e98d66..00000000 --- a/src/c#/GeneralUpdate.Common/Shared/Object/ConfigurationMapper.cs +++ /dev/null @@ -1,135 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Text; - -namespace GeneralUpdate.Common.Shared.Object -{ - /// - /// Provides centralized mapping utilities for converting between configuration objects. - /// This class ensures consistent field mapping across Configinfo, GlobalConfigInfo, and ProcessInfo, - /// reducing the risk of missing or incorrectly mapped fields during maintenance. - /// - public static class ConfigurationMapper - { - /// - /// Maps user-provided configuration (Configinfo) to internal runtime configuration (GlobalConfigInfo). - /// This method performs a one-to-one field mapping for all shared configuration properties. - /// - /// The user-provided configuration object containing initial settings. - /// The internal configuration object to be populated. If null, a new instance is created. - /// A GlobalConfigInfo object populated with values from the source Configinfo. - public static GlobalConfigInfo MapToGlobalConfigInfo(Configinfo source, GlobalConfigInfo target = null) - { - // Create new instance if both source and target are not provided - if (target == null) - target = new GlobalConfigInfo(); - - // Return empty target if source is null - if (source == null) - return target; - - // Map common fields from base configuration - target.AppName = source.AppName; - target.MainAppName = source.MainAppName; - target.ClientVersion = source.ClientVersion; - target.InstallPath = source.InstallPath; - target.UpdateLogUrl = source.UpdateLogUrl; - target.AppSecretKey = source.AppSecretKey; - target.BlackFiles = source.BlackFiles; - target.BlackFormats = source.BlackFormats; - target.SkipDirectorys = source.SkipDirectorys; - target.ReportUrl = source.ReportUrl; - target.Bowl = source.Bowl; - target.Scheme = source.Scheme; - target.Token = source.Token; - target.Script = source.Script; - target.DriverDirectory = source.DriverDirectory; - - // Map GlobalConfigInfo-specific fields - target.UpdateUrl = source.UpdateUrl; - target.UpgradeClientVersion = source.UpgradeClientVersion; - target.ProductId = source.ProductId; - - return target; - } - - /// - /// Maps internal runtime configuration (GlobalConfigInfo) to process transfer parameters (ProcessInfo). - /// This method consolidates the complex parameter passing logic previously scattered in bootstrap code. - /// - /// The internal configuration object containing all runtime state. - /// List of version information objects from the update server response. - /// List of blacklisted file formats from the BlackListManager. - /// List of blacklisted files from the BlackListManager. - /// List of directories to skip from the BlackListManager. - /// A ProcessInfo object ready for serialization and inter-process communication. - /// Thrown when source is null - public static ProcessInfo MapToProcessInfo( - GlobalConfigInfo source, - List updateVersions, - List blackFileFormats, - List blackFiles, - List skipDirectories) - { - if (source == null) - throw new ArgumentNullException(nameof(source), "GlobalConfigInfo source cannot be null"); - - // Create ProcessInfo with all required parameters in a single location - // This replaces the error-prone manual parameter passing in GeneralClientBootstrap - return new ProcessInfo( - appName: source.MainAppName, // Maps MainAppName to ProcessInfo.AppName - installPath: source.InstallPath, - currentVersion: source.ClientVersion, // Maps ClientVersion to ProcessInfo.CurrentVersion - lastVersion: source.LastVersion, // Computed value set before calling this method - updateLogUrl: source.UpdateLogUrl, - compressEncoding: source.Encoding, // Computed value set before calling this method - compressFormat: source.Format, // Computed value set before calling this method - downloadTimeOut: source.DownloadTimeOut, // Computed value set before calling this method - appSecretKey: source.AppSecretKey, - updateVersions: updateVersions, // From API response - reportUrl: source.ReportUrl, - backupDirectory: source.BackupDirectory, // Computed value set before calling this method - bowl: source.Bowl, - scheme: source.Scheme, - token: source.Token, - script: source.Script, - driverDirectory: source.DriverDirectory, // Driver directory for driver updates - blackFileFormats: blackFileFormats, // From BlackListManager - blackFiles: blackFiles, // From BlackListManager - skipDirectories: skipDirectories // From BlackListManager - ); - } - - /// - /// Copies common configuration fields from a base configuration object to another. - /// This utility method helps maintain consistency when transferring configuration data. - /// - /// The source configuration type (must inherit from BaseConfigInfo). - /// The target configuration type (must inherit from BaseConfigInfo). - /// The source configuration object to copy from. - /// The target configuration object to copy to. - public static void CopyBaseFields(TSource source, TTarget target) - where TSource : BaseConfigInfo - where TTarget : BaseConfigInfo - { - if (source == null || target == null) - return; - - target.AppName = source.AppName; - target.MainAppName = source.MainAppName; - target.InstallPath = source.InstallPath; - target.UpdateLogUrl = source.UpdateLogUrl; - target.AppSecretKey = source.AppSecretKey; - target.ClientVersion = source.ClientVersion; - target.BlackFiles = source.BlackFiles; - target.BlackFormats = source.BlackFormats; - target.SkipDirectorys = source.SkipDirectorys; - target.ReportUrl = source.ReportUrl; - target.Bowl = source.Bowl; - target.Scheme = source.Scheme; - target.Token = source.Token; - target.Script = source.Script; - target.DriverDirectory = source.DriverDirectory; - } - } -} diff --git a/src/c#/GeneralUpdate.Common/Shared/Object/DTO/BaseResponseDTO.cs b/src/c#/GeneralUpdate.Common/Shared/Object/DTO/BaseResponseDTO.cs deleted file mode 100644 index dfa9b49a..00000000 --- a/src/c#/GeneralUpdate.Common/Shared/Object/DTO/BaseResponseDTO.cs +++ /dev/null @@ -1,16 +0,0 @@ -using System.Text.Json.Serialization; - -namespace GeneralUpdate.Common.Shared.Object -{ - public class BaseResponseDTO - { - [JsonPropertyName("code")] - public int Code { get; set; } - - [JsonPropertyName("body")] - public TBody Body { get; set; } - - [JsonPropertyName("message")] - public string Message { get; set; } - } -} \ No newline at end of file diff --git a/src/c#/GeneralUpdate.Common/Shared/Object/DTO/VersionRespDTO.cs b/src/c#/GeneralUpdate.Common/Shared/Object/DTO/VersionRespDTO.cs deleted file mode 100644 index d1c9eca6..00000000 --- a/src/c#/GeneralUpdate.Common/Shared/Object/DTO/VersionRespDTO.cs +++ /dev/null @@ -1,4 +0,0 @@ -using System.Collections.Generic; - -namespace GeneralUpdate.Common.Shared.Object; -public class VersionRespDTO : BaseResponseDTO>; \ No newline at end of file diff --git a/src/c#/GeneralUpdate.Common/Shared/Object/Enum/AppType.cs b/src/c#/GeneralUpdate.Common/Shared/Object/Enum/AppType.cs deleted file mode 100644 index 18648695..00000000 --- a/src/c#/GeneralUpdate.Common/Shared/Object/Enum/AppType.cs +++ /dev/null @@ -1,15 +0,0 @@ -namespace GeneralUpdate.Common.Shared.Object -{ - public class AppType - { - /// - /// main program - /// - public const int ClientApp = 1; - - /// - /// upgrade program. - /// - public const int UpgradeApp = 2; - } -} \ No newline at end of file diff --git a/src/c#/GeneralUpdate.Common/Shared/Object/Enum/Format.cs b/src/c#/GeneralUpdate.Common/Shared/Object/Enum/Format.cs deleted file mode 100644 index abb8efdd..00000000 --- a/src/c#/GeneralUpdate.Common/Shared/Object/Enum/Format.cs +++ /dev/null @@ -1,7 +0,0 @@ -namespace GeneralUpdate.Common.Shared.Object -{ - public class Format - { - public const string ZIP = ".zip"; - } -} \ No newline at end of file diff --git a/src/c#/GeneralUpdate.Common/Shared/Object/Enum/PlatformType.cs b/src/c#/GeneralUpdate.Common/Shared/Object/Enum/PlatformType.cs deleted file mode 100644 index 1382a881..00000000 --- a/src/c#/GeneralUpdate.Common/Shared/Object/Enum/PlatformType.cs +++ /dev/null @@ -1,8 +0,0 @@ -namespace GeneralUpdate.Common.Shared.Object.Enum; - -public class PlatformType -{ - public const int Windows = 1; - - public const int Linux = 2; -} \ No newline at end of file diff --git a/src/c#/GeneralUpdate.Common/Shared/Object/Enum/ReportType.cs b/src/c#/GeneralUpdate.Common/Shared/Object/Enum/ReportType.cs deleted file mode 100644 index c792ba6b..00000000 --- a/src/c#/GeneralUpdate.Common/Shared/Object/Enum/ReportType.cs +++ /dev/null @@ -1,10 +0,0 @@ -namespace GeneralUpdate.Common.Shared.Object.Enum; - -public class ReportType -{ - public const int None = 0; - - public const int Success = 2; - - public const int Failure = 3; -} \ No newline at end of file diff --git a/src/c#/GeneralUpdate.Common/Shared/Object/Enum/UpdateMode.cs b/src/c#/GeneralUpdate.Common/Shared/Object/Enum/UpdateMode.cs deleted file mode 100644 index cd05d00a..00000000 --- a/src/c#/GeneralUpdate.Common/Shared/Object/Enum/UpdateMode.cs +++ /dev/null @@ -1,7 +0,0 @@ -namespace GeneralUpdate.Common.Shared.Object.Enum; - -public enum UpdateMode -{ - Default = 0, - Scripts = 1 -} \ No newline at end of file diff --git a/src/c#/GeneralUpdate.Common/Shared/Object/GlobalConfigInfo.cs b/src/c#/GeneralUpdate.Common/Shared/Object/GlobalConfigInfo.cs deleted file mode 100644 index cb9bfc69..00000000 --- a/src/c#/GeneralUpdate.Common/Shared/Object/GlobalConfigInfo.cs +++ /dev/null @@ -1,120 +0,0 @@ -using System.Collections.Generic; -using System.Text; - -namespace GeneralUpdate.Common.Shared.Object; - -/// -/// Internal runtime configuration class that combines user settings with computed state. -/// This class serves as the central configuration holder during the update workflow execution. -/// Inherits common fields from BaseConfigInfo and adds runtime-specific properties. -/// -/// Responsibilities: -/// - Stores user-provided configuration from Configinfo -/// - Maintains computed runtime values (encoding, format, paths, versions) -/// - Tracks update workflow state (IsMainUpdate, IsUpgradeUpdate) -/// -public class GlobalConfigInfo : BaseConfigInfo -{ - // User-provided configuration fields (mapped from Configinfo) - - /// - /// The API endpoint URL for checking available updates. - /// Inherited from user configuration, used for version validation requests. - /// - public string UpdateUrl { get; set; } - - /// - /// The current version of the upgrade application (the updater itself). - /// Used separately from ClientVersion to manage updater upgrades. - /// - public string UpgradeClientVersion { get; set; } - - /// - /// The unique product identifier for this application. - /// Used to distinguish between multiple products sharing the same update server. - /// - public string ProductId { get; set; } - - // Runtime computed fields (calculated during workflow execution) - - - // Runtime computed fields (calculated during workflow execution) - - /// - /// The compression format of update packages (e.g., "ZIP", "7Z"). - /// Computed from UpdateOption.Format or defaults to ZIP. - /// - public string Format { get; set; } - - /// - /// Indicates whether the upgrade application itself needs to be updated. - /// Computed by comparing UpgradeClientVersion with server response. - /// - public bool IsUpgradeUpdate { get; set; } - - /// - /// Indicates whether the main application needs to be updated. - /// Computed by comparing ClientVersion with server response. - /// - public bool IsMainUpdate { get; set; } - - /// - /// List of version information objects to be updated. - /// Populated from the update server response based on IsUpgradeUpdate/IsMainUpdate flags. - /// - public List UpdateVersions { get; set; } - - /// - /// The encoding format used for file operations (e.g., UTF-8, ASCII). - /// Computed from UpdateOption.Encoding or defaults to system default encoding. - /// - public Encoding Encoding { get; set; } - - /// - /// Timeout duration in seconds for download operations. - /// Computed from UpdateOption.DownloadTimeOut or defaults to 60 seconds. - /// - public int DownloadTimeOut { get; set; } - - /// - /// The latest available version from the update server. - /// Extracted from the server response body after version validation. - /// - public string LastVersion { get; set; } - - /// - /// Temporary directory path for downloading and extracting update files. - /// Computed using StorageManager.GetTempDirectory("main_temp"). - /// - public string TempPath { get; set; } - - /// - /// Serialized JSON string of ProcessInfo object for inter-process communication. - /// Contains all parameters needed by the upgrade process. - /// - public string ProcessInfo { get; set; } - - /// - /// Indicates whether driver update functionality is enabled. - /// Computed from UpdateOption.Drive or defaults to false. - /// - public bool? DriveEnabled { get; set; } - - /// - /// Directory path containing driver files for update. - /// Used when DriveEnabled is true to locate driver files for installation. - /// - public string DriverDirectory { get; set; } - - /// - /// Indicates whether differential patch update is enabled. - /// Computed from UpdateOption.Patch or defaults to true. - /// - public bool? PatchEnabled { get; set; } - - /// - /// Directory path where the current version files are backed up before update. - /// Computed by combining InstallPath with a versioned directory name. - /// - public string BackupDirectory { get; set; } -} \ No newline at end of file diff --git a/src/c#/GeneralUpdate.Common/Shared/Object/GlobalConfigInfoOSS.cs b/src/c#/GeneralUpdate.Common/Shared/Object/GlobalConfigInfoOSS.cs deleted file mode 100644 index 1bd152ba..00000000 --- a/src/c#/GeneralUpdate.Common/Shared/Object/GlobalConfigInfoOSS.cs +++ /dev/null @@ -1,35 +0,0 @@ -using System; -using System.Text.Json.Serialization; - -namespace GeneralUpdate.Common.Shared.Object -{ - public class GlobalConfigInfoOSS - { - [JsonPropertyName("Url")] - public string Url { get; set; } - - [JsonPropertyName("AppName")] - public string AppName { get; set; } - - [JsonPropertyName("CurrentVersion")] - public string CurrentVersion { get; set; } - - [JsonPropertyName("VersionFileName")] - public string VersionFileName { get; set; } - - [JsonPropertyName("Encoding")] - public string Encoding { get; set; } - - public GlobalConfigInfoOSS() - { - } - - public GlobalConfigInfoOSS(string url, string appName, string currentVersion, string versionFileName) - { - Url = url ?? throw new ArgumentNullException(nameof(url)); - AppName = appName ?? throw new ArgumentNullException(nameof(appName)); - CurrentVersion = currentVersion ?? throw new ArgumentNullException(nameof(currentVersion)); - VersionFileName = versionFileName ?? "versions.json"; - } - } -} \ No newline at end of file diff --git a/src/c#/GeneralUpdate.Common/Shared/Object/ObjectTranslator.cs b/src/c#/GeneralUpdate.Common/Shared/Object/ObjectTranslator.cs deleted file mode 100644 index 28f55cbf..00000000 --- a/src/c#/GeneralUpdate.Common/Shared/Object/ObjectTranslator.cs +++ /dev/null @@ -1,7 +0,0 @@ -namespace GeneralUpdate.Common.Shared.Object; - -public sealed class ObjectTranslator -{ - public static string GetPacketHash(object version) => - !GeneralTracer.IsTracingEnabled() ? string.Empty : $"[PacketHash]:{(version as VersionInfo).Hash} "; -} \ No newline at end of file diff --git a/src/c#/GeneralUpdate.Common/Shared/Object/Packet.cs b/src/c#/GeneralUpdate.Common/Shared/Object/Packet.cs deleted file mode 100644 index 19fa1f4b..00000000 --- a/src/c#/GeneralUpdate.Common/Shared/Object/Packet.cs +++ /dev/null @@ -1,40 +0,0 @@ -using System; -using System.Text.Json.Serialization; - -namespace GeneralUpdate.Common.Shared.Object; - -/// -/// Currently used only for upgrade push. -/// -public class Packet -{ - [JsonPropertyName("Name")] - public string? Name { get; set; } - - [JsonPropertyName("Hash")] - public string Hash { get; set; } - - [JsonPropertyName("ReleaseDate")] - public DateTime? ReleaseDate { get; set; } - - [JsonPropertyName("Url")] - public string? Url { get; set; } - - [JsonPropertyName("Version")] - public string? Version { get; set; } - - [JsonPropertyName("AppType")] - public int? AppType { get; set; } - - [JsonPropertyName("Platform")] - public int? Platform { get; set; } - - [JsonPropertyName("ProductId")] - public string? ProductId { get; set; } - - [JsonPropertyName("IsForcibly")] - public bool? IsForcibly { get; set; } - - [JsonPropertyName("IsFreeze")] - public bool? IsFreeze { get; set; } -} \ No newline at end of file diff --git a/src/c#/GeneralUpdate.Common/Shared/Object/ProcessInfo.cs b/src/c#/GeneralUpdate.Common/Shared/Object/ProcessInfo.cs deleted file mode 100644 index 8b8f6932..00000000 --- a/src/c#/GeneralUpdate.Common/Shared/Object/ProcessInfo.cs +++ /dev/null @@ -1,243 +0,0 @@ -using System; -using System.Collections.Generic; -using System.IO; -using System.Text; -using System.Text.Json.Serialization; - -namespace GeneralUpdate.Common.Shared.Object -{ - /// - /// Inter-process communication parameter object. - /// This class is serialized to JSON and passed to the upgrade process, enabling - /// the separate upgrade application to perform updates with the correct configuration. - /// - /// Design Notes: - /// - All fields are serialized using [JsonPropertyName] attributes - /// - Constructor performs validation to ensure required parameters are provided - /// - Field naming differs slightly from other config classes for backward compatibility - /// - public class ProcessInfo - { - /// - /// Default constructor for deserialization. - /// - public ProcessInfo() { } - - /// - /// Parameterized constructor with validation for creating ProcessInfo instances. - /// All parameters are validated to ensure the upgrade process receives valid configuration. - /// - /// The name of the application to start after update (maps from MainAppName) - /// The installation directory path (must exist) - /// The current version before update - /// The target version after update - /// The URL for viewing update logs - /// The encoding used for compressed files - /// The compression format (ZIP, 7Z, etc.) - /// The download timeout in seconds (must be > 0) - /// The application secret key for authentication - /// List of version information to update (must not be empty) - /// The URL for reporting update status - /// The directory path for backup files - /// The process name to terminate before updating - /// The URL scheme for update requests - /// The authentication token - /// The Linux permission script - /// The directory path containing driver files - /// List of file format extensions to skip - /// List of specific files to skip - /// List of directories to skip - /// Thrown when required parameters are null - /// Thrown when parameters fail validation - public ProcessInfo(string appName - , string installPath - , string currentVersion - , string lastVersion - , string updateLogUrl - , Encoding compressEncoding - , string compressFormat - , int downloadTimeOut - , string appSecretKey - , List updateVersions - , string reportUrl - , string backupDirectory - , string bowl - , string scheme - , string token - , string script - , string driverDirectory - , List blackFileFormats - , List blackFiles - , List skipDirectories) - { - // Validate required string parameters - AppName = appName ?? throw new ArgumentNullException(nameof(appName)); - if (!Directory.Exists(installPath)) throw new ArgumentException($"{nameof(installPath)} path does not exist ! {installPath}."); - InstallPath = installPath ?? throw new ArgumentNullException(nameof(installPath)); - CurrentVersion = currentVersion ?? throw new ArgumentNullException(nameof(currentVersion)); - LastVersion = lastVersion ?? throw new ArgumentNullException(nameof(lastVersion)); - UpdateLogUrl = updateLogUrl; - - // Validate and set compression parameters - CompressEncoding = compressEncoding.WebName; - CompressFormat = compressFormat; - if (downloadTimeOut < 0) throw new ArgumentException("Timeout must be greater than 0 !"); - DownloadTimeOut = downloadTimeOut; - - // Validate authentication parameters - AppSecretKey = appSecretKey ?? throw new ArgumentNullException(nameof(appSecretKey)); - - // Validate update versions collection - if (updateVersions == null || updateVersions.Count == 0) throw new ArgumentException("Collection cannot be null or has 0 elements !"); - UpdateVersions = updateVersions; - - // Set reporting and backup parameters - ReportUrl = reportUrl ?? throw new ArgumentNullException(nameof(reportUrl)); - BackupDirectory = backupDirectory ?? throw new ArgumentNullException(nameof(backupDirectory)); - - // Set optional parameters - Bowl = bowl; - Scheme = scheme; - Token = token; - Script = script; - DriverDirectory = driverDirectory; - - // Set blacklist parameters - BlackFileFormats = blackFileFormats; - BlackFiles = blackFiles; - SkipDirectorys = skipDirectories; - } - - /// - /// The name of the application to start after the update completes. - /// Note: In ProcessInfo, this field holds the MainAppName value from other config classes. - /// - [JsonPropertyName("AppName")] - public string AppName { get; set; } - - /// - /// The installation directory where files will be updated. - /// All update operations are performed relative to this path. - /// - [JsonPropertyName("InstallPath")] - public string InstallPath { get; set; } - - /// - /// The current version of the application before the update. - /// Note: Maps from GlobalConfigInfo.ClientVersion. - /// - [JsonPropertyName("CurrentVersion")] - public string CurrentVersion { get; set; } - - /// - /// The target version after the update completes. - /// This is the latest version available from the update server. - /// - [JsonPropertyName("LastVersion")] - public string LastVersion { get; set; } - - /// - /// The URL where users can view detailed update logs and changelogs. - /// - [JsonPropertyName("UpdateLogUrl")] - public string UpdateLogUrl { get; set; } - - /// - /// The text encoding used for compressing/decompressing update packages. - /// Stored as WebName string (e.g., "utf-8", "ascii"). - /// - [JsonPropertyName("CompressEncoding")] - public string CompressEncoding { get; set; } - - /// - /// The compression format of update packages (e.g., "ZIP", "7Z"). - /// - [JsonPropertyName("CompressFormat")] - public string CompressFormat { get; set; } - - /// - /// The timeout duration in seconds for downloading update packages. - /// Download operations will fail if they exceed this duration. - /// - [JsonPropertyName("DownloadTimeOut")] - public int DownloadTimeOut { get; set; } - - /// - /// The application secret key used for authenticating update requests. - /// - [JsonPropertyName("AppSecretKey")] - public string AppSecretKey { get; set; } - - /// - /// List of version information objects describing all versions to be updated. - /// Can contain multiple versions for incremental updates. - /// - [JsonPropertyName("UpdateVersions")] - public List UpdateVersions { get; set; } - - /// - /// The API endpoint URL for reporting update progress and completion status. - /// - [JsonPropertyName("ReportUrl")] - public string ReportUrl { get; set; } - - /// - /// The directory path where current version files are backed up before updating. - /// Used for rollback if the update fails. - /// - [JsonPropertyName("BackupDirectory")] - public string BackupDirectory { get; set; } - - /// - /// The name of a process that should be terminated before starting the update. - /// Typically used to close conflicting background processes. - /// - [JsonPropertyName("Bowl")] - public string Bowl { get; set; } - - /// - /// The URL scheme for update server communication (e.g., "http", "https"). - /// - [JsonPropertyName("Scheme")] - public string Scheme { get; set; } - - /// - /// The authentication token included in HTTP headers for API requests. - /// - [JsonPropertyName("Token")] - public string Token { get; set; } - - /// - /// List of file format extensions that should be excluded from updates. - /// Note: Different property name (BlackFileFormats) than in other config classes (BlackFormats). - /// - [JsonPropertyName("BlackFileFormats")] - public List BlackFileFormats { get; set; } - - /// - /// List of specific file names that should be excluded from updates. - /// - [JsonPropertyName("BlackFiles")] - public List BlackFiles { get; set; } - - /// - /// List of directory paths that should be skipped during update operations. - /// - [JsonPropertyName("SkipDirectorys")] - public List SkipDirectorys { get; set; } - - /// - /// Shell script content for granting file permissions on Linux/Unix systems. - /// Executed after update to ensure updated files have correct permissions. - /// - [JsonPropertyName("Script")] - public string Script { get; set; } - - /// - /// The directory path containing driver files for driver update functionality. - /// Used when DriveEnabled is true to locate and install driver files. - /// - [JsonPropertyName("DriverDirectory")] - public string DriverDirectory { get; set; } - } -} \ No newline at end of file diff --git a/src/c#/GeneralUpdate.Common/Shared/Object/README-ConfiginfoBuilder.md b/src/c#/GeneralUpdate.Common/Shared/Object/README-ConfiginfoBuilder.md deleted file mode 100644 index 83790f6d..00000000 --- a/src/c#/GeneralUpdate.Common/Shared/Object/README-ConfiginfoBuilder.md +++ /dev/null @@ -1,263 +0,0 @@ -# ConfiginfoBuilder - Universal Configuration Builder - -## Overview - -The `ConfiginfoBuilder` class provides a simple, fluent API for creating `Configinfo` objects with minimal effort. It automatically handles platform-specific defaults and only requires three essential parameters. - -**NEW**: ConfiginfoBuilder now supports loading configuration from a JSON file (`update_config.json`) placed in the running directory. When this file exists, it takes the highest priority, overriding any parameters passed to the builder. - -**Design Inspiration**: The zero-configuration approach is inspired by projects like [Velopack](https://github.com/velopack/velopack), focusing on sensible defaults extracted from the running application with minimal user input required. - -## Configuration Priority - -The ConfiginfoBuilder follows this priority order: - -1. **JSON Configuration File** (`update_config.json`) - **HIGHEST PRIORITY** -2. **Builder Setter Methods** (via `.SetXXX()` methods) -3. **Default Values** (platform-specific defaults) - -## JSON Configuration File Support - -Place an `update_config.json` file in your application's running directory to configure all update settings. When this file is present, ConfiginfoBuilder will automatically load settings from it, ignoring parameters passed to the `Create()` method. - -**Example `update_config.json`:** -```json -{ - "UpdateUrl": "https://api.example.com/updates", - "Token": "your-authentication-token", - "Scheme": "https", - "AppName": "Update.exe", - "MainAppName": "MyApplication.exe", - "ClientVersion": "1.0.0", - "InstallPath": "/path/to/installation" -} -``` - -See [update_config.example.json](update_config.example.json) for a complete example with all available options. - -## Auto-Configuration Features - -🔍 **Application Name Detection**: Automatically reads `` from `.csproj` -📊 **Version Extraction**: Reads `` field for `ClientVersion` -🏢 **Publisher Info**: Extracts `` or `` for `ProductId` -📂 **Path Extraction**: Uses host program's base directory -🖥️ **Platform Detection**: Adapts to Windows, Linux, and macOS - -## Quick Start - -```csharp -using GeneralUpdate.Common.Shared.Object; - -// Method 1: With JSON configuration file (RECOMMENDED) -// Place update_config.json in your app's running directory -// The Create method will automatically load from the file -var config = ConfiginfoBuilder - .Create("https://fallback.com/updates", "fallback-token", "https") - .Build(); -// If update_config.json exists, those values are used instead of the parameters - -// Method 2: Using code configuration only (no JSON file) -var config2 = ConfiginfoBuilder - .Create("https://api.example.com/updates", "your-auth-token", "https") - .Build(); - -// Method 3: Code configuration with custom overrides -var config3 = ConfiginfoBuilder - .Create("https://api.example.com/updates", "your-auth-token", "https") - .SetAppName("MyApp.exe") - .SetInstallPath("/custom/path") - .Build(); -``` - -## Key Features - -✅ **JSON Configuration Support**: Load settings from `update_config.json` with highest priority -✅ **Minimal Parameters**: Only 3 required parameters (UpdateUrl, Token, Scheme) -✅ **Cross-Platform**: Automatically detects and adapts to Windows/Linux/macOS -✅ **Smart Defaults**: Platform-appropriate paths, separators, and configurations -✅ **Fluent API**: Clean, readable method chaining -✅ **Type-Safe**: Compile-time parameter validation -✅ **Well-Tested**: 39 comprehensive unit tests including JSON configuration scenarios - -## Platform Detection - -The builder automatically adapts based on your runtime environment: - -| Aspect | Windows | Linux | macOS | -|--------|---------|-------|-------| -| Install Path | Current app directory | Current app directory | Current app directory | -| App Names | Auto from csproj + `.exe` | Auto from csproj | Auto from csproj | -| Script | Empty | chmod script | chmod script | -| Path Separator | `\` (automatic) | `/` (automatic) | `/` (automatic) | - -## Documentation - -- **[Usage Guide](ConfiginfoBuilder-Usage.md)** - Comprehensive documentation with examples -- **[Example Code](ConfiginfoBuilder-Example.cs)** - Runnable examples demonstrating all features - -## Examples - -### Using JSON Configuration File -```csharp -// Create update_config.json in your app directory: -// { -// "UpdateUrl": "https://api.example.com/updates", -// "Token": "my-token", -// "Scheme": "https", -// "AppName": "MyApp.exe", -// "ClientVersion": "2.0.0" -// } - -// The builder will automatically load from the file -var config = ConfiginfoBuilder - .Create("ignored", "ignored", "ignored") - .Build(); -// Values come from update_config.json! -``` - -### Basic Usage (No JSON File) -```csharp -var config = ConfiginfoBuilder - .Create(updateUrl, token, scheme) - .Build(); -``` - -### Custom Configuration -```csharp -var config = ConfiginfoBuilder - .Create(updateUrl, token, scheme) - .SetAppName("MyApp.exe") - .SetClientVersion("2.0.0") - .SetInstallPath("/opt/myapp") - .Build(); -``` - -### With File Filters -```csharp -var config = ConfiginfoBuilder - .Create(updateUrl, token, scheme) - .SetBlackFormats(new List { ".log", ".tmp", ".cache" }) - .SetSkipDirectorys(new List { "/temp", "/logs" }) - .Build(); -``` - -## Default Values - -The builder and `Configinfo` class provide these defaults: - -### Configinfo Class Defaults (Property Initializers) -- **AppName**: "Update.exe" -- **InstallPath**: Current program's running directory (`AppDomain.CurrentDomain.BaseDirectory`) - -### ConfiginfoBuilder Defaults (for Builder Pattern) -- **ClientVersion**: "1.0.0" -- **UpgradeClientVersion**: "1.0.0" -- **AppSecretKey**: "default-secret-key" -- **ProductId**: "default-product-id" -- **MainAppName**: "App.exe" -- **BlackFormats**: `.log`, `.tmp`, `.cache`, `.bak` (via `ConfiginfoBuilder.DefaultBlackFormats`) -- **BlackFiles**: Empty list -- **SkipDirectorys**: Empty list - -All defaults can be overridden using: -1. JSON configuration file (`update_config.json`) - highest priority -2. Builder setter methods (`.SetXXX()`) -3. Direct property assignment on `Configinfo` objects - -## Error Handling - -The builder validates parameters at two stages: - -1. **Construction Time**: Validates required parameters (UpdateUrl, Token, Scheme) -2. **Build Time**: Validates the complete configuration before returning - -```csharp -try { - var config = new ConfiginfoBuilder(invalidUrl, token, scheme).Build(); -} catch (ArgumentException ex) { - Console.WriteLine($"Invalid parameter: {ex.Message}"); -} catch (InvalidOperationException ex) { - Console.WriteLine($"Build failed: {ex.Message}"); -} -``` - -## Testing - -The implementation includes 39 comprehensive unit tests covering: -- Constructor validation -- Default value generation -- Platform-specific behavior -- Method chaining -- Error handling -- JSON configuration file loading -- Fallback behavior when JSON is invalid -- Complete integration scenarios - -Run tests with: -```bash -dotnet test CoreTest/CoreTest.csproj --filter "FullyQualifiedName~ConfiginfoBuilderTests" -``` - -## Security - -✅ No security vulnerabilities detected by CodeQL -✅ Input validation on all parameters -✅ No hardcoded secrets or credentials - -## Compatibility - -- **.NET Standard 2.0** and above -- **.NET 10.0** and above -- **Cross-platform**: Windows, Linux (and other Unix-like systems) - -## Migration from Manual Construction - -**Before:** -```csharp -var config = new Configinfo -{ - UpdateUrl = url, - Token = token, - Scheme = scheme, - AppName = "App.exe", - MainAppName = "App.exe", - ClientVersion = "1.0.0", - // ... 15+ more properties -}; -``` - -**After (with JSON configuration):** -```json -// update_config.json -{ - "UpdateUrl": "https://api.example.com/updates", - "Token": "my-token", - "Scheme": "https", - "AppName": "MyApp.exe", - "ClientVersion": "1.0.0" -} -``` -```csharp -var config = ConfiginfoBuilder - .Create("fallback", "fallback", "https") - .Build(); -``` - -**After (with code only):** -```csharp -var config = ConfiginfoBuilder - .Create(url, token, scheme) - .Build(); -``` - -## Contributing - -This implementation follows the repository's coding standards and includes: -- Comprehensive XML documentation -- Extensive unit tests -- Usage examples -- Error handling - -## License - -Part of the GeneralUpdate project. See the main LICENSE file for details. diff --git a/src/c#/GeneralUpdate.Common/Shared/Object/VersionInfo.cs b/src/c#/GeneralUpdate.Common/Shared/Object/VersionInfo.cs deleted file mode 100644 index 4a7144ed..00000000 --- a/src/c#/GeneralUpdate.Common/Shared/Object/VersionInfo.cs +++ /dev/null @@ -1,61 +0,0 @@ -using System; -using System.Text.Json.Serialization; - -namespace GeneralUpdate.Common.Shared.Object; - -public class VersionInfo -{ - [JsonPropertyName("recordId")] - public int RecordId { get; set; } - - [JsonPropertyName("name")] - public string? Name { get; set; } - - [JsonPropertyName("hash")] - public string? Hash { get; set; } - - [JsonPropertyName("releaseDate")] - public DateTime? ReleaseDate { get; set; } - - [JsonPropertyName("url")] - public string? Url { get; set; } - - [JsonPropertyName("version")] - public string? Version { get; set; } - - [JsonPropertyName("appType")] - public int? AppType { get; set; } - - [JsonPropertyName("platform")] - public int? Platform { get; set; } - - [JsonPropertyName("productId")] - public string? ProductId { get; set; } - - [JsonPropertyName("isForcibly")] - public bool? IsForcibly { get; set; } - - [JsonPropertyName("format")] - public string? Format { get; set; } - - [JsonPropertyName("size")] - public long? Size { get; set; } - - /// - /// HTTP authentication scheme (e.g., "Bearer", "Basic") for download requests. - /// - [JsonPropertyName("authScheme")] - public string? AuthScheme { get; set; } - - /// - /// HTTP authentication token for download requests. - /// - [JsonPropertyName("authToken")] - public string? AuthToken { get; set; } - - /// - /// Update log or release notes for this version. - /// - [JsonPropertyName("updateLog")] - public string? UpdateLog { get; set; } -} \ No newline at end of file diff --git a/src/c#/GeneralUpdate.Common/Shared/Object/VersionOSS.cs b/src/c#/GeneralUpdate.Common/Shared/Object/VersionOSS.cs deleted file mode 100644 index 6fbcf120..00000000 --- a/src/c#/GeneralUpdate.Common/Shared/Object/VersionOSS.cs +++ /dev/null @@ -1,42 +0,0 @@ -using System; -using System.Text.Json.Serialization; - -namespace GeneralUpdate.Common.Shared.Object -{ - /// - /// Version data persistence. - /// - public class VersionOSS - { - /// - /// Update package release time. - /// - - [JsonPropertyName("PubTime")] - public DateTime PubTime { get; set; } - - /// - /// Update package name. - /// - [JsonPropertyName("PacketName")] - public string PacketName { get; set; } - - /// - /// Compare and verify with the downloaded update package. - /// - [JsonPropertyName("Hash")] - public string Hash { get; set; } - - /// - /// The version number. - /// - [JsonPropertyName("Version")] - public string Version { get; set; } - - /// - /// Remote service url address. - /// - [JsonPropertyName("Url")] - public string Url { get; set; } - } -} \ No newline at end of file diff --git a/src/c#/GeneralUpdate.Common/Shared/Object/update_config.example.json b/src/c#/GeneralUpdate.Common/Shared/Object/update_config.example.json deleted file mode 100644 index ce6b7797..00000000 --- a/src/c#/GeneralUpdate.Common/Shared/Object/update_config.example.json +++ /dev/null @@ -1,31 +0,0 @@ -{ - "UpdateUrl": "https://api.example.com/updates", - "Token": "your-authentication-token-here", - "Scheme": "https", - "AppName": "Update.exe", - "MainAppName": "MyApplication.exe", - "ClientVersion": "1.0.0", - "UpgradeClientVersion": "1.0.0", - "AppSecretKey": "your-secret-key-here", - "ProductId": "your-product-id", - "InstallPath": "/path/to/installation", - "UpdateLogUrl": "https://example.com/changelog", - "ReportUrl": "https://api.example.com/report", - "Bowl": "Bowl.exe", - "Script": "#!/bin/bash\nchmod +x *", - "DriverDirectory": "/path/to/drivers", - "BlackFiles": [ - "config.json", - "user.dat" - ], - "BlackFormats": [ - ".log", - ".tmp", - ".cache", - ".bak" - ], - "SkipDirectorys": [ - "/temp", - "/logs" - ] -} diff --git a/src/c#/GeneralUpdate.Common/Shared/Service/VersionService.cs b/src/c#/GeneralUpdate.Common/Shared/Service/VersionService.cs deleted file mode 100644 index 3bcfd10b..00000000 --- a/src/c#/GeneralUpdate.Common/Shared/Service/VersionService.cs +++ /dev/null @@ -1,116 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Diagnostics; -using System.Net.Http; -using System.Security.Cryptography.X509Certificates; -using System.Net.Security; -using System.Text; -using System.Text.Json; -using System.Text.Json.Serialization.Metadata; -using System.Threading.Tasks; -using GeneralUpdate.Common.Internal; -using GeneralUpdate.Common.Internal.JsonContext; -using GeneralUpdate.Common.Shared.Object; - -namespace GeneralUpdate.Common.Shared.Service -{ - public class VersionService - { - private VersionService() { } - - /// - /// Report the result of this update: whether it was successful. - /// - /// - /// - /// - /// - /// - public static async Task Report(string httpUrl - , int recordId - , int status - , int? type - , string scheme = null - , string token = null) - { - var parameters = new Dictionary - { - { "RecordId", recordId }, - { "Status", status }, - { "Type", type } - }; - await PostTaskAsync>(httpUrl, parameters, ReportRespJsonContext.Default.BaseResponseDTOBoolean, scheme, token); - } - - /// - /// Verify whether the current version needs an update. - /// - /// - /// - /// - /// - /// - /// - /// - public static async Task Validate(string httpUrl - , string version - , int appType - , string appKey - , int platform - , string productId - , string scheme = null - , string token = null) - { - var parameters = new Dictionary - { - { "Version", version }, - { "AppType", appType }, - { "AppKey", appKey }, - { "Platform", platform }, - { "ProductId", productId } - }; - return await PostTaskAsync(httpUrl, parameters, VersionRespJsonContext.Default.VersionRespDTO, scheme, token); - } - - private static async Task PostTaskAsync(string httpUrl, Dictionary parameters, JsonTypeInfo? typeInfo = null, string scheme = null, string token = null) - { - try - { - var uri = new Uri(httpUrl); - using var httpClient = new HttpClient(new HttpClientHandler - { - ServerCertificateCustomValidationCallback = CheckValidationResult - }); - httpClient.Timeout = TimeSpan.FromSeconds(15); - httpClient.DefaultRequestHeaders.Accept.ParseAdd("text/html, application/xhtml+xml, */*"); - - if (!string.IsNullOrEmpty(scheme) && !string.IsNullOrEmpty(token)) - { - httpClient.DefaultRequestHeaders.Authorization = - new System.Net.Http.Headers.AuthenticationHeaderValue(scheme, token); - } - - var parametersJson = - JsonSerializer.Serialize(parameters, HttpParameterJsonContext.Default.DictionaryStringObject); - var stringContent = new StringContent(parametersJson, Encoding.UTF8, "application/json"); - var result = await httpClient.PostAsync(uri, stringContent); - var reseponseJson = await result.Content.ReadAsStringAsync(); - return typeInfo == null - ? JsonSerializer.Deserialize(reseponseJson) - : JsonSerializer.Deserialize(reseponseJson, typeInfo); - } - catch (Exception e) - { - GeneralTracer.Error("The PostTaskAsync method in the VersionService class throws an exception.", e); - throw e; - } - } - - private static bool CheckValidationResult( - HttpRequestMessage message, - X509Certificate2 certificate, - X509Chain chain, - SslPolicyErrors sslPolicyErrors - ) => true; - } -} \ No newline at end of file diff --git a/src/c#/GeneralUpdate.Common/Shared/Trace/GeneralTracer.cs b/src/c#/GeneralUpdate.Common/Shared/Trace/GeneralTracer.cs deleted file mode 100644 index 4fbd1bf3..00000000 --- a/src/c#/GeneralUpdate.Common/Shared/Trace/GeneralTracer.cs +++ /dev/null @@ -1,163 +0,0 @@ -using System; -using System.Diagnostics; -using System.IO; -using System.Runtime.InteropServices; - -namespace GeneralUpdate.Common.Shared; - -public static class GeneralTracer -{ - private static readonly object _lockObj = new(); - private static bool _isTracingEnabled; - private static string _currentLogDate; - private static TextTraceListener _fileListener; - - static GeneralTracer() - { - Trace.Listeners.Clear(); - - if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) - { - Trace.Listeners.Add(new WindowsOutputDebugListener()); - } - - Trace.Listeners.Add(new TextWriterTraceListener(Console.Out) { Name = "ConsoleListener" }); - - InitializeFileListener(); - - if (Debugger.IsAttached) - Trace.Listeners.Add(new DefaultTraceListener()); - - Trace.AutoFlush = true; - _isTracingEnabled = true; - } - - private static void InitializeFileListener() - { - //Ensure that log files are rotated on a daily basis - var today = DateTime.Now.ToString("yyyy-MM-dd"); - if (today == _currentLogDate && _fileListener != null) - return; - - if (_fileListener != null) - { - Trace.Listeners.Remove(_fileListener); - _fileListener.Flush(); - _fileListener.Close(); - _fileListener.Dispose(); - } - - var logDir = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Logs"); - Directory.CreateDirectory(logDir); - - var logFileName = Path.Combine(logDir, $"generalupdate-trace {today}.log"); - _fileListener = new TextTraceListener(logFileName); - - Trace.Listeners.Add(_fileListener); - _currentLogDate = today; - } - - public static void Debug(string message) => WriteTraceMessage(TraceLevel.Verbose, message); - - public static void Info(string message) => WriteTraceMessage(TraceLevel.Info, message); - - public static void Warn(string message) => WriteTraceMessage(TraceLevel.Warning, message); - - public static void Error(string message) => WriteTraceMessage(TraceLevel.Error, message); - - public static void Fatal(string message) => WriteTraceMessage(TraceLevel.Off, message); - - public static void Error(string message, Exception ex) - { - var fullMessage = $"{message}{Environment.NewLine} Exception Details: {ex}"; - WriteTraceMessage(TraceLevel.Error, fullMessage); - } - - public static void Fatal(string message, Exception ex) - { - var fullMessage = $"{message}{Environment.NewLine} Exception Details: {ex}"; - WriteTraceMessage(TraceLevel.Off, fullMessage); - } - - public static void SetTracingEnabled(bool enabled) - { - lock (_lockObj) - { - Trace.AutoFlush = enabled; - _isTracingEnabled = enabled; - foreach (TraceListener listener in Trace.Listeners) - { - listener.Filter = enabled ? null : new EventTypeFilter(SourceLevels.Off); - } - } - } - - public static bool IsTracingEnabled() - { - lock (_lockObj) - { - return _isTracingEnabled; - } - } - - private static void WriteTraceMessage(TraceLevel level, string message) - { - if(!IsTracingEnabled()) - return; - - InitializeFileListener(); - var timestamp = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff"); - var levelName = GetLevelName(level); - var fullMessage = string.Empty; - - try - { - var stackFrame = new StackFrame(2, true); - var method = stackFrame.GetMethod(); - var className = method.DeclaringType?.Name ?? "UnknownType"; - var methodName = method.Name; - var lineNumber = stackFrame.GetFileLineNumber(); - var lineInfo = lineNumber > 0 ? $"Line {lineNumber}" : "Line N/A (Line numbers may not be displayed in Release mode)"; - fullMessage = $"[{timestamp}] [{levelName}] {className}.{methodName} ({lineInfo}): {message}"; - } - catch - { - fullMessage = $"[{timestamp}] [{levelName}] : {message}"; - } - - Trace.WriteLine(fullMessage); - } - - private static string GetLevelName(TraceLevel level) => level switch - { - TraceLevel.Verbose => "DEBUG", - TraceLevel.Info => "INFO", - TraceLevel.Warning => "WARN", - TraceLevel.Error => "ERROR", - TraceLevel.Off => "FATAL", - _ => "UNKNOWN" - }; - - public static void Dispose() - { - try - { - lock (_lockObj) - { - if (_fileListener is not null) - { - _fileListener.Flush(); - _fileListener.Close(); - _fileListener.Dispose(); - _fileListener = null; - } - - Trace.Listeners.Clear(); - } - } - catch - { - // ignored - } - } -} \ No newline at end of file diff --git a/src/c#/GeneralUpdate.Common/Shared/Trace/TextTraceListener.cs b/src/c#/GeneralUpdate.Common/Shared/Trace/TextTraceListener.cs deleted file mode 100644 index 835f04ab..00000000 --- a/src/c#/GeneralUpdate.Common/Shared/Trace/TextTraceListener.cs +++ /dev/null @@ -1,63 +0,0 @@ -using System; -using System.Diagnostics; -using System.IO; -using System.Collections.Concurrent; -using System.Threading; - -public class TextTraceListener : TraceListener, IDisposable -{ - private readonly string _filePath; - private readonly BlockingCollection _messageQueue; - private readonly Thread _loggingThread; - private volatile bool _isDisposed; - - public TextTraceListener(string filePath) - { - _filePath = filePath; - _messageQueue = new BlockingCollection(); - _loggingThread = new Thread(ProcessQueue); - _loggingThread.IsBackground = true; - _loggingThread.Start(); - } - - public override void Write(string? message) - { - QueueMessage(message); - } - - public override void WriteLine(string? message) - { - QueueMessage(message + Environment.NewLine); - } - - private void QueueMessage(string? message) - { - if (!_isDisposed && message != null) - { - _messageQueue.Add(message); - } - } - - private void ProcessQueue() - { - foreach (var message in _messageQueue.GetConsumingEnumerable()) - { - using (var fileStream = new FileStream(_filePath, FileMode.Append, FileAccess.Write, FileShare.ReadWrite)) - using (var writer = new StreamWriter(fileStream)) - { - writer.Write(message); - } - } - } - - public void Dispose() - { - if (!_isDisposed) - { - _isDisposed = true; - _messageQueue.CompleteAdding(); - _loggingThread.Join(); - _messageQueue.Dispose(); - } - } -} \ No newline at end of file diff --git a/src/c#/GeneralUpdate.Common/Shared/Trace/WindowsOutputDebugListener.cs b/src/c#/GeneralUpdate.Common/Shared/Trace/WindowsOutputDebugListener.cs deleted file mode 100644 index f7455a32..00000000 --- a/src/c#/GeneralUpdate.Common/Shared/Trace/WindowsOutputDebugListener.cs +++ /dev/null @@ -1,31 +0,0 @@ -using System; -using System.Diagnostics; -using System.Runtime.InteropServices; - -namespace GeneralUpdate.Common.Shared; - -public class WindowsOutputDebugListener : TraceListener -{ - /// - /// Does not affect .NET AOT compilation and runtime on the Windows platform, provided that the following conditions are met: - /// The target platform is restricted to Windows (due to the dependency on kernel32.dll); - /// The function declaration is static (not dynamically generated). - /// This syntax is safe in an AOT environment and can properly work with Dbgview.exe to capture logs. - /// - /// - [DllImport("kernel32.dll", CharSet = CharSet.Auto)] - private static extern void OutputDebugString(string lpOutputString); - - public override void Write(string message) - { - if (!string.IsNullOrEmpty(message)) - { - OutputDebugString(message); - } - } - - public override void WriteLine(string message) - { - Write($"{message}{Environment.NewLine}"); - } -} \ No newline at end of file diff --git a/src/c#/GeneralUpdate.Core/GeneralUpdate.Core.csproj b/src/c#/GeneralUpdate.Core/GeneralUpdate.Core.csproj index de6d0919..e93ee2a2 100644 --- a/src/c#/GeneralUpdate.Core/GeneralUpdate.Core.csproj +++ b/src/c#/GeneralUpdate.Core/GeneralUpdate.Core.csproj @@ -12,6 +12,9 @@ upgrade,update,client 10.0.0-preview netstandard2.0 + + true + true diff --git a/src/c#/GeneralUpdate.Drivelution/Core/DriverUpdaterFactory.cs b/src/c#/GeneralUpdate.Drivelution/Core/DriverUpdaterFactory.cs index b65af1b9..ef8b8a59 100644 --- a/src/c#/GeneralUpdate.Drivelution/Core/DriverUpdaterFactory.cs +++ b/src/c#/GeneralUpdate.Drivelution/Core/DriverUpdaterFactory.cs @@ -1,5 +1,5 @@ using System.Runtime.InteropServices; -using GeneralUpdate.Common.Shared; +using GeneralUpdate.Core; using GeneralUpdate.Drivelution.Abstractions; using GeneralUpdate.Drivelution.Abstractions.Configuration; using GeneralUpdate.Drivelution.Core.Execution; diff --git a/src/c#/GeneralUpdate.Drivelution/Core/Execution/CommandRunner.cs b/src/c#/GeneralUpdate.Drivelution/Core/Execution/CommandRunner.cs index 85dbce29..21c72e24 100644 --- a/src/c#/GeneralUpdate.Drivelution/Core/Execution/CommandRunner.cs +++ b/src/c#/GeneralUpdate.Drivelution/Core/Execution/CommandRunner.cs @@ -1,6 +1,6 @@ using System.Diagnostics; using System.Text; -using GeneralUpdate.Common.Shared; +using GeneralUpdate.Core; namespace GeneralUpdate.Drivelution.Core.Execution; diff --git a/src/c#/GeneralUpdate.Drivelution/Core/Pipeline/BaseDriverUpdater.cs b/src/c#/GeneralUpdate.Drivelution/Core/Pipeline/BaseDriverUpdater.cs index 70702027..4853c642 100644 --- a/src/c#/GeneralUpdate.Drivelution/Core/Pipeline/BaseDriverUpdater.cs +++ b/src/c#/GeneralUpdate.Drivelution/Core/Pipeline/BaseDriverUpdater.cs @@ -1,4 +1,4 @@ -using GeneralUpdate.Common.Shared; +using GeneralUpdate.Core; using GeneralUpdate.Drivelution.Abstractions; using GeneralUpdate.Drivelution.Abstractions.Configuration; using GeneralUpdate.Drivelution.Abstractions.Exceptions; diff --git a/src/c#/GeneralUpdate.Drivelution/Core/Pipeline/DefaultPipelineSteps.cs b/src/c#/GeneralUpdate.Drivelution/Core/Pipeline/DefaultPipelineSteps.cs index 3b04d02f..f20e8fc8 100644 --- a/src/c#/GeneralUpdate.Drivelution/Core/Pipeline/DefaultPipelineSteps.cs +++ b/src/c#/GeneralUpdate.Drivelution/Core/Pipeline/DefaultPipelineSteps.cs @@ -1,4 +1,4 @@ -using GeneralUpdate.Common.Shared; +using GeneralUpdate.Core; using GeneralUpdate.Drivelution.Abstractions; using GeneralUpdate.Drivelution.Abstractions.Models; using GeneralUpdate.Drivelution.Core.Utilities; diff --git a/src/c#/GeneralUpdate.Drivelution/GeneralDrivelution.cs b/src/c#/GeneralUpdate.Drivelution/GeneralDrivelution.cs index 2b58ab25..1b848880 100644 --- a/src/c#/GeneralUpdate.Drivelution/GeneralDrivelution.cs +++ b/src/c#/GeneralUpdate.Drivelution/GeneralDrivelution.cs @@ -1,4 +1,4 @@ -using GeneralUpdate.Common.Shared; +using GeneralUpdate.Core; using GeneralUpdate.Drivelution.Abstractions; using GeneralUpdate.Drivelution.Abstractions.Configuration; using GeneralUpdate.Drivelution.Abstractions.Models; diff --git a/src/c#/GeneralUpdate.Drivelution/GeneralUpdate.Drivelution.csproj b/src/c#/GeneralUpdate.Drivelution/GeneralUpdate.Drivelution.csproj index 7b3594ca..7c72c449 100644 --- a/src/c#/GeneralUpdate.Drivelution/GeneralUpdate.Drivelution.csproj +++ b/src/c#/GeneralUpdate.Drivelution/GeneralUpdate.Drivelution.csproj @@ -1,4 +1,4 @@ - + net10.0 @@ -28,11 +28,9 @@ true - + - - - + @@ -48,6 +46,7 @@ ./
+ diff --git a/src/c#/GeneralUpdate.Drivelution/Linux/Implementation/LinuxDriverBackup.cs b/src/c#/GeneralUpdate.Drivelution/Linux/Implementation/LinuxDriverBackup.cs index 0fda85e8..4cc2901c 100644 --- a/src/c#/GeneralUpdate.Drivelution/Linux/Implementation/LinuxDriverBackup.cs +++ b/src/c#/GeneralUpdate.Drivelution/Linux/Implementation/LinuxDriverBackup.cs @@ -1,5 +1,5 @@ using System.Runtime.Versioning; -using GeneralUpdate.Common.Shared; +using GeneralUpdate.Core; using GeneralUpdate.Drivelution.Abstractions; using GeneralUpdate.Drivelution.Abstractions.Exceptions; diff --git a/src/c#/GeneralUpdate.Drivelution/Linux/Implementation/LinuxDriverValidator.cs b/src/c#/GeneralUpdate.Drivelution/Linux/Implementation/LinuxDriverValidator.cs index aed8cc49..1e1a6416 100644 --- a/src/c#/GeneralUpdate.Drivelution/Linux/Implementation/LinuxDriverValidator.cs +++ b/src/c#/GeneralUpdate.Drivelution/Linux/Implementation/LinuxDriverValidator.cs @@ -1,5 +1,5 @@ using System.Runtime.Versioning; -using GeneralUpdate.Common.Shared; +using GeneralUpdate.Core; using GeneralUpdate.Drivelution.Abstractions; using GeneralUpdate.Drivelution.Abstractions.Models; using GeneralUpdate.Drivelution.Core.Utilities; diff --git a/src/c#/GeneralUpdate.Drivelution/Linux/Implementation/LinuxGeneralDrivelution.cs b/src/c#/GeneralUpdate.Drivelution/Linux/Implementation/LinuxGeneralDrivelution.cs index 0de6387a..af73c40a 100644 --- a/src/c#/GeneralUpdate.Drivelution/Linux/Implementation/LinuxGeneralDrivelution.cs +++ b/src/c#/GeneralUpdate.Drivelution/Linux/Implementation/LinuxGeneralDrivelution.cs @@ -1,5 +1,5 @@ using System.Runtime.Versioning; -using GeneralUpdate.Common.Shared; +using GeneralUpdate.Core; using GeneralUpdate.Drivelution.Abstractions; using GeneralUpdate.Drivelution.Abstractions.Configuration; using GeneralUpdate.Drivelution.Abstractions.Exceptions; diff --git a/src/c#/GeneralUpdate.Drivelution/MacOS/Implementation/MacOsGeneralDrivelution.cs b/src/c#/GeneralUpdate.Drivelution/MacOS/Implementation/MacOsGeneralDrivelution.cs index 6e01d098..e8c4a6a5 100644 --- a/src/c#/GeneralUpdate.Drivelution/MacOS/Implementation/MacOsGeneralDrivelution.cs +++ b/src/c#/GeneralUpdate.Drivelution/MacOS/Implementation/MacOsGeneralDrivelution.cs @@ -1,6 +1,6 @@ using System.Runtime.InteropServices; using System.Runtime.Versioning; -using GeneralUpdate.Common.Shared; +using GeneralUpdate.Core; using GeneralUpdate.Drivelution.Abstractions; using GeneralUpdate.Drivelution.Abstractions.Configuration; using GeneralUpdate.Drivelution.Abstractions.Exceptions; diff --git a/src/c#/GeneralUpdate.Drivelution/Windows/Implementation/WindowsDriverBackup.cs b/src/c#/GeneralUpdate.Drivelution/Windows/Implementation/WindowsDriverBackup.cs index 40f88f42..530ad80f 100644 --- a/src/c#/GeneralUpdate.Drivelution/Windows/Implementation/WindowsDriverBackup.cs +++ b/src/c#/GeneralUpdate.Drivelution/Windows/Implementation/WindowsDriverBackup.cs @@ -1,5 +1,5 @@ using System.Runtime.Versioning; -using GeneralUpdate.Common.Shared; +using GeneralUpdate.Core; using GeneralUpdate.Drivelution.Abstractions; using GeneralUpdate.Drivelution.Abstractions.Exceptions; diff --git a/src/c#/GeneralUpdate.Drivelution/Windows/Implementation/WindowsDriverValidator.cs b/src/c#/GeneralUpdate.Drivelution/Windows/Implementation/WindowsDriverValidator.cs index d3cff48c..9997a972 100644 --- a/src/c#/GeneralUpdate.Drivelution/Windows/Implementation/WindowsDriverValidator.cs +++ b/src/c#/GeneralUpdate.Drivelution/Windows/Implementation/WindowsDriverValidator.cs @@ -1,5 +1,5 @@ using System.Runtime.Versioning; -using GeneralUpdate.Common.Shared; +using GeneralUpdate.Core; using GeneralUpdate.Drivelution.Abstractions; using GeneralUpdate.Drivelution.Abstractions.Exceptions; using GeneralUpdate.Drivelution.Abstractions.Models; diff --git a/src/c#/GeneralUpdate.Drivelution/Windows/Implementation/WindowsGeneralDrivelution.cs b/src/c#/GeneralUpdate.Drivelution/Windows/Implementation/WindowsGeneralDrivelution.cs index cd45cb59..59693570 100644 --- a/src/c#/GeneralUpdate.Drivelution/Windows/Implementation/WindowsGeneralDrivelution.cs +++ b/src/c#/GeneralUpdate.Drivelution/Windows/Implementation/WindowsGeneralDrivelution.cs @@ -1,5 +1,5 @@ using System.Runtime.Versioning; -using GeneralUpdate.Common.Shared; +using GeneralUpdate.Core; using GeneralUpdate.Drivelution.Abstractions; using GeneralUpdate.Drivelution.Abstractions.Configuration; using GeneralUpdate.Drivelution.Abstractions.Exceptions; diff --git a/src/c#/GeneralUpdate.Extension/Core/GeneralExtensionHost.cs b/src/c#/GeneralUpdate.Extension/Core/GeneralExtensionHost.cs index 62521da0..c1f0246f 100644 --- a/src/c#/GeneralUpdate.Extension/Core/GeneralExtensionHost.cs +++ b/src/c#/GeneralUpdate.Extension/Core/GeneralExtensionHost.cs @@ -7,7 +7,7 @@ using GeneralUpdate.Extension.Dependencies; using GeneralUpdate.Extension.Communication; using GeneralUpdate.Extension.Common.Models; -using GeneralUpdate.Common.Shared; +using GeneralUpdate.Core; using Newtonsoft.Json; using System; diff --git a/src/c#/GeneralUpdate.Extension/Download/DownloadQueueManager.cs b/src/c#/GeneralUpdate.Extension/Download/DownloadQueueManager.cs index 55ad81e8..0ebc8068 100644 --- a/src/c#/GeneralUpdate.Extension/Download/DownloadQueueManager.cs +++ b/src/c#/GeneralUpdate.Extension/Download/DownloadQueueManager.cs @@ -7,7 +7,7 @@ using GeneralUpdate.Extension.Dependencies; using GeneralUpdate.Extension.Communication; using GeneralUpdate.Extension.Common.Models; -using GeneralUpdate.Common.Shared; +using GeneralUpdate.Core; using System; using System.Collections.Generic; diff --git a/src/c#/GeneralUpdate.Extension/GeneralUpdate.Extension.csproj b/src/c#/GeneralUpdate.Extension/GeneralUpdate.Extension.csproj index fa075471..a2677d11 100644 --- a/src/c#/GeneralUpdate.Extension/GeneralUpdate.Extension.csproj +++ b/src/c#/GeneralUpdate.Extension/GeneralUpdate.Extension.csproj @@ -23,22 +23,19 @@ - - - + + + + + + + - - - - - - -