Implemented container helper status checked. Started implementing container helper ui. Improved update modal
This commit is contained in:
@@ -1,6 +1,9 @@
|
|||||||
using Microsoft.AspNetCore.Http;
|
using Microsoft.AspNetCore.Http;
|
||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
using Microsoft.Extensions.Options;
|
||||||
|
using Moonlight.Api.Configuration;
|
||||||
using Moonlight.Api.Services;
|
using Moonlight.Api.Services;
|
||||||
|
using Moonlight.Shared.Http.Responses.Admin;
|
||||||
|
|
||||||
namespace Moonlight.Api.Http.Controllers.Admin;
|
namespace Moonlight.Api.Http.Controllers.Admin;
|
||||||
|
|
||||||
@@ -9,10 +12,23 @@ namespace Moonlight.Api.Http.Controllers.Admin;
|
|||||||
public class ChController : Controller
|
public class ChController : Controller
|
||||||
{
|
{
|
||||||
private readonly ContainerHelperService ContainerHelperService;
|
private readonly ContainerHelperService ContainerHelperService;
|
||||||
|
private readonly IOptions<ContainerHelperOptions> Options;
|
||||||
|
|
||||||
public ChController(ContainerHelperService containerHelperService)
|
public ChController(ContainerHelperService containerHelperService, IOptions<ContainerHelperOptions> options)
|
||||||
{
|
{
|
||||||
ContainerHelperService = containerHelperService;
|
ContainerHelperService = containerHelperService;
|
||||||
|
Options = options;
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpGet("status")]
|
||||||
|
public async Task<ActionResult<ContainerHelperStatusDto>> GetStatusAsync()
|
||||||
|
{
|
||||||
|
if (!Options.Value.IsEnabled)
|
||||||
|
return new ContainerHelperStatusDto(false, false);
|
||||||
|
|
||||||
|
var status = await ContainerHelperService.CheckConnectionAsync();
|
||||||
|
|
||||||
|
return new ContainerHelperStatusDto(true, status);
|
||||||
}
|
}
|
||||||
|
|
||||||
[HttpPost("rebuild")]
|
[HttpPost("rebuild")]
|
||||||
|
|||||||
@@ -13,6 +13,23 @@ public class ContainerHelperService
|
|||||||
HttpClientFactory = httpClientFactory;
|
HttpClientFactory = httpClientFactory;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async Task<bool> 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<RebuildEvent> RebuildAsync()
|
public async IAsyncEnumerable<RebuildEvent> RebuildAsync()
|
||||||
{
|
{
|
||||||
var options = new JsonSerializerOptions()
|
var options = new JsonSerializerOptions()
|
||||||
@@ -29,6 +46,7 @@ public class ContainerHelperService
|
|||||||
if (!response.IsSuccessStatusCode)
|
if (!response.IsSuccessStatusCode)
|
||||||
{
|
{
|
||||||
var responseText = await response.Content.ReadAsStringAsync();
|
var responseText = await response.Content.ReadAsStringAsync();
|
||||||
|
|
||||||
yield return new RebuildEvent()
|
yield return new RebuildEvent()
|
||||||
{
|
{
|
||||||
Type = RebuildEventType.Failed,
|
Type = RebuildEventType.Failed,
|
||||||
@@ -56,6 +74,11 @@ public class ContainerHelperService
|
|||||||
var deserializedData = JsonSerializer.Deserialize<RebuildEvent>(data, options);
|
var deserializedData = JsonSerializer.Deserialize<RebuildEvent>(data, options);
|
||||||
|
|
||||||
yield return deserializedData;
|
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);
|
} while (true);
|
||||||
|
|
||||||
yield return new RebuildEvent()
|
yield return new RebuildEvent()
|
||||||
|
|||||||
@@ -2,14 +2,12 @@
|
|||||||
|
|
||||||
@using System.Text.Json
|
@using System.Text.Json
|
||||||
@using LucideBlazor
|
@using LucideBlazor
|
||||||
@using Moonlight.Shared.Http
|
|
||||||
@using Moonlight.Shared.Http.Events
|
@using Moonlight.Shared.Http.Events
|
||||||
|
@using ShadcnBlazor.Buttons
|
||||||
@using ShadcnBlazor.Dialogs
|
@using ShadcnBlazor.Dialogs
|
||||||
@using ShadcnBlazor.Extras.AlertDialogs
|
|
||||||
@using ShadcnBlazor.Progresses
|
@using ShadcnBlazor.Progresses
|
||||||
@using ShadcnBlazor.Spinners
|
@using ShadcnBlazor.Spinners
|
||||||
|
|
||||||
@inject AlertDialogService AlertService
|
|
||||||
@inject HttpClient HttpClient
|
@inject HttpClient HttpClient
|
||||||
|
|
||||||
<DialogHeader>
|
<DialogHeader>
|
||||||
@@ -62,46 +60,57 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<DialogFooter>
|
@if (CurrentStep == Steps.Length)
|
||||||
<Progress Value="@Progress"></Progress>
|
{
|
||||||
</DialogFooter>
|
<DialogFooter ClassName="justify-end">
|
||||||
|
<Button Variant="ButtonVariant.Outline" @onclick="CloseAsync">Close</Button>
|
||||||
|
</DialogFooter>
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
<DialogFooter>
|
||||||
|
<Progress ClassName="my-1" Value="@Progress"></Progress>
|
||||||
|
</DialogFooter>
|
||||||
|
}
|
||||||
|
|
||||||
@code
|
@code
|
||||||
{
|
{
|
||||||
private int Progress = 0;
|
private int Progress;
|
||||||
|
|
||||||
private int CurrentStep;
|
private int CurrentStep;
|
||||||
|
|
||||||
private string[] Steps =
|
private readonly string[] Steps =
|
||||||
[
|
[
|
||||||
"Preparing",
|
"Checking", // 0
|
||||||
"Updating configuration files",
|
"Updating configuration files", // 1
|
||||||
"Starting rebuild task",
|
"Starting rebuild task", // 2
|
||||||
"Building docker image",
|
"Building docker image", // 3
|
||||||
"Redeploying container instance",
|
"Redeploying container instance", // 4
|
||||||
"Waiting for container instance to start up",
|
"Waiting for container instance to start up", // 5
|
||||||
"Update complete"
|
"Update complete" // 6
|
||||||
];
|
];
|
||||||
|
|
||||||
private List<string?> LogLines = new();
|
private readonly List<string?> LogLines = new();
|
||||||
|
|
||||||
protected override async Task OnAfterRenderAsync(bool firstRender)
|
protected override async Task OnAfterRenderAsync(bool firstRender)
|
||||||
{
|
{
|
||||||
if (!firstRender)
|
if (!firstRender)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
// Checking
|
||||||
CurrentStep = 0;
|
CurrentStep = 0;
|
||||||
Progress = 0;
|
Progress = 0;
|
||||||
await InvokeAsync(StateHasChanged);
|
await InvokeAsync(StateHasChanged);
|
||||||
|
|
||||||
await Task.Delay(2000);
|
await Task.Delay(2000);
|
||||||
|
|
||||||
|
// Update configuration
|
||||||
CurrentStep = 1;
|
CurrentStep = 1;
|
||||||
Progress = 20;
|
Progress = 20;
|
||||||
await InvokeAsync(StateHasChanged);
|
await InvokeAsync(StateHasChanged);
|
||||||
|
|
||||||
await Task.Delay(2000);
|
await Task.Delay(2000);
|
||||||
|
|
||||||
|
// Starting rebuild task
|
||||||
CurrentStep = 2;
|
CurrentStep = 2;
|
||||||
Progress = 30;
|
Progress = 30;
|
||||||
await InvokeAsync(StateHasChanged);
|
await InvokeAsync(StateHasChanged);
|
||||||
@@ -140,20 +149,22 @@
|
|||||||
switch (deserializedData.Data)
|
switch (deserializedData.Data)
|
||||||
{
|
{
|
||||||
case "BuildImage":
|
case "BuildImage":
|
||||||
|
|
||||||
|
// Building docker image
|
||||||
|
|
||||||
CurrentStep = 3;
|
CurrentStep = 3;
|
||||||
Progress = 40;
|
Progress = 40;
|
||||||
|
|
||||||
await InvokeAsync(StateHasChanged);
|
await InvokeAsync(StateHasChanged);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case "ServiceDown":
|
case "ServiceDown":
|
||||||
|
|
||||||
|
// Redeploying container instance
|
||||||
|
|
||||||
CurrentStep = 4;
|
CurrentStep = 4;
|
||||||
Progress = 60;
|
Progress = 60;
|
||||||
await InvokeAsync(StateHasChanged);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case "ServiceUp":
|
|
||||||
CurrentStep = 4;
|
|
||||||
Progress = 80;
|
|
||||||
await InvokeAsync(StateHasChanged);
|
await InvokeAsync(StateHasChanged);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -163,17 +174,21 @@
|
|||||||
|
|
||||||
await InvokeAsync(StateHasChanged);
|
await InvokeAsync(StateHasChanged);
|
||||||
}
|
}
|
||||||
catch (Exception e)
|
catch (Exception)
|
||||||
{
|
{
|
||||||
// TODO: Log
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
} while (true);
|
} while (true);
|
||||||
|
|
||||||
|
// Waiting for container instance to start up
|
||||||
|
|
||||||
CurrentStep = 5;
|
CurrentStep = 5;
|
||||||
Progress = 90;
|
Progress = 90;
|
||||||
await InvokeAsync(StateHasChanged);
|
await InvokeAsync(StateHasChanged);
|
||||||
|
|
||||||
|
// Wait some time for instance to shut down
|
||||||
|
await Task.Delay(TimeSpan.FromSeconds(5));
|
||||||
|
|
||||||
// Ping instance until its reachable again
|
// Ping instance until its reachable again
|
||||||
while (true)
|
while (true)
|
||||||
{
|
{
|
||||||
@@ -186,21 +201,13 @@
|
|||||||
{
|
{
|
||||||
// Ignored
|
// Ignored
|
||||||
}
|
}
|
||||||
|
|
||||||
await Task.Delay(3000);
|
await Task.Delay(3000);
|
||||||
}
|
}
|
||||||
|
|
||||||
CurrentStep = 6;
|
// Update complete
|
||||||
|
CurrentStep = 7;
|
||||||
Progress = 100;
|
Progress = 100;
|
||||||
await InvokeAsync(StateHasChanged);
|
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();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -31,6 +31,10 @@
|
|||||||
<HeartPulseIcon/>
|
<HeartPulseIcon/>
|
||||||
Diagnose
|
Diagnose
|
||||||
</TabsTrigger>
|
</TabsTrigger>
|
||||||
|
<TabsTrigger Value="instance">
|
||||||
|
<ContainerIcon/>
|
||||||
|
Instance
|
||||||
|
</TabsTrigger>
|
||||||
</TabsList>
|
</TabsList>
|
||||||
<TabsContent Value="settings">
|
<TabsContent Value="settings">
|
||||||
<Card ClassName="mt-5">
|
<Card ClassName="mt-5">
|
||||||
@@ -65,6 +69,9 @@
|
|||||||
<Moonlight.Frontend.UI.Admin.Views.Sys.Themes.Index />
|
<Moonlight.Frontend.UI.Admin.Views.Sys.Themes.Index />
|
||||||
</TabsContent>
|
</TabsContent>
|
||||||
}
|
}
|
||||||
|
<TabsContent Value="instance">
|
||||||
|
<Instance />
|
||||||
|
</TabsContent>
|
||||||
</Tabs>
|
</Tabs>
|
||||||
|
|
||||||
@code
|
@code
|
||||||
|
|||||||
56
Moonlight.Frontend/UI/Admin/Views/Sys/Instance.razor
Normal file
56
Moonlight.Frontend/UI/Admin/Views/Sys/Instance.razor
Normal file
@@ -0,0 +1,56 @@
|
|||||||
|
@using LucideBlazor
|
||||||
|
@using Moonlight.Shared.Http.Responses.Admin
|
||||||
|
@using ShadcnBlazor.Emptys
|
||||||
|
@using ShadcnBlazor.Extras.Common
|
||||||
|
|
||||||
|
@inject HttpClient HttpClient
|
||||||
|
|
||||||
|
<div class="mt-5">
|
||||||
|
<LazyLoader Load="LoadAsync">
|
||||||
|
@if (StatusDto.IsEnabled)
|
||||||
|
{
|
||||||
|
if (StatusDto.IsReachable)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
<Empty>
|
||||||
|
<EmptyHeader>
|
||||||
|
<EmptyMedia Variant="EmptyMediaVariant.Icon">
|
||||||
|
<CircleAlertIcon ClassName="text-red-500"/>
|
||||||
|
</EmptyMedia>
|
||||||
|
<EmptyTitle>Container Helper unreachable</EmptyTitle>
|
||||||
|
<EmptyDescription>
|
||||||
|
The container helper is unreachable. No management actions are available
|
||||||
|
</EmptyDescription>
|
||||||
|
</EmptyHeader>
|
||||||
|
</Empty>
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
<Empty>
|
||||||
|
<EmptyHeader>
|
||||||
|
<EmptyMedia Variant="EmptyMediaVariant.Icon">
|
||||||
|
<ToggleLeftIcon/>
|
||||||
|
</EmptyMedia>
|
||||||
|
<EmptyTitle>Container Helper is disabled</EmptyTitle>
|
||||||
|
<EmptyDescription>
|
||||||
|
The container helper is disabled on this instance.
|
||||||
|
This might be due to running a multiple container moonlight setup
|
||||||
|
</EmptyDescription>
|
||||||
|
</EmptyHeader>
|
||||||
|
</Empty>
|
||||||
|
}
|
||||||
|
</LazyLoader>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
@code
|
||||||
|
{
|
||||||
|
private ContainerHelperStatusDto StatusDto;
|
||||||
|
|
||||||
|
private async Task LoadAsync(LazyLoader _)
|
||||||
|
{
|
||||||
|
StatusDto = (await HttpClient.GetFromJsonAsync<ContainerHelperStatusDto>("api/admin/ch/status"))!;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
namespace Moonlight.Shared.Http.Responses.Admin;
|
||||||
|
|
||||||
|
public record ContainerHelperStatusDto(bool IsEnabled, bool IsReachable);
|
||||||
@@ -47,6 +47,9 @@ namespace Moonlight.Shared.Http;
|
|||||||
|
|
||||||
// Events
|
// Events
|
||||||
[JsonSerializable(typeof(RebuildEvent))]
|
[JsonSerializable(typeof(RebuildEvent))]
|
||||||
|
|
||||||
|
// Container Helper
|
||||||
|
[JsonSerializable(typeof(ContainerHelperStatusDto))]
|
||||||
public partial class SerializationContext : JsonSerializerContext
|
public partial class SerializationContext : JsonSerializerContext
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user