From 55cb346fb41d7f08df0e2713ecd5039f9a8733d2 Mon Sep 17 00:00:00 2001 From: Lightczx <1686188646@qq.com> Date: Fri, 22 Dec 2023 16:29:36 +0800 Subject: [PATCH] update service --- .../Snap.Hutao/Core/IO/Hashing/SHA256.cs | 23 +++++ .../Service/Abstraction/IUpdateService.cs | 8 ++ .../Snap.Hutao/Service/UpdateService.cs | 95 +++++++++++++++++++ .../Snap.Hutao/Service/UpdateStatus.cs | 25 +++++ .../Web/Hutao/HutaoVersionInformation.cs | 6 ++ 5 files changed, 157 insertions(+) create mode 100644 src/Snap.Hutao/Snap.Hutao/Core/IO/Hashing/SHA256.cs create mode 100644 src/Snap.Hutao/Snap.Hutao/Service/Abstraction/IUpdateService.cs create mode 100644 src/Snap.Hutao/Snap.Hutao/Service/UpdateService.cs create mode 100644 src/Snap.Hutao/Snap.Hutao/Service/UpdateStatus.cs diff --git a/src/Snap.Hutao/Snap.Hutao/Core/IO/Hashing/SHA256.cs b/src/Snap.Hutao/Snap.Hutao/Core/IO/Hashing/SHA256.cs new file mode 100644 index 00000000..4302ff77 --- /dev/null +++ b/src/Snap.Hutao/Snap.Hutao/Core/IO/Hashing/SHA256.cs @@ -0,0 +1,23 @@ +// Copyright (c) DGP Studio. All rights reserved. +// Licensed under the MIT license. + +using System.IO; + +namespace Snap.Hutao.Core.IO.Hashing; + +internal static class SHA256 +{ + public static async ValueTask HashFileAsync(string filePath, CancellationToken token = default) + { + using (FileStream stream = File.OpenRead(filePath)) + { + return await HashAsync(stream, token).ConfigureAwait(false); + } + } + + public static async ValueTask HashAsync(Stream stream, CancellationToken token = default) + { + byte[] bytes = await System.Security.Cryptography.SHA256.HashDataAsync(stream, token).ConfigureAwait(false); + return System.Convert.ToHexString(bytes); + } +} \ No newline at end of file diff --git a/src/Snap.Hutao/Snap.Hutao/Service/Abstraction/IUpdateService.cs b/src/Snap.Hutao/Snap.Hutao/Service/Abstraction/IUpdateService.cs new file mode 100644 index 00000000..162fecc4 --- /dev/null +++ b/src/Snap.Hutao/Snap.Hutao/Service/Abstraction/IUpdateService.cs @@ -0,0 +1,8 @@ +// Copyright (c) DGP Studio. All rights reserved. +// Licensed under the MIT license. + +namespace Snap.Hutao.Service.Abstraction; + +internal interface IUpdateService +{ +} \ No newline at end of file diff --git a/src/Snap.Hutao/Snap.Hutao/Service/UpdateService.cs b/src/Snap.Hutao/Snap.Hutao/Service/UpdateService.cs new file mode 100644 index 00000000..f6fa7ef4 --- /dev/null +++ b/src/Snap.Hutao/Snap.Hutao/Service/UpdateService.cs @@ -0,0 +1,95 @@ +// Copyright (c) DGP Studio. All rights reserved. +// Licensed under the MIT license. + +using Snap.Hutao.Core; +using Snap.Hutao.Core.IO.Hashing; +using Snap.Hutao.Core.IO.Http.Sharding; +using Snap.Hutao.Service.Abstraction; +using Snap.Hutao.Web.Hutao; +using Snap.Hutao.Web.Hutao.Response; +using System.IO; +using System.Net.Http; + +namespace Snap.Hutao.Service; + +[ConstructorGenerated] +[Injection(InjectAs.Singleton, typeof(IUpdateService))] +internal sealed partial class UpdateService : IUpdateService +{ + private readonly IServiceProvider serviceProvider; + + public async ValueTask InitializeAsync(IProgress progress, CancellationToken token = default) + { + using (IServiceScope scope = serviceProvider.CreateScope()) + { + ITaskContext taskContext = scope.ServiceProvider.GetRequiredService(); + await taskContext.SwitchToBackgroundAsync(); + + HutaoInfrastructureClient infrastructureClient = serviceProvider.GetRequiredService(); + HutaoResponse response = await infrastructureClient.GetHutaoVersionInfomationAsync(token).ConfigureAwait(false); + + if (!response.IsOk()) + { + return false; + } + + HutaoVersionInformation versionInformation = response.Data; + + if (versionInformation.Sha256 is not { } sha256) + { + return false; + } + + string dataFolder = scope.ServiceProvider.GetRequiredService().DataFolder; + string msixPath = Path.Combine(dataFolder, "UpdateCache/Snap.Hutao.msix"); + + if (await CheckUpdateCacheSHA256Async(msixPath, sha256, token).ConfigureAwait(false)) + { + return true; + } + + return await DownloadUpdatePackageAsync(versionInformation, msixPath, progress, token).ConfigureAwait(false); + } + } + + private static async ValueTask CheckUpdateCacheSHA256Async(string filePath, string remoteHash, CancellationToken token = default) + { + string localHash = await SHA256.HashFileAsync(filePath, token).ConfigureAwait(false); + return string.Equals(localHash, remoteHash, StringComparison.OrdinalIgnoreCase); + } + + private async ValueTask DownloadUpdatePackageAsync(HutaoVersionInformation versionInformation, string filePath, IProgress progress, CancellationToken token = default) + { + using (IServiceScope scope = serviceProvider.CreateScope()) + { + using (HttpClient httpClient = scope.ServiceProvider.GetRequiredService()) + { + string version = versionInformation.Version.ToString(); + foreach (string url in versionInformation.Urls) + { + HttpShardCopyWorkerOptions options = new() + { + HttpClient = httpClient, + SourceUrl = url, + DestinationFilePath = filePath, + StatusFactory = (bytesRead, totalBytes) => new UpdateStatus(version, bytesRead, totalBytes), + }; + + using (HttpShardCopyWorker worker = await HttpShardCopyWorker.CreateAsync(options).ConfigureAwait(false)) + { + await worker.CopyAsync(progress, token).ConfigureAwait(false); + } + + string? remoteHash = versionInformation.Sha256; + ArgumentNullException.ThrowIfNull(remoteHash); + if (await CheckUpdateCacheSHA256Async(filePath, remoteHash, token).ConfigureAwait(false)) + { + return true; + } + } + } + } + + return false; + } +} \ No newline at end of file diff --git a/src/Snap.Hutao/Snap.Hutao/Service/UpdateStatus.cs b/src/Snap.Hutao/Snap.Hutao/Service/UpdateStatus.cs new file mode 100644 index 00000000..163492e8 --- /dev/null +++ b/src/Snap.Hutao/Snap.Hutao/Service/UpdateStatus.cs @@ -0,0 +1,25 @@ +// Copyright (c) DGP Studio. All rights reserved. +// Licensed under the MIT license. + +using CommunityToolkit.Common; + +namespace Snap.Hutao.Service; + +internal sealed class UpdateStatus +{ + public UpdateStatus(string version, long bytesRead, long totalBytes) + { + Version = version; + BytesRead = bytesRead; + TotalBytes = totalBytes; + ProgressDescription = $"{Converters.ToFileSizeString(bytesRead)}/{Converters.ToFileSizeString(totalBytes)}"; + } + + public string? Version { get; set; } + + public long BytesRead { get; set; } + + public long TotalBytes { get; set; } + + public string ProgressDescription { get; } +} \ No newline at end of file diff --git a/src/Snap.Hutao/Snap.Hutao/Web/Hutao/HutaoVersionInformation.cs b/src/Snap.Hutao/Snap.Hutao/Web/Hutao/HutaoVersionInformation.cs index 729019c5..d05a3416 100644 --- a/src/Snap.Hutao/Snap.Hutao/Web/Hutao/HutaoVersionInformation.cs +++ b/src/Snap.Hutao/Snap.Hutao/Web/Hutao/HutaoVersionInformation.cs @@ -7,4 +7,10 @@ internal sealed class HutaoVersionInformation { [JsonPropertyName("version")] public Version Version { get; set; } = default!; + + [JsonPropertyName("urls")] + public List Urls { get; set; } = default!; + + [JsonPropertyName("sha256")] + public string? Sha256 { get; set; } = default!; } \ No newline at end of file