Renamed SharedSerializationContext to SerializationContext. Added error handling and no build cache functionality

This commit is contained in:
2026-01-29 13:59:24 +01:00
parent 8181404f0c
commit 660319afec
10 changed files with 109 additions and 37 deletions

View File

@@ -4,6 +4,7 @@ using Microsoft.Extensions.Options;
using Moonlight.Api.Configuration;
using Moonlight.Api.Mappers;
using Moonlight.Api.Services;
using Moonlight.Shared.Http.Events;
using Moonlight.Shared.Http.Requests.Admin.ContainerHelper;
using Moonlight.Shared.Http.Responses.Admin;
@@ -34,9 +35,9 @@ public class ContainerHelperController : Controller
}
[HttpPost("rebuild")]
public Task<IResult> RebuildAsync()
public Task<IResult> RebuildAsync([FromBody] RequestRebuildDto request)
{
var result = ContainerHelperService.RebuildAsync();
var result = ContainerHelperService.RebuildAsync(request.NoBuildCache);
var mappedResult = result.Select(ContainerHelperMapper.ToDto);
return Task.FromResult<IResult>(

View File

@@ -0,0 +1,3 @@
namespace Moonlight.Api.Http.Services.ContainerHelper.Requests;
public record RequestRebuildDto(bool NoBuildCache);

View File

@@ -8,6 +8,7 @@ namespace Moonlight.Api.Http.Services.ContainerHelper;
[JsonSerializable(typeof(SetVersionDto))]
[JsonSerializable(typeof(ProblemDetails))]
[JsonSerializable(typeof(RebuildEventDto))]
[JsonSerializable(typeof(RequestRebuildDto))]
public partial class SerializationContext : JsonSerializerContext
{
private static JsonSerializerOptions? InternalTunedOptions;

View File

@@ -1,4 +1,5 @@
using System.Net.Http.Json;
using System.Net.Http.Headers;
using System.Net.Http.Json;
using System.Text.Json;
using Moonlight.Api.Http.Services.ContainerHelper;
using Moonlight.Api.Http.Services.ContainerHelper.Requests;
@@ -32,11 +33,22 @@ public class ContainerHelperService
}
}
public async IAsyncEnumerable<RebuildEventDto> RebuildAsync()
public async IAsyncEnumerable<RebuildEventDto> RebuildAsync(bool noBuildCache)
{
var client = HttpClientFactory.CreateClient("ContainerHelper");
var response = await client.GetAsync("api/rebuild", HttpCompletionOption.ResponseHeadersRead);
var request = new HttpRequestMessage(HttpMethod.Post, "api/rebuild");
request.Content = JsonContent.Create(
new RequestRebuildDto(noBuildCache),
null,
SerializationContext.TunedOptions
);
var response = await client.SendAsync(
request,
HttpCompletionOption.ResponseHeadersRead
);
if (!response.IsSuccessStatusCode)
{
@@ -91,12 +103,13 @@ public class ContainerHelperService
SerializationContext.TunedOptions
);
if(response.IsSuccessStatusCode)
if (response.IsSuccessStatusCode)
return;
var problemDetails = await response.Content.ReadFromJsonAsync<ProblemDetails>(SerializationContext.TunedOptions);
var problemDetails =
await response.Content.ReadFromJsonAsync<ProblemDetails>(SerializationContext.TunedOptions);
if(problemDetails == null)
if (problemDetails == null)
throw new HttpRequestException($"Failed to set version: {response.ReasonPhrase}");
throw new HttpRequestException($"Failed to set version: {problemDetails.Detail ?? problemDetails.Title}");

View File

@@ -19,7 +19,7 @@ public partial class Startup
{
builder.Services.AddControllers().AddJsonOptions(options =>
{
options.JsonSerializerOptions.TypeInfoResolverChain.Add(SharedSerializationContext.Default);
options.JsonSerializerOptions.TypeInfoResolverChain.Add(SerializationContext.Default);
});
builder.Logging.ClearProviders();

View File

@@ -18,7 +18,7 @@ public static class Constants
};
// Add source generated options from shared project
InternalOptions.TypeInfoResolverChain.Add(SharedSerializationContext.Default);
InternalOptions.TypeInfoResolverChain.Add(SerializationContext.Default);
return InternalOptions;
}

View File

@@ -2,6 +2,7 @@
@using System.Text.Json
@using LucideBlazor
@using Moonlight.Shared.Http
@using Moonlight.Shared.Http.Events
@using Moonlight.Shared.Http.Requests.Admin.ContainerHelper
@using ShadcnBlazor.Buttons
@@ -18,13 +19,20 @@
</DialogHeader>
<div class="grid grid-cols-1 xl:grid-cols-2 w-full gap-5">
<div class="text-base flex flex-col p-2 gap-y-0.5">
<div class="text-base flex flex-col p-2 gap-y-1">
@for (var i = 0; i < Steps.Length; i++)
{
if (CurrentStep == i)
{
<div class="flex flex-row items-center gap-x-2">
<Spinner ClassName="size-4"/>
<div class="flex flex-row items-center gap-x-1">
@if (IsFailed)
{
<CircleXIcon ClassName="text-red-500 size-5"/>
}
else
{
<Spinner ClassName="size-5"/>
}
<span>
@Steps[i]
</span>
@@ -34,8 +42,8 @@
{
if (i < CurrentStep)
{
<div class="flex flex-row items-center gap-x-2">
<CheckIcon ClassName="text-green-500 size-4"/>
<div class="flex flex-row items-center gap-x-1">
<CircleCheckIcon ClassName="text-green-500 size-5"/>
<span>
@Steps[i]
</span>
@@ -43,8 +51,8 @@
}
else
{
<div class="text-muted-foreground flex flex-row items-center gap-x-2">
<span class="size-4"></span>
<div class="text-muted-foreground flex flex-row items-center gap-x-1">
<span class="size-5"></span>
@Steps[i]
</div>
}
@@ -61,7 +69,7 @@
</div>
</div>
@if (CurrentStep == Steps.Length)
@if (CurrentStep == Steps.Length || IsFailed)
{
<DialogFooter ClassName="justify-end">
<Button Variant="ButtonVariant.Outline" @onclick="CloseAsync">Close</Button>
@@ -77,7 +85,9 @@ else
@code
{
[Parameter] public string Version { get; set; }
[Parameter] public bool NoBuildCache { get; set; }
private bool IsFailed;
private int Progress;
private int CurrentStep;
@@ -114,15 +124,23 @@ else
await HttpClient.PostAsJsonAsync("api/admin/ch/version", new SetVersionDto()
{
Version = Version
});
}, SerializationContext.TunedOptions);
// Starting rebuild task
CurrentStep = 2;
Progress = 30;
await InvokeAsync(StateHasChanged);
var request = new HttpRequestMessage(HttpMethod.Post, "api/admin/ch/rebuild");
request.Content = JsonContent.Create(
new RequestRebuildDto(NoBuildCache),
null,
SerializationContext.TunedOptions
);
var response = await HttpClient.SendAsync(
new HttpRequestMessage(HttpMethod.Post, "api/admin/ch/rebuild"),
request,
HttpCompletionOption.ResponseHeadersRead
);
@@ -176,6 +194,13 @@ else
}
break;
case RebuildEventType.Failed:
IsFailed = true;
await InvokeAsync(StateHasChanged);
return;
}
await InvokeAsync(StateHasChanged);

View File

@@ -10,6 +10,7 @@
@using ShadcnBlazor.Extras.Dialogs
@using ShadcnBlazor.Fields
@using ShadcnBlazor.Selects
@using ShadcnBlazor.Switches
@inject HttpClient HttpClient
@inject DialogService DialogService
@@ -30,9 +31,7 @@
<FieldGroup>
<FieldSet>
<Field>
<FieldLabel>
Version
</FieldLabel>
<FieldLabel>Version / Branch</FieldLabel>
<FieldContent>
<Select DefaultValue="@SelectedVersion" @bind-Value="SelectedVersion">
<SelectTrigger ClassName="w-64">
@@ -40,12 +39,19 @@
</SelectTrigger>
<SelectContent ClassName="w-64">
<SelectItem Value="v2.1">v2.1</SelectItem>
<SelectItem Value="v2.1.1">v2.1.1</SelectItem>
<SelectItem Value="feat/ContainerHelper">feat/ContainerHelper
</SelectItem>
</SelectContent>
</Select>
</FieldContent>
</Field>
<Field>
<FieldLabel>Bypass Build Cache</FieldLabel>
<FieldContent>
<Switch @bind-Value="NoBuildCache" />
</FieldContent>
</Field>
</FieldSet>
<Field Orientation="FieldOrientation.Horizontal">
<Button @onclick="AskApplyAsync">Apply</Button>
@@ -110,6 +116,7 @@
{
private ContainerHelperStatusDto StatusDto;
private string SelectedVersion = "v2.1";
private bool NoBuildCache;
private async Task LoadAsync(LazyLoader _)
{
@@ -119,7 +126,11 @@
private async Task ApplyAsync()
{
await DialogService.LaunchAsync<UpdateInstanceModal>(
parameters => { parameters[nameof(UpdateInstanceModal.Version)] = SelectedVersion; },
parameters =>
{
parameters[nameof(UpdateInstanceModal.Version)] = SelectedVersion;
parameters[nameof(UpdateInstanceModal.NoBuildCache)] = NoBuildCache;
},
onConfigure: model =>
{
model.ShowCloseButton = false;
@@ -130,7 +141,7 @@
private async Task AskApplyAsync()
{
if(string.IsNullOrWhiteSpace(SelectedVersion))
if (string.IsNullOrWhiteSpace(SelectedVersion))
return;
var shouldContinue = await ConfirmRiskyVersionAsync(
@@ -138,7 +149,7 @@
"If you continue the moonlight instance will become unavailable during the rebuild process. This will impact users on this instance"
);
if(!shouldContinue)
if (!shouldContinue)
return;
if (!Regex.IsMatch(SelectedVersion, @"^v\d+(\.\d+)*b?$"))

View File

@@ -0,0 +1,15 @@
namespace Moonlight.Shared.Http.Requests.Admin.ContainerHelper;
public class RequestRebuildDto
{
public bool NoBuildCache { get; set; }
public RequestRebuildDto()
{
}
public RequestRebuildDto(bool noBuildCache)
{
NoBuildCache = noBuildCache;
}
}

View File

@@ -2,6 +2,7 @@
using System.Text.Json.Serialization;
using Moonlight.Shared.Http.Events;
using Moonlight.Shared.Http.Requests.Admin.ApiKeys;
using Moonlight.Shared.Http.Requests.Admin.ContainerHelper;
using Moonlight.Shared.Http.Requests.Admin.Roles;
using Moonlight.Shared.Http.Requests.Admin.Themes;
using Moonlight.Shared.Http.Requests.Admin.Users;
@@ -51,10 +52,12 @@ namespace Moonlight.Shared.Http;
// Container Helper
[JsonSerializable(typeof(ContainerHelperStatusDto))]
[JsonSerializable(typeof(RequestRebuildDto))]
[JsonSerializable(typeof(SetVersionDto))]
// Misc
[JsonSerializable(typeof(ProblemDetails))]
public partial class SharedSerializationContext : JsonSerializerContext
public partial class SerializationContext : JsonSerializerContext
{
private static JsonSerializerOptions? InternalTunedOptions;