fix: ajuste de warnings e combos
This commit is contained in:
parent
4a7cdbf26d
commit
dbd3c8851b
@ -38,7 +38,7 @@ namespace QRRapidoApp.Configuration
|
||||
{
|
||||
try
|
||||
{
|
||||
var secretValue = File.ReadAllText(secretFilePath).Trim();
|
||||
var secretValue = (string?)File.ReadAllText(secretFilePath).Trim();
|
||||
if (!string.IsNullOrEmpty(secretValue))
|
||||
{
|
||||
Data[configKey] = secretValue;
|
||||
|
||||
@ -170,7 +170,9 @@ namespace QRRapidoApp.Controllers
|
||||
var user = await _userService.GetUserByProviderAsync(scheme, providerId);
|
||||
if (user == null)
|
||||
{
|
||||
user = await _userService.CreateUserAsync(email, name ?? email, scheme, providerId);
|
||||
// Fix CS8625: Ensure name is not null
|
||||
var safeName = !string.IsNullOrEmpty(name) ? name : (email ?? "User");
|
||||
user = await _userService.CreateUserAsync(email, safeName, scheme, providerId);
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -180,10 +182,10 @@ namespace QRRapidoApp.Controllers
|
||||
// Create application claims
|
||||
var claims = new List<Claim>
|
||||
{
|
||||
new Claim(ClaimTypes.NameIdentifier, user.Id),
|
||||
new Claim(ClaimTypes.Email, user.Email),
|
||||
new Claim(ClaimTypes.Name, user.Name),
|
||||
new Claim("Provider", user.Provider),
|
||||
new Claim(ClaimTypes.NameIdentifier, user.Id ?? string.Empty), // Fix CS8625
|
||||
new Claim(ClaimTypes.Email, user.Email ?? string.Empty), // Fix CS8625
|
||||
new Claim(ClaimTypes.Name, user.Name ?? string.Empty), // Fix CS8625
|
||||
new Claim("Provider", user.Provider ?? string.Empty),
|
||||
new Claim("IsPremium", user.IsPremium.ToString())
|
||||
};
|
||||
|
||||
@ -230,6 +232,7 @@ namespace QRRapidoApp.Controllers
|
||||
return RedirectToAction("Login");
|
||||
}
|
||||
|
||||
// Ensure we are passing a non-null userId
|
||||
ViewBag.QRHistory = await _userService.GetUserQRHistoryAsync(userId, 10);
|
||||
ViewBag.MonthlyQRCount = await _userService.GetQRCountThisMonthAsync(userId);
|
||||
ViewBag.IsPremium = await _adDisplayService.HasValidPremiumSubscription(userId);
|
||||
|
||||
@ -33,21 +33,69 @@ namespace QRRapidoApp.Controllers
|
||||
_markdownService = markdownService;
|
||||
}
|
||||
|
||||
public async Task<IActionResult> Index()
|
||||
// Default fallback route handled by Program.cs map
|
||||
[HttpGet]
|
||||
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 ?? "";
|
||||
ViewBag.IsAuthenticated = User?.Identity?.IsAuthenticated ?? false;
|
||||
ViewBag.UserName = User?.Identity?.Name ?? "";
|
||||
_adDisplayService.SetViewBagAds(ViewBag);
|
||||
|
||||
// SEO and Analytics data
|
||||
ViewBag.Title = _config["App:TaglinePT"];
|
||||
ViewBag.Keywords = _config["SEO:KeywordsPT"];
|
||||
ViewBag.Description = _localizer["QRGenerateDescription"];
|
||||
|
||||
// User stats for logged in users
|
||||
if (!string.IsNullOrEmpty(userId))
|
||||
{
|
||||
@ -55,9 +103,42 @@ namespace QRRapidoApp.Controllers
|
||||
ViewBag.MonthlyQRCount = await _userService.GetQRCountThisMonthAsync(userId);
|
||||
}
|
||||
|
||||
return View();
|
||||
return View("Index");
|
||||
}
|
||||
|
||||
// Dedicated SEO Routes - These act as virtual pages
|
||||
[Route("pix")]
|
||||
[Route("{culture}/pix")]
|
||||
public async Task<IActionResult> Pix() => await Index("pix");
|
||||
|
||||
[Route("wifi")]
|
||||
[Route("{culture}/wifi")]
|
||||
public async Task<IActionResult> Wifi() => await Index("wifi");
|
||||
|
||||
[Route("vcard")]
|
||||
[Route("{culture}/vcard")]
|
||||
public async Task<IActionResult> VCard() => await Index("vcard");
|
||||
|
||||
[Route("whatsapp")]
|
||||
[Route("{culture}/whatsapp")]
|
||||
public async Task<IActionResult> WhatsApp() => await Index("whatsapp");
|
||||
|
||||
[Route("email")]
|
||||
[Route("{culture}/email")]
|
||||
public async Task<IActionResult> Email() => await Index("email");
|
||||
|
||||
[Route("sms")]
|
||||
[Route("{culture}/sms")]
|
||||
public async Task<IActionResult> Sms() => await Index("sms");
|
||||
|
||||
[Route("text")]
|
||||
[Route("{culture}/text")]
|
||||
public async Task<IActionResult> Text() => await Index("text");
|
||||
|
||||
[Route("url")]
|
||||
[Route("{culture}/url")]
|
||||
public async Task<IActionResult> UrlType() => await Index("url");
|
||||
|
||||
public IActionResult Privacy()
|
||||
{
|
||||
var userId = User?.FindFirst(ClaimTypes.NameIdentifier)?.Value;
|
||||
@ -205,91 +286,102 @@ namespace QRRapidoApp.Controllers
|
||||
return Ok(new { status = "healthy", timestamp = DateTime.UtcNow });
|
||||
}
|
||||
|
||||
// Sitemap endpoint for SEO
|
||||
[Route("sitemap.xml")]
|
||||
public async Task<IActionResult> Sitemap()
|
||||
{
|
||||
var baseUrl = "https://qrrapido.site";
|
||||
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("/pt-BR", "daily", "0.9");
|
||||
AppendUrl("/es-PY", "daily", "0.9");
|
||||
|
||||
var cultures = new[] { "pt-BR", "es-PY" };
|
||||
var informationalPages = new[]
|
||||
{
|
||||
new { Path = "Home/About", ChangeFreq = "monthly", Priority = "0.8" },
|
||||
new { Path = "Home/Contact", ChangeFreq = "monthly", Priority = "0.8" },
|
||||
new { Path = "Home/FAQ", ChangeFreq = "weekly", Priority = "0.9" },
|
||||
new { Path = "Home/HowToUse", ChangeFreq = "weekly", Priority = "0.8" },
|
||||
new { Path = "Home/Privacy", ChangeFreq = "monthly", Priority = "0.5" },
|
||||
new { Path = "Home/Terms", ChangeFreq = "monthly", Priority = "0.5" }
|
||||
};
|
||||
|
||||
foreach (var page in informationalPages)
|
||||
{
|
||||
AppendUrl($"/{page.Path}", page.ChangeFreq, page.Priority);
|
||||
}
|
||||
|
||||
foreach (var culture in cultures)
|
||||
{
|
||||
foreach (var page in informationalPages)
|
||||
{
|
||||
AppendUrl($"/{culture}/{page.Path}", page.ChangeFreq, page.Priority);
|
||||
}
|
||||
}
|
||||
|
||||
// Dynamic tutorial pages
|
||||
try
|
||||
{
|
||||
var allArticles = await _markdownService.GetAllArticlesForSitemapAsync();
|
||||
|
||||
foreach (var article in allArticles)
|
||||
{
|
||||
var slug = !string.IsNullOrWhiteSpace(article.Slug)
|
||||
? article.Slug
|
||||
: article.Title.ToLower().Replace(" ", "-");
|
||||
|
||||
var encodedSlug = System.Uri.EscapeDataString(slug);
|
||||
var lastMod = article.LastMod.ToString("yyyy-MM-dd");
|
||||
|
||||
AppendUrl($"/{article.Culture}/tutoriais/{encodedSlug}", "weekly", "0.8", lastMod);
|
||||
}
|
||||
|
||||
_logger.LogInformation("Generated sitemap with {Count} tutorial articles", allArticles.Count);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "Error adding tutorials to sitemap");
|
||||
}
|
||||
|
||||
sitemapBuilder.AppendLine("</urlset>");
|
||||
|
||||
return Content(sitemapBuilder.ToString(), "application/xml");
|
||||
}
|
||||
// Sitemap endpoint for SEO
|
||||
[Route("sitemap.xml")]
|
||||
public async Task<IActionResult> Sitemap()
|
||||
{
|
||||
var baseUrl = "https://qrrapido.site";
|
||||
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("/pt-BR", "daily", "0.9");
|
||||
AppendUrl("/es-PY", "daily", "0.9");
|
||||
|
||||
// Tools (Virtual Pages)
|
||||
var tools = new[] { "pix", "wifi", "vcard", "whatsapp", "email", "sms", "text", "url" };
|
||||
var cultures = new[] { "pt-BR", "es-PY" };
|
||||
|
||||
foreach (var culture in cultures)
|
||||
{
|
||||
foreach (var tool in tools)
|
||||
{
|
||||
AppendUrl($"/{culture}/{tool}", "weekly", "0.9");
|
||||
}
|
||||
}
|
||||
|
||||
var informationalPages = new[]
|
||||
{
|
||||
new { Path = "Home/About", ChangeFreq = "monthly", Priority = "0.8" },
|
||||
new { Path = "Home/Contact", ChangeFreq = "monthly", Priority = "0.8" },
|
||||
new { Path = "Home/FAQ", ChangeFreq = "weekly", Priority = "0.9" },
|
||||
new { Path = "Home/HowToUse", ChangeFreq = "weekly", Priority = "0.8" },
|
||||
new { Path = "Home/Privacy", ChangeFreq = "monthly", Priority = "0.5" },
|
||||
new { Path = "Home/Terms", ChangeFreq = "monthly", Priority = "0.5" }
|
||||
};
|
||||
|
||||
foreach (var page in informationalPages)
|
||||
{
|
||||
AppendUrl($"/{page.Path}", page.ChangeFreq, page.Priority);
|
||||
}
|
||||
|
||||
foreach (var culture in cultures)
|
||||
{
|
||||
foreach (var page in informationalPages)
|
||||
{
|
||||
AppendUrl($"/{culture}/{page.Path}", page.ChangeFreq, page.Priority);
|
||||
}
|
||||
}
|
||||
|
||||
// Dynamic tutorial pages
|
||||
try
|
||||
{
|
||||
var allArticles = await _markdownService.GetAllArticlesForSitemapAsync();
|
||||
|
||||
foreach (var article in allArticles)
|
||||
{
|
||||
var slug = !string.IsNullOrWhiteSpace(article.Slug)
|
||||
? article.Slug
|
||||
: article.Title.ToLower().Replace(" ", "-");
|
||||
|
||||
var encodedSlug = System.Uri.EscapeDataString(slug);
|
||||
var lastMod = article.LastMod.ToString("yyyy-MM-dd");
|
||||
|
||||
AppendUrl($"/{article.Culture}/tutoriais/{encodedSlug}", "weekly", "0.8", lastMod);
|
||||
}
|
||||
|
||||
_logger.LogInformation("Generated sitemap with {Count} tutorial articles", allArticles.Count);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "Error adding tutorials to sitemap");
|
||||
}
|
||||
|
||||
sitemapBuilder.AppendLine("</urlset>");
|
||||
|
||||
return Content(sitemapBuilder.ToString(), "application/xml");
|
||||
}
|
||||
}
|
||||
|
||||
//public class ErrorViewModel
|
||||
@ -297,4 +389,4 @@ namespace QRRapidoApp.Controllers
|
||||
// public string? RequestId { get; set; }
|
||||
// public bool ShowRequestId => !string.IsNullOrEmpty(RequestId);
|
||||
//}
|
||||
}
|
||||
}
|
||||
|
||||
@ -40,13 +40,9 @@ namespace QRRapidoApp.Middleware
|
||||
|
||||
var detectedCulture = DetectBrowserLanguage(context);
|
||||
|
||||
// If the detected culture is the default, do not redirect.
|
||||
if (detectedCulture == DefaultCulture)
|
||||
{
|
||||
await _next(context);
|
||||
return;
|
||||
}
|
||||
|
||||
// ALWAYS Redirect to include culture in path for consistency and SEO
|
||||
// This ensures /pix becomes /pt-BR/pix or /es-PY/pix
|
||||
|
||||
var redirectUrl = $"/{detectedCulture}";
|
||||
if (!string.IsNullOrEmpty(path))
|
||||
{
|
||||
|
||||
16
Program.cs
16
Program.cs
@ -30,22 +30,10 @@ using System.Threading.RateLimiting;
|
||||
using Microsoft.AspNetCore.Server.Kestrel.Core;
|
||||
using AspNetCore.DataProtection.MongoDb;
|
||||
|
||||
// Fix for WSL path issues - disable StaticWebAssets completely
|
||||
var options = new WebApplicationOptions
|
||||
{
|
||||
Args = args,
|
||||
ContentRootPath = Directory.GetCurrentDirectory(),
|
||||
WebRootPath = Path.Combine(Directory.GetCurrentDirectory(), "wwwroot")
|
||||
};
|
||||
|
||||
// Disable StaticWebAssets for WSL compatibility
|
||||
// Disable StaticWebAssets for WSL compatibility (keeping this as it might still be needed for your mixed env)
|
||||
Environment.SetEnvironmentVariable("ASPNETCORE_HOSTINGSTARTUP__STATICWEBASSETS__ENABLED", "false");
|
||||
|
||||
var builder = WebApplication.CreateBuilder(options);
|
||||
|
||||
// Add Docker Secrets as configuration source (for Swarm deployments)
|
||||
// Secrets override values from appsettings.json
|
||||
builder.Configuration.AddDockerSecrets();
|
||||
var builder = WebApplication.CreateBuilder(args);
|
||||
|
||||
// Configure Serilog
|
||||
var loggerConfig = new LoggerConfiguration()
|
||||
|
||||
@ -22,7 +22,7 @@
|
||||
<PackageReference Include="Serilog.Sinks.Async" Version="2.1.0" />
|
||||
<PackageReference Include="Serilog.Sinks.Console" Version="6.0.1-dev-00953" />
|
||||
<PackageReference Include="Serilog.Sinks.OpenSearch" Version="1.3.0" />
|
||||
<PackageReference Include="SixLabors.ImageSharp" Version="3.1.6" />
|
||||
<PackageReference Include="SixLabors.ImageSharp" Version="3.1.7" />
|
||||
<PackageReference Include="SixLabors.ImageSharp.Drawing" Version="2.1.4" />
|
||||
<PackageReference Include="Stripe.net" Version="48.4.0" />
|
||||
<PackageReference Include="StackExchange.Redis" Version="2.7.4" />
|
||||
|
||||
@ -760,6 +760,9 @@
|
||||
<data name="FastestQRGeneratorDescription" xml:space="preserve">
|
||||
<value>El generador de códigos QR pya'eve (ás rápido) de la web. Rei rehe (Gratis), seguro y confiable.</value>
|
||||
</data>
|
||||
<data name="Tools" xml:space="preserve">
|
||||
<value>Herramientas</value>
|
||||
</data>
|
||||
<data name="UsefulLinks" xml:space="preserve">
|
||||
<value>Enlaces Útiles</value>
|
||||
</data>
|
||||
|
||||
@ -813,6 +813,9 @@
|
||||
<data name="FastestQRGeneratorDescription" xml:space="preserve">
|
||||
<value>O gerador de QR codes mais rápido da web. Grátis, seguro e confiável.</value>
|
||||
</data>
|
||||
<data name="Tools" xml:space="preserve">
|
||||
<value>Ferramentas</value>
|
||||
</data>
|
||||
<data name="UsefulLinks" xml:space="preserve">
|
||||
<value>Links Úteis</value>
|
||||
</data>
|
||||
|
||||
@ -97,47 +97,61 @@ namespace QRRapidoApp.Services
|
||||
switch (stripeEvent.Type)
|
||||
{
|
||||
case "checkout.session.completed":
|
||||
var session = stripeEvent.Data.Object as Session;
|
||||
if (session?.SubscriptionId != null)
|
||||
if (stripeEvent.Data.Object is Session session)
|
||||
{
|
||||
var subscriptionService = new SubscriptionService();
|
||||
var subscription = await subscriptionService.GetAsync(session.SubscriptionId);
|
||||
await ProcessSubscriptionActivation(session.ClientReferenceId, subscription);
|
||||
if (session.SubscriptionId != null)
|
||||
{
|
||||
var subscriptionService = new SubscriptionService();
|
||||
var subscription = await subscriptionService.GetAsync(session.SubscriptionId);
|
||||
// Fix CS8604: Ensure ClientReferenceId is not null
|
||||
var userId = session.ClientReferenceId ??
|
||||
(session.Metadata != null && session.Metadata.ContainsKey("user_id") ? session.Metadata["user_id"] : null);
|
||||
|
||||
if (!string.IsNullOrEmpty(userId))
|
||||
{
|
||||
await ProcessSubscriptionActivation(userId, subscription);
|
||||
}
|
||||
else
|
||||
{
|
||||
_logger.LogWarning($"Missing userId in checkout session {session.Id}");
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case "invoice.finalized":
|
||||
var invoice = stripeEvent.Data.Object as Invoice;
|
||||
var subscriptionLineItem = invoice.Lines?.Data
|
||||
.FirstOrDefault(line =>
|
||||
!string.IsNullOrEmpty(line.SubscriptionId) ||
|
||||
line.Subscription != null
|
||||
);
|
||||
|
||||
string subscriptionId = null;
|
||||
|
||||
if (subscriptionLineItem != null)
|
||||
if (stripeEvent.Data.Object is Invoice invoice)
|
||||
{
|
||||
// Tenta obter o ID da assinatura de duas formas diferentes
|
||||
subscriptionId = subscriptionLineItem.SubscriptionId
|
||||
?? subscriptionLineItem.Subscription?.Id;
|
||||
}
|
||||
var subscriptionLineItem = invoice.Lines?.Data
|
||||
.FirstOrDefault(line =>
|
||||
!string.IsNullOrEmpty(line.SubscriptionId) ||
|
||||
line.Subscription != null
|
||||
);
|
||||
|
||||
if (subscriptionId != null)
|
||||
{
|
||||
var subscriptionService = new SubscriptionService();
|
||||
var subscription = await subscriptionService.GetAsync(subscriptionId);
|
||||
var user = await _userService.GetUserByStripeCustomerIdAsync(subscription.CustomerId);
|
||||
if (user != null)
|
||||
string? subscriptionId = null;
|
||||
|
||||
if (subscriptionLineItem != null)
|
||||
{
|
||||
await ProcessSubscriptionActivation(user.Id, subscription);
|
||||
// Tenta obter o ID da assinatura de duas formas diferentes
|
||||
subscriptionId = subscriptionLineItem.SubscriptionId
|
||||
?? subscriptionLineItem.Subscription?.Id;
|
||||
}
|
||||
|
||||
if (subscriptionId != null)
|
||||
{
|
||||
var subscriptionService = new SubscriptionService();
|
||||
var subscription = await subscriptionService.GetAsync(subscriptionId);
|
||||
var user = await _userService.GetUserByStripeCustomerIdAsync(subscription.CustomerId);
|
||||
if (user != null)
|
||||
{
|
||||
await ProcessSubscriptionActivation(user.Id, subscription);
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case "customer.subscription.deleted":
|
||||
var deletedSubscription = stripeEvent.Data.Object as Subscription;
|
||||
if (deletedSubscription != null)
|
||||
if (stripeEvent.Data.Object is Subscription deletedSubscription)
|
||||
{
|
||||
await _userService.DeactivatePremiumStatus(deletedSubscription.Id);
|
||||
}
|
||||
|
||||
@ -1580,6 +1580,24 @@
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Auto-select QR Type from Server Route (SEO)
|
||||
const serverSelectedType = '@(ViewBag.SelectedQRType ?? "")';
|
||||
if (serverSelectedType) {
|
||||
const qrTypeSelect = document.getElementById('qr-type');
|
||||
if (qrTypeSelect) {
|
||||
// Find option with this value (case-insensitive)
|
||||
const option = Array.from(qrTypeSelect.options).find(opt =>
|
||||
opt.value.toLowerCase() === serverSelectedType.toLowerCase()
|
||||
);
|
||||
|
||||
if (option) {
|
||||
qrTypeSelect.value = option.value;
|
||||
// Trigger change event to load correct interface
|
||||
qrTypeSelect.dispatchEvent(new Event('change'));
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
</script>
|
||||
}
|
||||
|
||||
@ -404,15 +404,24 @@
|
||||
<h5>QR Rapido</h5>
|
||||
<p class="small">@Localizer["FastestQRGeneratorDescription"]</p>
|
||||
</div>
|
||||
<div class="col-md-3">
|
||||
<div class="col-md-2">
|
||||
<h6>@Localizer["Tools"]</h6>
|
||||
<ul class="list-unstyled small">
|
||||
<li><a href="/pix" class="text-light text-decoration-none">Gerador de PIX</a></li>
|
||||
<li><a href="/wifi" class="text-light text-decoration-none">QR Code WiFi</a></li>
|
||||
<li><a href="/whatsapp" class="text-light text-decoration-none">Link WhatsApp</a></li>
|
||||
<li><a href="/vcard" class="text-light text-decoration-none">Cartão Digital</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="col-md-2">
|
||||
<h6>@Localizer["UsefulLinks"]</h6>
|
||||
<ul class="list-unstyled">
|
||||
<ul class="list-unstyled small">
|
||||
<li><a href="@Url.Action("Privacy", "Home")" class="text-light">@Localizer["Privacy"]</a></li>
|
||||
<li><a href="@Url.Action("Terms", "Home")" class="text-light">@Localizer["TermsOfUse"]</a></li>
|
||||
<li><a href="@Url.Action("Upgrade", "Premium")" class="text-warning">Premium</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="col-md-3">
|
||||
<div class="col-md-2">
|
||||
<h6>@Localizer["Support"]</h6>
|
||||
<ul class="list-unstyled">
|
||||
<li><a href="mailto:contato@qrrapido.site" class="text-light">Contato</a></li>
|
||||
|
||||
@ -1994,8 +1994,8 @@ class QRRapidoGenerator {
|
||||
const urlPreview = document.getElementById('url-preview');
|
||||
|
||||
// Helper to safely hide
|
||||
const safeHide = (el) => { if (el) el.style.display = 'none'; };
|
||||
const safeShow = (el) => { if (el) el.style.display = 'block'; };
|
||||
const safeHide = (el) => { if (el) { el.style.display = 'none'; el.classList.add('disabled-state'); } };
|
||||
const safeShow = (el) => { if (el) { el.style.display = 'block'; el.classList.remove('disabled-state'); } };
|
||||
|
||||
// 1. Hide EVERYTHING specific first
|
||||
safeHide(vcardInterface);
|
||||
|
||||
Loading…
Reference in New Issue
Block a user