diff --git a/Middleware/LanguageRedirectionMiddleware.cs b/Middleware/LanguageRedirectionMiddleware.cs index 87db80e..0929994 100644 --- a/Middleware/LanguageRedirectionMiddleware.cs +++ b/Middleware/LanguageRedirectionMiddleware.cs @@ -1,5 +1,6 @@ using System.Globalization; using System.Linq; +using Microsoft.AspNetCore.Localization; namespace QRRapidoApp.Middleware { @@ -92,8 +93,9 @@ namespace QRRapidoApp.Middleware } } - // No culture prefix or unknown segment -> Serve in Portuguese (default) - SetCulture(context, DefaultCulture); + // No culture prefix → don't override culture cookie (CookieRequestCultureProvider + // will pick it up). Just set thread culture from cookie if present, else pt-BR. + SetCulture(context, DefaultCulture, persistCookie: false); await _next(context); } @@ -151,12 +153,33 @@ namespace QRRapidoApp.Middleware return true; } - private void SetCulture(HttpContext context, string culture) + private void SetCulture(HttpContext context, string culture, bool persistCookie = true) { var cultureInfo = new CultureInfo(culture); CultureInfo.CurrentCulture = cultureInfo; CultureInfo.CurrentUICulture = cultureInfo; 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) diff --git a/Program.cs b/Program.cs index aee731d..bfbffe1 100644 --- a/Program.cs +++ b/Program.cs @@ -105,7 +105,16 @@ builder.Host.UseSerilog(); // 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) .AddDataAnnotationsLocalization(); @@ -180,6 +189,22 @@ builder.Services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationSc options.LogoutPath = "/Account/Logout"; options.ExpireTimeSpan = TimeSpan.FromDays(30); 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 => { @@ -235,10 +260,11 @@ builder.Services.Configure(options => options.FallBackToParentCultures = 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.Add(new CustomRouteDataRequestCultureProvider()); options.RequestCultureProviders.Add(new QueryStringRequestCultureProvider()); + options.RequestCultureProviders.Add(new CookieRequestCultureProvider()); }); // Custom Services diff --git a/Views/Pagamento/SelecaoPlano.cshtml b/Views/Pagamento/SelecaoPlano.cshtml index fff6057..2121997 100644 --- a/Views/Pagamento/SelecaoPlano.cshtml +++ b/Views/Pagamento/SelecaoPlano.cshtml @@ -5,9 +5,8 @@ ViewData["Title"] = "Comprar Créditos"; Layout = "~/Views/Shared/_Layout.cshtml"; - var reqPath = Context.Request.Path.Value ?? ""; - var isBrazilian = !reqPath.StartsWith("/en", StringComparison.OrdinalIgnoreCase) - && !reqPath.StartsWith("/es", StringComparison.OrdinalIgnoreCase); + var currentCulture = System.Globalization.CultureInfo.CurrentCulture.Name; + var isBrazilian = currentCulture.StartsWith("pt", StringComparison.OrdinalIgnoreCase); }
@@ -46,8 +45,8 @@
}
- Cartão - R$ @package.PriceCard.ToString("F2") + @(isBrazilian ? "Cartão" : "Card") + @(isBrazilian ? "R$" : "BRL") @package.PriceCard.ToString("F2")