feat: Modificar do Seq para o OpenSearch
All checks were successful
BCards Deployment Pipeline / Run Tests (push) Successful in 6s
BCards Deployment Pipeline / PR Validation (push) Has been skipped
BCards Deployment Pipeline / Build and Push Image (push) Successful in 15m0s
BCards Deployment Pipeline / Deploy to Production (ARM - OCI) (push) Successful in 1m20s
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
All checks were successful
BCards Deployment Pipeline / Run Tests (push) Successful in 6s
BCards Deployment Pipeline / PR Validation (push) Has been skipped
BCards Deployment Pipeline / Build and Push Image (push) Successful in 15m0s
BCards Deployment Pipeline / Deploy to Production (ARM - OCI) (push) Successful in 1m20s
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:
parent
ce1c1409de
commit
1978193777
@ -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"
|
||||
|
||||
@ -22,7 +22,7 @@
|
||||
<PackageReference Include="SendGrid" Version="9.29.3" />
|
||||
<PackageReference Include="Serilog.AspNetCore" Version="8.0.0" />
|
||||
<PackageReference Include="Serilog.Sinks.Console" Version="5.0.0" />
|
||||
<PackageReference Include="Serilog.Sinks.Seq" Version="6.0.0" />
|
||||
<PackageReference Include="Serilog.Sinks.OpenSearch" Version="1.3.0" />
|
||||
<PackageReference Include="Serilog.Enrichers.Environment" Version="3.0.0" />
|
||||
<PackageReference Include="Serilog.Enrichers.Process" Version="3.0.0" />
|
||||
<PackageReference Include="Serilog.Enrichers.Thread" Version="4.0.0" />
|
||||
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@ -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}"));
|
||||
outputTemplate: "[{Timestamp:HH:mm:ss} {Level:u3}] [{Hostname}] {Message:lj} {Properties:j}{NewLine}{Exception}");
|
||||
|
||||
var seqUrl = builder.Configuration["Serilog:SeqUrl"];
|
||||
if (!string.IsNullOrEmpty(seqUrl))
|
||||
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<string, string>
|
||||
TemplateCustomSettings = new Dictionary<string, string>
|
||||
{
|
||||
{"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
|
||||
{
|
||||
// Production: Log balanceado - reduzir spam de requisições
|
||||
Console.WriteLine("[WARNING] OpenSearchUrl não configurada!");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// 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}"));
|
||||
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 seqUrl = builder.Configuration["Serilog:SeqUrl"];
|
||||
if (!string.IsNullOrEmpty(seqUrl))
|
||||
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<ForwardedHeadersOptions>(options =>
|
||||
options.RequireHeaderSymmetry = false;
|
||||
options.KnownNetworks.Clear();
|
||||
options.KnownProxies.Clear();
|
||||
// 🚨 PERMITIR QUALQUER PROXY (NGINX)
|
||||
options.ForwardLimit = null;
|
||||
});
|
||||
|
||||
@ -142,13 +240,11 @@ builder.Services.AddControllersWithViews()
|
||||
builder.Services.Configure<MongoDbSettings>(
|
||||
builder.Configuration.GetSection("MongoDb"));
|
||||
|
||||
// 🔥 OTIMIZAÇÃO: Pool de conexões do MongoDB
|
||||
builder.Services.AddSingleton<IMongoClient>(serviceProvider =>
|
||||
{
|
||||
var settings = serviceProvider.GetRequiredService<IOptions<MongoDbSettings>>().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("?") ? "&" : "?";
|
||||
@ -170,7 +266,6 @@ builder.Services.AddScoped(serviceProvider =>
|
||||
builder.Services.Configure<StripeSettings>(
|
||||
builder.Configuration.GetSection("Stripe"));
|
||||
|
||||
// Validate Stripe configuration
|
||||
var stripeSettings = builder.Configuration.GetSection("Stripe").Get<StripeSettings>();
|
||||
if (stripeSettings == null || string.IsNullOrEmpty(stripeSettings.SecretKey))
|
||||
{
|
||||
@ -197,7 +292,6 @@ builder.Services.Configure<GoogleAuthSettings>(
|
||||
builder.Services.Configure<MicrosoftAuthSettings>(
|
||||
builder.Configuration.GetSection("Authentication:Microsoft"));
|
||||
|
||||
// Adicionar configurações
|
||||
builder.Services.Configure<ModerationSettings>(
|
||||
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 =>
|
||||
@ -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<ILogger<Program>>();
|
||||
|
||||
// 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<IUserPageRepository, UserPageRepository>();
|
||||
builder.Services.AddScoped<ICategoryRepository, CategoryRepository>();
|
||||
builder.Services.AddScoped<ISubscriptionRepository, SubscriptionRepository>();
|
||||
builder.Services.AddSingleton<IModerationAuthService, ModerationAuthService>();
|
||||
//builder.Services.AddScoped<IModerationAuthService, ModerationAuthService>();
|
||||
|
||||
builder.Services.AddScoped<IUserPageService, UserPageService>();
|
||||
builder.Services.AddScoped<IThemeService, ThemeService>();
|
||||
@ -351,47 +431,35 @@ builder.Services.AddScoped<IOpenGraphService, OpenGraphService>();
|
||||
builder.Services.AddScoped<IModerationService, ModerationService>();
|
||||
builder.Services.AddScoped<IEmailService, EmailService>();
|
||||
|
||||
// Image Storage Service
|
||||
builder.Services.AddScoped<IImageStorageService, GridFSImageStorage>();
|
||||
|
||||
// Configure upload limits for file uploads
|
||||
builder.Services.Configure<FormOptions>(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<ILivePageRepository, LivePageRepository>();
|
||||
builder.Services.AddScoped<ILivePageService, LivePageService>();
|
||||
|
||||
// Add HttpClient for OpenGraphService
|
||||
builder.Services.AddHttpClient<OpenGraphService>();
|
||||
|
||||
// Add SendGrid
|
||||
builder.Services.AddSingleton<ISendGridClient>(provider =>
|
||||
{
|
||||
var apiKey = builder.Configuration["SendGrid:ApiKey"];
|
||||
return new SendGridClient(apiKey);
|
||||
});
|
||||
|
||||
// Background Services
|
||||
builder.Services.AddHostedService<TrialExpirationService>();
|
||||
|
||||
// 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<IOAuthHealthService, OAuthHealthService>();
|
||||
|
||||
// HttpClient otimizado para OAuth checks - usar implementação concreta
|
||||
builder.Services.AddHttpClient<OAuthHealthService>(client =>
|
||||
{
|
||||
client.Timeout = TimeSpan.FromSeconds(5);
|
||||
@ -399,37 +467,28 @@ builder.Services.AddHttpClient<OAuthHealthService>(client =>
|
||||
});
|
||||
|
||||
builder.Services.AddHealthChecks()
|
||||
// 🔴 CRÍTICO (ALERTA VERMELHO) - Serviços essenciais
|
||||
.AddCheck<CriticalServicesHealthCheck>(
|
||||
name: "critical_services",
|
||||
failureStatus: HealthStatus.Unhealthy, // Falha = site fora do ar
|
||||
failureStatus: HealthStatus.Unhealthy,
|
||||
timeout: TimeSpan.FromSeconds(30))
|
||||
|
||||
// 🟡 DEGRADADO (ALERTA AMARELO) - OAuth providers
|
||||
.AddCheck<OAuthProvidersHealthCheck>(
|
||||
name: "oauth_providers",
|
||||
failureStatus: HealthStatus.Degraded, // Falha = login offline, site funciona
|
||||
failureStatus: HealthStatus.Degraded,
|
||||
timeout: TimeSpan.FromSeconds(10))
|
||||
|
||||
// 🟡 DEGRADADO (ALERTA AMARELO) - Serviços auxiliares
|
||||
.AddCheck<SendGridHealthCheck>(
|
||||
name: "sendgrid",
|
||||
failureStatus: HealthStatus.Degraded, // Email não impede funcionamento
|
||||
failureStatus: HealthStatus.Degraded,
|
||||
timeout: TimeSpan.FromSeconds(10))
|
||||
|
||||
// 🟡 DEGRADADO (ALERTA AMARELO) - Recursos do sistema
|
||||
.AddCheck<SystemResourcesHealthCheck>(
|
||||
name: "resources",
|
||||
failureStatus: HealthStatus.Degraded, // Performance reduzida
|
||||
failureStatus: HealthStatus.Degraded,
|
||||
timeout: TimeSpan.FromSeconds(5));
|
||||
|
||||
// Registrar health checks customizados no DI
|
||||
builder.Services.AddTransient<CriticalServicesHealthCheck>();
|
||||
builder.Services.AddTransient<OAuthProvidersHealthCheck>();
|
||||
builder.Services.AddTransient<SendGridHealthCheck>();
|
||||
builder.Services.AddTransient<SystemResourcesHealthCheck>();
|
||||
|
||||
// HttpClient para Critical Services Health Check
|
||||
builder.Services.AddHttpClient<CriticalServicesHealthCheck>(client =>
|
||||
{
|
||||
client.Timeout = TimeSpan.FromSeconds(15);
|
||||
@ -438,18 +497,14 @@ builder.Services.AddHttpClient<CriticalServicesHealthCheck>(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,7 +514,6 @@ 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");
|
||||
@ -467,7 +521,6 @@ app.Use(async (context, next) =>
|
||||
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.Headers.Remove("Server");
|
||||
@ -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
|
||||
@ -499,27 +549,22 @@ app.UseStaticFiles(new StaticFileOptions
|
||||
|
||||
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" ||
|
||||
extension == ".eot" || extension == ".svg" || extension == ".otf")
|
||||
{
|
||||
// Fontes: 1 ano (raramente mudam)
|
||||
maxAge = TimeSpan.FromDays(365);
|
||||
}
|
||||
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);
|
||||
}
|
||||
|
||||
@ -529,31 +574,25 @@ app.UseStaticFiles(new StaticFileOptions
|
||||
});
|
||||
|
||||
app.UseRouting();
|
||||
|
||||
app.UseRequestLocalization();
|
||||
|
||||
app.UseAuthentication();
|
||||
app.UseAuthorization();
|
||||
|
||||
// Add custom middleware
|
||||
app.UseMiddleware<BCards.Web.Middleware.PlanLimitationMiddleware>();
|
||||
app.UseMiddleware<BCards.Web.Middleware.PageStatusMiddleware>();
|
||||
app.UseMiddleware<BCards.Web.Middleware.PreviewTokenMiddleware>();
|
||||
app.UseMiddleware<ModerationAuthMiddleware>();
|
||||
|
||||
// 🔥 DEBUG MIDDLEWARE MELHORADO - Apenas para desenvolvimento
|
||||
if (app.Environment.IsDevelopment())
|
||||
{
|
||||
app.Use(async (context, next) =>
|
||||
{
|
||||
var logger = context.RequestServices.GetRequiredService<ILogger<Program>>();
|
||||
|
||||
// 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.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}",
|
||||
@ -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<ILogger<Program>>();
|
||||
logger.LogError(ex, "Error initializing default data");
|
||||
//var logger = scope.ServiceProvider.GetRequiredService<ILogger<Program>>();
|
||||
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();
|
||||
}
|
||||
|
||||
|
||||
@ -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": {
|
||||
|
||||
@ -7,8 +7,9 @@
|
||||
}
|
||||
},
|
||||
"Serilog": {
|
||||
"SeqUrl": "http://172.17.0.1:5341",
|
||||
"ApiKey": ""
|
||||
"OpenSearchUrl": "https://localhost:9201",
|
||||
"OpenSearchUsername": "admin",
|
||||
"OpenSearchPassword": "C4rn31r0#13"
|
||||
},
|
||||
"AllowedHosts": "*",
|
||||
"Stripe": {
|
||||
|
||||
Loading…
Reference in New Issue
Block a user