fix: Ajustes de cache e planos
All checks were successful
BCards Deployment Pipeline / Run Tests (push) Successful in 2s
BCards Deployment Pipeline / PR Validation (push) Has been skipped
BCards Deployment Pipeline / Build and Push Image (push) Successful in 15m3s
BCards Deployment Pipeline / Deploy to Production (ARM - OCI) (push) Successful in 2m22s
BCards Deployment Pipeline / Deploy to Staging (x86 - Local) (push) Has been skipped
BCards Deployment Pipeline / Cleanup Old Resources (push) Has been skipped
BCards Deployment Pipeline / Deployment Summary (push) Successful in 0s

This commit is contained in:
Ricardo Carneiro 2025-09-09 19:13:21 -03:00
parent 61453b65c9
commit 9a850d239f
4 changed files with 197 additions and 2 deletions

View File

@ -218,6 +218,108 @@ jobs:
"FromEmail": "${{ vars.SENDGRID_FROM_EMAIL || 'ricardo.carneiro@jobmaker.com.br' }}",
"FromName": "${{ vars.SENDGRID_FROM_NAME || 'Ricardo Carneiro' }}"
},
"Plans": {
"Basic": {
"Name": "Básico",
"PriceId": "price_1RycPaBMIadsOxJVKioZZofK",
"Price": 5.90,
"MaxPages": 3,
"MaxLinks": 8,
"AllowPremiumThemes": false,
"AllowProductLinks": false,
"AllowAnalytics": true,
"Features": [ "URL Personalizada", "20 temas básicos", "Analytics simples" ],
"Interval": "month"
},
"Professional": {
"Name": "Profissional",
"PriceId": "price_1RycQmBMIadsOxJVGqjVMaOj",
"Price": 12.90,
"MaxPages": 5,
"MaxLinks": 20,
"AllowPremiumThemes": false,
"AllowProductLinks": false,
"AllowAnalytics": true,
"Features": [ "URL Personalizada", "20 temas básicos", "Analytics avançado" ],
"Interval": "month"
},
"Premium": {
"Name": "Premium",
"PriceId": "price_1RycRUBMIadsOxJVkxGOh3uu",
"Price": 19.90,
"MaxPages": 15,
"MaxLinks": -1,
"AllowPremiumThemes": true,
"AllowProductLinks": false,
"AllowAnalytics": true,
"SpecialModeration": false,
"Features": [ "URL Personalizada", "40 temas (básicos + premium)", "Suporte prioritário", "Links ilimitados" ],
"Interval": "month"
},
"PremiumAffiliate": {
"Name": "Premium+Afiliados",
"PriceId": "price_1RycTaBMIadsOxJVeDLseXQq",
"Price": 29.90,
"MaxPages": 15,
"MaxLinks": -1,
"AllowPremiumThemes": true,
"AllowProductLinks": true,
"AllowAnalytics": true,
"SpecialModeration": true,
"Features": [ "Tudo do Premium", "Links de produto", "Moderação especial", "Até 10 links afiliados" ],
"Interval": "month"
},
"BasicYearly": {
"Name": "Básico Anual",
"PriceId": "price_1RycWgBMIadsOxJVGdtEeoMS",
"Price": 59.00,
"MaxPages": 3,
"MaxLinks": 8,
"AllowPremiumThemes": false,
"AllowProductLinks": false,
"AllowAnalytics": true,
"Features": [ "URL Personalizada", "20 temas básicos", "Analytics simples", "Economize R$ 11,80 (2 meses grátis)" ],
"Interval": "year"
},
"ProfessionalYearly": {
"Name": "Profissional Anual",
"PriceId": "price_1RycXdBMIadsOxJV5cNX7dHm",
"Price": 129.00,
"MaxPages": 5,
"MaxLinks": 20,
"AllowPremiumThemes": false,
"AllowProductLinks": false,
"AllowAnalytics": true,
"Features": [ "URL Personalizada", "20 temas básicos", "Analytics avançado", "Economize R$ 25,80 (2 meses grátis)" ],
"Interval": "year"
},
"PremiumYearly": {
"Name": "Premium Anual",
"PriceId": "price_1RycYnBMIadsOxJVPdKmzy4m",
"Price": 199.00,
"MaxPages": 15,
"MaxLinks": -1,
"AllowPremiumThemes": true,
"AllowProductLinks": false,
"AllowAnalytics": true,
"SpecialModeration": false,
"Features": [ "URL Personalizada", "40 temas (básicos + premium)", "Suporte prioritário", "Links ilimitados", "Economize R$ 39,80 (2 meses grátis)" ],
"Interval": "year"
},
"PremiumAffiliateYearly": {
"Name": "Premium+Afiliados Anual",
"PriceId": "price_1RycaEBMIadsOxJVEhsdB2Y1",
"Price": 299.00,
"MaxPages": 15,
"MaxLinks": -1,
"AllowPremiumThemes": true,
"AllowProductLinks": true,
"AllowAnalytics": true,
"SpecialModeration": true,
"Features": [ "Tudo do Premium", "Links de produto", "Moderação especial", "Até 10 links afiliados", "Economize R$ 59,80 (2 meses grátis)" ],
"Interval": "year"
}
},
"Moderation": {
"PriorityTimeframes": {
"Trial": "7.00:00:00",

View File

@ -21,9 +21,19 @@ public class HomeController : Controller
_stripeSettings = stripeSettings.Value;
}
[ResponseCache(Duration = 600, Location = ResponseCacheLocation.Any)] // 10 minutos
public async Task<IActionResult> Index()
{
// Cache condicional: apenas para usuários não logados
if (User.Identity?.IsAuthenticated != true)
{
Response.Headers["Cache-Control"] = "public, max-age=600"; // 10 minutos
}
else
{
Response.Headers["Cache-Control"] = "no-cache, must-revalidate";
Response.Headers["Vary"] = "Cookie";
}
ViewBag.IsHomePage = true; // Flag para identificar home
ViewBag.Categories = await _categoryService.GetAllCategoriesAsync();
ViewBag.RecentPages = await _userPageService.GetRecentPagesAsync(6);
@ -39,9 +49,19 @@ public class HomeController : Controller
}
[Route("Pricing")]
[ResponseCache(Duration = 1800, Location = ResponseCacheLocation.Any)] // 30 minutos
public IActionResult Pricing()
{
// Cache condicional: apenas para usuários não logados
if (User.Identity?.IsAuthenticated != true)
{
Response.Headers["Cache-Control"] = "public, max-age=1800"; // 30 minutos
}
else
{
Response.Headers["Cache-Control"] = "no-cache, must-revalidate";
Response.Headers["Vary"] = "Cookie";
}
ViewBag.IsHomePage = true;
return View();
}

View File

@ -0,0 +1,71 @@
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Logging;
using System.Threading.Tasks;
namespace BCards.Web.Middleware
{
/// <summary>
/// Middleware para garantir que páginas que exibem conteúdo dependente de autenticação
/// tenham os headers de cache corretos para evitar problemas de cache do menu
/// </summary>
public class AuthCacheMiddleware
{
private readonly RequestDelegate _next;
private readonly ILogger<AuthCacheMiddleware> _logger;
public AuthCacheMiddleware(RequestDelegate next, ILogger<AuthCacheMiddleware> logger)
{
_next = next;
_logger = logger;
}
public async Task InvokeAsync(HttpContext context)
{
await _next(context);
// Aplicar headers apenas para páginas HTML (não APIs, imagens, etc)
if (context.Response.ContentType?.StartsWith("text/html") == true)
{
var path = context.Request.Path.Value?.ToLower() ?? string.Empty;
// Páginas que sempre mostram menu com estado de autenticação
bool isPageWithAuthMenu = path == "/" ||
path.StartsWith("/home") ||
path == "/pricing" ||
path.StartsWith("/planos") ||
path.StartsWith("/admin") ||
path.StartsWith("/payment") ||
path.StartsWith("/subscription");
if (isPageWithAuthMenu)
{
// Se usuário está logado, garantir que não use cache
if (context.User?.Identity?.IsAuthenticated == true)
{
// Só adicionar se não foi definido explicitamente pelo controller
if (!context.Response.Headers.ContainsKey("Cache-Control"))
{
context.Response.Headers["Cache-Control"] = "no-cache, must-revalidate";
context.Response.Headers["Vary"] = "Cookie";
_logger.LogDebug("AuthCache: Applied no-cache for authenticated user on {Path}", path);
}
}
else
{
// Para usuários não logados, garantir Vary: Cookie para cache adequado
if (!context.Response.Headers.ContainsKey("Vary") ||
!context.Response.Headers["Vary"].ToString().Contains("Cookie"))
{
var existingVary = context.Response.Headers["Vary"].ToString();
var newVary = string.IsNullOrEmpty(existingVary) ? "Cookie" : $"{existingVary}, Cookie";
context.Response.Headers["Vary"] = newVary;
_logger.LogDebug("AuthCache: Added Vary: Cookie for anonymous user on {Path}", path);
}
}
}
}
}
}
}

View File

@ -555,6 +555,8 @@ app.UseRequestLocalization();
app.UseAuthentication();
app.UseAuthorization();
app.UseMiddleware<BCards.Web.Middleware.SmartCacheMiddleware>();
app.UseMiddleware<BCards.Web.Middleware.AuthCacheMiddleware>();
app.UseMiddleware<BCards.Web.Middleware.PlanLimitationMiddleware>();
app.UseMiddleware<BCards.Web.Middleware.PageStatusMiddleware>();
app.UseMiddleware<BCards.Web.Middleware.PreviewTokenMiddleware>();