From 68a735716d1ff0f419a542ac1099b81d11d0f059 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=E2=96=88=E2=96=88=E2=96=88=E2=96=88=E2=96=88?= Date: Sun, 22 Jun 2025 16:53:00 -0400 Subject: [PATCH] feat: adding button to restart machine --- src/SiteMonitor/Constants.cs | 13 ++ .../ViewModels/MainWindowViewModel.cs | 74 +++++++++-- src/SiteMonitor/Views/MainWindow.axaml | 117 ++++++++++++------ src/SiteMonitor/Views/MainWindow.axaml.cs | 65 +++++++--- 4 files changed, 211 insertions(+), 58 deletions(-) create mode 100644 src/SiteMonitor/Constants.cs diff --git a/src/SiteMonitor/Constants.cs b/src/SiteMonitor/Constants.cs new file mode 100644 index 0000000..e189039 --- /dev/null +++ b/src/SiteMonitor/Constants.cs @@ -0,0 +1,13 @@ +using System.Reflection; + +namespace SiteMonitor; + +/// +/// Constants used throughout the file. +/// +public class Constants { + /// + /// The version of the application being run right now. + /// + public static readonly string? APP_VERSION = Assembly.GetEntryAssembly()?.GetName().Version?.ToString()[..^2]; +} \ No newline at end of file diff --git a/src/SiteMonitor/ViewModels/MainWindowViewModel.cs b/src/SiteMonitor/ViewModels/MainWindowViewModel.cs index 6ddda96..4fad562 100644 --- a/src/SiteMonitor/ViewModels/MainWindowViewModel.cs +++ b/src/SiteMonitor/ViewModels/MainWindowViewModel.cs @@ -3,14 +3,16 @@ using System.Net; using System.Net.Http; using System.Net.NetworkInformation; +using System.Threading; using System.Threading.Tasks; +using System.Windows.Input; using Avalonia.Controls; -using Nullinside.Api.Common; - using ReactiveUI; +using Renci.SshNet; + using SiteMonitor.Models; namespace SiteMonitor.ViewModels; @@ -19,19 +21,24 @@ namespace SiteMonitor.ViewModels; /// The view model for the main UI. /// public class MainWindowViewModel : ViewModelBase { - private bool _apiUp; + private bool _apiUp = true; private string? _chatTimestamp; + private bool _isDisplayingAdvancedCommands; private bool _isMinimized; - private bool _nullUp; + private bool _nullUp = true; private string? _serverAddress; - private bool _serverUp; - private bool _websiteUp; + private bool _serverUp = true; + private string? _sshPassword; + private string? _sshUsername; + private bool _websiteUp = true; private WindowState _windowState; /// /// Initializes a new instance of the class. /// public MainWindowViewModel() { + OnShowCommandsCommand = ReactiveCommand.Create(OnShowCommands); + OnRestartCommand = ReactiveCommand.Create(OnRestart); Task.Factory.StartNew(PingServer); Task.Factory.StartNew(PingSite); ServerAddress = Configuration.Instance.ServerAddress; @@ -112,6 +119,57 @@ public bool IsMinimized { set => this.RaiseAndSetIfChanged(ref _isMinimized, value); } + /// + /// Shows the commands in the UI. + /// + public ICommand OnShowCommandsCommand { get; set; } + + /// + /// True if displaying advanced commands, false otherwise. + /// + public bool IsDisplayingAdvancedCommands { + get => _isDisplayingAdvancedCommands; + set => this.RaiseAndSetIfChanged(ref _isDisplayingAdvancedCommands, value); + } + + /// + /// The username to use for the SSH session for commands. + /// + public string? SshUsername { + get => _sshUsername; + set => this.RaiseAndSetIfChanged(ref _sshUsername, value); + } + + /// + /// The password to use for the SSH session for commands. + /// + public string? SshPassword { + get => _sshPassword; + set => this.RaiseAndSetIfChanged(ref _sshPassword, value); + } + + /// + /// Restarts the remote machine. + /// + public ICommand OnRestartCommand { get; set; } + + /// + /// Restarts the remote machine. + /// + private async Task OnRestart() { + using SshClient client = new(_serverAddress!, _sshUsername!, _sshPassword!); + await client.ConnectAsync(CancellationToken.None); + string command = "shutdown -r now"; + using SshCommand? ssh = client.RunCommand($"echo {_sshPassword} | sudo -S {command}"); + } + + /// + /// Handles showing the server commands. + /// + private void OnShowCommands() { + IsDisplayingAdvancedCommands = true; + } + /// /// Pings the web resources. /// @@ -152,7 +210,7 @@ private async Task SendHeadRequest(string address) { handler.AutomaticDecompression = ~DecompressionMethods.None; using var httpClient = new HttpClient(handler); using var request = new HttpRequestMessage(HttpMethod.Get, address); - request.Headers.TryAddWithoutValidation("user-agent", Constants.FAKE_USER_AGENT); + request.Headers.TryAddWithoutValidation("user-agent", Nullinside.Api.Common.Constants.FAKE_USER_AGENT); HttpResponseMessage response = await httpClient.SendAsync(request); return response.IsSuccessStatusCode; } @@ -172,7 +230,7 @@ private async Task SendHeadRequest(string address) { handler.AutomaticDecompression = ~DecompressionMethods.None; using var httpClient = new HttpClient(handler); using var request = new HttpRequestMessage(HttpMethod.Get, address); - request.Headers.TryAddWithoutValidation("user-agent", Constants.FAKE_USER_AGENT); + request.Headers.TryAddWithoutValidation("user-agent", Nullinside.Api.Common.Constants.FAKE_USER_AGENT); HttpResponseMessage response = await httpClient.SendAsync(request); return await response.Content.ReadAsStringAsync(); } diff --git a/src/SiteMonitor/Views/MainWindow.axaml b/src/SiteMonitor/Views/MainWindow.axaml index 3f7141f..e70bdde 100644 --- a/src/SiteMonitor/Views/MainWindow.axaml +++ b/src/SiteMonitor/Views/MainWindow.axaml @@ -7,7 +7,7 @@ d:DesignWidth="475" d:DesignHeight="300" Width="475" - Height="300" + Height="400" CanResize="False" WindowState="{Binding WindowState, Mode=TwoWay}" ShowInTaskbar="{Binding !IsMinimized, Mode=TwoWay}" @@ -24,41 +24,88 @@ - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + - - - - + + + - - - - - - - - - + + + + + + + + + + + + + + + + + + + + - - + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/SiteMonitor/Views/MainWindow.axaml.cs b/src/SiteMonitor/Views/MainWindow.axaml.cs index e552282..0f05a92 100644 --- a/src/SiteMonitor/Views/MainWindow.axaml.cs +++ b/src/SiteMonitor/Views/MainWindow.axaml.cs @@ -1,13 +1,20 @@ using System; -using System.Reflection; +using System.Linq; using System.Threading.Tasks; using Avalonia.Controls; -using Avalonia.Threading; using Nullinside.Api.Common.Desktop; +#if !DEBUG +using Microsoft.Extensions.DependencyInjection; + +using Avalonia.Threading; using SiteMonitor.ViewModels; +#else +using Avalonia; +#endif + namespace SiteMonitor.Views; @@ -20,41 +27,69 @@ public partial class MainWindow : Window { /// public MainWindow() { InitializeComponent(); +#if DEBUG + this.AttachDevTools(); +#endif } + /// + /// The service provider for DI. + /// + public IServiceProvider? ServiceProvider { get; set; } + /// /// Checks for a new version number of the application. /// protected override void OnInitialized() { base.OnInitialized(); + // handle the command line arguments for updating the application if applicable. + string[] args = Environment.GetCommandLineArgs(); + if (args.Contains("--update")) { + _ = GitHubUpdateManager.PerformUpdateAndRestart("nullinside-development-group", "twitch-streaming-tools", args[2], "windows-x64.zip"); + return; + } + + if (args.Contains("--justUpdated")) { + _ = GitHubUpdateManager.CleanupUpdate(); + } + + // check for a new version of the application. Task.Factory.StartNew(async () => { GithubLatestReleaseJson? serverVersion = await GitHubUpdateManager.GetLatestVersion("nullinside-development-group", "nullinside-site-monitor"); - string? localVersion = Assembly.GetEntryAssembly()?.GetName().Version?.ToString(); - if (null == serverVersion || string.IsNullOrWhiteSpace(serverVersion.name) || - string.IsNullOrWhiteSpace(localVersion)) { + if (null == serverVersion || string.IsNullOrWhiteSpace(serverVersion.name)) { return; } - localVersion = localVersion.Substring(0, localVersion.LastIndexOf('.')); - if (string.IsNullOrWhiteSpace(localVersion)) { + if (serverVersion.name?.Equals(Constants.APP_VERSION, StringComparison.InvariantCultureIgnoreCase) ?? true) { +// Had to add this because code clean up tools were removing the "redundant" return statement. +// which was causing the check to always be ignored. +#if !DEBUG return; +#endif } - if (serverVersion.name?.Equals(localVersion, StringComparison.InvariantCultureIgnoreCase) ?? true) { +#if !DEBUG + var vm = ServiceProvider?.GetRequiredService(); + if (null == vm) { return; } - Dispatcher.UIThread.Post(async () => { - var versionWindow = new NewVersionWindow { - DataContext = new NewVersionWindowViewModel { - LocalVersion = localVersion - } - }; + vm.LocalVersion = Constants.APP_VERSION; + Dispatcher.UIThread.Post(async void () => { + try { + var versionWindow = new NewVersionWindow { + DataContext = vm + }; - await versionWindow.ShowDialog(this); + await versionWindow.ShowDialog(this); + } + catch { + // do nothing, don't crash + } }); +#endif }); } } \ No newline at end of file