fix: quota mensal
All checks were successful
Deploy QR Rapido / test (push) Successful in 1m14s
Deploy QR Rapido / build-and-push (push) Successful in 17m36s
Deploy QR Rapido / deploy-staging (push) Has been skipped
Deploy QR Rapido / deploy-production (push) Successful in 2m46s

This commit is contained in:
Ricardo Carneiro 2026-05-08 07:17:41 -03:00
parent 4570e9b70b
commit 7b78f11b72
3 changed files with 58 additions and 10 deletions

View File

@ -1,5 +1,6 @@
using System.Globalization; using System.Globalization;
using System.Linq; using System.Linq;
using Microsoft.AspNetCore.Localization;
namespace QRRapidoApp.Middleware namespace QRRapidoApp.Middleware
{ {
@ -92,8 +93,9 @@ namespace QRRapidoApp.Middleware
} }
} }
// No culture prefix or unknown segment -> Serve in Portuguese (default) // No culture prefix → don't override culture cookie (CookieRequestCultureProvider
SetCulture(context, DefaultCulture); // will pick it up). Just set thread culture from cookie if present, else pt-BR.
SetCulture(context, DefaultCulture, persistCookie: false);
await _next(context); await _next(context);
} }
@ -151,12 +153,33 @@ namespace QRRapidoApp.Middleware
return true; return true;
} }
private void SetCulture(HttpContext context, string culture) private void SetCulture(HttpContext context, string culture, bool persistCookie = true)
{ {
var cultureInfo = new CultureInfo(culture); var cultureInfo = new CultureInfo(culture);
CultureInfo.CurrentCulture = cultureInfo; CultureInfo.CurrentCulture = cultureInfo;
CultureInfo.CurrentUICulture = cultureInfo; CultureInfo.CurrentUICulture = cultureInfo;
context.Items["Culture"] = culture; context.Items["Culture"] = culture;
// Persist culture cookie so CookieRequestCultureProvider works on
// routes without a culture prefix (e.g. /Pagamento/SelecaoPlano).
// Don't persist when using the default fallback — the user's stored
// preference (from a previous explicit navigation) must not be overwritten.
if (persistCookie)
{
var cookieValue = CookieRequestCultureProvider.MakeCookieValue(
new RequestCulture(culture, culture));
context.Response.Cookies.Append(
CookieRequestCultureProvider.DefaultCookieName,
cookieValue,
new CookieOptions
{
Expires = DateTimeOffset.UtcNow.AddDays(365),
IsEssential = true,
HttpOnly = false, // JS may need to read it
SameSite = SameSiteMode.Lax,
Secure = false // Must work on HTTP in dev too
});
}
} }
private bool IsSpecialRoute(string path) private bool IsSpecialRoute(string path)

View File

@ -105,7 +105,16 @@ builder.Host.UseSerilog();
// Add services to the container // Add services to the container
builder.Services.AddControllersWithViews() builder.Services.AddControllersWithViews(mvc =>
{
// Prevent Cloudflare/proxies from caching dynamic MVC responses.
// Static files bypass this filter and have their own cache headers.
mvc.Filters.Add(new Microsoft.AspNetCore.Mvc.ResponseCacheAttribute
{
NoStore = true,
Location = Microsoft.AspNetCore.Mvc.ResponseCacheLocation.None
});
})
.AddViewLocalization(Microsoft.AspNetCore.Mvc.Razor.LanguageViewLocationExpanderFormat.Suffix) .AddViewLocalization(Microsoft.AspNetCore.Mvc.Razor.LanguageViewLocationExpanderFormat.Suffix)
.AddDataAnnotationsLocalization(); .AddDataAnnotationsLocalization();
@ -180,6 +189,22 @@ builder.Services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationSc
options.LogoutPath = "/Account/Logout"; options.LogoutPath = "/Account/Logout";
options.ExpireTimeSpan = TimeSpan.FromDays(30); options.ExpireTimeSpan = TimeSpan.FromDays(30);
options.SlidingExpiration = true; options.SlidingExpiration = true;
options.Cookie.HttpOnly = true;
options.Cookie.SecurePolicy = CookieSecurePolicy.Always;
options.Cookie.SameSite = SameSiteMode.Lax;
options.Events.OnRedirectToLogin = ctx =>
{
Log.Warning("Auth challenge: {Path} → login. User authenticated: {Auth}",
ctx.Request.Path, ctx.HttpContext.User.Identity?.IsAuthenticated);
ctx.Response.Redirect(ctx.RedirectUri);
return Task.CompletedTask;
};
options.Events.OnValidatePrincipal = ctx =>
{
if (!ctx.Principal?.Identity?.IsAuthenticated ?? false)
Log.Warning("Cookie validation failed for {Path}", ctx.Request.Path);
return Task.CompletedTask;
};
}) })
.AddGoogle(options => .AddGoogle(options =>
{ {
@ -235,10 +260,11 @@ builder.Services.Configure<RequestLocalizationOptions>(options =>
options.FallBackToParentCultures = false; options.FallBackToParentCultures = false;
options.FallBackToParentUICultures = false; options.FallBackToParentUICultures = false;
// Clear default providers and add custom ones in priority order // Priority: Route → QueryString → Cookie → Default (pt-BR)
options.RequestCultureProviders.Clear(); options.RequestCultureProviders.Clear();
options.RequestCultureProviders.Add(new CustomRouteDataRequestCultureProvider()); options.RequestCultureProviders.Add(new CustomRouteDataRequestCultureProvider());
options.RequestCultureProviders.Add(new QueryStringRequestCultureProvider()); options.RequestCultureProviders.Add(new QueryStringRequestCultureProvider());
options.RequestCultureProviders.Add(new CookieRequestCultureProvider());
}); });
// Custom Services // Custom Services

View File

@ -5,9 +5,8 @@
ViewData["Title"] = "Comprar Créditos"; ViewData["Title"] = "Comprar Créditos";
Layout = "~/Views/Shared/_Layout.cshtml"; Layout = "~/Views/Shared/_Layout.cshtml";
var reqPath = Context.Request.Path.Value ?? ""; var currentCulture = System.Globalization.CultureInfo.CurrentCulture.Name;
var isBrazilian = !reqPath.StartsWith("/en", StringComparison.OrdinalIgnoreCase) var isBrazilian = currentCulture.StartsWith("pt", StringComparison.OrdinalIgnoreCase);
&& !reqPath.StartsWith("/es", StringComparison.OrdinalIgnoreCase);
} }
<div class="container py-5"> <div class="container py-5">
@ -46,8 +45,8 @@
</div> </div>
} }
<div class="d-flex justify-content-between align-items-center"> <div class="d-flex justify-content-between align-items-center">
<span class="text-muted"><i class="fas fa-credit-card"></i> Cartão</span> <span class="text-muted"><i class="fas fa-credit-card"></i> @(isBrazilian ? "Cartão" : "Card")</span>
<span class="h5 text-secondary mb-0">R$ @package.PriceCard.ToString("F2")</span> <span class="h5 text-secondary mb-0">@(isBrazilian ? "R$" : "BRL") @package.PriceCard.ToString("F2")</span>
</div> </div>
</div> </div>