Files
FrontOffice/src/FrontOffice.Main/Utilities/Seo/SeoMetadataProvider.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

214 lines
9.6 KiB
C#

using System.Text.RegularExpressions;
using Microsoft.Extensions.Options;
namespace FrontOffice.Main.Utilities.Seo;
public class SeoMetadataProvider
{
private readonly BlogPostService _blogPostService;
private readonly SeoSettings _settings;
private static readonly Dictionary<string, (string Title, string Description)> StaticPages = new(StringComparer.OrdinalIgnoreCase)
{
["/"] = (
"کارابازار سلامت | باشگاه مشتریان KBS",
"باشگاه مشتریان کارابازار سلامت — رشد تیم، فروش واقعی و پاداش شفاف. فروشگاه محصولات سلامت، کیف پول و ثبت‌نام رایگان."),
[RouteConstants.About.Index] = (
"درباره ما | کارابازار سلامت",
"آشنایی با کارابازار سلامت، باشگاه مشتریان KBS و خدمات فروشگاه محصولات سلامت."),
[RouteConstants.FAQ.Index] = (
"سوالات متداول | کارابازار سلامت",
"پاسخ سوالات متداول درباره باشگاه مشتریان، کیف پول، پاداش‌ها و خرید از کارابازار سلامت."),
[RouteConstants.Contact.Index] = (
"ارتباط با ما | کارابازار سلامت",
"تماس با تیم پشتیبانی کارابازار سلامت — سوالات، پیشنهادات و پشتیبانی کاربران."),
[RouteConstants.Licenses.Index] = (
"مجوزها و گواهینامه‌ها | کارابازار سلامت",
"مجوزها، گواهینامه‌ها و مدارک رسمی کارابازار سلامت."),
[RouteConstants.Blog.Index] = (
"بلاگ | کارابازار سلامت",
"آخرین مقالات و اخبار باشگاه مشتریان کارابازار سلامت."),
[RouteConstants.Store.Products] = (
"محصولات | کارابازار سلامت",
"فروشگاه محصولات سلامت کارابازار — خرید آنلاین با پاداش باشگاه مشتریان."),
[RouteConstants.Store.Categories] = (
"دسته‌بندی‌ها | کارابازار سلامت",
"دسته‌بندی محصولات فروشگاه کارابازار سلامت."),
[RouteConstants.Package.List] = (
"پکیج‌ها | کارابازار سلامت",
"پکیج‌های عضویت باشگاه مشتریان کارابازار سلامت."),
[RouteConstants.Club.Membership] = (
"عضویت باشگاه مشتریان | کارابازار سلامت",
"عضویت در باشگاه مشتریان KBS و بهره‌مندی از مزایا و پاداش‌ها."),
[RouteConstants.Club.Features] = (
"ویژگی‌های باشگاه مشتریان | کارابازار سلامت",
"امکانات و ویژگی‌های باشگاه مشتریان کارابازار سلامت."),
[RouteConstants.DiscountStore.Products] = (
"فروشگاه اعتباری | کارابازار سلامت",
"خرید از فروشگاه اعتباری کارابازار سلامت با کیف پول تخفیفی."),
[RouteConstants.Registration.Wizard] = (
"ثبت‌نام | کارابازار سلامت",
"ثبت‌نام سریع در باشگاه مشتریان کارابازار سلامت."),
[RouteConstants.Gateway.StoreChooser] = (
"فروشگاه‌ها | کارابازار سلامت",
"انتخاب فروشگاه اصلی یا اعتباری در کارابازار سلامت."),
};
private static readonly HashSet<string> NoIndexPaths = new(StringComparer.OrdinalIgnoreCase)
{
"/wishlist",
"/wp-admin",
"/wp-login.php",
"/feed",
"/xmlrpc.php",
};
private static readonly HashSet<string> NoIndexPrefixes = new(StringComparer.OrdinalIgnoreCase)
{
RouteConstants.Profile.Index,
"/profile/",
RouteConstants.Checkout.Index,
RouteConstants.Store.Cart,
RouteConstants.Store.CheckoutSummary,
RouteConstants.Store.Orders,
"/order/",
RouteConstants.DiscountStore.Cart,
RouteConstants.DiscountStore.Checkout,
RouteConstants.DiscountStore.Orders,
"/discount-store/order/",
RouteConstants.Gateway.OrdersChooser,
RouteConstants.Gateway.CartChooser,
RouteConstants.Commission.Dashboard,
"/commission/",
RouteConstants.Network.Statistics,
"/network/",
RouteConstants.Package.MyPackages,
"/wp-content/",
"/wp-includes/",
};
private static readonly Regex ProductDetailPattern = new(@"^/product/\d+$", RegexOptions.IgnoreCase | RegexOptions.CultureInvariant);
private static readonly Regex DiscountProductPattern = new(@"^/discount-store/product/\d+$", RegexOptions.IgnoreCase | RegexOptions.CultureInvariant);
private static readonly Regex PackageDetailPattern = new(@"^/package/\d+$", RegexOptions.IgnoreCase | RegexOptions.CultureInvariant);
private static readonly Regex CheckoutPackagePattern = new(@"^/checkout/\d+$", RegexOptions.IgnoreCase | RegexOptions.CultureInvariant);
public SeoMetadataProvider(BlogPostService blogPostService, IOptions<SeoSettings> settings)
{
_blogPostService = blogPostService;
_settings = settings.Value;
}
public async Task<SeoPageMetadata> GetForPathAsync(string path, CancellationToken cancellationToken = default)
{
var normalizedPath = NormalizePath(path);
var baseUrl = _settings.SiteUrl.TrimEnd('/');
if (IsNoIndexPath(normalizedPath))
{
return new SeoPageMetadata
{
Title = _settings.SiteName,
Description = _settings.DefaultDescription,
CanonicalUrl = $"{baseUrl}{normalizedPath}",
NoIndex = true
};
}
if (normalizedPath.StartsWith(RouteConstants.Blog.Post, StringComparison.OrdinalIgnoreCase)
&& normalizedPath.Length > RouteConstants.Blog.Post.Length)
{
var slug = normalizedPath[RouteConstants.Blog.Post.Length..].Trim('/');
if (!string.IsNullOrWhiteSpace(slug))
{
var post = await _blogPostService.GetBySlugAsync(slug);
if (post != null)
{
var description = !string.IsNullOrWhiteSpace(post.Summary)
? post.Summary
: $"مقاله {post.Title} — بلاگ کارابازار سلامت";
return new SeoPageMetadata
{
Title = $"{post.Title} | بلاگ کارابازار سلامت",
Description = description,
CanonicalUrl = $"{baseUrl}{RouteConstants.Blog.Post}{post.Slug}",
CrawlableContent = $"{post.Title}. {description}"
};
}
}
}
if (StaticPages.TryGetValue(normalizedPath, out var page))
{
return new SeoPageMetadata
{
Title = page.Title,
Description = page.Description,
CanonicalUrl = $"{baseUrl}{normalizedPath}",
CrawlableContent = normalizedPath == "/" ? GetHomeCrawlableContent() : null
};
}
if (IsIndexableDynamicRoute(normalizedPath))
{
return new SeoPageMetadata
{
Title = _settings.SiteName,
Description = _settings.DefaultDescription,
CanonicalUrl = $"{baseUrl}{normalizedPath}"
};
}
return new SeoPageMetadata
{
Title = _settings.SiteName,
Description = _settings.DefaultDescription,
CanonicalUrl = $"{baseUrl}{normalizedPath}",
NoIndex = true
};
}
private static bool IsIndexableDynamicRoute(string path) =>
ProductDetailPattern.IsMatch(path)
|| DiscountProductPattern.IsMatch(path)
|| PackageDetailPattern.IsMatch(path)
|| CheckoutPackagePattern.IsMatch(path);
private static string NormalizePath(string path)
{
if (string.IsNullOrWhiteSpace(path))
return "/";
var normalized = path.Split('?', '#')[0].Trim();
if (!normalized.StartsWith('/'))
normalized = "/" + normalized;
if (normalized.Length > 1 && normalized.EndsWith('/'))
normalized = normalized.TrimEnd('/');
return normalized;
}
private static bool IsNoIndexPath(string path)
{
if (NoIndexPaths.Contains(path))
return true;
foreach (var prefix in NoIndexPrefixes)
{
if (path.Equals(prefix, StringComparison.OrdinalIgnoreCase)
|| path.StartsWith(prefix, StringComparison.OrdinalIgnoreCase))
{
return true;
}
}
return false;
}
private static string GetHomeCrawlableContent() =>
"کارابازار سلامت (KBS) — باشگاه مشتریان و فروشگاه محصولات سلامت. " +
"با عضویت در باشگاه مشتریان KBS از پاداش شفاف، کیف پول جادویی، فروشگاه اعتباری و شبکه فروش بهره‌مند شوید. " +
"ثبت‌نام رایگان در kbs2.ir.";
}