diff --git a/dotnet/EcencyApi/Handlers/PrivateApi.Misc.cs b/dotnet/EcencyApi/Handlers/PrivateApi.Misc.cs index 9fa3226f..8d77c4da 100644 --- a/dotnet/EcencyApi/Handlers/PrivateApi.Misc.cs +++ b/dotnet/EcencyApi/Handlers/PrivateApi.Misc.cs @@ -566,6 +566,25 @@ await Upstream.Pipe( ApiClient.ApiRequest("ai-image-generate", HttpMethod.Post, null, data, null, 120000), ctx); } + /// + /// Per-user AI image generation history (the upstream's last 20 successful + /// generations). The username comes ONLY from the validated code: prompts are + /// private to their author, so a body-supplied username would let any caller + /// read someone else's generation history. + /// + public static async Task AiImagesHistory(HttpContext ctx) + { + var body = await ctx.ReadBody(); + var username = await ValidateCode(body); + if (username == null) + { + await ctx.SendText(401, "Unauthorized"); + return; + } + await Upstream.Pipe( + ApiClient.ApiRequest($"users/{username}/ai-images", HttpMethod.Get), ctx); + } + public static async Task AiAssistPrice(HttpContext ctx) { var body = await ctx.ReadBody(); diff --git a/dotnet/EcencyApi/Handlers/Routes.cs b/dotnet/EcencyApi/Handlers/Routes.cs index bcff2f2c..dd74c8d6 100644 --- a/dotnet/EcencyApi/Handlers/Routes.cs +++ b/dotnet/EcencyApi/Handlers/Routes.cs @@ -155,6 +155,7 @@ public static void Map(WebApplication app) app.MapPost("/private-api/boosted-post", PrivateApi.BoostedPost); app.MapPost("/private-api/ai-generate-price", PrivateApi.AiGeneratePrice); app.MapPost("/private-api/ai-generate-image", PrivateApi.AiGenerateImage); + app.MapPost("/private-api/ai-images", PrivateApi.AiImagesHistory); app.MapPost("/private-api/ai-assist-price", PrivateApi.AiAssistPrice); app.MapPost("/private-api/ai-assist", PrivateApi.AiAssist); app.MapPost("/private-api/ai-transcribe-price", PrivateApi.AiTranscribePrice); diff --git a/dotnet/parity/driver.py b/dotnet/parity/driver.py index 30948790..c65239e3 100644 --- a/dotnet/parity/driver.py +++ b/dotnet/parity/driver.py @@ -224,6 +224,13 @@ def norm_body(text): "::pop and ::badcode for every POST route, which is why all three appear here." ) +AI_IMAGES_DIVERGENCE = ( + "Per-user AI image history route added after the port (ePoints users//ai-images). " + "The reference build has no such route and answers 404; this one validates the " + "signed code and answers 401 or proxies the history. Deterministic and additive -- " + "no behavior the reference ever had is changing." +) + # Cases where the C# port intentionally differs from Node (Node bugs the port fixes). KNOWN_DIVERGENCES = { "/auth-api/hs-token-refresh::min": @@ -256,6 +263,15 @@ def norm_body(text): AI_TRANSCRIBE_DIVERGENCE, "/private-api/ai-transcribe::badcode": AI_TRANSCRIBE_DIVERGENCE, + # Per-user AI image history, added after the port (ePoints users//ai-images). + # Same shape as the dictation entries: the reference build has no such route and + # answers 404 while this one validates the code and answers 401/proxies. Additive. + "/private-api/ai-images::min": + AI_IMAGES_DIVERGENCE, + "/private-api/ai-images::pop": + AI_IMAGES_DIVERGENCE, + "/private-api/ai-images::badcode": + AI_IMAGES_DIVERGENCE, } # Deliberately NOT listed above: /wallet-api/portfolio-v2::pop, whose HP action list