feat(AppVersion): Add AppVersionService for cache invalidation on version update
Build and Deploy to Production / build-and-deploy (push) Successful in 1m37s

- Add AppVersionService to check version and clear cache if needed
- Update Configuration.Protobuf package to 0.0.4 with AppVersion proto
- Integrate version check in App.razor on app initialization
This commit is contained in:
masoodafar-web
2025-12-25 18:45:01 +03:30
parent 45a291b529
commit 516da671c6
4 changed files with 166 additions and 1 deletions
+5
View File
@@ -11,10 +11,15 @@ public partial class App
{ {
[Inject] private ILocalStorageService LocalStorage { get; set; } = default!; [Inject] private ILocalStorageService LocalStorage { get; set; } = default!;
[Inject] private AuthService AuthService { get; set; } = default!; [Inject] private AuthService AuthService { get; set; } = default!;
[Inject] private AppVersionService AppVersionService { get; set; } = default!;
protected override async Task OnInitializedAsync() protected override async Task OnInitializedAsync()
{ {
await base.OnInitializedAsync(); await base.OnInitializedAsync();
// Check app version and clear cache if needed
await AppVersionService.CheckVersionAndClearCacheIfNeededAsync();
// Check for referral code in URL query parameters // Check for referral code in URL query parameters
var uri = Navigation.ToAbsoluteUri(Navigation.Uri); var uri = Navigation.ToAbsoluteUri(Navigation.Uri);
var query = QueryHelpers.ParseQuery(uri.Query); var query = QueryHelpers.ParseQuery(uri.Query);
@@ -20,6 +20,7 @@ using FrontOffice.BFF.NetworkMembership.Protobuf.Protos.NetworkMembership;
using FrontOffice.BFF.DiscountShop.Protobuf.Protos.DiscountShop; using FrontOffice.BFF.DiscountShop.Protobuf.Protos.DiscountShop;
using FrontOffice.BFF.City.Protobuf; using FrontOffice.BFF.City.Protobuf;
using FrontOffice.BFF.Configuration.Protobuf.Protos.Configuration; using FrontOffice.BFF.Configuration.Protobuf.Protos.Configuration;
using FrontOffice.BFF.Configuration.Protobuf.Protos.AppVersion;
using FrontOffice.Main.Utilities; using FrontOffice.Main.Utilities;
namespace Microsoft.Extensions.DependencyInjection; namespace Microsoft.Extensions.DependencyInjection;
@@ -61,6 +62,8 @@ public static class ConfigureServices
services.AddScoped<ClubConfigurationService>(); services.AddScoped<ClubConfigurationService>();
services.AddScoped<NetworkMembershipService>(); services.AddScoped<NetworkMembershipService>();
services.AddScoped<CommissionService>(); services.AddScoped<CommissionService>();
// App Version Service for cache invalidation
services.AddScoped<AppVersionService>();
// Device detection: very light, dependency-free // Device detection: very light, dependency-free
services.AddTransient<IDeviceDetector, DeviceDetector>(); services.AddTransient<IDeviceDetector, DeviceDetector>();
// PDF generation (Chromium only) // PDF generation (Chromium only)
@@ -110,6 +113,7 @@ public static class ConfigureServices
services.AddScoped(CreateAuthenticatedClient<NetworkMembershipContract.NetworkMembershipContractClient>); services.AddScoped(CreateAuthenticatedClient<NetworkMembershipContract.NetworkMembershipContractClient>);
services.AddScoped(CreateAuthenticatedClient<DiscountShopContract.DiscountShopContractClient>); services.AddScoped(CreateAuthenticatedClient<DiscountShopContract.DiscountShopContractClient>);
services.AddScoped(CreateAuthenticatedClient<CityContract.CityContractClient>); services.AddScoped(CreateAuthenticatedClient<CityContract.CityContractClient>);
services.AddScoped(CreateAuthenticatedClient<AppVersionContract.AppVersionContractClient>);
return services; return services;
} }
+1 -1
View File
@@ -13,7 +13,7 @@
<PackageReference Include="Foursat.FrontOffice.BFF.City.Protobuf" Version="0.0.2" /> <PackageReference Include="Foursat.FrontOffice.BFF.City.Protobuf" Version="0.0.2" />
<PackageReference Include="Foursat.FrontOffice.BFF.ClubMembership.Protobuf" Version="0.0.4" /> <PackageReference Include="Foursat.FrontOffice.BFF.ClubMembership.Protobuf" Version="0.0.4" />
<PackageReference Include="Foursat.FrontOffice.BFF.Commission.Protobuf" Version="0.0.3" /> <PackageReference Include="Foursat.FrontOffice.BFF.Commission.Protobuf" Version="0.0.3" />
<PackageReference Include="Foursat.FrontOffice.BFF.Configuration.Protobuf" Version="0.0.3" /> <PackageReference Include="Foursat.FrontOffice.BFF.Configuration.Protobuf" Version="0.0.4" />
<!-- <PackageReference Include="Foursat.FrontOffice.BFF.ClubMembership.Protobuf" Version="0.0.3" /> --> <!-- <PackageReference Include="Foursat.FrontOffice.BFF.ClubMembership.Protobuf" Version="0.0.3" /> -->
<!-- <PackageReference Include="Foursat.FrontOffice.BFF.Commission.Protobuf" Version="0.0.2" /> --> <!-- <PackageReference Include="Foursat.FrontOffice.BFF.Commission.Protobuf" Version="0.0.2" /> -->
<PackageReference Include="Foursat.FrontOffice.BFF.DiscountShop.Protobuf" Version="0.0.3" /> <PackageReference Include="Foursat.FrontOffice.BFF.DiscountShop.Protobuf" Version="0.0.3" />
@@ -0,0 +1,156 @@
using FrontOffice.BFF.Configuration.Protobuf.Protos.AppVersion;
using Blazored.LocalStorage;
namespace FrontOffice.Main.Utilities;
/// <summary>
/// سرویس مدیریت نسخه اپلیکیشن و کش
/// وقتی نسخه جدید منتشر بشه، این سرویس متوجه میشه و کش رو پاک می‌کنه
/// </summary>
public class AppVersionService
{
private readonly AppVersionContract.AppVersionContractClient _client;
private readonly ILocalStorageService _localStorage;
private readonly ILogger<AppVersionService> _logger;
private const string APP_NAME = "FrontOffice";
private const string LOCAL_VERSION_KEY = "app_version";
private const string LAST_CHECK_KEY = "app_version_last_check";
public AppVersionService(
AppVersionContract.AppVersionContractClient client,
ILocalStorageService localStorage,
ILogger<AppVersionService> logger)
{
_client = client;
_localStorage = localStorage;
_logger = logger;
}
/// <summary>
/// بررسی نسخه اپلیکیشن و پاک کردن کش در صورت نیاز
/// </summary>
public async Task CheckVersionAndClearCacheIfNeededAsync()
{
try
{
// دریافت نسخه فعلی از localStorage
var localVersion = await _localStorage.GetItemAsStringAsync(LOCAL_VERSION_KEY);
// بررسی نسخه از سرور
var response = await _client.GetAppVersionAsync(new GetAppVersionRequest
{
AppName = APP_NAME,
CurrentClientVersion = localVersion
});
if (!response.Found)
{
_logger.LogWarning("App version not found on server for {AppName}", APP_NAME);
return;
}
var serverVersion = response.CurrentVersion;
// اگر نسخه جدید باشه یا پاک کردن کش لازم باشه
if (localVersion != serverVersion || response.RequiresFullCacheClear)
{
_logger.LogInformation(
"New version detected: {OldVersion} -> {NewVersion}, CacheClear: {CacheClear}",
localVersion ?? "null", serverVersion, response.RequiresFullCacheClear);
// پاک کردن کش
await ClearAllCacheAsync();
// ذخیره نسخه جدید
await _localStorage.SetItemAsStringAsync(LOCAL_VERSION_KEY, serverVersion);
await _localStorage.SetItemAsStringAsync(LAST_CHECK_KEY, DateTime.UtcNow.ToString("O"));
_logger.LogInformation("Cache cleared and version updated to {Version}", serverVersion);
}
}
catch (Exception ex)
{
_logger.LogError(ex, "Error checking app version");
}
}
/// <summary>
/// پاک کردن همه کش‌های اپلیکیشن
/// </summary>
private async Task ClearAllCacheAsync()
{
try
{
// لیست کلیدهایی که باید حفظ بشن (مثل توکن و اطلاعات کاربر)
var keysToKeep = new HashSet<string>
{
"access_token",
"refresh_token",
"user_info",
"user_roles"
};
// دریافت همه کلیدها
var allKeys = await _localStorage.KeysAsync();
// پاک کردن کلیدهایی که در لیست حفظ نیستن
foreach (var key in allKeys)
{
if (!keysToKeep.Contains(key) && key != LOCAL_VERSION_KEY)
{
await _localStorage.RemoveItemAsync(key);
}
}
_logger.LogInformation("Local storage cache cleared, {Count} keys removed",
allKeys.Count() - keysToKeep.Count);
}
catch (Exception ex)
{
_logger.LogError(ex, "Error clearing cache");
}
}
/// <summary>
/// دریافت اطلاعات نسخه فعلی
/// </summary>
public async Task<AppVersionInfo?> GetCurrentVersionInfoAsync()
{
try
{
var response = await _client.GetAppVersionAsync(new GetAppVersionRequest
{
AppName = APP_NAME
});
if (!response.Found)
return null;
return new AppVersionInfo
{
CurrentVersion = response.CurrentVersion,
MinRequiredVersion = response.MinRequiredVersion,
RequiresUpdate = response.RequiresUpdate,
UpdateMessage = response.UpdateMessage,
ReleaseNotes = response.ReleaseNotes,
LastUpdated = response.LastUpdated?.ToDateTime()
};
}
catch (Exception ex)
{
_logger.LogError(ex, "Error getting version info");
return null;
}
}
}
public class AppVersionInfo
{
public string CurrentVersion { get; set; } = string.Empty;
public string MinRequiredVersion { get; set; } = string.Empty;
public bool RequiresUpdate { get; set; }
public string? UpdateMessage { get; set; }
public string? ReleaseNotes { get; set; }
public DateTime? LastUpdated { get; set; }
}