diff --git a/Moonlight.Api/Http/Controllers/Admin/ChController.cs b/Moonlight.Api/Http/Controllers/Admin/ChController.cs index 4fe148df..cb501b41 100644 --- a/Moonlight.Api/Http/Controllers/Admin/ChController.cs +++ b/Moonlight.Api/Http/Controllers/Admin/ChController.cs @@ -1,6 +1,9 @@ using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.Options; +using Moonlight.Api.Configuration; using Moonlight.Api.Services; +using Moonlight.Shared.Http.Responses.Admin; namespace Moonlight.Api.Http.Controllers.Admin; @@ -9,10 +12,23 @@ namespace Moonlight.Api.Http.Controllers.Admin; public class ChController : Controller { private readonly ContainerHelperService ContainerHelperService; + private readonly IOptions Options; - public ChController(ContainerHelperService containerHelperService) + public ChController(ContainerHelperService containerHelperService, IOptions options) { ContainerHelperService = containerHelperService; + Options = options; + } + + [HttpGet("status")] + public async Task> GetStatusAsync() + { + if (!Options.Value.IsEnabled) + return new ContainerHelperStatusDto(false, false); + + var status = await ContainerHelperService.CheckConnectionAsync(); + + return new ContainerHelperStatusDto(true, status); } [HttpPost("rebuild")] diff --git a/Moonlight.Api/Services/ContainerHelperService.cs b/Moonlight.Api/Services/ContainerHelperService.cs index 578fc4ba..feb6a142 100644 --- a/Moonlight.Api/Services/ContainerHelperService.cs +++ b/Moonlight.Api/Services/ContainerHelperService.cs @@ -13,6 +13,23 @@ public class ContainerHelperService HttpClientFactory = httpClientFactory; } + public async Task CheckConnectionAsync() + { + var client = HttpClientFactory.CreateClient("ContainerHelper"); + + try + { + var response = await client.GetAsync("api/ping"); + response.EnsureSuccessStatusCode(); + + return true; + } + catch (Exception) + { + return false; + } + } + public async IAsyncEnumerable RebuildAsync() { var options = new JsonSerializerOptions() @@ -29,6 +46,7 @@ public class ContainerHelperService if (!response.IsSuccessStatusCode) { var responseText = await response.Content.ReadAsStringAsync(); + yield return new RebuildEvent() { Type = RebuildEventType.Failed, @@ -56,6 +74,11 @@ public class ContainerHelperService var deserializedData = JsonSerializer.Deserialize(data, options); yield return deserializedData; + + // Exit if service will go down for a clean exit + if(deserializedData is {Type: RebuildEventType.Step, Data: "ServiceDown"}) + yield break; + } while (true); yield return new RebuildEvent() diff --git a/Moonlight.Frontend/UI/Admin/Modals/UpdateInstanceModal.razor b/Moonlight.Frontend/UI/Admin/Modals/UpdateInstanceModal.razor index 68f9eb96..8a48574f 100644 --- a/Moonlight.Frontend/UI/Admin/Modals/UpdateInstanceModal.razor +++ b/Moonlight.Frontend/UI/Admin/Modals/UpdateInstanceModal.razor @@ -2,14 +2,12 @@ @using System.Text.Json @using LucideBlazor -@using Moonlight.Shared.Http @using Moonlight.Shared.Http.Events +@using ShadcnBlazor.Buttons @using ShadcnBlazor.Dialogs -@using ShadcnBlazor.Extras.AlertDialogs @using ShadcnBlazor.Progresses @using ShadcnBlazor.Spinners -@inject AlertDialogService AlertService @inject HttpClient HttpClient @@ -62,46 +60,57 @@ - - - +@if (CurrentStep == Steps.Length) +{ + + + +} +else +{ + + + +} @code { - private int Progress = 0; - + private int Progress; private int CurrentStep; - private string[] Steps = + private readonly string[] Steps = [ - "Preparing", - "Updating configuration files", - "Starting rebuild task", - "Building docker image", - "Redeploying container instance", - "Waiting for container instance to start up", - "Update complete" + "Checking", // 0 + "Updating configuration files", // 1 + "Starting rebuild task", // 2 + "Building docker image", // 3 + "Redeploying container instance", // 4 + "Waiting for container instance to start up", // 5 + "Update complete" // 6 ]; - private List LogLines = new(); + private readonly List LogLines = new(); protected override async Task OnAfterRenderAsync(bool firstRender) { if (!firstRender) return; + // Checking CurrentStep = 0; Progress = 0; await InvokeAsync(StateHasChanged); await Task.Delay(2000); + // Update configuration CurrentStep = 1; Progress = 20; await InvokeAsync(StateHasChanged); await Task.Delay(2000); + // Starting rebuild task CurrentStep = 2; Progress = 30; await InvokeAsync(StateHasChanged); @@ -140,20 +149,22 @@ switch (deserializedData.Data) { case "BuildImage": + + // Building docker image + CurrentStep = 3; Progress = 40; + await InvokeAsync(StateHasChanged); break; case "ServiceDown": + + // Redeploying container instance + CurrentStep = 4; Progress = 60; - await InvokeAsync(StateHasChanged); - break; - case "ServiceUp": - CurrentStep = 4; - Progress = 80; await InvokeAsync(StateHasChanged); break; } @@ -163,17 +174,21 @@ await InvokeAsync(StateHasChanged); } - catch (Exception e) + catch (Exception) { - // TODO: Log break; } } while (true); + + // Waiting for container instance to start up CurrentStep = 5; Progress = 90; await InvokeAsync(StateHasChanged); + // Wait some time for instance to shut down + await Task.Delay(TimeSpan.FromSeconds(5)); + // Ping instance until its reachable again while (true) { @@ -186,21 +201,13 @@ { // Ignored } - + await Task.Delay(3000); } - - CurrentStep = 6; + + // Update complete + CurrentStep = 7; Progress = 100; await InvokeAsync(StateHasChanged); - - await Task.Delay(1000); - - await AlertService.SuccessAsync( - "Update completed", - "Update successfully completed. Please refresh the page to load new frontend changes" - ); - - await CloseAsync(); } } diff --git a/Moonlight.Frontend/UI/Admin/Views/Sys/Index.razor b/Moonlight.Frontend/UI/Admin/Views/Sys/Index.razor index 6bc70e12..87c5422f 100644 --- a/Moonlight.Frontend/UI/Admin/Views/Sys/Index.razor +++ b/Moonlight.Frontend/UI/Admin/Views/Sys/Index.razor @@ -31,6 +31,10 @@ Diagnose + + + Instance + @@ -65,6 +69,9 @@ } + + + @code diff --git a/Moonlight.Frontend/UI/Admin/Views/Sys/Instance.razor b/Moonlight.Frontend/UI/Admin/Views/Sys/Instance.razor new file mode 100644 index 00000000..2e58a953 --- /dev/null +++ b/Moonlight.Frontend/UI/Admin/Views/Sys/Instance.razor @@ -0,0 +1,56 @@ +@using LucideBlazor +@using Moonlight.Shared.Http.Responses.Admin +@using ShadcnBlazor.Emptys +@using ShadcnBlazor.Extras.Common + +@inject HttpClient HttpClient + +
+ + @if (StatusDto.IsEnabled) + { + if (StatusDto.IsReachable) + { + } + else + { + + + + + + Container Helper unreachable + + The container helper is unreachable. No management actions are available + + + + } + } + else + { + + + + + + Container Helper is disabled + + The container helper is disabled on this instance. + This might be due to running a multiple container moonlight setup + + + + } + +
+ +@code +{ + private ContainerHelperStatusDto StatusDto; + + private async Task LoadAsync(LazyLoader _) + { + StatusDto = (await HttpClient.GetFromJsonAsync("api/admin/ch/status"))!; + } +} diff --git a/Moonlight.Shared/Http/Responses/Admin/ContainerHelperStatusDto.cs b/Moonlight.Shared/Http/Responses/Admin/ContainerHelperStatusDto.cs new file mode 100644 index 00000000..16bc5866 --- /dev/null +++ b/Moonlight.Shared/Http/Responses/Admin/ContainerHelperStatusDto.cs @@ -0,0 +1,3 @@ +namespace Moonlight.Shared.Http.Responses.Admin; + +public record ContainerHelperStatusDto(bool IsEnabled, bool IsReachable); \ No newline at end of file diff --git a/Moonlight.Shared/Http/SerializationContext.cs b/Moonlight.Shared/Http/SerializationContext.cs index dfe8de60..3f22bc9e 100644 --- a/Moonlight.Shared/Http/SerializationContext.cs +++ b/Moonlight.Shared/Http/SerializationContext.cs @@ -47,6 +47,9 @@ namespace Moonlight.Shared.Http; // Events [JsonSerializable(typeof(RebuildEvent))] + +// Container Helper +[JsonSerializable(typeof(ContainerHelperStatusDto))] public partial class SerializationContext : JsonSerializerContext { } \ No newline at end of file