- ASP.NET Core 9 Razor Pages + Minimal API hybrid - 14 validators (CPF, CEP, CNPJ, email, phone, name, yes-no, birthdate, handoff, cancel-intent, company-name, plate-br, postal-code, validate_reply) - OAuth login (Google, Microsoft, GitHub) + cookie auth - MongoDB usage tracking + CEP cache collection - Stripe checkout with inline PriceData (no Price IDs) - MCP server for Claude Code / Cursor integration - Playground (10 calls/IP/day, no auth) - Docs: Quickstart, API Reference, N8N, MCP, Créditos, Erros, Fluxos - Credit system: 3 cr standard validators, 5 cr validate_reply - SmartSuggestion: contextual re-ask via IA when obtained=false - Per-IP rate limiting + daily cap + shared-IP abuse detection - Lightbox for comic images - Validadores page split: Brasileiros / Universais + Em breve Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
59 lines
2.0 KiB
C#
59 lines
2.0 KiB
C#
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");
|
|
}
|
|
}
|