Files
FrontOffice/src/FrontOffice.Main/Utilities/Seo/SitemapGenerator.cs
T
masoodafar-web 7f5a4f2c37
Build and Deploy to Kubernetes / build-and-deploy (push) Has been cancelled
feat: enhance SEO and sitemap functionality
- Updated the sitemap generation to return a byte array instead of a string for improved performance.
- Added new SEO metadata for categories and store chooser routes to enhance search engine visibility.
- Implemented redirects for legacy wishlist URLs to the homepage, improving user experience.
- Updated robots.txt to disallow indexing of specific paths, ensuring better control over search engine crawling.

These changes significantly improve the SEO capabilities and sitemap handling of the application, enhancing both performance and user navigation.
2026-07-03 06:04:44 +03:30

118 lines
3.5 KiB
C#

using System.Text;
using System.Xml;
using System.Xml.Linq;
using Microsoft.Extensions.Options;
namespace FrontOffice.Main.Utilities.Seo;
public class SitemapGenerator
{
private readonly BlogPostService _blogPostService;
private readonly SeoSettings _settings;
private static readonly string[] StaticPaths =
[
RouteConstants.Main.MainPage,
RouteConstants.About.Index,
RouteConstants.FAQ.Index,
RouteConstants.Contact.Index,
RouteConstants.Licenses.Index,
RouteConstants.Blog.Index,
RouteConstants.Store.Products,
RouteConstants.Package.List,
RouteConstants.Club.Membership,
RouteConstants.Club.Features,
RouteConstants.DiscountStore.Products,
RouteConstants.Registration.Wizard,
];
public SitemapGenerator(BlogPostService blogPostService, IOptions<SeoSettings> settings)
{
_blogPostService = blogPostService;
_settings = settings.Value;
}
public async Task<string> GenerateAsync(CancellationToken cancellationToken = default)
{
var baseUrl = _settings.SiteUrl.TrimEnd('/');
var ns = XNamespace.Get("http://www.sitemaps.org/schemas/sitemap/0.9");
var urls = new List<XElement>();
foreach (var path in StaticPaths)
{
urls.Add(CreateUrlElement(ns, $"{baseUrl}{path}", priority: path == "/" ? "1.0" : "0.8"));
}
var page = 1;
const int pageSize = 100;
while (true)
{
var result = await _blogPostService.GetPublishedPostsAsync(page: page, pageSize: pageSize);
if (result.Posts.Count == 0)
break;
foreach (var post in result.Posts)
{
if (string.IsNullOrWhiteSpace(post.Slug))
continue;
var lastMod = post.PublishedAt?.ToString("yyyy-MM-dd") ?? DateTime.UtcNow.ToString("yyyy-MM-dd");
urls.Add(CreateUrlElement(
ns,
$"{baseUrl}{RouteConstants.Blog.Post}{post.Slug}",
lastMod: lastMod,
priority: "0.6"));
}
if (page >= result.TotalPages)
break;
page++;
}
var document = new XDocument(
new XDeclaration("1.0", "utf-8", null),
new XElement(ns + "urlset", urls));
var settings = new XmlWriterSettings
{
Encoding = new UTF8Encoding(encoderShouldEmitUTF8Identifier: false),
Indent = true,
OmitXmlDeclaration = false
};
using var stream = new MemoryStream();
using (var writer = XmlWriter.Create(stream, settings))
{
document.Save(writer);
}
return Encoding.UTF8.GetString(stream.ToArray());
}
public async Task<byte[]> GenerateBytesAsync(CancellationToken cancellationToken = default)
{
var xml = await GenerateAsync(cancellationToken);
return Encoding.UTF8.GetBytes(xml);
}
private static XElement CreateUrlElement(
XNamespace ns,
string loc,
string? lastMod = null,
string changefreq = "weekly",
string priority = "0.5")
{
var element = new XElement(ns + "url",
new XElement(ns + "loc", loc),
new XElement(ns + "changefreq", changefreq),
new XElement(ns + "priority", priority));
if (!string.IsNullOrWhiteSpace(lastMod))
element.Add(new XElement(ns + "lastmod", lastMod));
return element;
}
}