using BCards.Web.Configuration; using BCards.Web.Services; using BCards.Web.Repositories; using Microsoft.AspNetCore.Authentication.Cookies; using Microsoft.AspNetCore.Authentication.Google; using Microsoft.AspNetCore.Authentication.MicrosoftAccount; using Microsoft.AspNetCore.Localization; using Microsoft.Extensions.Options; using MongoDB.Driver; using System.Globalization; using Stripe; using Microsoft.AspNetCore.Authentication.OAuth; using SendGrid; using BCards.Web.Middleware; using Microsoft.AspNetCore.Http.Features; using Microsoft.AspNetCore.HttpOverrides; var builder = WebApplication.CreateBuilder(args); // đŸ”„ CONFIGURAR FORWARDED HEADERS NO BUILDER builder.Services.Configure(options => { options.ForwardedHeaders = ForwardedHeaders.XForwardedFor | ForwardedHeaders.XForwardedProto; options.RequireHeaderSymmetry = false; options.KnownNetworks.Clear(); options.KnownProxies.Clear(); // 🚹 PERMITIR QUALQUER PROXY (NGINX) options.ForwardLimit = null; }); // Add services to the container. builder.Services.AddControllersWithViews() .AddRazorRuntimeCompilation() .AddViewLocalization() .AddDataAnnotationsLocalization(); // MongoDB Configuration builder.Services.Configure( builder.Configuration.GetSection("MongoDb")); builder.Services.AddSingleton(serviceProvider => { var settings = serviceProvider.GetRequiredService>().Value; return new MongoClient(settings.ConnectionString); }); builder.Services.AddScoped(serviceProvider => { var client = serviceProvider.GetRequiredService(); var settings = serviceProvider.GetRequiredService>().Value; return client.GetDatabase(settings.DatabaseName); }); // Stripe Configuration builder.Services.Configure( builder.Configuration.GetSection("Stripe")); // OAuth Configuration builder.Services.Configure( builder.Configuration.GetSection("Authentication:Google")); builder.Services.Configure( builder.Configuration.GetSection("Authentication:Microsoft")); // Adicionar configuraçÔes builder.Services.Configure( builder.Configuration.GetSection("Moderation")); // Authentication builder.Services.AddAuthentication(options => { options.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme; options.DefaultChallengeScheme = GoogleDefaults.AuthenticationScheme; }) .AddCookie(options => { options.LoginPath = "/Auth/Login"; options.LogoutPath = "/Auth/Logout"; options.ExpireTimeSpan = TimeSpan.FromDays(30); options.SlidingExpiration = true; }) .AddGoogle(options => { var googleAuth = builder.Configuration.GetSection("Authentication:Google"); options.ClientId = googleAuth["ClientId"] ?? ""; options.ClientSecret = googleAuth["ClientSecret"] ?? ""; }) .AddMicrosoftAccount(options => { var msAuth = builder.Configuration.GetSection("Authentication:Microsoft"); options.ClientId = msAuth["ClientId"] ?? ""; options.ClientSecret = msAuth["ClientSecret"] ?? ""; options.CallbackPath = "/signin-microsoft"; // 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"; // đŸ”„ SOLUÇÃO RADICAL: SEMPRE FORÇAR HTTPS 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}"); logger.LogWarning($"Request IsHttps: {context.Request.IsHttps}"); 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 .Replace("http://bcards.site", "https://bcards.site") .Replace("http%3A%2F%2Fbcards.site", "https%3A%2F%2Fbcards.site"); 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=")) { redirectUri += "&prompt=login"; } logger.LogWarning($"Final RedirectUri: {redirectUri}"); context.Response.Redirect(redirectUri); return Task.CompletedTask; } }; }); // Localization builder.Services.AddLocalization(options => options.ResourcesPath = "Resources"); builder.Services.Configure(options => { var supportedCultures = new[] { new CultureInfo("pt-BR"), new CultureInfo("es-ES") }; options.DefaultRequestCulture = new RequestCulture("pt-BR"); options.SupportedCultures = supportedCultures; options.SupportedUICultures = supportedCultures; }); // Register Services builder.Services.AddScoped(); builder.Services.AddScoped(); builder.Services.AddScoped(); builder.Services.AddScoped(); builder.Services.AddSingleton(); //builder.Services.AddScoped(); builder.Services.AddScoped(); builder.Services.AddScoped(); builder.Services.AddScoped(); builder.Services.AddScoped(); builder.Services.AddScoped(); builder.Services.AddScoped(); 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.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(); 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); } await next(); }); } // Configure the HTTP request pipeline. if (!app.Environment.IsDevelopment()) { app.UseExceptionHandler("/Home/Error"); app.UseHsts(); } app.UseHttpsRedirection(); app.UseStaticFiles(); app.UseRouting(); app.UseRequestLocalization(); app.UseAuthentication(); app.UseAuthorization(); // Add custom middleware app.UseMiddleware(); app.UseMiddleware(); app.UseMiddleware(); app.UseMiddleware(); // đŸ”„ DEBUG MIDDLEWARE MELHORADO app.Use(async (context, next) => { // Debug geral Console.WriteLine($"=== REQUEST DEBUG ==="); Console.WriteLine($"Path: {context.Request.Path}"); Console.WriteLine($"Query: {context.Request.QueryString}"); Console.WriteLine($"Method: {context.Request.Method}"); Console.WriteLine($"Scheme: {context.Request.Scheme}"); Console.WriteLine($"IsHttps: {context.Request.IsHttps}"); Console.WriteLine($"Host: {context.Request.Host}"); // Debug especĂ­fico para Microsoft signin if (context.Request.Path.StartsWithSegments("/signin-microsoft")) { var logger = context.RequestServices.GetRequiredService>(); logger.LogWarning($"=== SIGNIN-MICROSOFT CALLBACK DEBUG ==="); logger.LogWarning($"Path: {context.Request.Path}"); logger.LogWarning($"Query: {context.Request.QueryString}"); logger.LogWarning($"Method: {context.Request.Method}"); logger.LogWarning($"Scheme: {context.Request.Scheme}"); logger.LogWarning($"IsHttps: {context.Request.IsHttps}"); logger.LogWarning($"Host: {context.Request.Host}"); logger.LogWarning($"X-Forwarded-Proto: {context.Request.Headers["X-Forwarded-Proto"]}"); logger.LogWarning($"X-Forwarded-For: {context.Request.Headers["X-Forwarded-For"]}"); logger.LogWarning($"All Headers:"); foreach (var header in context.Request.Headers) { logger.LogWarning($" {header.Key}: {header.Value}"); } } await next(); }); app.UseResponseCaching(); // Rotas especĂ­ficas primeiro app.MapControllerRoute( name: "userpage-preview-path", pattern: "page/preview/{category}/{slug}", defaults: new { controller = "UserPage", action = "Preview" }, constraints: new { category = @"^[a-zA-Z-]+$", slug = @"^[a-z0-9-]+$" }); app.MapControllerRoute( name: "userpage-click", pattern: "page/click/{pageId}", defaults: new { controller = "UserPage", action = "RecordClick" }); app.MapControllerRoute( name: "moderation", 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 slug = @"^[a-z0-9-]+$" }); // Rota padrĂŁo por Ășltimo app.MapControllerRoute( name: "default", pattern: "{controller=Home}/{action=Index}/{id?}"); // Initialize default data using (var scope = app.Services.CreateScope()) { var themeService = scope.ServiceProvider.GetRequiredService(); var categoryService = scope.ServiceProvider.GetRequiredService(); 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(); } } catch (Exception ex) { var logger = scope.ServiceProvider.GetRequiredService>(); logger.LogError(ex, "Error initializing default data"); } } app.Run(); // Make Program accessible for integration tests public partial class Program { }