444 lines
18 KiB
C#
444 lines
18 KiB
C#
using MongoDB.Driver;
|
|
using Microsoft.Extensions.Caching.Memory;
|
|
using System.Text;
|
|
using OnlyOneAccessTemplate.Models;
|
|
|
|
namespace OnlyOneAccessTemplate.Services
|
|
{
|
|
public class SiteConfigurationService : ISiteConfigurationService
|
|
{
|
|
private readonly IMongoDatabase _database;
|
|
private readonly IMongoCollection<SiteConfiguration> _configurations;
|
|
private readonly IMemoryCache _cache;
|
|
private readonly IConfiguration _config;
|
|
private readonly ILogger<SiteConfigurationService> _logger;
|
|
private readonly TimeSpan _cacheExpiration = TimeSpan.FromMinutes(30);
|
|
|
|
public SiteConfigurationService(
|
|
IMongoDatabase database,
|
|
IMemoryCache cache,
|
|
IConfiguration config,
|
|
ILogger<SiteConfigurationService> logger)
|
|
{
|
|
_database = database;
|
|
_cache = cache;
|
|
_config = config;
|
|
_logger = logger;
|
|
_configurations = _database.GetCollection<SiteConfiguration>("site_configurations");
|
|
}
|
|
|
|
public async Task<SiteConfiguration> GetConfigurationAsync(string language)
|
|
{
|
|
var cacheKey = $"site_config_{language}";
|
|
|
|
if (_cache.TryGetValue(cacheKey, out SiteConfiguration? cachedConfig) && cachedConfig != null)
|
|
{
|
|
return cachedConfig;
|
|
}
|
|
|
|
try
|
|
{
|
|
var filter = Builders<SiteConfiguration>.Filter.Eq(x => x.Language, language);
|
|
var configuration = await _configurations.Find(filter).FirstOrDefaultAsync();
|
|
|
|
if (configuration == null)
|
|
{
|
|
configuration = await CreateDefaultConfigurationAsync(language);
|
|
}
|
|
|
|
_cache.Set(cacheKey, configuration, _cacheExpiration);
|
|
return configuration;
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
_logger.LogError(ex, "Erro ao buscar configuração para idioma {Language}", language);
|
|
return await CreateDefaultConfigurationAsync(language);
|
|
}
|
|
}
|
|
|
|
public SiteConfiguration GetConfiguration(string language)
|
|
{
|
|
var cacheKey = $"site_config_{language}";
|
|
|
|
if (_cache.TryGetValue(cacheKey, out SiteConfiguration? cachedConfig) && cachedConfig != null)
|
|
{
|
|
return cachedConfig;
|
|
}
|
|
|
|
try
|
|
{
|
|
var filter = Builders<SiteConfiguration>.Filter.Eq(x => x.Language, language);
|
|
var configuration = _configurations.Find(filter).FirstOrDefault();
|
|
|
|
if (configuration == null)
|
|
{
|
|
configuration = CreateDefaultConfiguration(language);
|
|
}
|
|
|
|
_cache.Set(cacheKey, configuration, _cacheExpiration);
|
|
return configuration;
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
_logger.LogError(ex, "Erro ao buscar configuração para idioma {Language}", language);
|
|
return CreateDefaultConfiguration(language);
|
|
}
|
|
}
|
|
|
|
public async Task<PageContent?> GetPageContentAsync(string language, string pageName)
|
|
{
|
|
var configuration = await GetConfigurationAsync(language);
|
|
|
|
if (configuration.Pages.TryGetValue(pageName.ToLowerInvariant(), out var pageContent))
|
|
{
|
|
return pageContent;
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
public async Task<string> GenerateSitemapAsync()
|
|
{
|
|
var sb = new StringBuilder();
|
|
sb.AppendLine("<?xml version=\"1.0\" encoding=\"UTF-8\"?>");
|
|
sb.AppendLine("<urlset xmlns=\"http://www.sitemaps.org/schemas/sitemap/0.9\" xmlns:xhtml=\"http://www.w3.org/1999/xhtml\">");
|
|
|
|
var baseUrl = _config.GetValue<string>("SEO:DefaultDomain") ?? "https://localhost";
|
|
var languages = await GetAvailableLanguagesAsync();
|
|
|
|
// Páginas principais para cada idioma
|
|
var mainPages = new[] { "", "about", "contact", "privacy", "terms" };
|
|
|
|
foreach (var page in mainPages)
|
|
{
|
|
foreach (var lang in languages)
|
|
{
|
|
var url = BuildUrl(baseUrl, lang, page);
|
|
var lastMod = DateTime.UtcNow.ToString("yyyy-MM-ddTHH:mm:ssZ");
|
|
var priority = page == "" ? "1.0" : "0.8";
|
|
|
|
sb.AppendLine(" <url>");
|
|
sb.AppendLine($" <loc>{url}</loc>");
|
|
sb.AppendLine($" <lastmod>{lastMod}</lastmod>");
|
|
sb.AppendLine($" <changefreq>weekly</changefreq>");
|
|
sb.AppendLine($" <priority>{priority}</priority>");
|
|
|
|
// Adicionar links alternativos (hreflang)
|
|
foreach (var altLang in languages)
|
|
{
|
|
var altUrl = BuildUrl(baseUrl, altLang, page);
|
|
sb.AppendLine($" <xhtml:link rel=\"alternate\" hreflang=\"{altLang}\" href=\"{altUrl}\" />");
|
|
}
|
|
|
|
sb.AppendLine(" </url>");
|
|
}
|
|
}
|
|
|
|
sb.AppendLine("</urlset>");
|
|
return sb.ToString();
|
|
}
|
|
|
|
public async Task<string> GenerateRobotsAsync()
|
|
{
|
|
var sb = new StringBuilder();
|
|
var baseUrl = _config.GetValue<string>("SEO:DefaultDomain") ?? "https://localhost";
|
|
|
|
sb.AppendLine("User-agent: *");
|
|
sb.AppendLine("Allow: /");
|
|
sb.AppendLine("");
|
|
sb.AppendLine("# Sitemaps");
|
|
sb.AppendLine($"Sitemap: {baseUrl}/sitemap.xml");
|
|
sb.AppendLine("");
|
|
sb.AppendLine("# Crawl delay");
|
|
sb.AppendLine("Crawl-delay: 1");
|
|
|
|
return sb.ToString();
|
|
}
|
|
|
|
public async Task UpdateConfigurationAsync(SiteConfiguration config)
|
|
{
|
|
try
|
|
{
|
|
config.UpdatedAt = DateTime.UtcNow;
|
|
|
|
var filter = Builders<SiteConfiguration>.Filter.Eq(x => x.Language, config.Language);
|
|
var options = new ReplaceOptions { IsUpsert = true };
|
|
|
|
await _configurations.ReplaceOneAsync(filter, config, options);
|
|
|
|
// Invalidar cache
|
|
var cacheKey = $"site_config_{config.Language}";
|
|
_cache.Remove(cacheKey);
|
|
|
|
_logger.LogInformation("Configuração atualizada para idioma {Language}", config.Language);
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
_logger.LogError(ex, "Erro ao atualizar configuração para idioma {Language}", config.Language);
|
|
throw;
|
|
}
|
|
}
|
|
|
|
public async Task<List<string>> GetAvailableLanguagesAsync()
|
|
{
|
|
const string cacheKey = "available_languages";
|
|
|
|
if (_cache.TryGetValue(cacheKey, out List<string>? cachedLanguages) && cachedLanguages != null)
|
|
{
|
|
return cachedLanguages;
|
|
}
|
|
|
|
try
|
|
{
|
|
var projection = Builders<SiteConfiguration>.Projection.Include(x => x.Language);
|
|
var languages = await _configurations
|
|
.Find(Builders<SiteConfiguration>.Filter.Empty)
|
|
.Project<SiteConfiguration>(projection)
|
|
.ToListAsync();
|
|
|
|
var availableLanguages = languages.Select(x => x.Language).Distinct().ToList();
|
|
|
|
if (!availableLanguages.Any())
|
|
{
|
|
availableLanguages = new List<string> { "pt", "en", "es" };
|
|
}
|
|
|
|
_cache.Set(cacheKey, availableLanguages, TimeSpan.FromHours(1));
|
|
return availableLanguages;
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
_logger.LogError(ex, "Erro ao buscar idiomas disponíveis");
|
|
return new List<string> { "pt", "en", "es" };
|
|
}
|
|
}
|
|
|
|
private async Task<SiteConfiguration> CreateDefaultConfigurationAsync(string language)
|
|
{
|
|
var defaultConfig = new SiteConfiguration
|
|
{
|
|
Language = language,
|
|
Seo = CreateDefaultSeoConfig(language),
|
|
Pages = CreateDefaultPages(language),
|
|
Conversion = CreateDefaultConversionConfig(language),
|
|
CreatedAt = DateTime.UtcNow,
|
|
UpdatedAt = DateTime.UtcNow
|
|
};
|
|
|
|
try
|
|
{
|
|
await _configurations.InsertOneAsync(defaultConfig);
|
|
_logger.LogInformation("Configuração padrão criada para idioma {Language}", language);
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
_logger.LogError(ex, "Erro ao criar configuração padrão para idioma {Language}", language);
|
|
}
|
|
|
|
return defaultConfig;
|
|
}
|
|
|
|
private SiteConfiguration CreateDefaultConfiguration(string language)
|
|
{
|
|
var defaultConfig = new SiteConfiguration
|
|
{
|
|
Language = language,
|
|
Seo = CreateDefaultSeoConfig(language),
|
|
Pages = CreateDefaultPages(language),
|
|
Conversion = CreateDefaultConversionConfig(language),
|
|
CreatedAt = DateTime.UtcNow,
|
|
UpdatedAt = DateTime.UtcNow
|
|
};
|
|
|
|
return defaultConfig;
|
|
}
|
|
|
|
public async Task<bool> ConfigurationExistsAsync(string language)
|
|
{
|
|
try
|
|
{
|
|
var filter = Builders<SiteConfiguration>.Filter.Eq(x => x.Language, language);
|
|
var count = await _configurations.CountDocumentsAsync(filter);
|
|
return count > 0;
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
_logger.LogError(ex, "Erro ao verificar se configuração existe para idioma {Language}", language);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
public async Task DeleteConfigurationAsync(string language)
|
|
{
|
|
try
|
|
{
|
|
var filter = Builders<SiteConfiguration>.Filter.Eq(x => x.Language, language);
|
|
await _configurations.DeleteOneAsync(filter);
|
|
|
|
// Invalidar cache
|
|
var cacheKey = $"site_config_{language}";
|
|
_cache.Remove(cacheKey);
|
|
|
|
_logger.LogInformation("Configuração deletada para idioma {Language}", language);
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
_logger.LogError(ex, "Erro ao deletar configuração para idioma {Language}", language);
|
|
throw;
|
|
}
|
|
}
|
|
|
|
private SeoConfig CreateDefaultSeoConfig(string language)
|
|
{
|
|
var defaultSiteName = _config.GetValue<string>("SEO:DefaultSiteName") ?? "Site de Conversão";
|
|
|
|
return language switch
|
|
{
|
|
"en" => new SeoConfig
|
|
{
|
|
SiteName = defaultSiteName,
|
|
DefaultTitle = "Conversion Site - Transform Your Business",
|
|
DefaultDescription = "Professional conversion solutions to boost your business results",
|
|
DefaultKeywords = "conversion, business, marketing, results",
|
|
TwitterCard = "summary_large_image",
|
|
GoogleTagManagerId = _config.GetValue<string>("SEO:GoogleTagManagerId") ?? "",
|
|
GoogleAnalyticsId = _config.GetValue<string>("SEO:GoogleAnalyticsId") ?? "",
|
|
Author = "Conversion Site Team"
|
|
},
|
|
"es" => new SeoConfig
|
|
{
|
|
SiteName = defaultSiteName,
|
|
DefaultTitle = "Sitio de Conversión - Transforma Tu Negocio",
|
|
DefaultDescription = "Soluciones profesionales de conversión para potenciar los resultados de tu negocio",
|
|
DefaultKeywords = "conversión, negocio, marketing, resultados",
|
|
TwitterCard = "summary_large_image",
|
|
GoogleTagManagerId = _config.GetValue<string>("SEO:GoogleTagManagerId") ?? "",
|
|
GoogleAnalyticsId = _config.GetValue<string>("SEO:GoogleAnalyticsId") ?? "",
|
|
Author = "Equipo Sitio de Conversión"
|
|
},
|
|
_ => new SeoConfig
|
|
{
|
|
SiteName = defaultSiteName,
|
|
DefaultTitle = "Site de Conversão - Transforme Seu Negócio",
|
|
DefaultDescription = "Soluções profissionais de conversão para alavancar os resultados do seu negócio",
|
|
DefaultKeywords = "conversão, negócio, marketing, resultados",
|
|
TwitterCard = "summary_large_image",
|
|
GoogleTagManagerId = _config.GetValue<string>("SEO:GoogleTagManagerId") ?? "",
|
|
GoogleAnalyticsId = _config.GetValue<string>("SEO:GoogleAnalyticsId") ?? "",
|
|
Author = "Equipe Site de Conversão"
|
|
}
|
|
};
|
|
}
|
|
|
|
private Dictionary<string, PageContent> CreateDefaultPages(string language)
|
|
{
|
|
return language switch
|
|
{
|
|
"en" => new Dictionary<string, PageContent>
|
|
{
|
|
["index"] = new PageContent
|
|
{
|
|
Title = "Home - Transform Your Business",
|
|
Description = "Professional conversion solutions to boost your business results",
|
|
H1 = "Transform Your Business Today",
|
|
MetaTitle = "Business Transformation | Conversion Solutions"
|
|
},
|
|
["about"] = new PageContent
|
|
{
|
|
Title = "About Us - Our Mission",
|
|
Description = "Learn about our mission to help businesses achieve better conversion rates",
|
|
H1 = "About Our Company",
|
|
MetaTitle = "About Us | Conversion Experts"
|
|
}
|
|
},
|
|
"es" => new Dictionary<string, PageContent>
|
|
{
|
|
["index"] = new PageContent
|
|
{
|
|
Title = "Inicio - Transforma Tu Negocio",
|
|
Description = "Soluciones profesionales de conversión para potenciar los resultados de tu negocio",
|
|
H1 = "Transforma Tu Negocio Hoy",
|
|
MetaTitle = "Transformación Empresarial | Soluciones de Conversión"
|
|
},
|
|
["about"] = new PageContent
|
|
{
|
|
Title = "Acerca de Nosotros - Nuestra Misión",
|
|
Description = "Conoce nuestra misión de ayudar a las empresas a lograr mejores tasas de conversión",
|
|
H1 = "Acerca de Nuestra Empresa",
|
|
MetaTitle = "Acerca de Nosotros | Expertos en Conversión"
|
|
}
|
|
},
|
|
_ => new Dictionary<string, PageContent>
|
|
{
|
|
["index"] = new PageContent
|
|
{
|
|
Title = "Início - Transforme Seu Negócio",
|
|
Description = "Soluções profissionais de conversão para alavancar os resultados do seu negócio",
|
|
H1 = "Transforme Seu Negócio Hoje",
|
|
MetaTitle = "Transformação Empresarial | Soluções de Conversão"
|
|
},
|
|
["about"] = new PageContent
|
|
{
|
|
Title = "Sobre Nós - Nossa Missão",
|
|
Description = "Conheça nossa missão de ajudar empresas a alcançar melhores taxas de conversão",
|
|
H1 = "Sobre Nossa Empresa",
|
|
MetaTitle = "Sobre Nós | Especialistas em Conversão"
|
|
}
|
|
}
|
|
};
|
|
}
|
|
|
|
private ConversionConfig CreateDefaultConversionConfig(string language)
|
|
{
|
|
return new ConversionConfig
|
|
{
|
|
FormAction = "/conversion/submit",
|
|
ThankYouPage = language switch
|
|
{
|
|
"en" => "/thank-you",
|
|
"es" => "/gracias",
|
|
_ => "/obrigado"
|
|
},
|
|
FormFields = language switch
|
|
{
|
|
"en" => new List<FormField>
|
|
{
|
|
new() { Name = "name", Type = "text", Label = "Full Name", Placeholder = "Enter your full name", Required = true, Order = 1 },
|
|
new() { Name = "email", Type = "email", Label = "Email", Placeholder = "Enter your email", Required = true, Order = 2 },
|
|
new() { Name = "phone", Type = "tel", Label = "Phone", Placeholder = "Enter your phone number", Required = false, Order = 3 }
|
|
},
|
|
"es" => new List<FormField>
|
|
{
|
|
new() { Name = "name", Type = "text", Label = "Nombre Completo", Placeholder = "Ingresa tu nombre completo", Required = true, Order = 1 },
|
|
new() { Name = "email", Type = "email", Label = "Correo Electrónico", Placeholder = "Ingresa tu correo", Required = true, Order = 2 },
|
|
new() { Name = "phone", Type = "tel", Label = "Teléfono", Placeholder = "Ingresa tu número de teléfono", Required = false, Order = 3 }
|
|
},
|
|
_ => new List<FormField>
|
|
{
|
|
new() { Name = "name", Type = "text", Label = "Nome Completo", Placeholder = "Digite seu nome completo", Required = true, Order = 1 },
|
|
new() { Name = "email", Type = "email", Label = "E-mail", Placeholder = "Digite seu e-mail", Required = true, Order = 2 },
|
|
new() { Name = "phone", Type = "tel", Label = "Telefone", Placeholder = "Digite seu telefone", Required = false, Order = 3 }
|
|
}
|
|
}
|
|
};
|
|
}
|
|
|
|
private string BuildUrl(string baseUrl, string language, string page)
|
|
{
|
|
var url = baseUrl.TrimEnd('/');
|
|
|
|
if (language != "pt") // português é o padrão
|
|
{
|
|
url += $"/{language}";
|
|
}
|
|
|
|
if (!string.IsNullOrEmpty(page))
|
|
{
|
|
url += $"/{page}";
|
|
}
|
|
|
|
return url;
|
|
}
|
|
}
|
|
}
|