feat: full FrontOffice updates - discount store, blog, payment gateway, UI improvements
Build and Deploy to Kubernetes / build-and-deploy (push) Failing after 4m55s
Build and Deploy to Kubernetes / build-and-deploy (push) Failing after 4m55s
- Discount store pages (products, cart, orders, order detail) - Blog pages and services - Payment gateway callback page - AppImage component, EmptyState, LoadingState, PageHeader - DiscountCartService, DiscountOrderService, DiscountProductService - BlogCategoryService, BlogPostService, SitePageService, ImageCacheService - PhoneVerifyForm component - Profile Hub page - UI/UX improvements across all pages - landing.js for homepage - Config and routing updates
This commit is contained in:
@@ -0,0 +1,122 @@
|
||||
using System.Collections.Concurrent;
|
||||
using CMSMicroservice.Protobuf.Protos.ImageResolver;
|
||||
|
||||
namespace FrontOffice.Main.Utilities;
|
||||
|
||||
/// <summary>
|
||||
/// سرویس مرکزی resolve تصاویر — تمام تصاویر دینامیک FrontOffice از این سرویس عبور میکنند.
|
||||
/// مسیرهای نسبی از طریق gRPC به CMS ارسال شده و base64 data-URI دریافت میشود.
|
||||
/// نتایج در حافظه کش میشوند تا از فراخوانی مجدد جلوگیری شود.
|
||||
/// </summary>
|
||||
public class ImageCacheService
|
||||
{
|
||||
private readonly ImageResolverContract.ImageResolverContractClient _client;
|
||||
private readonly ConcurrentDictionary<string, string> _cache = new();
|
||||
|
||||
public ImageCacheService(ImageResolverContract.ImageResolverContractClient client)
|
||||
{
|
||||
_client = client;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// تبدیل یک مسیر تصویر به base64 data-URI.
|
||||
/// اگر مسیر خالی باشد → رشته خالی.
|
||||
/// اگر قبلاً data-URI باشد → بدون تغییر.
|
||||
/// اگر در کش باشد → از کش.
|
||||
/// در غیر این صورت → فراخوانی gRPC.
|
||||
/// </summary>
|
||||
public async Task<string> ResolveAsync(string? path)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(path))
|
||||
return string.Empty;
|
||||
|
||||
// اگر قبلاً base64 هست (مثلاً از interceptor آمده)
|
||||
if (path.StartsWith("data:", StringComparison.Ordinal))
|
||||
return path;
|
||||
|
||||
// مسیرهای استاتیک (wwwroot) یا لینکهای خارجی نیاز به resolve ندارند
|
||||
if (path.StartsWith('/') || path.StartsWith("http://", StringComparison.Ordinal)
|
||||
|| path.StartsWith("https://", StringComparison.Ordinal))
|
||||
return path;
|
||||
|
||||
// جستجو در کش
|
||||
if (_cache.TryGetValue(path, out var cached))
|
||||
return cached;
|
||||
|
||||
try
|
||||
{
|
||||
var response = await _client.ResolveImagesAsync(
|
||||
new ResolveImagesRequest { Paths = { path } });
|
||||
|
||||
var result = response?.Images?.FirstOrDefault()?.DataUri ?? string.Empty;
|
||||
_cache.TryAdd(path, result);
|
||||
return result;
|
||||
}
|
||||
catch
|
||||
{
|
||||
// در صورت خطا، رشته خالی برگردان تا UI خراب نشود
|
||||
return string.Empty;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// تبدیل دستهای مسیرهای تصویر به base64 data-URI.
|
||||
/// مسیرهای کششده و data-URIها بدون فراخوانی gRPC برگردانده میشوند.
|
||||
/// </summary>
|
||||
public async Task<Dictionary<string, string>> ResolveBatchAsync(IEnumerable<string?> paths)
|
||||
{
|
||||
var result = new Dictionary<string, string>();
|
||||
var toResolve = new List<string>();
|
||||
|
||||
foreach (var path in paths)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(path))
|
||||
continue;
|
||||
|
||||
if (path.StartsWith("data:", StringComparison.Ordinal)
|
||||
|| path.StartsWith('/')
|
||||
|| path.StartsWith("http://", StringComparison.Ordinal)
|
||||
|| path.StartsWith("https://", StringComparison.Ordinal))
|
||||
{
|
||||
result[path] = path;
|
||||
}
|
||||
else if (_cache.TryGetValue(path, out var cached))
|
||||
{
|
||||
result[path] = cached;
|
||||
}
|
||||
else
|
||||
{
|
||||
toResolve.Add(path);
|
||||
}
|
||||
}
|
||||
|
||||
if (toResolve.Count == 0)
|
||||
return result;
|
||||
|
||||
try
|
||||
{
|
||||
var response = await _client.ResolveImagesAsync(
|
||||
new ResolveImagesRequest { Paths = { toResolve } });
|
||||
|
||||
if (response?.Images != null)
|
||||
{
|
||||
foreach (var img in response.Images)
|
||||
{
|
||||
_cache.TryAdd(img.OriginalPath, img.DataUri);
|
||||
result[img.OriginalPath] = img.DataUri;
|
||||
}
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
// در صورت خطا، مسیرهای resolve نشده خالی بمانند
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// پاکسازی تمام کش (مثلاً هنگام logout)
|
||||
/// </summary>
|
||||
public void ClearCache() => _cache.Clear();
|
||||
}
|
||||
Reference in New Issue
Block a user