diff --git a/src/AltGen.API/Program.cs b/src/AltGen.API/Program.cs index 6caf5b4..742d76c 100644 --- a/src/AltGen.API/Program.cs +++ b/src/AltGen.API/Program.cs @@ -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(); if (app.Environment.IsDevelopment()) @@ -57,6 +72,8 @@ if (app.Environment.IsProduction()) app.UseRateLimiter(); } +app.UseCors(corsPolicyName); + app.UseHttpsRedirection(); app.UseStatusCodePages(); diff --git a/src/AltGen.Console/Generate/AltGenService.cs b/src/AltGen.Console/Generate/AltGenService.cs index f782c7c..7b5802f 100644 --- a/src/AltGen.Console/Generate/AltGenService.cs +++ b/src/AltGen.Console/Generate/AltGenService.cs @@ -1,5 +1,6 @@ namespace AltGen.Console.Generate; +// TODO: Use shared library version of this service sealed class AltGenService(HttpClient client) : IAltGenService { static readonly JsonSerializerOptions JsonOptions = new() diff --git a/src/AltGen.Shared/AltGen.Shared.csproj b/src/AltGen.Shared/AltGen.Shared.csproj new file mode 100644 index 0000000..125f4c9 --- /dev/null +++ b/src/AltGen.Shared/AltGen.Shared.csproj @@ -0,0 +1,9 @@ + + + + net9.0 + enable + enable + + + diff --git a/src/AltGen.Shared/Generate/AltGenService.cs b/src/AltGen.Shared/Generate/AltGenService.cs new file mode 100644 index 0000000..bfd773d --- /dev/null +++ b/src/AltGen.Shared/Generate/AltGenService.cs @@ -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 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(content, JsonOptions) ?? throw new AltTextException("Failed to deserialize response."); + + return altTextResponse.AltText; + } +} + +record AltTextResponse(string AltText); + +class AltTextException(string message) : Exception(message) +{ +} \ No newline at end of file diff --git a/src/AltGen.Shared/Generate/GenerateAltTextRequest.cs b/src/AltGen.Shared/Generate/GenerateAltTextRequest.cs new file mode 100644 index 0000000..a0d24b4 --- /dev/null +++ b/src/AltGen.Shared/Generate/GenerateAltTextRequest.cs @@ -0,0 +1,9 @@ +namespace AltGen.Shared.Generate; + +public record GenerateAltTextRequest( + string Provider, + string ProviderKey, + string FileName, + byte[] Image, + string ContentType +); diff --git a/src/AltGen.Shared/Generate/IAltGenService.cs b/src/AltGen.Shared/Generate/IAltGenService.cs new file mode 100644 index 0000000..0a1a593 --- /dev/null +++ b/src/AltGen.Shared/Generate/IAltGenService.cs @@ -0,0 +1,6 @@ +namespace AltGen.Shared.Generate; + +public interface IAltGenService +{ + public Task GenerateAltTextAsync(GenerateAltTextRequest req); +} \ No newline at end of file diff --git a/src/AltGen.Web.Client/AltGen.Web.Client.csproj b/src/AltGen.Web.Client/AltGen.Web.Client.csproj index c2ddab3..adeed6c 100644 --- a/src/AltGen.Web.Client/AltGen.Web.Client.csproj +++ b/src/AltGen.Web.Client/AltGen.Web.Client.csproj @@ -10,6 +10,11 @@ + + + + + diff --git a/src/AltGen.Web.Client/Components/Pages/Home.razor b/src/AltGen.Web.Client/Components/Pages/Home.razor index b04083a..7643028 100644 --- a/src/AltGen.Web.Client/Components/Pages/Home.razor +++ b/src/AltGen.Web.Client/Components/Pages/Home.razor @@ -1,24 +1,51 @@ -@inject ILogger Logger +@using AltGen.Shared.Generate +@inject ILogger Logger +@inject IAltGenService AltGenService @page "/" -@rendermode InteractiveWebAssembly +@rendermode @(new InteractiveWebAssemblyRenderMode(prerender: false)) AltGen.Web

AI Generated Alt Text

- - +
+ + +
+
+ + +
+ @code { - private void LoadFiles(InputFileChangeEventArgs e) + private string? _provider; + private string? _providerKey; + + private async Task HandleFileChange(InputFileChangeEventArgs e) { - var files = e.GetMultipleFiles(5); - - foreach (var file in files) + var file = e.File; + + if (_provider is null || _providerKey is null || file is null) { - Logger.LogInformation("File loaded: {Name}, Size: {Size}", file.Name, file.Size); + 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); } -} \ No newline at end of file +} diff --git a/src/AltGen.Web.Client/Program.cs b/src/AltGen.Web.Client/Program.cs index 519269f..5f7cdd1 100644 --- a/src/AltGen.Web.Client/Program.cs +++ b/src/AltGen.Web.Client/Program.cs @@ -1,5 +1,10 @@ using Microsoft.AspNetCore.Components.WebAssembly.Hosting; +using AltGen.Shared.Generate; var builder = WebAssemblyHostBuilder.CreateDefault(args); +// TODO: Read base url +// from configuration +builder.Services.AddHttpClient(c => c.BaseAddress = new("https://localhost:7297")); + await builder.Build().RunAsync(); diff --git a/src/AltGen.Web/AltGen.Web.csproj b/src/AltGen.Web/AltGen.Web.csproj index 469ac5c..345882e 100644 --- a/src/AltGen.Web/AltGen.Web.csproj +++ b/src/AltGen.Web/AltGen.Web.csproj @@ -11,4 +11,8 @@ + + + + diff --git a/src/AltGen.sln b/src/AltGen.sln index 6bafee2..b9fb0f9 100644 --- a/src/AltGen.sln +++ b/src/AltGen.sln @@ -15,6 +15,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AltGen.Web", "AltGen.Web\Al EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AltGen.Web.Client", "AltGen.Web.Client\AltGen.Web.Client.csproj", "{DCE07E79-23A4-490E-A796-EA140D2040C1}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AltGen.Shared", "AltGen.Shared\AltGen.Shared.csproj", "{68D4BC5D-A6FE-41CF-B744-1DB83CCFBC89}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution 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}.Release|Any CPU.ActiveCfg = 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 EndGlobal