396 lines
16 KiB
C#
396 lines
16 KiB
C#
using Microsoft.AspNetCore.Mvc;
|
||
using QRRapidoApp.Models;
|
||
using QRRapidoApp.Services;
|
||
using System.Diagnostics;
|
||
using System.Security.Claims;
|
||
using Microsoft.Extensions.Localization;
|
||
|
||
namespace QRRapidoApp.Controllers
|
||
{
|
||
public class HomeController : Controller
|
||
{
|
||
private readonly ILogger<HomeController> _logger;
|
||
private readonly AdDisplayService _adDisplayService;
|
||
private readonly IUserService _userService;
|
||
private readonly IConfiguration _config;
|
||
private readonly IStringLocalizer<QRRapidoApp.Resources.SharedResource> _localizer;
|
||
private readonly IMarkdownService _markdownService;
|
||
|
||
public HomeController(
|
||
ILogger<HomeController> logger,
|
||
AdDisplayService adDisplayService,
|
||
IUserService userService,
|
||
IConfiguration config,
|
||
IStringLocalizer<QRRapidoApp.Resources.SharedResource> localizer,
|
||
IMarkdownService markdownService
|
||
)
|
||
{
|
||
_logger = logger;
|
||
_adDisplayService = adDisplayService;
|
||
_userService = userService;
|
||
_config = config;
|
||
_localizer = localizer;
|
||
_markdownService = markdownService;
|
||
}
|
||
|
||
// Home page routes
|
||
// "/" → Portuguese (canonical)
|
||
// "/es-PY" → Spanish
|
||
[HttpGet]
|
||
[Route("/")]
|
||
[Route("es-PY")]
|
||
public async Task<IActionResult> Index(string? qrType = null)
|
||
{
|
||
var userId = User?.FindFirst(ClaimTypes.NameIdentifier)?.Value;
|
||
|
||
// Pass the requested QR type to the view to auto-select in combo
|
||
ViewBag.SelectedQRType = qrType;
|
||
|
||
// Set SEO Meta Tags based on QR Type
|
||
if (!string.IsNullOrEmpty(qrType))
|
||
{
|
||
switch (qrType.ToLower())
|
||
{
|
||
case "pix":
|
||
ViewBag.Title = "Gerador de Pix Grátis";
|
||
ViewBag.Description = "Crie QR Code PIX estático gratuitamente. Ideal para receber pagamentos e doações.";
|
||
ViewBag.Keywords = "pix, qr code pix, gerador pix, pix estatico, receber pix";
|
||
break;
|
||
case "wifi":
|
||
ViewBag.Title = _localizer["WiFiQRTitle"];
|
||
ViewBag.Description = _localizer["WiFiQRDescription"];
|
||
break;
|
||
case "vcard":
|
||
ViewBag.Title = _localizer["VCardQRTitle"] ?? "Gerador de Cartão de Visita Digital";
|
||
ViewBag.Description = _localizer["VCardQRDescription"];
|
||
break;
|
||
case "whatsapp":
|
||
ViewBag.Title = "Gerador de Link WhatsApp";
|
||
ViewBag.Description = _localizer["WhatsAppQRDescription"];
|
||
break;
|
||
case "email":
|
||
ViewBag.Title = "Gerador de QR Code Email";
|
||
ViewBag.Description = _localizer["EmailQRDescription"];
|
||
break;
|
||
case "sms":
|
||
ViewBag.Title = "Gerador de QR Code SMS";
|
||
ViewBag.Description = _localizer["SMSQRDescription"];
|
||
break;
|
||
case "text":
|
||
ViewBag.Title = "Gerador de Texto para QR";
|
||
ViewBag.Description = _localizer["TextQRDescription"];
|
||
break;
|
||
case "url":
|
||
ViewBag.Title = "Gerador de URL para QR";
|
||
ViewBag.Description = _localizer["URLQRDescription"];
|
||
break;
|
||
}
|
||
}
|
||
else
|
||
{
|
||
// Default SEO
|
||
ViewBag.Title = _config["App:TaglinePT"];
|
||
ViewBag.Keywords = _config["SEO:KeywordsPT"];
|
||
ViewBag.Description = _localizer["QRGenerateDescription"];
|
||
}
|
||
|
||
ViewBag.ShowAds = await _adDisplayService.ShouldShowAds(userId);
|
||
ViewBag.IsPremium = await _adDisplayService.HasValidPremiumSubscription(userId ?? "");
|
||
ViewBag.IsAuthenticated = User?.Identity?.IsAuthenticated ?? false;
|
||
ViewBag.UserName = User?.Identity?.Name ?? "";
|
||
_adDisplayService.SetViewBagAds(ViewBag);
|
||
|
||
// User stats for logged in users
|
||
if (!string.IsNullOrEmpty(userId))
|
||
{
|
||
ViewBag.DailyQRCount = await _userService.GetDailyQRCountAsync(userId);
|
||
ViewBag.MonthlyQRCount = await _userService.GetQRCountThisMonthAsync(userId);
|
||
}
|
||
|
||
return View("Index");
|
||
}
|
||
|
||
// Dedicated SEO Routes - Landing pages for each QR type
|
||
// Portuguese (default): /pix, /wifi, etc. (no culture prefix)
|
||
// Spanish: /es-PY/pix, /es-PY/wifi, etc.
|
||
|
||
[Route("pix")]
|
||
[Route("es-PY/pix")]
|
||
public async Task<IActionResult> Pix() => await Index("pix");
|
||
|
||
[Route("wifi")]
|
||
[Route("es-PY/wifi")]
|
||
public async Task<IActionResult> Wifi() => await Index("wifi");
|
||
|
||
[Route("vcard")]
|
||
[Route("es-PY/vcard")]
|
||
public async Task<IActionResult> VCard() => await Index("vcard");
|
||
|
||
[Route("whatsapp")]
|
||
[Route("es-PY/whatsapp")]
|
||
public async Task<IActionResult> WhatsApp() => await Index("whatsapp");
|
||
|
||
[Route("email")]
|
||
[Route("es-PY/email")]
|
||
public async Task<IActionResult> Email() => await Index("email");
|
||
|
||
[Route("sms")]
|
||
[Route("es-PY/sms")]
|
||
public async Task<IActionResult> Sms() => await Index("sms");
|
||
|
||
[Route("texto")]
|
||
[Route("text")]
|
||
[Route("es-PY/texto")]
|
||
[Route("es-PY/text")]
|
||
public async Task<IActionResult> Text() => await Index("text");
|
||
|
||
[Route("url")]
|
||
[Route("es-PY/url")]
|
||
public async Task<IActionResult> UrlType() => await Index("url");
|
||
|
||
public IActionResult Privacy()
|
||
{
|
||
var userId = User?.FindFirst(ClaimTypes.NameIdentifier)?.Value;
|
||
|
||
ViewBag.ShowAds = _adDisplayService.ShouldShowAds(userId).Result;
|
||
ViewBag.IsPremium = _adDisplayService.HasValidPremiumSubscription(userId ?? "").Result;
|
||
ViewBag.IsAuthenticated = User.Identity?.IsAuthenticated ?? false;
|
||
ViewBag.UserName = User.Identity?.Name ?? "";
|
||
|
||
ViewBag.Title = _localizer["PrivacyPolicyTitle"];
|
||
ViewBag.Description = _localizer["PrivacyPolicyDescription"];
|
||
_adDisplayService.SetViewBagAds(ViewBag);
|
||
|
||
return View();
|
||
}
|
||
|
||
public IActionResult Terms()
|
||
{
|
||
var userId = User?.FindFirst(ClaimTypes.NameIdentifier)?.Value;
|
||
|
||
ViewBag.ShowAds = _adDisplayService.ShouldShowAds(userId).Result;
|
||
ViewBag.IsPremium = _adDisplayService.HasValidPremiumSubscription(userId ?? "").Result;
|
||
ViewBag.IsAuthenticated = User.Identity?.IsAuthenticated ?? false;
|
||
ViewBag.UserName = User.Identity?.Name ?? "";
|
||
|
||
ViewBag.Title = _localizer["TermsOfUseTitle"];
|
||
ViewBag.Description = _localizer["TermsOfUseDescription"];
|
||
_adDisplayService.SetViewBagAds(ViewBag);
|
||
|
||
return View();
|
||
}
|
||
|
||
public IActionResult About()
|
||
{
|
||
var userId = User?.FindFirst(ClaimTypes.NameIdentifier)?.Value;
|
||
|
||
ViewBag.ShowAds = _adDisplayService.ShouldShowAds(userId).Result;
|
||
ViewBag.IsPremium = _adDisplayService.HasValidPremiumSubscription(userId ?? "").Result;
|
||
ViewBag.IsAuthenticated = User.Identity?.IsAuthenticated ?? false;
|
||
ViewBag.UserName = User.Identity?.Name ?? "";
|
||
|
||
ViewBag.Title = _localizer["AboutPageTitle"];
|
||
ViewBag.Description = _localizer["AboutPageDescription"];
|
||
_adDisplayService.SetViewBagAds(ViewBag);
|
||
|
||
return View();
|
||
}
|
||
|
||
public IActionResult Contact()
|
||
{
|
||
var userId = User?.FindFirst(ClaimTypes.NameIdentifier)?.Value;
|
||
|
||
ViewBag.ShowAds = _adDisplayService.ShouldShowAds(userId).Result;
|
||
ViewBag.IsPremium = _adDisplayService.HasValidPremiumSubscription(userId ?? "").Result;
|
||
ViewBag.IsAuthenticated = User.Identity?.IsAuthenticated ?? false;
|
||
ViewBag.UserName = User.Identity?.Name ?? "";
|
||
|
||
ViewBag.Title = _localizer["ContactPageTitle"];
|
||
ViewBag.Description = _localizer["ContactPageDescription"];
|
||
_adDisplayService.SetViewBagAds(ViewBag);
|
||
|
||
return View();
|
||
}
|
||
|
||
public IActionResult FAQ()
|
||
{
|
||
var userId = User?.FindFirst(ClaimTypes.NameIdentifier)?.Value;
|
||
|
||
ViewBag.ShowAds = _adDisplayService.ShouldShowAds(userId).Result;
|
||
ViewBag.IsPremium = _adDisplayService.HasValidPremiumSubscription(userId ?? "").Result;
|
||
ViewBag.IsAuthenticated = User.Identity?.IsAuthenticated ?? false;
|
||
ViewBag.UserName = User.Identity?.Name ?? "";
|
||
|
||
ViewBag.Title = _localizer["FAQPageTitle"];
|
||
ViewBag.Description = _localizer["FAQPageDescription"];
|
||
_adDisplayService.SetViewBagAds(ViewBag);
|
||
|
||
return View();
|
||
}
|
||
|
||
public IActionResult HowToUse()
|
||
{
|
||
var userId = User?.FindFirst(ClaimTypes.NameIdentifier)?.Value;
|
||
|
||
ViewBag.ShowAds = _adDisplayService.ShouldShowAds(userId).Result;
|
||
ViewBag.IsPremium = _adDisplayService.HasValidPremiumSubscription(userId ?? "").Result;
|
||
ViewBag.IsAuthenticated = User.Identity?.IsAuthenticated ?? false;
|
||
ViewBag.UserName = User.Identity?.Name ?? "";
|
||
|
||
ViewBag.Title = _localizer["HowToUsePageTitle"];
|
||
ViewBag.Description = _localizer["HowToUsePageDescription"];
|
||
_adDisplayService.SetViewBagAds(ViewBag);
|
||
|
||
return View();
|
||
}
|
||
|
||
//[ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)]
|
||
//public IActionResult Error()
|
||
//{
|
||
// return View(new ErrorViewModel { RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier });
|
||
//}
|
||
|
||
[ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)]
|
||
public IActionResult Error()
|
||
{
|
||
var errorCode = Request.Query["code"].ToString();
|
||
var errorMessage = "";
|
||
|
||
// Interpretar c<>digos de erro espec<65>ficos
|
||
if (errorCode.StartsWith("M.C506"))
|
||
{
|
||
errorMessage = "Erro de autentica<63><61>o. Verifique suas credenciais e tente novamente.";
|
||
}
|
||
|
||
ViewBag.ErrorCode = errorCode;
|
||
ViewBag.ErrorMessage = errorMessage;
|
||
|
||
return View(new ErrorViewModel
|
||
{
|
||
RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier
|
||
});
|
||
}
|
||
|
||
// Dynamic QR redirect endpoint
|
||
[Route("d/{id}")]
|
||
public async Task<IActionResult> DynamicRedirect(string id)
|
||
{
|
||
try
|
||
{
|
||
_adDisplayService.SetViewBagAds(ViewBag);
|
||
// This would lookup the dynamic QR content from cache/database
|
||
// For now, return a placeholder
|
||
return Redirect("https://qrrapido.site");
|
||
}
|
||
catch
|
||
{
|
||
return NotFound();
|
||
}
|
||
}
|
||
|
||
// Health check endpoint
|
||
[Route("health")]
|
||
public IActionResult Health()
|
||
{
|
||
return Ok(new { status = "healthy", timestamp = DateTime.UtcNow });
|
||
}
|
||
|
||
// Sitemap endpoint for SEO
|
||
[Route("sitemap.xml")]
|
||
public async Task<IActionResult> Sitemap()
|
||
{
|
||
var baseUrl = $"{Request.Scheme}://{Request.Host}";
|
||
var now = DateTime.UtcNow.ToString("yyyy-MM-dd");
|
||
|
||
var sitemapBuilder = new System.Text.StringBuilder();
|
||
sitemapBuilder.AppendLine(@"<?xml version=""1.0"" encoding=""UTF-8""?>");
|
||
sitemapBuilder.AppendLine(@"<urlset xmlns=""http://www.sitemaps.org/schemas/sitemap/0.9"">");
|
||
|
||
void AppendUrl(string relativePath, string changeFreq, string priority, string? lastModOverride = null)
|
||
{
|
||
var normalizedPath = relativePath.StartsWith("/")
|
||
? relativePath
|
||
: $"/{relativePath}";
|
||
|
||
var lastModValue = lastModOverride ?? now;
|
||
|
||
sitemapBuilder.AppendLine($@"
|
||
<url>
|
||
<loc>{baseUrl}{normalizedPath}</loc>
|
||
<lastmod>{lastModValue}</lastmod>
|
||
<changefreq>{changeFreq}</changefreq>
|
||
<priority>{priority}</priority>
|
||
</url>");
|
||
}
|
||
|
||
// Core entry points
|
||
AppendUrl("/", "daily", "1.0");
|
||
AppendUrl("/es-PY", "daily", "0.9");
|
||
|
||
// Tools (Landing Pages)
|
||
var tools = new[] { "pix", "wifi", "vcard", "whatsapp", "email", "sms", "texto", "url" };
|
||
|
||
foreach (var tool in tools)
|
||
{
|
||
AppendUrl($"/{tool}", "weekly", "0.9");
|
||
AppendUrl($"/es-PY/{tool}", "weekly", "0.8");
|
||
}
|
||
|
||
// Informational pages
|
||
var informationalPages = new[]
|
||
{
|
||
new { Path = "Home/About", ChangeFreq = "monthly", Priority = "0.7" },
|
||
new { Path = "Home/Contact", ChangeFreq = "monthly", Priority = "0.7" },
|
||
new { Path = "Home/FAQ", ChangeFreq = "weekly", Priority = "0.8" },
|
||
new { Path = "Home/HowToUse", ChangeFreq = "weekly", Priority = "0.8" },
|
||
new { Path = "Home/Privacy", ChangeFreq = "monthly", Priority = "0.4" },
|
||
new { Path = "Home/Terms", ChangeFreq = "monthly", Priority = "0.4" }
|
||
};
|
||
|
||
foreach (var page in informationalPages)
|
||
{
|
||
AppendUrl($"/{page.Path}", page.ChangeFreq, page.Priority);
|
||
AppendUrl($"/es-PY/{page.Path}", page.ChangeFreq, page.Priority);
|
||
}
|
||
|
||
// Dynamic tutorial pages from Markdown
|
||
try
|
||
{
|
||
var allArticles = await _markdownService.GetAllArticlesForSitemapAsync();
|
||
|
||
foreach (var article in allArticles)
|
||
{
|
||
// Use slug from metadata or generate from title if missing
|
||
var slug = !string.IsNullOrWhiteSpace(article.Slug)
|
||
? article.Slug
|
||
: article.Title.ToLower().Replace(" ", "-");
|
||
|
||
// Ensure slug is only encoded once and special characters are handled
|
||
var encodedSlug = slug; // Assuming slug is already URL-safe from filename
|
||
|
||
var tutorialPath = article.Culture == "pt-BR"
|
||
? $"/tutoriais/{encodedSlug}"
|
||
: $"/es-PY/tutoriais/{encodedSlug}";
|
||
|
||
var lastMod = article.LastMod.ToString("yyyy-MM-dd");
|
||
AppendUrl(tutorialPath, "weekly", "0.8", lastMod);
|
||
}
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
_logger.LogError(ex, "Error adding tutorials to sitemap");
|
||
}
|
||
|
||
sitemapBuilder.AppendLine("</urlset>");
|
||
|
||
return Content(sitemapBuilder.ToString(), "application/xml", System.Text.Encoding.UTF8);
|
||
}
|
||
}
|
||
|
||
//public class ErrorViewModel
|
||
//{
|
||
// public string? RequestId { get; set; }
|
||
// public bool ShowRequestId => !string.IsNullOrEmpty(RequestId);
|
||
//}
|
||
}
|