mirror of
https://github.com/wwiinnddyy/LanMountainDesktop.git
synced 2026-06-22 09:14:25 +08:00
试试
This commit is contained in:
8
LanMountainDesktop.Launcher/Assets/public-key.pem
Normal file
8
LanMountainDesktop.Launcher/Assets/public-key.pem
Normal file
@@ -0,0 +1,8 @@
|
||||
-----BEGIN RSA PUBLIC KEY-----
|
||||
MIIBCgKCAQEAxPqgXsrnG8Re0kV4HBb+x61HQpjCahJoilzKvvlnXanuGtGxbjZT
|
||||
B+kMzmPUwyx8gt1fcaBNoKPwpwP0UZRWjvJDZQ++5ex7LGGw0YRWtJmeeigS17YI
|
||||
90vEfX3xQ5InJoBKnndsRy2a742chE6YwHGrJ4b107ZJ+zd26FmokQS47Uzay3go
|
||||
msbQHdehwCdCiW1mh8YFDm0xny+PYoYZkGXiDOYY0nvg4yJ/BG2fQkkC5TNizr0l
|
||||
YcE3RrMRcyJB7zU3jN1QnjHIvIvwfCOXaLdcXtxgQFRv45sYpmj9amNjuurM5iUa
|
||||
20Mk1ilYBuLxqe6P9C8DakZY/akVxpzxrQIDAQAB
|
||||
-----END RSA PUBLIC KEY-----
|
||||
@@ -19,6 +19,30 @@
|
||||
<PackageReference Include="Avalonia.Themes.Fluent" Version="11.3.12" />
|
||||
<PackageReference Include="Avalonia.Fonts.Inter" Version="11.3.12" />
|
||||
<PackageReference Include="Microsoft.Windows.CsWin32" Version="0.3.106" PrivateAssets="all" />
|
||||
<PackageReference Include="Tmds.DBus.Protocol" Version="0.92.0" />
|
||||
</ItemGroup>
|
||||
|
||||
<!-- Embed public-key.pem and copy to .launcher/update/ in output directory -->
|
||||
<ItemGroup>
|
||||
<None Include="Assets\public-key.pem" CopyToOutputDirectory="PreserveNewest" />
|
||||
</ItemGroup>
|
||||
|
||||
<Target Name="CopyPublicKeyToLauncherDir" AfterTargets="Build">
|
||||
<PropertyGroup>
|
||||
<PublicKeySource>$(MSBuildProjectDirectory)\Assets\public-key.pem</PublicKeySource>
|
||||
<PublicKeyDestDir>$(OutDir).launcher\update</PublicKeyDestDir>
|
||||
</PropertyGroup>
|
||||
<MakeDir Directories="$(PublicKeyDestDir)" />
|
||||
<Copy SourceFiles="$(PublicKeySource)" DestinationFolder="$(PublicKeyDestDir)" SkipUnchangedFiles="true" />
|
||||
</Target>
|
||||
|
||||
<Target Name="CopyPublicKeyToPublishDir" AfterTargets="Publish">
|
||||
<PropertyGroup>
|
||||
<PublishedKeySource>$(MSBuildProjectDirectory)\Assets\public-key.pem</PublishedKeySource>
|
||||
<PublishedKeyDestDir>$(PublishDir).launcher\update</PublishedKeyDestDir>
|
||||
</PropertyGroup>
|
||||
<MakeDir Directories="$(PublishedKeyDestDir)" />
|
||||
<Copy SourceFiles="$(PublishedKeySource)" DestinationFolder="$(PublishedKeyDestDir)" SkipUnchangedFiles="true" />
|
||||
</Target>
|
||||
|
||||
</Project>
|
||||
|
||||
@@ -89,7 +89,7 @@ internal static class Commands
|
||||
return context.SubCommand.ToLowerInvariant() switch
|
||||
{
|
||||
"check" => updateEngine.CheckPendingUpdate(),
|
||||
"apply" => updateEngine.ApplyPendingUpdate(),
|
||||
"apply" => await updateEngine.ApplyPendingUpdateAsync().ConfigureAwait(false),
|
||||
"rollback" => updateEngine.RollbackLatest(),
|
||||
"download" => await updateEngine.DownloadAsync(
|
||||
context.GetOption("manifest-url") ?? throw new InvalidOperationException("Missing --manifest-url."),
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
using Avalonia.Threading;
|
||||
using LanMountainDesktop.Launcher.Views;
|
||||
|
||||
namespace LanMountainDesktop.Launcher.Services;
|
||||
|
||||
internal sealed class DeferredSplashStageReporter : ISplashStageReporter
|
||||
{
|
||||
private ISplashStageReporter? _inner;
|
||||
private readonly List<(string Stage, string Message)> _pending = [];
|
||||
|
||||
public void SetInner(ISplashStageReporter inner)
|
||||
{
|
||||
_inner = inner;
|
||||
foreach (var (stage, message) in _pending)
|
||||
{
|
||||
_inner.Report(stage, message);
|
||||
}
|
||||
_pending.Clear();
|
||||
}
|
||||
|
||||
public void Report(string stage, string message)
|
||||
{
|
||||
if (_inner is not null)
|
||||
{
|
||||
_inner.Report(stage, message);
|
||||
}
|
||||
else
|
||||
{
|
||||
_pending.Add((stage, message));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -13,7 +13,6 @@ internal sealed class LauncherFlowCoordinator
|
||||
private readonly UpdateEngineService _updateEngine;
|
||||
private readonly UpdateCheckService _updateCheckService;
|
||||
private readonly PluginInstallerService _pluginInstallerService;
|
||||
private readonly ISplashStageReporter _splashStageReporter;
|
||||
private readonly IReadOnlyList<IOobeStep> _oobeSteps;
|
||||
|
||||
public LauncherFlowCoordinator(
|
||||
@@ -30,7 +29,6 @@ internal sealed class LauncherFlowCoordinator
|
||||
_updateEngine = updateEngine;
|
||||
_updateCheckService = updateCheckService;
|
||||
_pluginInstallerService = pluginInstallerService;
|
||||
_splashStageReporter = new NullSplashStageReporter();
|
||||
_oobeSteps = [new WelcomeOobeStep(_oobeStateService)];
|
||||
}
|
||||
|
||||
@@ -41,7 +39,6 @@ internal sealed class LauncherFlowCoordinator
|
||||
// 清理待删除的旧版本
|
||||
_deploymentLocator.CleanupDestroyedDeployments();
|
||||
|
||||
_splashStageReporter.Report("bootstrap", "bootstrap");
|
||||
if (_oobeStateService.IsFirstRun())
|
||||
{
|
||||
foreach (var step in _oobeSteps)
|
||||
@@ -57,16 +54,18 @@ internal sealed class LauncherFlowCoordinator
|
||||
return window;
|
||||
});
|
||||
|
||||
var reporter = (ISplashStageReporter)splashWindow;
|
||||
|
||||
try
|
||||
{
|
||||
_splashStageReporter.Report("silentUpdate", "update");
|
||||
var updateResult = _updateEngine.ApplyPendingUpdate();
|
||||
reporter.Report("silentUpdate", "update");
|
||||
var updateResult = await _updateEngine.ApplyPendingUpdateAsync().ConfigureAwait(false);
|
||||
if (!updateResult.Success)
|
||||
{
|
||||
return updateResult;
|
||||
}
|
||||
|
||||
_splashStageReporter.Report("pluginTasks", "plugins");
|
||||
reporter.Report("pluginTasks", "plugins");
|
||||
var pluginsDir = _context.GetOption("plugins-dir")
|
||||
?? Path.Combine(_deploymentLocator.GetAppRoot(), "plugins");
|
||||
var queueResult = new PluginUpgradeQueueService(_pluginInstallerService).ApplyPendingUpgrades(pluginsDir);
|
||||
@@ -75,7 +74,7 @@ internal sealed class LauncherFlowCoordinator
|
||||
return queueResult;
|
||||
}
|
||||
|
||||
_splashStageReporter.Report("launchHost", "launch");
|
||||
reporter.Report("launchHost", "launch");
|
||||
var hostResult = LaunchHost();
|
||||
if (!hostResult.Success)
|
||||
{
|
||||
@@ -192,13 +191,4 @@ internal sealed class LauncherFlowCoordinator
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private sealed class NullSplashStageReporter : ISplashStageReporter
|
||||
{
|
||||
public void Report(string stage, string message)
|
||||
{
|
||||
_ = stage;
|
||||
_ = message;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -110,7 +110,7 @@ internal sealed class UpdateEngineService
|
||||
};
|
||||
}
|
||||
|
||||
public LauncherResult ApplyPendingUpdate()
|
||||
public async Task<LauncherResult> ApplyPendingUpdateAsync()
|
||||
{
|
||||
Directory.CreateDirectory(_incomingRoot);
|
||||
Directory.CreateDirectory(_snapshotsRoot);
|
||||
@@ -136,7 +136,7 @@ internal sealed class UpdateEngineService
|
||||
return Failed("update.apply", "signature_failed", verifyResult.Message);
|
||||
}
|
||||
|
||||
var fileMapText = File.ReadAllText(fileMapPath);
|
||||
var fileMapText = await File.ReadAllTextAsync(fileMapPath);
|
||||
var fileMap = JsonSerializer.Deserialize<SignedFileMap>(fileMapText);
|
||||
if (fileMap is null || fileMap.Files.Count == 0)
|
||||
{
|
||||
|
||||
@@ -3,15 +3,38 @@
|
||||
x:Class="LanMountainDesktop.Launcher.Views.SplashWindow"
|
||||
Title="阑山桌面"
|
||||
Width="420"
|
||||
Height="220"
|
||||
Height="240"
|
||||
CanResize="False"
|
||||
WindowStartupLocation="CenterScreen"
|
||||
SystemDecorations="None">
|
||||
<Grid Margin="24">
|
||||
<Grid Margin="24" RowDefinitions="*,Auto,Auto,Auto">
|
||||
<TextBlock x:Name="AppNameText"
|
||||
Text="阑山桌面"
|
||||
FontSize="34"
|
||||
VerticalAlignment="Center"
|
||||
HorizontalAlignment="Center" />
|
||||
HorizontalAlignment="Center"
|
||||
Grid.Row="0" />
|
||||
<ProgressBar x:Name="ProgressIndicator"
|
||||
Grid.Row="1"
|
||||
Minimum="0"
|
||||
Maximum="100"
|
||||
Value="0"
|
||||
Height="4"
|
||||
Margin="0,12,0,0"
|
||||
IsIndeterminate="True" />
|
||||
<TextBlock x:Name="StageText"
|
||||
Grid.Row="2"
|
||||
FontSize="12"
|
||||
Foreground="#999999"
|
||||
HorizontalAlignment="Center"
|
||||
Margin="0,8,0,0"
|
||||
Text="" />
|
||||
<TextBlock x:Name="DetailText"
|
||||
Grid.Row="3"
|
||||
FontSize="11"
|
||||
Foreground="#BBBBBB"
|
||||
HorizontalAlignment="Center"
|
||||
Margin="0,2,0,0"
|
||||
Text="" />
|
||||
</Grid>
|
||||
</Window>
|
||||
|
||||
@@ -1,12 +1,48 @@
|
||||
using Avalonia.Controls;
|
||||
using Avalonia.Markup.Xaml;
|
||||
using LanMountainDesktop.Launcher.Services;
|
||||
|
||||
namespace LanMountainDesktop.Launcher.Views;
|
||||
|
||||
internal partial class SplashWindow : Window
|
||||
internal partial class SplashWindow : Window, ISplashStageReporter
|
||||
{
|
||||
private static readonly (string Stage, string Label, double Progress)[] StageMap =
|
||||
[
|
||||
("bootstrap", "正在初始化...", 10),
|
||||
("silentUpdate", "正在应用更新...", 35),
|
||||
("pluginTasks", "正在处理插件...", 65),
|
||||
("launchHost", "正在启动...", 90),
|
||||
];
|
||||
|
||||
public SplashWindow()
|
||||
{
|
||||
AvaloniaXamlLoader.Load(this);
|
||||
}
|
||||
|
||||
public void Report(string stage, string message)
|
||||
{
|
||||
var (label, progress) = ResolveStageInfo(stage);
|
||||
|
||||
var stageText = this.GetControl<TextBlock>("StageText");
|
||||
var detailText = this.GetControl<TextBlock>("DetailText");
|
||||
var progressIndicator = this.GetControl<ProgressBar>("ProgressIndicator");
|
||||
|
||||
stageText.Text = label;
|
||||
detailText.Text = message;
|
||||
progressIndicator.IsIndeterminate = false;
|
||||
progressIndicator.Value = progress;
|
||||
}
|
||||
|
||||
private static (string Label, double Progress) ResolveStageInfo(string stage)
|
||||
{
|
||||
foreach (var (s, label, progress) in StageMap)
|
||||
{
|
||||
if (string.Equals(s, stage, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
return (label, progress);
|
||||
}
|
||||
}
|
||||
|
||||
return (stage, 0);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user