feat: get web app talking to api

This commit is contained in:
Stevan Freeborn
2025-04-06 23:23:21 -05:00
parent 5652fee422
commit 3e7e6323c2
11 changed files with 146 additions and 10 deletions
+17
View File
@@ -45,6 +45,21 @@ builder.Services.AddRateLimiter(
} }
); );
// TODO: Make CORS more restrictive
// only allow requests from the web
// client
const string corsPolicyName = "cors";
builder.Services.AddCors(o =>
{
o.AddPolicy(corsPolicyName, p =>
{
p.AllowAnyOrigin()
.AllowAnyOrigin()
.AllowAnyMethod();
});
});
var app = builder.Build(); var app = builder.Build();
if (app.Environment.IsDevelopment()) if (app.Environment.IsDevelopment())
@@ -57,6 +72,8 @@ if (app.Environment.IsProduction())
app.UseRateLimiter(); app.UseRateLimiter();
} }
app.UseCors(corsPolicyName);
app.UseHttpsRedirection(); app.UseHttpsRedirection();
app.UseStatusCodePages(); app.UseStatusCodePages();
@@ -1,5 +1,6 @@
namespace AltGen.Console.Generate; namespace AltGen.Console.Generate;
// TODO: Use shared library version of this service
sealed class AltGenService(HttpClient client) : IAltGenService sealed class AltGenService(HttpClient client) : IAltGenService
{ {
static readonly JsonSerializerOptions JsonOptions = new() static readonly JsonSerializerOptions JsonOptions = new()
+9
View File
@@ -0,0 +1,9 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net9.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
</Project>
@@ -0,0 +1,47 @@
using System.Net.Http.Headers;
using System.Text.Json;
namespace AltGen.Shared.Generate;
public sealed class AltGenService(HttpClient client) : IAltGenService
{
static readonly JsonSerializerOptions JsonOptions = new()
{
PropertyNameCaseInsensitive = true
};
readonly HttpClient _client = client;
public async Task<string> GenerateAltTextAsync(GenerateAltTextRequest req)
{
var byteContent = new ByteArrayContent(req.Image);
byteContent.Headers.ContentType = new MediaTypeHeaderValue(req.ContentType);
var request = new HttpRequestMessage(HttpMethod.Post, "/generate")
{
Content = new MultipartFormDataContent
{
{ new StringContent(req.Provider), "provider" },
{ new StringContent(req.ProviderKey), "providerKey" },
{ byteContent, "file", req.FileName }
}
};
var response = await _client.SendAsync(request);
var content = await response.Content.ReadAsStringAsync();
if (response.IsSuccessStatusCode is false)
{
throw new AltTextException("Failed to generate alt text.");
}
var altTextResponse = JsonSerializer.Deserialize<AltTextResponse>(content, JsonOptions) ?? throw new AltTextException("Failed to deserialize response.");
return altTextResponse.AltText;
}
}
record AltTextResponse(string AltText);
class AltTextException(string message) : Exception(message)
{
}
@@ -0,0 +1,9 @@
namespace AltGen.Shared.Generate;
public record GenerateAltTextRequest(
string Provider,
string ProviderKey,
string FileName,
byte[] Image,
string ContentType
);
@@ -0,0 +1,6 @@
namespace AltGen.Shared.Generate;
public interface IAltGenService
{
public Task<string> GenerateAltTextAsync(GenerateAltTextRequest req);
}
@@ -10,6 +10,11 @@
<ItemGroup> <ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly" Version="9.0.3" /> <PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly" Version="9.0.3" />
<PackageReference Include="Microsoft.Extensions.Http" Version="9.0.3" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\AltGen.Shared\AltGen.Shared.csproj" />
</ItemGroup> </ItemGroup>
</Project> </Project>
@@ -1,24 +1,51 @@
@inject ILogger<Home> Logger @using AltGen.Shared.Generate
@inject ILogger<Home> Logger
@inject IAltGenService AltGenService
@page "/" @page "/"
@rendermode InteractiveWebAssembly @rendermode @(new InteractiveWebAssemblyRenderMode(prerender: false))
<PageTitle>AltGen.Web</PageTitle> <PageTitle>AltGen.Web</PageTitle>
<h1>AI Generated Alt Text</h1> <h1>AI Generated Alt Text</h1>
<form method="post"> <form method="post">
<input type="text" @onchange="@(e => Logger.LogInformation("Text changed: {Value}", e.Value))" /> <div>
<InputFile OnChange="LoadFiles" multiple /> <label for="provider">Provider</label>
<input type="text" id="provider" name="provider" required @bind="_provider" />
</div>
<div>
<label for="providerKey">Provider Key</label>
<input type="password" id="providerKey" name="providerKey" required @bind="_providerKey" />
</div>
<InputFile OnChange="HandleFileChange" />
</form> </form>
@code { @code {
private void LoadFiles(InputFileChangeEventArgs e) private string? _provider;
{ private string? _providerKey;
var files = e.GetMultipleFiles(5);
foreach (var file in files) private async Task HandleFileChange(InputFileChangeEventArgs e)
{ {
Logger.LogInformation("File loaded: {Name}, Size: {Size}", file.Name, file.Size); var file = e.File;
if (_provider is null || _providerKey is null || file is null)
{
return;
} }
var fileBytes = new byte[file.Size];
using var stream = file.OpenReadStream();
await stream.ReadAsync(fileBytes, 0, (int)file.Size);
var altText = await AltGenService.GenerateAltTextAsync(new(
_provider,
_providerKey,
file.Name,
fileBytes,
file.ContentType
));
// TODO: Present on the screen to the user
Logger.LogInformation(altText);
} }
} }
+5
View File
@@ -1,5 +1,10 @@
using Microsoft.AspNetCore.Components.WebAssembly.Hosting; using Microsoft.AspNetCore.Components.WebAssembly.Hosting;
using AltGen.Shared.Generate;
var builder = WebAssemblyHostBuilder.CreateDefault(args); var builder = WebAssemblyHostBuilder.CreateDefault(args);
// TODO: Read base url
// from configuration
builder.Services.AddHttpClient<IAltGenService, AltGenService>(c => c.BaseAddress = new("https://localhost:7297"));
await builder.Build().RunAsync(); await builder.Build().RunAsync();
+4
View File
@@ -11,4 +11,8 @@
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.Server" Version="9.0.3" /> <PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.Server" Version="9.0.3" />
</ItemGroup> </ItemGroup>
<ItemGroup>
<ProjectReference Include="..\AltGen.Shared\AltGen.Shared.csproj" />
</ItemGroup>
</Project> </Project>
+6
View File
@@ -15,6 +15,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AltGen.Web", "AltGen.Web\Al
EndProject EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AltGen.Web.Client", "AltGen.Web.Client\AltGen.Web.Client.csproj", "{DCE07E79-23A4-490E-A796-EA140D2040C1}" Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AltGen.Web.Client", "AltGen.Web.Client\AltGen.Web.Client.csproj", "{DCE07E79-23A4-490E-A796-EA140D2040C1}"
EndProject EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AltGen.Shared", "AltGen.Shared\AltGen.Shared.csproj", "{68D4BC5D-A6FE-41CF-B744-1DB83CCFBC89}"
EndProject
Global Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU Debug|Any CPU = Debug|Any CPU
@@ -48,5 +50,9 @@ Global
{DCE07E79-23A4-490E-A796-EA140D2040C1}.Debug|Any CPU.Build.0 = Debug|Any CPU {DCE07E79-23A4-490E-A796-EA140D2040C1}.Debug|Any CPU.Build.0 = Debug|Any CPU
{DCE07E79-23A4-490E-A796-EA140D2040C1}.Release|Any CPU.ActiveCfg = Release|Any CPU {DCE07E79-23A4-490E-A796-EA140D2040C1}.Release|Any CPU.ActiveCfg = Release|Any CPU
{DCE07E79-23A4-490E-A796-EA140D2040C1}.Release|Any CPU.Build.0 = Release|Any CPU {DCE07E79-23A4-490E-A796-EA140D2040C1}.Release|Any CPU.Build.0 = Release|Any CPU
{68D4BC5D-A6FE-41CF-B744-1DB83CCFBC89}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{68D4BC5D-A6FE-41CF-B744-1DB83CCFBC89}.Debug|Any CPU.Build.0 = Debug|Any CPU
{68D4BC5D-A6FE-41CF-B744-1DB83CCFBC89}.Release|Any CPU.ActiveCfg = Release|Any CPU
{68D4BC5D-A6FE-41CF-B744-1DB83CCFBC89}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection EndGlobalSection
EndGlobal EndGlobal