feat: Implement file management and authorization features
Build and Deploy to Kubernetes / build-and-deploy (push) Failing after 3m9s

- Add RequiresPermissionAttribute for gRPC method access control.
- Create IFileManagementService interface for file upload and management.
- Implement AddProductImageCommand and handler for adding product images.
- Implement CreateNewProductsCommand and handler for creating new products with image uploads.
- Implement DeleteProductsCommand and handler for deleting products and their associations.
- Implement RemoveProductImageCommand and handler for removing product images from galleries.
- Implement UpdateProductsCommand and handler for updating product details and images.
- Create GetProductGalleryQuery and handler for retrieving product galleries.
- Implement PermissionService for role-based access control using JWT claims.
- Implement FileManagementService for handling file uploads and image optimization.
- Define gRPC service and messages for file management in fms.proto.
- Add FluentValidation for request validation in various commands.
- Create PermissionInterceptor for enforcing permissions on gRPC methods.
This commit is contained in:
masoodafar-web
2026-02-10 22:04:54 +03:30
parent f64b6be7da
commit b42d9e141d
65 changed files with 3082 additions and 2384 deletions
@@ -1,5 +1,6 @@
using CMSMicroservice.Application.Common.Interfaces;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Configuration;
using CMSMicroservice.Domain.Entities;
namespace CMSMicroservice.Application.UserCQ.Commands.AcceptContract;
@@ -7,25 +8,38 @@ public class AcceptContractCommandHandler : IRequestHandler<AcceptContractComman
{
private readonly IApplicationDbContext _context;
private readonly ICurrentUserService _currentUserService;
private readonly IHashService _hashService;
private readonly IGenerateJwtToken _generateJwt;
private readonly IConfiguration _cfg;
public AcceptContractCommandHandler(IApplicationDbContext context, ICurrentUserService currentUserService)
public AcceptContractCommandHandler(IApplicationDbContext context, ICurrentUserService currentUserService,
IHashService hashService, IGenerateJwtToken generateJwt, IConfiguration cfg)
{
_context = context;
_currentUserService = currentUserService;
_hashService = hashService;
_generateJwt = generateJwt;
_cfg = cfg;
}
public async Task<AcceptContractResponseDto> Handle(AcceptContractCommand request, CancellationToken cancellationToken)
{
// Verify OTP first
var otpToken = await _context.OtpTokens
.Where(x => x.Mobile == _currentUserService.Username && x.Purpose == "signContract" && !x.IsUsed && x.Code == request.Code)
.OrderByDescending(x => x.Id) // Use Id instead of CreatedAt for now
.Where(x => x.Mobile == _currentUserService.Username && x.Purpose == "signContract" && !x.IsUsed)
.OrderByDescending(x => x.Id)
.FirstOrDefaultAsync(cancellationToken);
if (otpToken == null || !otpToken.IsValid(request.Code))
var secret = _cfg["Otp:Secret"] ?? throw new InvalidOperationException("Otp:Secret not set");
if (otpToken == null || !otpToken.IsValid() || !_hashService.VerifyHmacSha256Hex(request.Code, otpToken.CodeHash, secret))
return new AcceptContractResponseDto { IsSuccess = false, Message = "کد تایید نامعتبر است" };
var user = await _context.Users
.Include(u => u.UserContracts)
.ThenInclude(uc => uc.Contract)
.Include(u => u.UserRoles)
.ThenInclude(ur => ur.Role)
.Include(u => u.ClubMembership)
.Where(x => x.Mobile == _currentUserService.Username)
.FirstOrDefaultAsync(cancellationToken);
@@ -48,12 +62,14 @@ public class AcceptContractCommandHandler : IRequestHandler<AcceptContractComman
await _context.SaveChangesAsync(cancellationToken);
// TODO: Implement JWT token generation
// Generate JWT token with updated contract status
var token = await _generateJwt.GenerateJwtToken(user);
return new AcceptContractResponseDto
{
IsSuccess = true,
Message = "قرارداد با موفقیت تایید شد",
Token = "TODO_IMPLEMENT_JWT_GENERATION"
Token = token
};
}
}
@@ -1,26 +1,41 @@
using CMSMicroservice.Application.Common.Interfaces;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Configuration;
namespace CMSMicroservice.Application.UserCQ.Commands.VerifyOtpToken;
public class VerifyOtpTokenCommandHandler : IRequestHandler<VerifyOtpTokenCommand, VerifyOtpTokenResponseDto>
{
private readonly IApplicationDbContext _context;
private readonly IGenerateJwtToken _generateJwt;
private readonly IHashService _hashService;
private readonly IConfiguration _cfg;
public VerifyOtpTokenCommandHandler(IApplicationDbContext context, IGenerateJwtToken generateJwt)
public VerifyOtpTokenCommandHandler(IApplicationDbContext context, IGenerateJwtToken generateJwt,
IHashService hashService, IConfiguration cfg)
{
_context = context;
_generateJwt = generateJwt;
_hashService = hashService;
_cfg = cfg;
}
public async Task<VerifyOtpTokenResponseDto> Handle(VerifyOtpTokenCommand request, CancellationToken cancellationToken)
{
var otpToken = await _context.OtpTokens
.Where(x => x.Mobile == request.Mobile && x.Purpose == request.Purpose && !x.IsUsed)
.OrderByDescending(x => x.Id) // Use Id instead of CreatedAt for now
.OrderByDescending(x => x.Id)
.FirstOrDefaultAsync(cancellationToken);
if (otpToken == null || !otpToken.IsValid(request.Code))
if (otpToken == null)
return new VerifyOtpTokenResponseDto { Success = false, Message = "کد تایید نامعتبر است" };
// Check expiry and usage
if (otpToken.IsUsed || DateTime.Now > otpToken.ExpiresAt)
return new VerifyOtpTokenResponseDto { Success = false, Message = "کد تایید منقضی شده است" };
// Verify using the same HMAC-SHA256 method used during creation
var secret = _cfg["Otp:Secret"] ?? throw new InvalidOperationException("Otp:Secret not set");
if (!_hashService.VerifyHmacSha256Hex(request.Code, otpToken.CodeHash, secret))
return new VerifyOtpTokenResponseDto { Success = false, Message = "کد تایید نامعتبر است" };
var user = await _context.Users