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,16 +1,22 @@
using CMSMicroservice.Protobuf.Protos.City;
using CMSMicroservice.WebApi.Common.Services;
using CMSMicroservice.Application.CityCQ.Queries.GetAllCitiesByFilter;
using CMSMicroservice.Application.Common.Interfaces;
using Microsoft.EntityFrameworkCore;
using Google.Protobuf.WellKnownTypes;
using System.Linq;
namespace CMSMicroservice.WebApi.Services;
public class CityService : CityContract.CityContractBase
{
private readonly IDispatchRequestToCQRS _dispatchRequestToCQRS;
private readonly IApplicationDbContext _context;
public CityService(IDispatchRequestToCQRS dispatchRequestToCQRS)
public CityService(IDispatchRequestToCQRS dispatchRequestToCQRS, IApplicationDbContext context)
{
_dispatchRequestToCQRS = dispatchRequestToCQRS;
_context = context;
}
public override async Task<GetAllCitiesByFilterResponse> GetAllCitiesByFilter(
@@ -28,18 +34,50 @@ public class CityService : CityContract.CityContractBase
public override async Task<GetCitiesForCustomerResponse> GetCitiesForCustomer(
GetCitiesForCustomerRequest request, ServerCallContext context)
{
// TODO: Implement using existing CMS City Application layer
// For now, return empty response
var pageNumber = request.PageNumber > 0 ? request.PageNumber : 1;
var pageSize = request.PageSize > 0 ? request.PageSize : 50;
var query = _context.Cities
.Include(c => c.State)
.Where(c => !c.IsDeleted);
if (request.StateId != null)
query = query.Where(c => c.StateId == request.StateId.Value);
if (!string.IsNullOrWhiteSpace(request.SearchTerm))
query = query.Where(c => c.Name.Contains(request.SearchTerm) || c.Native.Contains(request.SearchTerm));
var totalCount = await query.CountAsync(context.CancellationToken);
var cities = await query
.OrderBy(c => c.Native)
.Skip((pageNumber - 1) * pageSize)
.Take(pageSize)
.Select(c => new CityDto
{
Id = c.Id,
ExternalId = c.ExternalId,
Name = c.Name,
Native = c.Native,
Latitude = c.Latitude ?? string.Empty,
Longitude = c.Longitude ?? string.Empty,
StateId = c.StateId,
StateName = c.State != null ? c.State.Name : string.Empty,
StateNative = c.State != null ? c.State.Native : string.Empty
})
.ToListAsync(context.CancellationToken);
return new GetCitiesForCustomerResponse
{
Cities = { cities },
MetaData = new CMSMicroservice.Protobuf.Protos.City.MetaData
{
CurrentPage = request.PageNumber,
PageSize = request.PageSize,
TotalCount = 0,
TotalPage = 0,
HasNext = false,
HasPrevious = false
CurrentPage = pageNumber,
PageSize = pageSize,
TotalCount = totalCount,
TotalPage = (int)Math.Ceiling(totalCount / (double)pageSize),
HasNext = pageNumber * pageSize < totalCount,
HasPrevious = pageNumber > 1
}
};
}
@@ -47,34 +85,126 @@ public class CityService : CityContract.CityContractBase
public override async Task<GetCityByIdForCustomerResponse> GetCityByIdForCustomer(
GetCityByIdForCustomerRequest request, ServerCallContext context)
{
// TODO: Implement using existing CMS City Application layer
throw new RpcException(new Status(StatusCode.Unimplemented, "GetCityByIdForCustomer not implemented yet"));
var city = await _context.Cities
.Include(c => c.State)
.Where(c => c.Id == request.Id && !c.IsDeleted)
.Select(c => new CityDto
{
Id = c.Id,
ExternalId = c.ExternalId,
Name = c.Name,
Native = c.Native,
Latitude = c.Latitude ?? string.Empty,
Longitude = c.Longitude ?? string.Empty,
StateId = c.StateId,
StateName = c.State != null ? c.State.Name : string.Empty,
StateNative = c.State != null ? c.State.Native : string.Empty
})
.FirstOrDefaultAsync(context.CancellationToken);
if (city == null)
throw new RpcException(new Status(StatusCode.NotFound, "شهر یافت نشد"));
return new GetCityByIdForCustomerResponse { City = city };
}
public override async Task<GetCitiesByStateForCustomerResponse> GetCitiesByStateForCustomer(
GetCitiesByStateForCustomerRequest request, ServerCallContext context)
{
// TODO: Implement using existing CMS City Application layer
throw new RpcException(new Status(StatusCode.Unimplemented, "GetCitiesByStateForCustomer not implemented yet"));
var pageNumber = request.PageNumber > 0 ? request.PageNumber : 1;
var pageSize = request.PageSize > 0 ? request.PageSize : 100;
var query = _context.Cities
.Include(c => c.State)
.Where(c => c.StateId == request.StateId && !c.IsDeleted);
var totalCount = await query.CountAsync(context.CancellationToken);
var cities = await query
.OrderBy(c => c.Native)
.Skip((pageNumber - 1) * pageSize)
.Take(pageSize)
.Select(c => new CityDto
{
Id = c.Id,
ExternalId = c.ExternalId,
Name = c.Name,
Native = c.Native,
Latitude = c.Latitude ?? string.Empty,
Longitude = c.Longitude ?? string.Empty,
StateId = c.StateId,
StateName = c.State != null ? c.State.Name : string.Empty,
StateNative = c.State != null ? c.State.Native : string.Empty
})
.ToListAsync(context.CancellationToken);
return new GetCitiesByStateForCustomerResponse
{
Cities = { cities },
MetaData = new CMSMicroservice.Protobuf.Protos.City.MetaData
{
CurrentPage = pageNumber,
PageSize = pageSize,
TotalCount = totalCount,
TotalPage = (int)Math.Ceiling(totalCount / (double)pageSize),
HasNext = pageNumber * pageSize < totalCount,
HasPrevious = pageNumber > 1
}
};
}
// Admin Methods placeholder for future expansion
// Admin Methods
public override async Task<CreateCityResponse> CreateCity(
CreateCityRequest request, ServerCallContext context)
{
throw new RpcException(new Status(StatusCode.Unimplemented, "CreateCity not implemented yet"));
var city = new Domain.Entities.Geography.City
{
ExternalId = request.ExternalId,
Name = request.Name,
Native = request.Native,
Latitude = request.Latitude ?? string.Empty,
Longitude = request.Longitude ?? string.Empty,
StateId = request.StateId
};
_context.Cities.Add(city);
await _context.SaveChangesAsync(context.CancellationToken);
return new CreateCityResponse
{
Id = city.Id,
Message = "شهر با موفقیت ایجاد شد"
};
}
public override async Task<Empty> UpdateCity(
UpdateCityRequest request, ServerCallContext context)
{
throw new RpcException(new Status(StatusCode.Unimplemented, "UpdateCity not implemented yet"));
var city = await _context.Cities.FindAsync(new object[] { request.Id }, context.CancellationToken);
if (city == null)
throw new RpcException(new Status(StatusCode.NotFound, "شهر یافت نشد"));
city.ExternalId = request.ExternalId;
city.Name = request.Name;
city.Native = request.Native;
if (request.Latitude != null) city.Latitude = request.Latitude;
if (request.Longitude != null) city.Longitude = request.Longitude;
city.StateId = request.StateId;
await _context.SaveChangesAsync(context.CancellationToken);
return new Empty();
}
public override async Task<Empty> DeleteCity(
DeleteCityRequest request, ServerCallContext context)
{
throw new RpcException(new Status(StatusCode.Unimplemented, "DeleteCity not implemented yet"));
var city = await _context.Cities.FindAsync(new object[] { request.Id }, context.CancellationToken);
if (city == null)
throw new RpcException(new Status(StatusCode.NotFound, "شهر یافت نشد"));
city.IsDeleted = true;
await _context.SaveChangesAsync(context.CancellationToken);
return new Empty();
}
#endregion