using System.Collections.Concurrent; using Microsoft.AspNetCore.Mvc; using Microsoft.Extensions.Caching.Memory; using Nalu.Web.Models; using Nalu.Web.Services; using Nalu.Web.Services.LlmRouter; namespace Nalu.Web.Endpoints; public static class PlaygroundEndpoints { private const int DailyLimit = 10; public static void MapPlaygroundEndpoints(this WebApplication app) { app.MapPost("/v1/playground/extract/{validator}", async ( string validator, HttpContext ctx, [FromBody] ExtractionRequest req, ExtractionPipeline pipeline, IMemoryCache cache, CancellationToken ct) => { // IP-based rate limit: 10 calls/IP/day var ip = ctx.Connection.RemoteIpAddress?.ToString() ?? "unknown"; var cacheKey = $"pg:{ip}:{DateTime.UtcNow:yyyyMMdd}"; var count = cache.GetOrCreate(cacheKey, e => { e.AbsoluteExpiration = DateTime.UtcNow.Date.AddDays(1); return 0; }); if (count >= DailyLimit) return Results.Json(new { error = "Limite diário de 10 chamadas atingido. Crie uma conta para 3.000 créditos grátis." }, statusCode: 429); cache.Set(cacheKey, count + 1, new MemoryCacheEntryOptions { AbsoluteExpiration = DateTime.UtcNow.Date.AddDays(1) }); ctx.Response.Headers["X-Playground-Calls-Remaining"] = (DailyLimit - count - 1).ToString(); try { var result = await pipeline.ExecuteAsync($"validate_{validator.Replace("-", "_")}", req, ct); return Results.Ok(result); } catch (Exception ex) { return Results.Json(new { error = ex.Message }, statusCode: 503); } }) .AllowAnonymous() .WithTags("Playground") .WithSummary("Playground — sem auth, 10 chamadas/IP/dia"); } }