Compare commits

..

14 Commits

Author SHA1 Message Date
masoodafar-web 4c5cff1a9a fix: update CMSMSAddress to remove port from URL
Build and Deploy / build (push) Successful in 1m44s
2025-12-26 07:34:21 +03:30
masoodafar-web 6bc45e4486 feat(bff): Add App Version management endpoints
Build and Deploy to Production / build-and-deploy (push) Successful in 1m47s
- Add appversion.proto with GetAll, Get, Update RPCs
- Add GetAllAppVersionsQuery and handler
- Add UpdateAppVersionCommand and handler
- Add AppVersionService gRPC service
- Add AppVersionProfile for Mapster mappings
- Update IApplicationContractContext with AppVersions client
- Bump Configuration.Protobuf to 1.0.20
2025-12-26 06:07:24 +03:30
masoodafar-web 8f34f0f650 fix: update CMSMSAddress to use secure URL
Build and Deploy to Production / build-and-deploy (push) Successful in 2m17s
2025-12-25 02:22:45 +03:30
masoodafar-web 554d1a65f2 feat: add production appsettings configuration for JWT and Seq
Build and Deploy to Production / build-and-deploy (push) Successful in 2m31s
2025-12-25 02:07:19 +03:30
admin f695290577 fix: correct dockerfile path and registry
Build and Deploy to Production / build-and-deploy (push) Successful in 1m45s
2025-12-23 22:32:25 +00:00
admin 13352879b9 fix: correct dockerfile path and registry
Build and Deploy to Production / build-and-deploy (push) Has been cancelled
2025-12-23 22:32:19 +00:00
admin 464b3e0c1d ci: re-trigger production build
Build and Deploy to Production / build-and-deploy (push) Failing after 17s
2025-12-23 22:31:03 +00:00
admin e627220c98 feat: add production deployment workflow
Build and Deploy to Production / build-and-deploy (push) Failing after 12s
2025-12-23 22:19:23 +00:00
admin 5f7f2a3355 fix: update appsettings for production K8s 2025-12-23 22:16:11 +00:00
admin b8b8a72e57 fix: update appsettings for production K8s 2025-12-23 22:15:51 +00:00
admin b223e37782 fix: update appsettings for production K8s 2025-12-23 22:15:31 +00:00
masoodafar-web ea3577893b fix: update Seq ServerUrl to use HTTPS 2025-12-20 05:19:59 +03:30
masoodafar-web 480d87cec6 fix: update CMSMSAddress and Seq ServerUrl to use secure URLs 2025-12-20 05:18:06 +03:30
masoodafar-web b24191d59f fix: update deployment branch to stage-new in workflow configuration 2025-12-20 05:09:47 +03:30
15 changed files with 425 additions and 6 deletions
+1 -1
View File
@@ -4,7 +4,7 @@ name: Push nuget and docker image Actions Workflow
on:
push:
branches:
- stage
- stage-new
jobs:
Deploy:
runs-on: windows
+83
View File
@@ -0,0 +1,83 @@
name: Build and Deploy to Production
on:
push:
branches:
- production
env:
REGISTRY: gitea-svc:3000
EXTERNAL_REGISTRY: git.foursat.afrino.co
IMAGE_NAME: admin/backoffice-bff
jobs:
build-and-deploy:
runs-on: ubuntu-latest
container:
image: docker:latest
options: --privileged
env:
HTTP_PROXY: http://proxyuser:87zH26nbqT2@46.249.98.211:3128
HTTPS_PROXY: http://proxyuser:87zH26nbqT2@46.249.98.211:3128
NO_PROXY: localhost,127.0.0.1,gitea-svc,45.149.79.127,194.5.195.53,10.0.0.0/8
steps:
- name: Install dependencies
run: |
apk add --no-cache git curl
curl -LO "https://dl.k8s.io/release/$(curl -L -s https://dl.k8s.io/release/stable.txt)/bin/linux/amd64/kubectl"
chmod +x kubectl
mv kubectl /usr/local/bin/
- name: Start Docker daemon
run: |
mkdir -p /etc/docker
cat > /etc/docker/daemon.json << 'DAEMON'
{
"insecure-registries": ["git.foursat.afrino.co", "gitea-svc:3000"]
}
DAEMON
mkdir -p ~/.docker
cat > ~/.docker/config.json << 'CONF'
{
"proxies": {
"default": {
"httpProxy": "http://proxyuser:87zH26nbqT2@46.249.98.211:3128",
"httpsProxy": "http://proxyuser:87zH26nbqT2@46.249.98.211:3128",
"noProxy": "localhost,127.0.0.1,gitea-svc,194.5.195.53,10.0.0.0/8"
}
}
}
CONF
dockerd &
for i in $(seq 1 30); do docker info >/dev/null 2>&1 && break || sleep 2; done
- name: Checkout code
run: |
git clone --depth 1 --branch production http://gitea-svc:3000/admin/BackOffice.BFF.git .
git log -1 --format="%H %s"
- name: Build Docker Image
run: |
cd src
docker build -f BackOffice.BFF.WebApi/Dockerfile \
-t ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ github.sha }} \
-t ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:prod \
-t ${{ env.EXTERNAL_REGISTRY }}/${{ env.IMAGE_NAME }}:prod \
--build-arg HTTP_PROXY=http://proxyuser:87zH26nbqT2@46.249.98.211:3128 \
--build-arg HTTPS_PROXY=http://proxyuser:87zH26nbqT2@46.249.98.211:3128 \
.
- name: Push to Registry
run: |
echo "87zH26nbqT" | docker login ${{ env.REGISTRY }} -u admin --password-stdin
docker push ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ github.sha }}
docker push ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:prod
echo "87zH26nbqT" | docker login ${{ env.EXTERNAL_REGISTRY }} -u admin --password-stdin
docker push ${{ env.EXTERNAL_REGISTRY }}/${{ env.IMAGE_NAME }}:prod
- name: Deploy to Production
run: |
mkdir -p ~/.kube
echo "${{ secrets.KUBECONFIG_PROD }}" | base64 -d > ~/.kube/config
kubectl rollout restart deployment/backoffice-bff || echo "Deployment not found"
kubectl rollout status deployment/backoffice-bff --timeout=5m || echo "Rollout pending"
@@ -23,6 +23,7 @@ using CMSMicroservice.Protobuf.Protos.Configuration;
using CMSMicroservice.Protobuf.Protos.DiscountOrder;
using CMSMicroservice.Protobuf.Protos.ManualPayment;
using CMSMicroservice.Protobuf.Protos.NetworkMembership;
using CMSMicroservice.Protobuf.Protos.AppVersion;
namespace BackOffice.BFF.Application.Common.Interfaces;
@@ -65,6 +66,9 @@ public interface IApplicationContractContext
// Manual Payments (Admin) - BackOffice BFF gRPC
ManualPaymentContract.ManualPaymentContractClient ManualPayments { get; }
// App Version Management
AppVersionContract.AppVersionContractClient AppVersions { get; }
#endregion
}
@@ -0,0 +1,39 @@
namespace BackOffice.BFF.Application.ConfigurationCQ.Commands.UpdateAppVersion;
public class UpdateAppVersionCommand : IRequest<Unit>
{
/// <summary>
/// نام اپلیکیشن (FrontOffice, BackOffice, Mobile)
/// </summary>
public string AppName { get; set; } = string.Empty;
/// <summary>
/// ورژن فعلی
/// </summary>
public string CurrentVersion { get; set; } = string.Empty;
/// <summary>
/// حداقل ورژن مورد نیاز
/// </summary>
public string? MinRequiredVersion { get; set; }
/// <summary>
/// آیا نیاز به پاک کردن کامل کش دارد؟
/// </summary>
public bool RequiresFullCacheClear { get; set; }
/// <summary>
/// پیام آپدیت برای کاربران
/// </summary>
public string? UpdateMessage { get; set; }
/// <summary>
/// یادداشت‌های نسخه
/// </summary>
public string? ReleaseNotes { get; set; }
/// <summary>
/// دلیل آپدیت (برای لاگ ادمین)
/// </summary>
public string? UpdateReason { get; set; }
}
@@ -0,0 +1,39 @@
using CMSMicroservice.Protobuf.Protos.AppVersion;
namespace BackOffice.BFF.Application.ConfigurationCQ.Commands.UpdateAppVersion;
public class UpdateAppVersionCommandHandler : IRequestHandler<UpdateAppVersionCommand, Unit>
{
private readonly IApplicationContractContext _context;
public UpdateAppVersionCommandHandler(IApplicationContractContext context)
{
_context = context;
}
public async Task<Unit> Handle(UpdateAppVersionCommand request, CancellationToken cancellationToken)
{
var cmsRequest = new UpdateAppVersionRequest
{
AppName = request.AppName,
CurrentVersion = request.CurrentVersion,
RequiresFullCacheClear = request.RequiresFullCacheClear
};
if (!string.IsNullOrEmpty(request.MinRequiredVersion))
cmsRequest.MinRequiredVersion = request.MinRequiredVersion;
if (!string.IsNullOrEmpty(request.UpdateMessage))
cmsRequest.UpdateMessage = request.UpdateMessage;
if (!string.IsNullOrEmpty(request.ReleaseNotes))
cmsRequest.ReleaseNotes = request.ReleaseNotes;
if (!string.IsNullOrEmpty(request.UpdateReason))
cmsRequest.UpdateReason = request.UpdateReason;
await _context.AppVersions.UpdateAppVersionAsync(cmsRequest, cancellationToken: cancellationToken);
return Unit.Value;
}
}
@@ -0,0 +1,6 @@
namespace BackOffice.BFF.Application.ConfigurationCQ.Queries.GetAllAppVersions;
public class GetAllAppVersionsQuery : IRequest<GetAllAppVersionsResponseDto>
{
public bool IncludeInactive { get; set; }
}
@@ -0,0 +1,40 @@
using CMSMicroservice.Protobuf.Protos.AppVersion;
namespace BackOffice.BFF.Application.ConfigurationCQ.Queries.GetAllAppVersions;
public class GetAllAppVersionsQueryHandler : IRequestHandler<GetAllAppVersionsQuery, GetAllAppVersionsResponseDto>
{
private readonly IApplicationContractContext _context;
public GetAllAppVersionsQueryHandler(IApplicationContractContext context)
{
_context = context;
}
public async Task<GetAllAppVersionsResponseDto> Handle(GetAllAppVersionsQuery request, CancellationToken cancellationToken)
{
var cmsRequest = new GetAllAppVersionsRequest
{
IncludeInactive = request.IncludeInactive
};
var response = await _context.AppVersions.GetAllAppVersionsAsync(cmsRequest, cancellationToken: cancellationToken);
return new GetAllAppVersionsResponseDto
{
Items = response.Items.Select(item => new AppVersionItemDto
{
Id = item.Id,
AppName = item.AppName,
CurrentVersion = item.CurrentVersion,
MinRequiredVersion = item.MinRequiredVersion,
RequiresFullCacheClear = item.RequiresFullCacheClear,
UpdateMessage = item.UpdateMessage,
ReleaseNotes = item.ReleaseNotes,
IsActive = item.IsActive,
Created = item.Created?.ToDateTimeOffset() ?? DateTimeOffset.MinValue,
LastModified = item.LastModified?.ToDateTimeOffset() ?? DateTimeOffset.MinValue
}).ToList()
};
}
}
@@ -0,0 +1,20 @@
namespace BackOffice.BFF.Application.ConfigurationCQ.Queries.GetAllAppVersions;
public class GetAllAppVersionsResponseDto
{
public List<AppVersionItemDto> Items { get; set; } = new();
}
public class AppVersionItemDto
{
public long Id { get; set; }
public string AppName { get; set; } = string.Empty;
public string CurrentVersion { get; set; } = string.Empty;
public string MinRequiredVersion { get; set; } = string.Empty;
public bool RequiresFullCacheClear { get; set; }
public string? UpdateMessage { get; set; }
public string? ReleaseNotes { get; set; }
public bool IsActive { get; set; }
public DateTimeOffset Created { get; set; }
public DateTimeOffset LastModified { get; set; }
}
@@ -7,7 +7,7 @@
<ItemGroup>
<PackageReference Include="Afrino.FMSMicroservice.Protobuf" Version="0.0.122" />
<PackageReference Include="Foursat.CMSMicroservice.Protobuf" Version="0.0.156" />
<PackageReference Include="Foursat.CMSMicroservice.Protobuf" Version="0.0.159" />
<PackageReference Include="Google.Protobuf" Version="3.23.3" />
<PackageReference Include="Grpc.Net.ClientFactory" Version="2.54.0" />
@@ -24,6 +24,7 @@ using CMSMicroservice.Protobuf.Protos.Configuration;
using CMSMicroservice.Protobuf.Protos.DiscountOrder;
using CMSMicroservice.Protobuf.Protos.ManualPayment;
using CMSMicroservice.Protobuf.Protos.NetworkMembership;
using CMSMicroservice.Protobuf.Protos.AppVersion;
// BFF Protobuf contracts
@@ -93,5 +94,8 @@ public class ApplicationContractContext : IApplicationContractContext
// Manual Payments (Admin)
public ManualPaymentContract.ManualPaymentContractClient ManualPayments => GetService<ManualPaymentContract.ManualPaymentContractClient>();
// App Version Management
public AppVersionContract.AppVersionContractClient AppVersions => GetService<AppVersionContract.AppVersionContractClient>();
#endregion
}
@@ -0,0 +1,42 @@
using BackOffice.BFF.Application.ConfigurationCQ.Commands.UpdateAppVersion;
using BackOffice.BFF.Application.ConfigurationCQ.Queries.GetAllAppVersions;
using Google.Protobuf.WellKnownTypes;
using ProtoDto = BackOffice.BFF.Configuration.Protobuf.Protos.AppVersion;
namespace BackOffice.BFF.WebApi.Common.Mappings;
public class AppVersionProfile : IRegister
{
void IRegister.Register(TypeAdapterConfig config)
{
// Request -> Query mappings
config.NewConfig<ProtoDto.GetAllAppVersionsRequest, GetAllAppVersionsQuery>()
.Map(dest => dest.IncludeInactive, src => src.IncludeInactive);
// Request -> Command mappings
config.NewConfig<ProtoDto.UpdateAppVersionRequest, UpdateAppVersionCommand>()
.Map(dest => dest.AppName, src => src.AppName)
.Map(dest => dest.CurrentVersion, src => src.CurrentVersion)
.Map(dest => dest.MinRequiredVersion, src => src.MinRequiredVersion)
.Map(dest => dest.RequiresFullCacheClear, src => src.RequiresFullCacheClear)
.Map(dest => dest.UpdateMessage, src => src.UpdateMessage)
.Map(dest => dest.ReleaseNotes, src => src.ReleaseNotes)
.Map(dest => dest.UpdateReason, src => src.UpdateReason);
// Response mappings
config.NewConfig<GetAllAppVersionsResponseDto, ProtoDto.GetAllAppVersionsResponse>()
.Map(dest => dest.Items, src => src.Items);
config.NewConfig<AppVersionItemDto, ProtoDto.AppVersionItem>()
.Map(dest => dest.Id, src => src.Id)
.Map(dest => dest.AppName, src => src.AppName)
.Map(dest => dest.CurrentVersion, src => src.CurrentVersion)
.Map(dest => dest.MinRequiredVersion, src => src.MinRequiredVersion)
.Map(dest => dest.RequiresFullCacheClear, src => src.RequiresFullCacheClear)
.Map(dest => dest.UpdateMessage, src => src.UpdateMessage ?? string.Empty)
.Map(dest => dest.ReleaseNotes, src => src.ReleaseNotes ?? string.Empty)
.Map(dest => dest.IsActive, src => src.IsActive)
.Map(dest => dest.Created, src => Timestamp.FromDateTimeOffset(src.Created))
.Map(dest => dest.LastModified, src => Timestamp.FromDateTimeOffset(src.LastModified));
}
}
@@ -0,0 +1,55 @@
using BackOffice.BFF.WebApi.Common.Services;
using BackOffice.BFF.Application.ConfigurationCQ.Commands.UpdateAppVersion;
using BackOffice.BFF.Application.ConfigurationCQ.Queries.GetAllAppVersions;
using BackOffice.BFF.Configuration.Protobuf.Protos.AppVersion;
namespace BackOffice.BFF.WebApi.Services;
public class AppVersionService : AppVersionContract.AppVersionContractBase
{
private readonly IDispatchRequestToCQRS _dispatchRequestToCQRS;
public AppVersionService(IDispatchRequestToCQRS dispatchRequestToCQRS)
{
_dispatchRequestToCQRS = dispatchRequestToCQRS;
}
[RequiresPermission(PermissionNames.SettingsView)]
public override async Task<GetAllAppVersionsResponse> GetAllAppVersions(
GetAllAppVersionsRequest request,
ServerCallContext context)
{
return await _dispatchRequestToCQRS.Handle<GetAllAppVersionsRequest, GetAllAppVersionsQuery, GetAllAppVersionsResponse>(
request,
context);
}
[RequiresPermission(PermissionNames.SettingsView)]
public override async Task<GetAppVersionResponse> GetAppVersion(
GetAppVersionRequest request,
ServerCallContext context)
{
// For single app version, we use GetAll and filter
var allVersionsResponse = await _dispatchRequestToCQRS.Handle<GetAllAppVersionsRequest, GetAllAppVersionsQuery, GetAllAppVersionsResponse>(
new GetAllAppVersionsRequest { IncludeInactive = false },
context);
var item = allVersionsResponse.Items.FirstOrDefault(x => x.AppName == request.AppName);
return new GetAppVersionResponse
{
Found = item != null,
Item = item
};
}
[RequiresPermission(PermissionNames.SettingsManageConfiguration)]
public override async Task<Empty> UpdateAppVersion(
UpdateAppVersionRequest request,
ServerCallContext context)
{
return await _dispatchRequestToCQRS.Handle<UpdateAppVersionRequest, UpdateAppVersionCommand, Empty>(
request,
context);
}
}
+1 -3
View File
@@ -11,9 +11,7 @@
},
"GrpcChannelOptions": {
"FMSMSAddress": "https://dl.afrino.co",
"CMSMSAddress": "http://cms-svc:80"
// "CMSMSAddress": "https://cms.foursat.afrino.co"
// "CMSMSAddress": "https://localhost:32846"
"CMSMSAddress": "http://cms-svc"
},
"Authentication": {
"Authority": "https://ids.domain.com/",
@@ -6,7 +6,7 @@
<Nullable>enable</Nullable>
<IsPackable>true</IsPackable>
<PackageId>Foursat.BackOffice.BFF.Configuration.Protobuf</PackageId>
<Version>1.0.7</Version>
<Version>1.0.20</Version>
<Authors>FourSat</Authors>
<Company>FourSat</Company>
<Product>Foursat.BackOffice.BFF.Configuration.Protobuf</Product>
@@ -25,6 +25,7 @@
<ItemGroup>
<Protobuf Include="Protos\configuration.proto" ProtoRoot="Protos\" GrpcServices="Both" AdditionalImportDirs="..\BackOffice.BFF.Common.Protobuf\Protos"/>
<Protobuf Include="Protos\appversion.proto" ProtoRoot="Protos\" GrpcServices="Both" AdditionalImportDirs="..\BackOffice.BFF.Common.Protobuf\Protos"/>
</ItemGroup>
<ItemGroup>
@@ -0,0 +1,88 @@
syntax = "proto3";
package appversion;
import "google/protobuf/empty.proto";
import "google/protobuf/wrappers.proto";
import "google/protobuf/timestamp.proto";
import "google/api/annotations.proto";
option csharp_namespace = "BackOffice.BFF.Configuration.Protobuf.Protos.AppVersion";
// Service for managing app versions (admin panel)
service AppVersionContract
{
// Get all app versions
rpc GetAllAppVersions(GetAllAppVersionsRequest) returns (GetAllAppVersionsResponse){
option (google.api.http) = {
get: "/AppVersion/GetAll"
};
};
// Get single app version
rpc GetAppVersion(GetAppVersionRequest) returns (GetAppVersionResponse){
option (google.api.http) = {
get: "/AppVersion/Get"
};
};
// Update/Create app version
rpc UpdateAppVersion(UpdateAppVersionRequest) returns (google.protobuf.Empty){
option (google.api.http) = {
post: "/AppVersion/Update"
body: "*"
};
};
}
// Request to get all app versions
message GetAllAppVersionsRequest
{
bool include_inactive = 1;
}
// Response with all app versions
message GetAllAppVersionsResponse
{
repeated AppVersionItem items = 1;
}
// Request to get single app version
message GetAppVersionRequest
{
string app_name = 1;
}
// Response with version info
message GetAppVersionResponse
{
bool found = 1;
AppVersionItem item = 2;
}
// Request to update/create app version
message UpdateAppVersionRequest
{
string app_name = 1;
string current_version = 2;
google.protobuf.StringValue min_required_version = 3;
bool requires_full_cache_clear = 4;
google.protobuf.StringValue update_message = 5;
google.protobuf.StringValue release_notes = 6;
google.protobuf.StringValue update_reason = 7; // For admin logs
}
// App version item
message AppVersionItem
{
int64 id = 1;
string app_name = 2;
string current_version = 3;
string min_required_version = 4;
bool requires_full_cache_clear = 5;
string update_message = 6;
string release_notes = 7;
bool is_active = 8;
google.protobuf.Timestamp created = 9;
google.protobuf.Timestamp last_modified = 10;
}