Merge branch 'kub-stage' into production
This commit is contained in:
@@ -138,7 +138,11 @@
|
||||
|
||||
<!-- CMS Protobuf NuGet package (used for both local dev and Docker builds) -->
|
||||
<ItemGroup>
|
||||
<<<<<<< HEAD
|
||||
<PackageReference Include="Foursat.CMSMicroservice.Protobuf" Version="0.0.191" />
|
||||
=======
|
||||
<PackageReference Include="Foursat.CMSMicroservice.Protobuf" Version="0.0.192" />
|
||||
>>>>>>> kub-stage
|
||||
</ItemGroup>
|
||||
|
||||
<!-- ============================================================ -->
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using BackOffice.Common.Utilities;
|
||||
using System.IdentityModel.Tokens.Jwt;
|
||||
using BackOffice.Common.Utilities;
|
||||
using Blazored.LocalStorage;
|
||||
|
||||
namespace BackOffice.Common.Utilities;
|
||||
@@ -19,9 +20,41 @@ public class AppTokenProvider : ITokenProvider
|
||||
{
|
||||
var authorizationToken = await _localStorage.GetItemAsync<string>(GlobalConstants.JwtTokenKey);
|
||||
if (!string.IsNullOrEmpty(authorizationToken))
|
||||
_token = authorizationToken.ToString().Replace("Bearer ", "");
|
||||
{
|
||||
var raw = authorizationToken.ToString().Replace("Bearer ", "");
|
||||
// Don't send expired tokens — they cause 401 on anonymous endpoints like login
|
||||
if (IsTokenExpired(raw))
|
||||
{
|
||||
await _localStorage.RemoveItemAsync(GlobalConstants.JwtTokenKey);
|
||||
_token = null;
|
||||
return _token;
|
||||
}
|
||||
_token = raw;
|
||||
}
|
||||
}
|
||||
else if (IsTokenExpired(_token))
|
||||
{
|
||||
// Cached token has expired since last check
|
||||
await _localStorage.RemoveItemAsync(GlobalConstants.JwtTokenKey);
|
||||
_token = null;
|
||||
}
|
||||
|
||||
return _token;
|
||||
}
|
||||
|
||||
private static bool IsTokenExpired(string token)
|
||||
{
|
||||
try
|
||||
{
|
||||
var handler = new JwtSecurityTokenHandler();
|
||||
var jwt = handler.ReadJwtToken(token);
|
||||
// Add 30-second buffer to avoid edge-case race
|
||||
return jwt.ValidTo < DateTime.UtcNow.AddSeconds(-30);
|
||||
}
|
||||
catch
|
||||
{
|
||||
// Malformed token — treat as expired
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -25,14 +25,14 @@ RUN rm -rf /usr/share/nginx/html/*
|
||||
# Copy published wwwroot (Blazor WASM output)
|
||||
COPY --from=build /app/publish/wwwroot .
|
||||
|
||||
# Configure nginx for SPA routing
|
||||
RUN echo 'server { \
|
||||
listen 80; \
|
||||
server_name _; \
|
||||
location / { \
|
||||
root /usr/share/nginx/html; \
|
||||
try_files $uri $uri/ /index.html; \
|
||||
} \
|
||||
}' > /etc/nginx/conf.d/default.conf
|
||||
# Copy entrypoint script that sets blazor-environment header
|
||||
COPY ["BackOffice/docker-entrypoint.sh", "/docker-entrypoint.sh"]
|
||||
RUN chmod +x /docker-entrypoint.sh
|
||||
|
||||
# Default Blazor environment (override via K8s env var BLAZOR_ENVIRONMENT)
|
||||
ENV BLAZOR_ENVIRONMENT=Staging
|
||||
# Default gateway URL for gRPC calls (injected into appsettings at startup)
|
||||
ENV GW_URL=https://cms.se.kbs1.ir
|
||||
|
||||
EXPOSE 80
|
||||
CMD ["/docker-entrypoint.sh"]
|
||||
|
||||
@@ -2,8 +2,11 @@
|
||||
<configuration>
|
||||
<packageSources>
|
||||
<clear />
|
||||
<<<<<<< HEAD
|
||||
<!-- Local packages (dev builds) -->
|
||||
<!-- <add key="Local" value="/home/masoud/Apps/project/FourSat/nupkg" /> -->
|
||||
=======
|
||||
>>>>>>> kub-stage
|
||||
<!-- Nexus (hosts FourSat packages + proxies nuget.org) -->
|
||||
<add key="Nexus" value="http://194.5.195.53:32081/repository/nuget-all/index.json" allowInsecureConnections="true" />
|
||||
</packageSources>
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
@using CMSMicroservice.Protobuf.Protos.ManualPayment
|
||||
@using CMSMicroservice.Protobuf.Protos.Package
|
||||
@using BackOffice.Pages.Payment.Components
|
||||
@using BackOffice.Pages.AutoComplete
|
||||
@using Microsoft.AspNetCore.Components.Forms
|
||||
@@ -13,15 +14,28 @@
|
||||
<UserAutoComplete Label="انتخاب کاربر"
|
||||
@bind-SelectedUserId="_selectedUserId" />
|
||||
|
||||
<MudNumericField T="long"
|
||||
Label="مبلغ"
|
||||
Variant="Variant.Outlined"
|
||||
Adornment="Adornment.End"
|
||||
AdornmentText="تومان"
|
||||
Required="true"
|
||||
RequiredError="مبلغ الزامی است"
|
||||
Min="1"
|
||||
@bind-Value="_createModel.Amount" />
|
||||
<MudSelect T="long?"
|
||||
Label="انتخاب پکیج"
|
||||
Value="@_selectedPackageId"
|
||||
ValueChanged="OnPackageChanged"
|
||||
Variant="Variant.Outlined"
|
||||
Margin="Margin.Dense"
|
||||
Required="true"
|
||||
RequiredError="انتخاب پکیج الزامی است">
|
||||
@foreach (var pkg in _packages)
|
||||
{
|
||||
<MudSelectItem T="long?" Value="@((long?)pkg.Id)">
|
||||
@pkg.Title — @pkg.Price.ToString("N0") تومان
|
||||
</MudSelectItem>
|
||||
}
|
||||
</MudSelect>
|
||||
|
||||
@if (_selectedPackagePrice.HasValue)
|
||||
{
|
||||
<MudAlert Severity="Severity.Info" Dense="true" Variant="Variant.Text">
|
||||
مبلغ پکیج: <strong>@_selectedPackagePrice.Value.ToString("N0") تومان</strong> — این مبلغ به کیفپول کاربر اضافه میشود.
|
||||
</MudAlert>
|
||||
}
|
||||
|
||||
<MudSelect T="int"
|
||||
Label="نوع پرداخت"
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using CMSMicroservice.Protobuf.Protos.ManualPayment;
|
||||
using CMSMicroservice.Protobuf.Protos.Package;
|
||||
using Google.Protobuf;
|
||||
using Microsoft.AspNetCore.Components;
|
||||
using Microsoft.AspNetCore.Components.Forms;
|
||||
@@ -24,19 +25,23 @@ public partial class ManualPaymentDialog
|
||||
{
|
||||
[CascadingParameter] IMudDialogInstance MudDialog { get; set; } = default!;
|
||||
[Inject] public ManualPaymentContract.ManualPaymentContractClient ManualPaymentClient { get; set; } = default!;
|
||||
[Inject] public PackageContract.PackageContractClient PackageClient { get; set; } = default!;
|
||||
// Snackbar is injected via _Imports.razor
|
||||
|
||||
[Parameter] public ManualPaymentDialogMode Mode { get; set; }
|
||||
[Parameter] public ManualPaymentModel? Model { get; set; }
|
||||
|
||||
private ManualPaymentModel _createModel = new(){
|
||||
Amount = 0,
|
||||
};
|
||||
private ManualPaymentModel _createModel = new(){ Amount = 0 };
|
||||
private string? _adminNote;
|
||||
|
||||
// User selection
|
||||
private long? _selectedUserId;
|
||||
|
||||
// Package selection
|
||||
private long? _selectedPackageId;
|
||||
private long? _selectedPackagePrice;
|
||||
private List<GetAllPackageByFilterResponseModel> _packages = new();
|
||||
|
||||
// Type and Description
|
||||
private int _selectedType = 1; // Default: CashDeposit
|
||||
private string _description = "عضویت دستی باشگاه مشتریان";
|
||||
@@ -48,6 +53,46 @@ public partial class ManualPaymentDialog
|
||||
|
||||
private bool _isSubmitting;
|
||||
|
||||
protected override async Task OnInitializedAsync()
|
||||
{
|
||||
await LoadPackages();
|
||||
}
|
||||
|
||||
private async Task LoadPackages()
|
||||
{
|
||||
try
|
||||
{
|
||||
var request = new GetAllPackageByFilterRequest
|
||||
{
|
||||
PaginationState = new() { PageNumber = 1, PageSize = 100 },
|
||||
Filter = new()
|
||||
};
|
||||
|
||||
var result = await PackageClient.GetAllPackageByFilterAsync(request);
|
||||
_packages = result.Models?.OrderBy(p => p.SortOrder).ToList()
|
||||
?? new List<GetAllPackageByFilterResponseModel>();
|
||||
|
||||
// اگر پکیج پایه وجود دارد، پیشفرض انتخاب کن
|
||||
var basePackage = _packages.FirstOrDefault(p => p.IsBasePackage);
|
||||
if (basePackage != null)
|
||||
{
|
||||
_selectedPackageId = basePackage.Id;
|
||||
_selectedPackagePrice = basePackage.Price;
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
_packages = new List<GetAllPackageByFilterResponseModel>();
|
||||
}
|
||||
}
|
||||
|
||||
private void OnPackageChanged(long? packageId)
|
||||
{
|
||||
_selectedPackageId = packageId;
|
||||
var pkg = _packages.FirstOrDefault(p => p.Id == packageId);
|
||||
_selectedPackagePrice = pkg?.Price;
|
||||
}
|
||||
|
||||
private async Task OnImageFileSelected(IBrowserFile? file)
|
||||
{
|
||||
if (file == null)
|
||||
@@ -81,6 +126,12 @@ public partial class ManualPaymentDialog
|
||||
return;
|
||||
}
|
||||
|
||||
if (!_selectedPackageId.HasValue || _selectedPackageId.Value <= 0)
|
||||
{
|
||||
Snackbar.Add("لطفاً پکیج را انتخاب کنید.", Severity.Warning);
|
||||
return;
|
||||
}
|
||||
|
||||
if (_selectedType <= 0)
|
||||
{
|
||||
Snackbar.Add("لطفاً نوع پرداخت را انتخاب کنید.", Severity.Warning);
|
||||
@@ -101,7 +152,8 @@ public partial class ManualPaymentDialog
|
||||
var request = new CreateManualPaymentRequest
|
||||
{
|
||||
UserId = _selectedUserId.Value,
|
||||
Amount = _createModel.Amount,
|
||||
Amount = 0, // deprecated — مبلغ از پکیج خوانده میشود
|
||||
PackageId = _selectedPackageId.Value,
|
||||
Type = (ManualPaymentType)_selectedType,
|
||||
Description = _description
|
||||
};
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
#!/bin/sh
|
||||
# Generate nginx config with blazor-environment header
|
||||
# BLAZOR_ENVIRONMENT defaults to "Staging" if not set
|
||||
BLAZOR_ENV="${BLAZOR_ENVIRONMENT:-Staging}"
|
||||
|
||||
cat > /etc/nginx/conf.d/default.conf <<NGINX_CONF
|
||||
server {
|
||||
listen 80;
|
||||
server_name _;
|
||||
location / {
|
||||
root /usr/share/nginx/html;
|
||||
try_files \$uri \$uri/ /index.html;
|
||||
add_header blazor-environment $BLAZOR_ENV;
|
||||
}
|
||||
}
|
||||
NGINX_CONF
|
||||
|
||||
# Inject GW_URL into all appsettings JSON files
|
||||
# This overrides GwUrl regardless of which environment Blazor detects,
|
||||
# which is critical because the service worker cache strips the
|
||||
# blazor-environment header and Blazor falls back to Production.
|
||||
WWWROOT="/usr/share/nginx/html"
|
||||
if [ -n "$GW_URL" ]; then
|
||||
echo "[entrypoint] Setting GwUrl to $GW_URL in all appsettings files"
|
||||
for f in "$WWWROOT"/appsettings*.json; do
|
||||
[ -f "$f" ] && sed -i "s|\"GwUrl\":\s*\"[^\"]*\"|\"GwUrl\": \"$GW_URL\"|g" "$f"
|
||||
done
|
||||
fi
|
||||
|
||||
exec nginx -g 'daemon off;'
|
||||
@@ -9,7 +9,7 @@ self.addEventListener('fetch', event => event.respondWith(onFetch(event)));
|
||||
const cacheNamePrefix = 'offline-cache-';
|
||||
const cacheName = `${cacheNamePrefix}${self.assetsManifest.version}`;
|
||||
const offlineAssetsInclude = [ /\.dll$/, /\.pdb$/, /\.wasm/, /\.html/, /\.js$/, /\.json$/, /\.css$/, /\.woff$/, /\.png$/, /\.jpe?g$/, /\.gif$/, /\.ico$/, /\.blat$/, /\.dat$/ ];
|
||||
const offlineAssetsExclude = [ /^service-worker\.js$/ ];
|
||||
const offlineAssetsExclude = [ /^service-worker\.js$/, /^appsettings.*\.json$/ ];
|
||||
|
||||
async function onInstall(event) {
|
||||
console.info('Service worker: Install');
|
||||
|
||||
Reference in New Issue
Block a user