diff --git a/.gitea/workflows/deploy-bcards.yml b/.gitea/workflows/deploy-bcards.yml
index 6edc650..32b2c18 100644
--- a/.gitea/workflows/deploy-bcards.yml
+++ b/.gitea/workflows/deploy-bcards.yml
@@ -231,6 +231,9 @@ jobs:
"${{ vars.MODERATOR_EMAIL_1 || 'rrcgoncalves@gmail.com' }}",
"${{ vars.MODERATOR_EMAIL_2 || 'rirocarneiro@gmail.com' }}"
]
+ },
+ "Serilog": {
+ "OpenSearchUrl": "${{ vars.OPENSEARCH_URL || 'http://localhost:9201' }}",
}
}
CONFIG_EOF
@@ -291,7 +294,7 @@ jobs:
-e MongoDb__ConnectionString="mongodb://admin:c4rn31r0@129.146.116.218:27017,141.148.162.114:27017/BCardsDB?replicaSet=rs0&authSource=admin" \
-e MongoDb__DatabaseName="BCardsDB" \
-e Logging__LogLevel__Default=Debug \
- -e Serilog__SeqUrl="http://localhost:5343" \
+ -e Serilog__OpenSearchUrl="https://localhost:9201" \
-v /home/ubuntu/bcards-config/appsettings.Production.json:/app/appsettings.Production.json:ro \
${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:latest
@@ -341,7 +344,7 @@ jobs:
-e MongoDb__ConnectionString="mongodb://admin:c4rn31r0@129.146.116.218:27017,141.148.162.114:27017/BCardsDB?replicaSet=rs0&authSource=admin" \
-e MongoDb__DatabaseName="BCardsDB" \
-e Logging__LogLevel__Default=Debug \
- -e Serilog__SeqUrl="http://localhost:5342" \
+ -e Serilog__OpenSearchUrl="https://localhost:9202" \
-v /home/ubuntu/bcards-config/appsettings.Production.json:/app/appsettings.Production.json:ro \
${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:latest
@@ -423,7 +426,7 @@ jobs:
--network host \
-e ASPNETCORE_ENVIRONMENT=Staging \
-e ASPNETCORE_URLS=http://+:8080 \
- -e Serilog__SeqUrl="http://localhost:5341" \
+ -e Serilog__OpenSearchUrl="https://192.168.0.100:9200" \
${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ steps.version.outputs.version }}
echo "✅ Servidor Staging atualizado"
diff --git a/src/BCards.Web/BCards.Web.csproj b/src/BCards.Web/BCards.Web.csproj
index 1819726..921bf20 100644
--- a/src/BCards.Web/BCards.Web.csproj
+++ b/src/BCards.Web/BCards.Web.csproj
@@ -22,7 +22,7 @@
-
+
diff --git a/src/BCards.Web/Controllers/StripeWebhookController.cs b/src/BCards.Web/Controllers/StripeWebhookController.cs
index 72f7736..ba76b01 100644
--- a/src/BCards.Web/Controllers/StripeWebhookController.cs
+++ b/src/BCards.Web/Controllers/StripeWebhookController.cs
@@ -91,18 +91,15 @@ public class StripeWebhookController : ControllerBase
break;
}
- await Task.Delay(2000); // 2 segundos
return Ok();
}
catch (StripeException ex)
{
- await Task.Delay(2000); // 2 segundos
_logger.LogError(ex, "Stripe webhook error");
return BadRequest($"Stripe error: {ex.Message}");
}
catch (Exception ex)
{
- await Task.Delay(2000); // 2 segundos
_logger.LogError(ex, "Webhook processing error");
return StatusCode(500, "Internal server error");
}
@@ -140,10 +137,12 @@ public class StripeWebhookController : ControllerBase
{
_logger.LogWarning($"Unexpected event type on HandlePaymentSucceeded: {stripeEvent.Type}");
}
+ await Task.Delay(4000);
}
catch (Exception ex)
{
_logger.LogError(ex, "Error handling payment succeeded event");
+ await Task.Delay(4000);
throw new Exception("Error handling payment succeeded event", ex);
}
}
@@ -327,15 +326,18 @@ public class StripeWebhookController : ControllerBase
// Try to find user by Stripe Customer ID and create/update subscription
await HandleSubscriptionCreatedForNewSubscription(stripeSubscription);
}
+
}
else
{
_logger.LogWarning($"Unexpected event type on HandleSubscriptionCreated: {stripeEvent.Type}");
}
+ await Task.Delay(4000);
}
catch (Exception ex)
{
_logger.LogError(ex, "Error handling subscription created event");
+ await Task.Delay(4000);
throw new Exception("Error handling subscription created event", ex);
}
}
diff --git a/src/BCards.Web/Program.cs b/src/BCards.Web/Program.cs
index 15ba091..066bc51 100644
--- a/src/BCards.Web/Program.cs
+++ b/src/BCards.Web/Program.cs
@@ -18,6 +18,7 @@ using Microsoft.AspNetCore.HttpOverrides;
using Serilog;
using Serilog.Events;
using Microsoft.Extensions.Diagnostics.HealthChecks;
+using Serilog.Sinks.OpenSearch;
var builder = WebApplication.CreateBuilder(args);
@@ -25,6 +26,13 @@ var builder = WebApplication.CreateBuilder(args);
var isDevelopment = builder.Environment.IsDevelopment();
var hostname = Environment.MachineName;
+// 🔥 CORREÇÃO 1: Habilitar SelfLog ANTES de configurar o logger
+Serilog.Debugging.SelfLog.Enable(msg =>
+{
+ Console.WriteLine($"[SERILOG SELF] {DateTime.Now:HH:mm:ss} {msg}");
+ System.Diagnostics.Debug.WriteLine($"[SERILOG SELF] {msg}");
+});
+
var loggerConfig = new LoggerConfiguration()
.ReadFrom.Configuration(builder.Configuration)
.Enrich.FromLogContext()
@@ -35,65 +43,156 @@ var loggerConfig = new LoggerConfiguration()
.Enrich.WithProperty("Environment", builder.Environment.EnvironmentName)
.Enrich.WithProperty("Hostname", hostname);
-// Nova abordagem de logging: Tudo em ambos, mas com controle de volume
if (isDevelopment)
{
- // Development: Debug level + detalhes completos
loggerConfig
.MinimumLevel.Debug()
.MinimumLevel.Override("Microsoft.AspNetCore.Hosting", LogEventLevel.Information)
.MinimumLevel.Override("Microsoft.AspNetCore.Routing", LogEventLevel.Information)
.MinimumLevel.Override("Microsoft", LogEventLevel.Warning)
.MinimumLevel.Override("System", LogEventLevel.Warning)
- // Console: Tudo acima de Information
- .WriteTo.Async(a => a.Console(
+ .WriteTo.Console(
restrictedToMinimumLevel: LogEventLevel.Information,
- outputTemplate: "[{Timestamp:HH:mm:ss} {Level:u3}] [{Hostname}] {Message:lj} {Properties:j}{NewLine}{Exception}"));
-
- var seqUrl = builder.Configuration["Serilog:SeqUrl"];
- if (!string.IsNullOrEmpty(seqUrl))
+ outputTemplate: "[{Timestamp:HH:mm:ss} {Level:u3}] [{Hostname}] {Message:lj} {Properties:j}{NewLine}{Exception}");
+
+ var openSearchUrl = builder.Configuration["Serilog:OpenSearchUrl"];
+ if (!string.IsNullOrEmpty(openSearchUrl))
{
- var apiKey = builder.Configuration["Serilog:ApiKey"];
- // Seq: Tudo (incluindo Debug)
- loggerConfig.WriteTo.Async(a => a.Seq(seqUrl,
- apiKey: string.IsNullOrEmpty(apiKey) ? null : apiKey,
- restrictedToMinimumLevel: LogEventLevel.Debug));
+ var indexFormat = "b-cards-dev-{0:yyyy-MM}";
+
+ Console.WriteLine($"[DEBUG] Configurando OpenSearch HTTP (SEM AUTENTICAÇÃO):");
+ Console.WriteLine($"[DEBUG] URL: {openSearchUrl}");
+ Console.WriteLine($"[DEBUG] Index Format: {indexFormat}");
+ Console.WriteLine($"[DEBUG] Protocol: {(openSearchUrl.StartsWith("https") ? "HTTPS" : "HTTP")}");
+
+ try
+ {
+ loggerConfig.WriteTo.OpenSearch(new OpenSearchSinkOptions(new Uri(openSearchUrl))
+ {
+ IndexFormat = indexFormat,
+ AutoRegisterTemplate = true,
+ BufferBaseFilename = "./logs/buffer",
+ // 🔥 REMOVIDO: BasicAuthentication - SEM AUTENTICAÇÃO
+ ModifyConnectionSettings = conn => conn
+ .RequestTimeout(TimeSpan.FromSeconds(30))
+ .PingTimeout(TimeSpan.FromSeconds(10))
+ .ThrowExceptions(true)
+ .OnRequestCompleted(details =>
+ {
+ if (details.Success)
+ {
+ Console.WriteLine($"[OPENSEARCH] ✅ Request OK: {details.HttpMethod} {details.Uri} - Status: {details.HttpStatusCode}");
+ }
+ else
+ {
+ Console.WriteLine($"[OPENSEARCH] ❌ Request FAILED: {details.HttpMethod} {details.Uri}");
+ Console.WriteLine($"[OPENSEARCH] Status: {details.HttpStatusCode}");
+ Console.WriteLine($"[OPENSEARCH] Exception: {details.OriginalException?.Message}");
+ if (details.ResponseBodyInBytes != null && details.ResponseBodyInBytes.Length > 0)
+ {
+ var responseBody = System.Text.Encoding.UTF8.GetString(details.ResponseBodyInBytes);
+ Console.WriteLine($"[OPENSEARCH] Response: {responseBody}");
+ }
+ }
+ }),
+ MinimumLogEventLevel = LogEventLevel.Debug,
+ EmitEventFailure = EmitEventFailureHandling.WriteToSelfLog,
+ RegisterTemplateFailure = RegisterTemplateRecovery.IndexAnyway,
+ BatchPostingLimit = 5, // Muito pequeno para debug
+ Period = TimeSpan.FromSeconds(2),
+ // 🔥 CORRIGIDO: Dictionary
+ TemplateCustomSettings = new Dictionary
+ {
+ {"number_of_shards", "1"},
+ {"number_of_replicas", "0"},
+ {"index.mapping.total_fields.limit", "2000"}
+ }
+ });
+
+ Console.WriteLine($"[DEBUG] ✅ Serilog configurado para OpenSearch HTTP!");
+ }
+ catch (Exception ex)
+ {
+ Console.WriteLine($"[ERROR] ❌ Falha ao configurar OpenSearch: {ex.Message}");
+ Console.WriteLine($"[ERROR] Stack trace: {ex.StackTrace}");
+ }
+ }
+ else
+ {
+ Console.WriteLine("[WARNING] OpenSearchUrl não configurada!");
}
}
else
{
- // Production: Log balanceado - reduzir spam de requisições
+ // PRODUÇÃO: Mesma lógica sem autenticação
loggerConfig
.MinimumLevel.Information()
- .MinimumLevel.Override("Microsoft.AspNetCore.Hosting.Diagnostics", LogEventLevel.Warning) // Reduzir spam de requests
+ .MinimumLevel.Override("Microsoft.AspNetCore.Hosting.Diagnostics", LogEventLevel.Warning)
.MinimumLevel.Override("Microsoft.AspNetCore.Routing.EndpointMiddleware", LogEventLevel.Warning)
.MinimumLevel.Override("Microsoft.AspNetCore.StaticFiles", LogEventLevel.Warning)
.MinimumLevel.Override("Microsoft", LogEventLevel.Warning)
.MinimumLevel.Override("System", LogEventLevel.Warning)
- // Docker/Console: Information e acima (reduzindo volume do log)
- .WriteTo.Async(a => a.Console(
- restrictedToMinimumLevel: LogEventLevel.Information, // Mudança: não só errors
- outputTemplate: "[{Timestamp:yyyy-MM-dd HH:mm:ss} {Level:u3}] [{Hostname}] {Message:lj}{NewLine}{Exception}"))
- // Arquivo de log local para backup (rotativo)
- .WriteTo.Async(a => a.File(
+ .WriteTo.Console(
+ restrictedToMinimumLevel: LogEventLevel.Information,
+ outputTemplate: "[{Timestamp:yyyy-MM-dd HH:mm:ss} {Level:u3}] [{Hostname}] {Message:lj}{NewLine}{Exception}")
+ .WriteTo.File(
"/app/logs/bcards-.log",
rollingInterval: RollingInterval.Day,
- retainedFileCountLimit: 7, // Manter 7 dias
- restrictedToMinimumLevel: LogEventLevel.Warning, // Só warnings e errors em arquivo
- outputTemplate: "[{Timestamp:yyyy-MM-dd HH:mm:ss.fff zzz} {Level:u3}] [{Hostname}] {Message:lj} {Properties:j}{NewLine}{Exception}"));
-
- var seqUrl = builder.Configuration["Serilog:SeqUrl"];
- if (!string.IsNullOrEmpty(seqUrl))
+ retainedFileCountLimit: 7,
+ restrictedToMinimumLevel: LogEventLevel.Warning,
+ outputTemplate: "[{Timestamp:yyyy-MM-dd HH:mm:ss.fff zzz} {Level:u3}] [{Hostname}] {Message:lj} {Properties:j}{NewLine}{Exception}");
+
+ var openSearchUrl = builder.Configuration["Serilog:OpenSearchUrl"];
+ if (!string.IsNullOrEmpty(openSearchUrl))
{
- var apiKey = builder.Configuration["Serilog:ApiKey"];
- // Seq: Tudo (Information e acima) - Seq pode estar offline
- loggerConfig.WriteTo.Async(a => a.Seq(seqUrl,
- apiKey: string.IsNullOrEmpty(apiKey) ? null : apiKey,
- restrictedToMinimumLevel: LogEventLevel.Information));
+ var environment = builder.Environment.EnvironmentName.ToLower();
+ var envMapping = environment switch
+ {
+ "production" => "prod",
+ "staging" => "release",
+ "development" => "dev",
+ _ => environment
+ };
+
+ var indexFormat = $"b-cards-{envMapping}-{{0:yyyy-MM}}";
+
+ loggerConfig.WriteTo.Async(a => a.OpenSearch(new OpenSearchSinkOptions(new Uri(openSearchUrl))
+ {
+ IndexFormat = indexFormat,
+ AutoRegisterTemplate = false,
+ BufferBaseFilename = "./logs/buffer",
+ // 🔥 SEM AUTENTICAÇÃO EM PRODUÇÃO TAMBÉM
+ ModifyConnectionSettings = conn => conn
+ .RequestTimeout(TimeSpan.FromSeconds(30))
+ .PingTimeout(TimeSpan.FromSeconds(10)),
+ MinimumLogEventLevel = LogEventLevel.Information,
+ EmitEventFailure = EmitEventFailureHandling.WriteToSelfLog,
+ RegisterTemplateFailure = RegisterTemplateRecovery.IndexAnyway,
+ BatchPostingLimit = 50,
+ Period = TimeSpan.FromSeconds(5),
+ }), bufferSize: 10000);
}
}
-Log.Logger = loggerConfig.CreateLogger();
+// 🔥 LOGS DE TESTE MAIS AGRESSIVOS
+var logger = loggerConfig.CreateLogger();
+Log.Logger = logger;
+
+Console.WriteLine($"[STARTUP] {DateTime.Now:HH:mm:ss} - Testando logs SEM AUTENTICAÇÃO...");
+Log.Information("=== TESTE DE LOG SEM AUTH ===");
+Log.Information("Aplicação BCards iniciando em {Environment} no host {Hostname}",
+ builder.Environment.EnvironmentName, hostname);
+Log.Warning("Log de WARNING - teste OpenSearch HTTP");
+Log.Error("Log de ERROR - forçando envio imediato");
+Log.Debug("Log de DEBUG - verificando conectividade");
+
+// Force flush para garantir que logs sejam enviados
+//Log.CloseAndFlush();
+await Task.Delay(5000); // Aguardar mais tempo
+
+// Recriar logger após flush
+//Log.Logger = loggerConfig.CreateLogger();
+Console.WriteLine($"[STARTUP] {DateTime.Now:HH:mm:ss} - Teste de logs concluído");
// Use Serilog for the host
builder.Host.UseSerilog();
@@ -108,7 +207,6 @@ builder.Services.Configure(options =>
options.RequireHeaderSymmetry = false;
options.KnownNetworks.Clear();
options.KnownProxies.Clear();
- // 🚨 PERMITIR QUALQUER PROXY (NGINX)
options.ForwardLimit = null;
});
@@ -142,19 +240,17 @@ builder.Services.AddControllersWithViews()
builder.Services.Configure(
builder.Configuration.GetSection("MongoDb"));
-// 🔥 OTIMIZAÇÃO: Pool de conexões do MongoDB
builder.Services.AddSingleton(serviceProvider =>
{
var settings = serviceProvider.GetRequiredService>().Value;
var connectionString = settings.ConnectionString;
- // Adiciona configurações de pool de conexões se não existirem
if (!connectionString.Contains("maxPoolSize"))
{
var separator = connectionString.Contains("?") ? "&" : "?";
connectionString += $"{separator}maxPoolSize=200&minPoolSize=20&maxIdleTimeMS=300000&socketTimeoutMS=60000&connectTimeoutMS=30000";
}
-
+
Log.Information("Connecting to MongoDB with optimized pool settings.");
return new MongoClient(connectionString);
});
@@ -170,7 +266,6 @@ builder.Services.AddScoped(serviceProvider =>
builder.Services.Configure(
builder.Configuration.GetSection("Stripe"));
-// Validate Stripe configuration
var stripeSettings = builder.Configuration.GetSection("Stripe").Get();
if (stripeSettings == null || string.IsNullOrEmpty(stripeSettings.SecretKey))
{
@@ -178,7 +273,7 @@ if (stripeSettings == null || string.IsNullOrEmpty(stripeSettings.SecretKey))
throw new InvalidOperationException("Stripe configuration is required");
}
-Log.Information("🔧 Stripe Environment: {Environment} | Test Mode: {IsTestMode}",
+Log.Information("🔧 Stripe Environment: {Environment} | Test Mode: {IsTestMode}",
stripeSettings.Environment.ToUpper(), stripeSettings.IsTestMode);
if (stripeSettings.IsTestMode)
@@ -197,7 +292,6 @@ builder.Services.Configure(
builder.Services.Configure(
builder.Configuration.GetSection("Authentication:Microsoft"));
-// Adicionar configurações
builder.Services.Configure(
builder.Configuration.GetSection("Moderation"));
@@ -213,26 +307,21 @@ builder.Services.AddAuthentication(options =>
options.LogoutPath = "/Auth/Logout";
options.ExpireTimeSpan = TimeSpan.FromHours(8);
options.SlidingExpiration = true;
-
- // 🔥 CORREÇÃO: Configurações de cookie seguras para evitar problemas de correlação
options.Cookie.HttpOnly = true;
options.Cookie.IsEssential = true;
- options.Cookie.SameSite = SameSiteMode.Lax; // Essencial para fluxos OAuth
- options.Cookie.SecurePolicy = CookieSecurePolicy.Always; // Garante HTTPS
+ options.Cookie.SameSite = SameSiteMode.Lax;
+ options.Cookie.SecurePolicy = CookieSecurePolicy.Always;
})
.AddGoogle(options =>
{
var googleAuth = builder.Configuration.GetSection("Authentication:Google");
options.ClientId = googleAuth["ClientId"] ?? "";
options.ClientSecret = googleAuth["ClientSecret"] ?? "";
- options.CallbackPath = "/signin-google"; // Path padrão do Google OAuth
+ options.CallbackPath = "/signin-google";
options.BackchannelTimeout = TimeSpan.FromSeconds(30);
-
- // Adicionar configurações mais robustas para Edge
options.SaveTokens = true;
- options.UsePkce = true; // Usar PKCE para maior segurança
+ options.UsePkce = true;
- // 🔥 CORREÇÃO: Handler para falhas remotas + debugging melhorado
options.Events = new OAuthEvents
{
OnRemoteFailure = context =>
@@ -240,7 +329,7 @@ builder.Services.AddAuthentication(options =>
var logger = context.HttpContext.RequestServices.GetRequiredService>();
logger.LogError("🔴 Google OAuth falhou: {Failure}", context.Failure?.Message);
logger.LogError("🔴 User Agent: {UserAgent}", context.Request.Headers.UserAgent.ToString());
-
+
context.Response.Redirect("/Auth/Login?error=google_oauth_failed");
context.HandleResponse();
return Task.CompletedTask;
@@ -258,21 +347,17 @@ builder.Services.AddAuthentication(options =>
var msAuth = builder.Configuration.GetSection("Authentication:Microsoft");
options.ClientId = msAuth["ClientId"] ?? "";
options.ClientSecret = msAuth["ClientSecret"] ?? "";
- options.CallbackPath = "/signin-microsoft"; // Path explícito
+ options.CallbackPath = "/signin-microsoft";
options.BackchannelTimeout = TimeSpan.FromSeconds(30);
-
- // Força seleção de conta a cada login
options.AuthorizationEndpoint = "https://login.microsoftonline.com/common/oauth2/v2.0/authorize";
options.TokenEndpoint = "https://login.microsoftonline.com/common/oauth2/v2.0/token";
- // 🔥 CORREÇÃO: Handler para falhas remotas e preservação do evento existente
options.Events = new OAuthEvents
{
OnRedirectToAuthorizationEndpoint = context =>
{
var logger = context.HttpContext.RequestServices.GetRequiredService>();
- // Debug info
logger.LogWarning($"=== MICROSOFT AUTH DEBUG ===");
logger.LogWarning($"Original RedirectUri: {context.RedirectUri}");
logger.LogWarning($"Request Scheme: {context.Request.Scheme}");
@@ -280,10 +365,8 @@ builder.Services.AddAuthentication(options =>
logger.LogWarning($"Request Host: {context.Request.Host}");
logger.LogWarning($"X-Forwarded-Proto: {context.Request.Headers["X-Forwarded-Proto"]}");
- // 🚨 FORÇA HTTPS SEMPRE (exceto localhost)
var originalUri = context.RedirectUri;
- // Se contém bcards.site, força HTTPS
if (originalUri.Contains("bcards.site"))
{
context.RedirectUri = originalUri
@@ -293,7 +376,6 @@ builder.Services.AddAuthentication(options =>
logger.LogWarning($"FORCED HTTPS - Modified RedirectUri: {context.RedirectUri}");
}
- // Adiciona prompt=login para forçar seleção de conta
var redirectUri = context.RedirectUri;
if (!redirectUri.Contains("prompt="))
{
@@ -301,7 +383,6 @@ builder.Services.AddAuthentication(options =>
}
logger.LogWarning($"Final RedirectUri: {redirectUri}");
-
context.Response.Redirect(redirectUri);
return Task.CompletedTask;
},
@@ -339,7 +420,6 @@ builder.Services.AddScoped();
builder.Services.AddScoped();
builder.Services.AddScoped();
builder.Services.AddSingleton();
-//builder.Services.AddScoped();
builder.Services.AddScoped();
builder.Services.AddScoped();
@@ -351,47 +431,35 @@ builder.Services.AddScoped();
builder.Services.AddScoped();
builder.Services.AddScoped();
-// Image Storage Service
builder.Services.AddScoped();
-// Configure upload limits for file uploads
builder.Services.Configure(options =>
{
- options.MultipartBodyLengthLimit = 10 * 1024 * 1024; // 10MB for forms with files
+ options.MultipartBodyLengthLimit = 10 * 1024 * 1024;
options.ValueLengthLimit = int.MaxValue;
options.ValueCountLimit = int.MaxValue;
options.KeyLengthLimit = int.MaxValue;
});
-// 🔥 NOVO: LivePage Services
builder.Services.AddScoped();
builder.Services.AddScoped();
-// Add HttpClient for OpenGraphService
builder.Services.AddHttpClient();
-// Add SendGrid
builder.Services.AddSingleton(provider =>
{
var apiKey = builder.Configuration["SendGrid:ApiKey"];
return new SendGridClient(apiKey);
});
-// Background Services
builder.Services.AddHostedService();
-// Response Caching
builder.Services.AddResponseCaching();
builder.Services.AddMemoryCache();
-
builder.Services.AddRazorPages();
-// ===== CONFIGURAÇÃO DOS HEALTH CHECKS POR GRAVIDADE =====
-
-// OAuth Health Service (para verificações rápidas de login)
builder.Services.AddScoped();
-// HttpClient otimizado para OAuth checks - usar implementação concreta
builder.Services.AddHttpClient(client =>
{
client.Timeout = TimeSpan.FromSeconds(5);
@@ -399,37 +467,28 @@ builder.Services.AddHttpClient(client =>
});
builder.Services.AddHealthChecks()
- // 🔴 CRÍTICO (ALERTA VERMELHO) - Serviços essenciais
.AddCheck(
name: "critical_services",
- failureStatus: HealthStatus.Unhealthy, // Falha = site fora do ar
+ failureStatus: HealthStatus.Unhealthy,
timeout: TimeSpan.FromSeconds(30))
-
- // 🟡 DEGRADADO (ALERTA AMARELO) - OAuth providers
.AddCheck(
- name: "oauth_providers",
- failureStatus: HealthStatus.Degraded, // Falha = login offline, site funciona
+ name: "oauth_providers",
+ failureStatus: HealthStatus.Degraded,
timeout: TimeSpan.FromSeconds(10))
-
- // 🟡 DEGRADADO (ALERTA AMARELO) - Serviços auxiliares
.AddCheck(
name: "sendgrid",
- failureStatus: HealthStatus.Degraded, // Email não impede funcionamento
+ failureStatus: HealthStatus.Degraded,
timeout: TimeSpan.FromSeconds(10))
-
- // 🟡 DEGRADADO (ALERTA AMARELO) - Recursos do sistema
.AddCheck(
name: "resources",
- failureStatus: HealthStatus.Degraded, // Performance reduzida
+ failureStatus: HealthStatus.Degraded,
timeout: TimeSpan.FromSeconds(5));
-// Registrar health checks customizados no DI
builder.Services.AddTransient();
builder.Services.AddTransient();
builder.Services.AddTransient();
builder.Services.AddTransient();
-// HttpClient para Critical Services Health Check
builder.Services.AddHttpClient(client =>
{
client.Timeout = TimeSpan.FromSeconds(15);
@@ -438,18 +497,14 @@ builder.Services.AddHttpClient(client =>
var app = builder.Build();
-// 🔥 PRIMEIRA COISA APÓS BUILD - FORWARDED HEADERS + BASE URL OVERRIDE
app.UseForwardedHeaders();
-// 🚨 FORÇA BASE URL HTTPS EM PRODUÇÃO
if (!app.Environment.IsDevelopment())
{
app.Use(async (context, next) =>
{
- // Força o contexto a pensar que está em HTTPS
context.Request.Scheme = "https";
- // Override do Host se necessário
if (context.Request.Host.Host == "bcards.site")
{
context.Request.Host = new HostString("bcards.site", 443);
@@ -459,16 +514,14 @@ if (!app.Environment.IsDevelopment())
});
}
-// 🔥 OTIMIZAÇÃO: Adiciona headers de segurança
app.Use(async (context, next) =>
{
context.Response.Headers.Append("X-Content-Type-Options", "nosniff");
context.Response.Headers.Append("X-Frame-Options", "DENY");
context.Response.Headers.Append("Referrer-Policy", "no-referrer");
context.Response.Headers.Append("Permissions-Policy", "camera=(), microphone=(), geolocation=()");
-
- // Remove headers que expõem a tecnologia
- context.Response.OnStarting(() =>
+
+ context.Response.OnStarting(() =>
{
context.Response.Headers.Remove("Server");
context.Response.Headers.Remove("X-Powered-By");
@@ -478,7 +531,6 @@ app.Use(async (context, next) =>
await next();
});
-// Configure the HTTP request pipeline.
if (!app.Environment.IsDevelopment())
{
app.UseExceptionHandler("/Home/Error");
@@ -486,8 +538,6 @@ if (!app.Environment.IsDevelopment())
}
app.UseHttpsRedirection();
-
-// 🔥 OTIMIZAÇÃO: Ativar compressão de response
app.UseResponseCompression();
app.UseStaticFiles(new StaticFileOptions
@@ -496,68 +546,57 @@ app.UseStaticFiles(new StaticFileOptions
{
var fileName = ctx.File.Name;
var extension = Path.GetExtension(fileName).ToLowerInvariant();
-
+
TimeSpan maxAge;
-
- // Cache mais agressivo para assets que mudam raramente
+
if (extension == ".css" || extension == ".js")
{
- // CSS/JS: 30 dias (podem mudar com updates)
maxAge = TimeSpan.FromDays(30);
}
- else if (extension == ".woff" || extension == ".woff2" || extension == ".ttf" ||
+ else if (extension == ".woff" || extension == ".woff2" || extension == ".ttf" ||
extension == ".eot" || extension == ".svg" || extension == ".otf")
{
- // Fontes: 1 ano (raramente mudam)
maxAge = TimeSpan.FromDays(365);
}
- else if (extension == ".png" || extension == ".jpg" || extension == ".jpeg" ||
+ else if (extension == ".png" || extension == ".jpg" || extension == ".jpeg" ||
extension == ".gif" || extension == ".ico" || extension == ".webp")
{
- // Imagens: 6 meses
maxAge = TimeSpan.FromDays(180);
}
else
{
- // Outros arquivos: 1 dia
maxAge = TimeSpan.FromDays(1);
}
-
+
ctx.Context.Response.Headers.CacheControl = $"public,max-age={maxAge.TotalSeconds}";
ctx.Context.Response.Headers.Append("Vary", "Accept-Encoding");
}
});
app.UseRouting();
-
app.UseRequestLocalization();
-
app.UseAuthentication();
app.UseAuthorization();
-// Add custom middleware
app.UseMiddleware();
app.UseMiddleware();
app.UseMiddleware();
app.UseMiddleware();
-// 🔥 DEBUG MIDDLEWARE MELHORADO - Apenas para desenvolvimento
if (app.Environment.IsDevelopment())
{
app.Use(async (context, next) =>
{
var logger = context.RequestServices.GetRequiredService>();
-
- // Debug geral apenas em desenvolvimento
+
logger.LogDebug("Request - Path: {Path}, Query: {Query}, Method: {Method}, Scheme: {Scheme}, IsHttps: {IsHttps}, Host: {Host}",
- context.Request.Path, context.Request.QueryString, context.Request.Method,
+ context.Request.Path, context.Request.QueryString, context.Request.Method,
context.Request.Scheme, context.Request.IsHttps, context.Request.Host);
- // Debug específico para Microsoft signin
if (context.Request.Path.StartsWithSegments("/signin-microsoft"))
{
logger.LogWarning("SIGNIN-MICROSOFT CALLBACK - Path: {Path}, Query: {Query}, Scheme: {Scheme}, IsHttps: {IsHttps}, Host: {Host}, X-Forwarded-Proto: {ForwardedProto}",
- context.Request.Path, context.Request.QueryString, context.Request.Scheme,
+ context.Request.Path, context.Request.QueryString, context.Request.Scheme,
context.Request.IsHttps, context.Request.Host, context.Request.Headers["X-Forwarded-Proto"]);
}
@@ -567,7 +606,6 @@ if (app.Environment.IsDevelopment())
app.UseResponseCaching();
-// Rotas específicas primeiro
app.MapControllerRoute(
name: "userpage-preview-path",
pattern: "page/preview/{category}/{slug}",
@@ -584,25 +622,16 @@ app.MapControllerRoute(
pattern: "moderation/{action=Dashboard}/{id?}",
defaults: new { controller = "Moderation" });
-// Rota principal que vai pegar ?preview=token
-//app.MapControllerRoute(
-// name: "userpage",
-// pattern: "page/{category}/{slug}",
-// defaults: new { controller = "UserPage", action = "Display" },
-// constraints: new { category = @"^[a-zA-Z-]+$", slug = @"^[a-z0-9-]+$" });
-
-// 🔥 NOVA ROTA: LivePageController para páginas otimizadas de SEO
app.MapControllerRoute(
name: "livepage",
pattern: "page/{category}/{slug}",
defaults: new { controller = "LivePage", action = "Display" },
constraints: new
{
- category = @"^[a-zA-Z0-9\-\u00C0-\u017F]+$", // ← Aceita acentos
+ category = @"^[a-zA-Z0-9\-\u00C0-\u017F]+$",
slug = @"^[a-z0-9-]+$"
});
-// Rotas de páginas legais
app.MapControllerRoute(
name: "privacy-pt",
pattern: "privacidade",
@@ -628,7 +657,6 @@ app.MapControllerRoute(
pattern: "terminos",
defaults: new { controller = "Legal", action = "TermsES" });
-// Rota padrão por último
app.MapControllerRoute(
name: "default",
pattern: "{controller=Home}/{action=Index}/{id?}");
@@ -641,30 +669,40 @@ using (var scope = app.Services.CreateScope())
try
{
- // Initialize themes
var existingThemes = await themeService.GetAvailableThemesAsync();
if (!existingThemes.Any())
{
await themeService.InitializeDefaultThemesAsync();
}
- // Initialize categories
var existingCategories = await categoryService.GetAllCategoriesAsync();
if (!existingCategories.Any())
{
await categoryService.InitializeDefaultCategoriesAsync();
}
+
+ // 🔥 CORREÇÃO 12: Logs adicionais após inicialização
+ Log.Information("Default themes and categories initialized successfully");
}
catch (Exception ex)
{
- var logger = scope.ServiceProvider.GetRequiredService>();
- logger.LogError(ex, "Error initializing default data");
+ //var logger = scope.ServiceProvider.GetRequiredService>();
+ Log.Error(ex, "Error initializing default data");
}
}
try
{
Log.Information("BCards application started successfully on {Hostname}", hostname);
+
+ // 🔥 CORREÇÃO 13: Aguardar um pouco para logs serem enviados antes da aplicação rodar
+ if (isDevelopment)
+ {
+ Console.WriteLine("[DEBUG] Aguardando envio de logs iniciais...");
+ await Task.Delay(2000);
+ Console.WriteLine("[DEBUG] Iniciando aplicação...");
+ }
+
app.Run();
}
catch (Exception ex)
@@ -675,6 +713,9 @@ catch (Exception ex)
finally
{
Log.Information("BCards application shutting down on {Hostname}", hostname);
+
+ // 🔥 CORREÇÃO 14: Aguardar logs finais serem enviados
+ await Task.Delay(5000);
Log.CloseAndFlush();
}
diff --git a/src/BCards.Web/appsettings.Development.json b/src/BCards.Web/appsettings.Development.json
index badc92c..ccf425a 100644
--- a/src/BCards.Web/appsettings.Development.json
+++ b/src/BCards.Web/appsettings.Development.json
@@ -14,8 +14,7 @@
"Environment": "test"
},
"Serilog": {
- "SeqUrl": "http://192.168.0.100:5341",
- "ApiKey": ""
+ "OpenSearchUrl": "http://192.168.0.100:9200",
},
"DetailedErrors": true,
"MongoDb": {
diff --git a/src/BCards.Web/appsettings.json b/src/BCards.Web/appsettings.json
index 84bcc87..09345c3 100644
--- a/src/BCards.Web/appsettings.json
+++ b/src/BCards.Web/appsettings.json
@@ -7,8 +7,9 @@
}
},
"Serilog": {
- "SeqUrl": "http://172.17.0.1:5341",
- "ApiKey": ""
+ "OpenSearchUrl": "https://localhost:9201",
+ "OpenSearchUsername": "admin",
+ "OpenSearchPassword": "C4rn31r0#13"
},
"AllowedHosts": "*",
"Stripe": {