Changed the changelog and the ERI specifications to load without a request to the app server

This commit is contained in:
Thorsten Sommer committed 2026-10-31 19:06:37 +01:00
1 parent 410ed06920
commit a72dc90f31
7 files changed
+92 -34

No files matched your search

@@ -13,7 +13,7 @@ namespace AIStudio.Assistants.ERI;
public partial class AssistantERI : AssistantBaseCore<SettingsDialogERIServer>
{
[Inject]
private HttpClient HttpClient { get; set; } = null!;
private WebRootFiles WebRootFiles { get; set; } = null!;
[Inject]
private IDialogService DialogService { get; init; } = null!;
@@ -843,7 +843,7 @@ public partial class AssistantERI : AssistantBaseCore<SettingsDialogERIServer>
var baseDirectorySnapshot = this.baseDirectory;
var previouslyGeneratedFilesSnapshot = this.previouslyGeneratedFiles.ToArray();
this.eriSpecification = await this.selectedERIVersion.ReadSpecification(this.HttpClient);
this.eriSpecification = await this.selectedERIVersion.ReadSpecification(this.WebRootFiles);
if (string.IsNullOrWhiteSpace(this.eriSpecification))
{
this.AddInputIssue(T("The ERI specification could not be loaded. Please try again later."));
@@ -2,26 +2,14 @@ namespace AIStudio.Assistants.ERI;
public static class ERIVersionExtensions
{
public static async Task<string> ReadSpecification(this ERIVersion version, HttpClient httpClient)
{
try
{
var url = version.SpecificationURL();
using var response = await httpClient.GetAsync(url);
return await response.Content.ReadAsStringAsync();
}
catch
{
return string.Empty;
}
}
public static Task<string> ReadSpecification(this ERIVersion version, WebRootFiles webRootFiles) => webRootFiles.ReadTextAsync(version.SpecificationURL());
public static string SpecificationURL(this ERIVersion version)
{
var nameLower = version.ToString().ToLowerInvariant();
var filename = $"{nameLower}.json";
return $"specs/eri/{filename}";
}
public static bool WasSpecificationSelected(this ERIVersion version) => version != ERIVersion.NONE;
}
@@ -5,7 +5,7 @@ namespace AIStudio.Components;
public partial class Changelog : MSGComponentBase
{
[Inject]
private HttpClient HttpClient { get; set; } = null!;
private WebRootFiles WebRootFiles { get; set; } = null!;
#region Overrides of ComponentBase
@@ -23,7 +23,6 @@ public partial class Changelog : MSGComponentBase
private async Task ReadLogAsync()
{
using var response = await this.HttpClient.GetAsync($"changelog/{this.SelectedLog.Filename}");
this.LogContent = await response.Content.ReadAsStringAsync();
this.LogContent = await this.WebRootFiles.ReadTextAsync($"changelog/{this.SelectedLog.Filename}");
}
}
+2 -3
View File
@@ -13,7 +13,7 @@ namespace AIStudio.Pages;
public partial class Home : MSGComponentBase
{
[Inject]
private HttpClient HttpClient { get; init; } = null!;
private WebRootFiles WebRootFiles { get; init; } = null!;
[Inject]
private NavigationManager NavigationManager { get; init; } = null!;
@@ -222,8 +222,7 @@ public partial class Home : MSGComponentBase
private async Task ReadLastChangeAsync()
{
var latest = Changelog.LOGS.MaxBy(n => n.Build);
using var response = await this.HttpClient.GetAsync($"changelog/{latest.Filename}");
this.LastChangeContent = await response.Content.ReadAsStringAsync();
this.LastChangeContent = await this.WebRootFiles.ReadTextAsync($"changelog/{latest.Filename}");
await this.InvokeAsync(this.StateHasChanged);
}
+4 -11
View File
@@ -32,11 +32,6 @@ using MudBlazor.Services;
using MudExtensions.Services;
#if !DEBUG
using System.Reflection;
using Microsoft.Extensions.FileProviders;
#endif
namespace AIStudio;
internal sealed class Program
@@ -283,10 +278,9 @@ internal sealed class Program
options.KeepAliveInterval = TimeSpan.FromSeconds(30);
});
builder.Services.AddSingleton(new HttpClient
{
BaseAddress = new Uri($"http://127.0.0.1:{appPort}")
});
// For absolute URLs only. The app reads its own files through WebRootFiles:
builder.Services.AddSingleton(new HttpClient());
builder.Services.AddSingleton<WebRootFiles>();
//
// We listen on the IPv4 loopback address only, where the app window goes. With localhost, the
@@ -356,10 +350,9 @@ internal sealed class Program
app.UseStaticFiles();
app.UseDeveloperExceptionPage();
#else
var fileProvider = new ManifestEmbeddedFileProvider(Assembly.GetAssembly(type: typeof(Program))!, "wwwroot");
app.UseStaticFiles(new StaticFileOptions
{
FileProvider = fileProvider,
FileProvider = app.Services.GetRequiredService<WebRootFiles>().FileProvider,
RequestPath = string.Empty,
});
#endif
@@ -0,0 +1,36 @@
using System.Reflection;
using Microsoft.Extensions.FileProviders;
namespace AIStudio.Tools;
/// <summary>
/// Reads the files below wwwroot, which every build embeds into the app.
/// </summary>
/// <remarks>
/// The app reads files such as its changelogs directly instead of requesting them over HTTP from
/// its own server. The release build serves its static files from the same provider.
/// </remarks>
public sealed class WebRootFiles
{
/// <summary>
/// Gets the files below wwwroot.
/// </summary>
public IFileProvider FileProvider { get; } = new ManifestEmbeddedFileProvider(Assembly.GetAssembly(type: typeof(Program))!, "wwwroot");
/// <summary>
/// Reads a text file below wwwroot.
/// </summary>
/// <param name="path">The path below wwwroot, such as <c>changelog/v26.10.1.md</c>.</param>
/// <returns>The text of the file, or an empty string when there is no such file.</returns>
public async Task<string> ReadTextAsync(string path)
{
var file = this.FileProvider.GetFileInfo(path);
if (!file.Exists)
return string.Empty;
await using var fileStream = file.CreateReadStream();
using var reader = new StreamReader(fileStream);
return await reader.ReadToEndAsync();
}
}
@@ -0,0 +1,43 @@
using AIStudio.Assistants.ERI;
using AIStudio.Tools;
using Changelog = AIStudio.Components.Changelog;
namespace AIStudio.Tests.Tools.AppAccess;
/// <summary>
/// Checks that the app reads the files below wwwroot which it shows itself.
/// </summary>
/// <remarks>
/// The changelogs and the ERI specifications come from the files embedded into the app, not from a
/// request to its own server. A file which is missing there would leave the changelog or the ERI
/// assistant empty without any error.
/// </remarks>
[TestFixture]
public sealed class WebRootFilesTests
{
private static IEnumerable<string> ChangelogFiles() => Changelog.LOGS.Select(log => log.Filename);
private static IEnumerable<ERIVersion> SpecificationVersions() => Enum.GetValues<ERIVersion>().Where(version => version.WasSpecificationSelected());
[TestCaseSource(nameof(ChangelogFiles))]
public async Task EveryChangelogCanBeRead(string filename)
{
var text = await new WebRootFiles().ReadTextAsync($"changelog/{filename}");
Assert.That(text, Is.Not.Empty);
}
[TestCaseSource(nameof(SpecificationVersions))]
public async Task EveryERISpecificationCanBeRead(ERIVersion version)
{
var text = await version.ReadSpecification(new WebRootFiles());
Assert.That(text, Is.Not.Empty);
}
[Test]
public async Task AMissingFileReadsAsEmptyText()
{
var text = await new WebRootFiles().ReadTextAsync("changelog/does-not-exist.md");
Assert.That(text, Is.Empty);
}
}