diff --git a/NetinaShop.Api/Controller/FileController.cs b/NetinaShop.Api/Controller/FileController.cs new file mode 100644 index 0000000..a2ebffc --- /dev/null +++ b/NetinaShop.Api/Controller/FileController.cs @@ -0,0 +1,45 @@ +using NetinaShop.Domain.Enums; + +namespace NetinaShop.Api.Controller; + +public class FileController : ICarterModule +{ + public void AddRoutes(IEndpointRouteBuilder app) + { + var group = app.NewVersionedApi("File") + .MapGroup("api/file") + .RequireAuthorization(builder => builder.AddAuthenticationSchemes("Bearer").RequireAuthenticatedUser()); + + group.MapGet("", GetFilesAsync) + .WithDisplayName("GetFilesAsync") + .HasApiVersion(1.0); + + group.MapPost("", UploadFileAsync) + .WithDisplayName("UploadFileAsync") + .HasApiVersion(1.0); + + } + + public async Task GetFilesAsync([FromQuery]StorageFileType? fileType,[FromServices] IStorageService storageService, CancellationToken cancellationToken) + => TypedResults.Ok(await storageService.GetStorageFiles(fileType: fileType ?? StorageFileType.Image)); + public async Task UploadFileAsync([FromBody] FileUploadRequest uploadRequest, [FromServices] IStorageService storageService, CancellationToken cancellationToken) + { + var bytes = Convert.FromBase64String(uploadRequest.StringBaseFile); + using var originalMedFileStream = new MemoryStream(bytes); + using var originalThumbFileStream = new MemoryStream(bytes); + using var thumbnailFileStream = new MemoryStream(); + using var mediumFileStream = new MemoryStream(); + + await uploadRequest.ImageResize(originalMedFileStream, mediumFileStream, 1280); + await uploadRequest.ImageResize(originalThumbFileStream, thumbnailFileStream, 200); + var medFileName = await storageService.UploadObjectFromFileAsync(uploadRequest.FileName, $"{uploadRequest.FileUploadType.ToDisplay()}/Med", uploadRequest.ContentType, mediumFileStream); + await storageService.UploadObjectFromFileAsync(uploadRequest.FileName, $"{uploadRequest.FileUploadType.ToDisplay()}/Thumb", uploadRequest.ContentType, thumbnailFileStream); + var response = new FileUploadResponse + { + FileName = medFileName, + FileLocation = $"{uploadRequest.FileUploadType.ToDisplay()}/Med/{medFileName}", + FileUrl = $"https://storage.vesmook.com/{uploadRequest.FileUploadType.ToDisplay()}/Med/{medFileName}" + }; + return TypedResults.Ok(response); + } +} \ No newline at end of file diff --git a/NetinaShop.Api/NetinaShop.Api.csproj b/NetinaShop.Api/NetinaShop.Api.csproj index d7a3ba1..6a07860 100644 --- a/NetinaShop.Api/NetinaShop.Api.csproj +++ b/NetinaShop.Api/NetinaShop.Api.csproj @@ -52,7 +52,7 @@ - + @@ -74,8 +74,10 @@ + + diff --git a/NetinaShop.Common/Models/Api/FileUploadRequest.cs b/NetinaShop.Common/Models/Api/FileUploadRequest.cs index 54ec5de..bbec564 100644 --- a/NetinaShop.Common/Models/Api/FileUploadRequest.cs +++ b/NetinaShop.Common/Models/Api/FileUploadRequest.cs @@ -2,14 +2,18 @@ { public enum FileUploadType { + [Display(Name = "Images")] + Image, + [Display(Name = "Handouts")] Handout, + [Display(Name = "Videos")] Video, - Image } public class FileUploadRequest { public string StringBaseFile { get; set; } public string FileName { get; set; } + public string ContentType { get; set; } public FileUploadType FileUploadType { get; set; } } } \ No newline at end of file diff --git a/NetinaShop.Common/Models/Api/FileUploadResponse.cs b/NetinaShop.Common/Models/Api/FileUploadResponse.cs new file mode 100644 index 0000000..7417bcc --- /dev/null +++ b/NetinaShop.Common/Models/Api/FileUploadResponse.cs @@ -0,0 +1,9 @@ +namespace NetinaShop.Common.Models.Api; + +public class FileUploadResponse +{ + public string FileUrl { get; set; } + public string FileLocation { get; set; } + public string FileName { get; set; } + public bool IsOriginal { get; set; } = true; +} \ No newline at end of file diff --git a/NetinaShop.Common/NetinaShop.Common.csproj b/NetinaShop.Common/NetinaShop.Common.csproj index 834cf1e..aaf30d6 100644 --- a/NetinaShop.Common/NetinaShop.Common.csproj +++ b/NetinaShop.Common/NetinaShop.Common.csproj @@ -24,7 +24,7 @@ - + diff --git a/NetinaShop.Core/Abstracts/IStorageService.cs b/NetinaShop.Core/Abstracts/IStorageService.cs new file mode 100644 index 0000000..93ff3a8 --- /dev/null +++ b/NetinaShop.Core/Abstracts/IStorageService.cs @@ -0,0 +1,9 @@ +namespace NetinaShop.Core.Abstracts; + +public interface IStorageService : IScopedDependency +{ + Task UploadObjectFromFileAsync(string fileName, string filePath, string contentType, byte[] fileBytes); + Task UploadObjectFromFileAsync(string fileName, string filePath, string contentType, Stream fileStream); + + Task> GetStorageFiles(StorageFileType fileType); +} \ No newline at end of file diff --git a/NetinaShop.Core/NetinaShop.Core.csproj b/NetinaShop.Core/NetinaShop.Core.csproj index 645548d..28f984a 100644 --- a/NetinaShop.Core/NetinaShop.Core.csproj +++ b/NetinaShop.Core/NetinaShop.Core.csproj @@ -12,6 +12,7 @@ + @@ -22,10 +23,10 @@ - + diff --git a/NetinaShop.Core/Utilities/ImageConvertor.cs b/NetinaShop.Core/Utilities/ImageConvertor.cs new file mode 100644 index 0000000..13914cd --- /dev/null +++ b/NetinaShop.Core/Utilities/ImageConvertor.cs @@ -0,0 +1,18 @@ +using SixLabors.ImageSharp; +using SixLabors.ImageSharp.Processing; + +namespace NetinaShop.Core.Utilities; + +public static class ImageConvertor +{ + public static async Task ImageResize(this FileUploadRequest fileUpload, Stream input, Stream output, int newWidth) + { + using var image = await Image.LoadAsync(input); + var height_width = image.Height / image.Width; + var new_Height = newWidth * height_width; + image.Mutate(x => x.Resize(newWidth, new_Height)); + image.Mutate(x => x.Resize(newWidth, new_Height)); + await image.SaveAsJpegAsync(output); + return output; + } +} \ No newline at end of file diff --git a/NetinaShop.Domain/Dtos/LargDtos/ProductLDto.cs b/NetinaShop.Domain/Dtos/LargDtos/ProductLDto.cs index f66c436..950c122 100644 --- a/NetinaShop.Domain/Dtos/LargDtos/ProductLDto.cs +++ b/NetinaShop.Domain/Dtos/LargDtos/ProductLDto.cs @@ -8,8 +8,15 @@ public class ProductLDto : BaseDto public string ExpertCheck { get; set; } = string.Empty; public string Tags { get; set; } = string.Empty; public string Warranty { get; set; } = string.Empty; + public bool BeDisplayed { get; set; } + public bool HasExpressDelivery { get; set; } + public int MaxOrderCount { get; set; } + public double Cost { get; set; } + public double PackingCost { get; set; } public Guid BrandId { get; set; } - public string BrandNames { get; set; } = string.Empty; + public string BrandName { get; set; } = string.Empty; + public Guid CategoryId { get; set; } + public string CategoryName { get; set; } = string.Empty; public List Specifications { get; set; } = new(); public List Reviews { get; set; } = new(); diff --git a/NetinaShop.Domain/Dtos/SmallDtos/StorageFileSDto.cs b/NetinaShop.Domain/Dtos/SmallDtos/StorageFileSDto.cs index 3c73894..20dc53e 100644 --- a/NetinaShop.Domain/Dtos/SmallDtos/StorageFileSDto.cs +++ b/NetinaShop.Domain/Dtos/SmallDtos/StorageFileSDto.cs @@ -1,11 +1,26 @@ -namespace NetinaShop.Domain.Dtos.SmallDtos; +using Microsoft.IdentityModel.Tokens; + +namespace NetinaShop.Domain.Dtos.SmallDtos; public class StorageFileSDto : BaseDto { - public string Name { get; internal set; } = string.Empty; - public string FileLocation { get; internal set; } = string.Empty; - public string FileName { get; internal set; } = string.Empty; - public bool IsHeader { get; internal set; } - public bool IsPrimary { get; internal set; } - public StorageFileType FileType { get; internal set; } + public string Name { get; set; } = string.Empty; + public string FileLocation { get; set; } = string.Empty; + public string FileName { get; set; } = string.Empty; + public bool IsHeader { get; set; } + public bool IsPrimary { get; set; } + public StorageFileType FileType { get; set; } + public bool Selected { get; set; } + + public DateTime CreatedAt + { + get + { + string date = FileName.Split('.').First().Split('_').Last(); + if (!date.IsNullOrEmpty() && long.TryParse(date, out long longDate)) + return DateTimeExtensions.UnixTimeStampToDateTime(longDate); + return DateTime.MinValue; + } + } + } \ No newline at end of file diff --git a/NetinaShop.Domain/Entities/Products/Product.Aggregate.cs b/NetinaShop.Domain/Entities/Products/Product.Aggregate.cs index 54ceb3c..34c4e16 100644 --- a/NetinaShop.Domain/Entities/Products/Product.Aggregate.cs +++ b/NetinaShop.Domain/Entities/Products/Product.Aggregate.cs @@ -36,7 +36,7 @@ public partial class Product public Specification AddSpecification(string title, string detail,string value, bool isFeature, Guid parentId) { - var ent = Specification.Create(title, detail, value, isFeature, Id, parentId); + var ent = Specification.Create(title, value, detail, isFeature, Id, parentId); Specifications.Add(ent); return ent; } diff --git a/NetinaShop.Domain/Entities/Products/Product.cs b/NetinaShop.Domain/Entities/Products/Product.cs index 621a413..95a4aa2 100644 --- a/NetinaShop.Domain/Entities/Products/Product.cs +++ b/NetinaShop.Domain/Entities/Products/Product.cs @@ -53,8 +53,8 @@ public partial class Product : ApiEntity public float Rate { get; internal set; } public int ReviewCount { get; internal set; } public int Viewed { get; internal set; } - public bool HasExpressDelivery { get; set; } - public int MaxOrderCount { get; set; } + public bool HasExpressDelivery { get; internal set; } + public int MaxOrderCount { get; internal set; } public Guid BrandId { get; internal set; } diff --git a/NetinaShop.Domain/Entities/Products/Specification.cs b/NetinaShop.Domain/Entities/Products/Specification.cs index 6e1befa..2eb83df 100644 --- a/NetinaShop.Domain/Entities/Products/Specification.cs +++ b/NetinaShop.Domain/Entities/Products/Specification.cs @@ -7,17 +7,20 @@ public partial class Specification : ApiEntity { public Specification() { - + } - public Specification(string title, string detail,string value, bool isFeature, Guid productId, Guid parentId) + public Specification(string title, string detail, string value, bool isFeature, Guid productId, Guid parentId) { Title = title; Detail = detail; Value = value; IsFeature = isFeature; ProductId = productId; - ParentId = parentId; + if (parentId != default) + ParentId = parentId; + else + ParentId = null; } public string Title { get; internal set; } = string.Empty; public string Detail { get; internal set; } = string.Empty; @@ -27,7 +30,7 @@ public partial class Specification : ApiEntity public Guid ProductId { get; internal set; } public Product? Product { get; internal set; } - public Guid ParentId { get; internal set; } + public Guid? ParentId { get; internal set; } public Specification? Parent { get; internal set; } public List Children { get; internal set; } = new(); diff --git a/NetinaShop.Domain/Enums/StorageFileType.cs b/NetinaShop.Domain/Enums/StorageFileType.cs index 5377304..e4db570 100644 --- a/NetinaShop.Domain/Enums/StorageFileType.cs +++ b/NetinaShop.Domain/Enums/StorageFileType.cs @@ -2,6 +2,8 @@ public enum StorageFileType { + [Display(Name = "Images")] Image, + [Display(Name = "Videos")] Video } \ No newline at end of file diff --git a/NetinaShop.Domain/Mappers/CategoryStorageFileMapper.g.cs b/NetinaShop.Domain/Mappers/CategoryStorageFileMapper.g.cs deleted file mode 100644 index 8b15157..0000000 --- a/NetinaShop.Domain/Mappers/CategoryStorageFileMapper.g.cs +++ /dev/null @@ -1,6 +0,0 @@ -namespace NetinaShop.Domain.Mappers -{ - public static partial class CategoryStorageFileMapper - { - } -} \ No newline at end of file diff --git a/NetinaShop.Domain/Mappers/ProductMapper.g.cs b/NetinaShop.Domain/Mappers/ProductMapper.g.cs index c800f52..c05fd07 100644 --- a/NetinaShop.Domain/Mappers/ProductMapper.g.cs +++ b/NetinaShop.Domain/Mappers/ProductMapper.g.cs @@ -20,7 +20,13 @@ namespace NetinaShop.Domain.Mappers ExpertCheck = p1.ExpertCheck, Tags = p1.Tags, Warranty = p1.Warranty, + Cost = p1.Cost, + BeDisplayed = p1.BeDisplayed, + PackingCost = p1.PackingCost, + HasExpressDelivery = p1.HasExpressDelivery, + MaxOrderCount = p1.MaxOrderCount, BrandId = p1.BrandId, + CategoryId = p1.CategoryId, Specifications = funcMain1(p1.Specifications), Reviews = funcMain2(p1.Reviews), Files = funcMain3(p1.Files), @@ -41,7 +47,13 @@ namespace NetinaShop.Domain.Mappers result.ExpertCheck = p5.ExpertCheck; result.Tags = p5.Tags; result.Warranty = p5.Warranty; + result.Cost = p5.Cost; + result.BeDisplayed = p5.BeDisplayed; + result.PackingCost = p5.PackingCost; + result.HasExpressDelivery = p5.HasExpressDelivery; + result.MaxOrderCount = p5.MaxOrderCount; result.BrandId = p5.BrandId; + result.CategoryId = p5.CategoryId; result.Specifications = funcMain4(p5.Specifications, result.Specifications); result.Reviews = funcMain5(p5.Reviews, result.Reviews); result.Files = funcMain6(p5.Files, result.Files); @@ -57,7 +69,13 @@ namespace NetinaShop.Domain.Mappers ExpertCheck = p13.ExpertCheck, Tags = p13.Tags, Warranty = p13.Warranty, + Cost = p13.Cost, + BeDisplayed = p13.BeDisplayed, + PackingCost = p13.PackingCost, + HasExpressDelivery = p13.HasExpressDelivery, + MaxOrderCount = p13.MaxOrderCount, BrandId = p13.BrandId, + CategoryId = p13.CategoryId, Specifications = p13.Specifications.Select(p14 => new Specification() { Title = p14.Title, @@ -65,7 +83,7 @@ namespace NetinaShop.Domain.Mappers Value = p14.Value, IsFeature = p14.IsFeature, ProductId = p14.ProductId, - ParentId = p14.ParentId, + ParentId = (Guid?)p14.ParentId, Id = p14.Id }).ToList(), Reviews = p13.Reviews.Select(p15 => new Review() @@ -100,7 +118,15 @@ namespace NetinaShop.Domain.Mappers ExpertCheck = p17.ExpertCheck, Tags = p17.Tags, Warranty = p17.Warranty, + BeDisplayed = p17.BeDisplayed, + HasExpressDelivery = p17.HasExpressDelivery, + MaxOrderCount = p17.MaxOrderCount, + Cost = p17.Cost, + PackingCost = p17.PackingCost, BrandId = p17.BrandId, + BrandName = p17.Brand == null ? null : p17.Brand.Name, + CategoryId = p17.CategoryId, + CategoryName = p17.Category == null ? null : p17.Category.Name, Specifications = funcMain7(p17.Specifications), Reviews = funcMain8(p17.Reviews), Files = funcMain9(p17.Files), @@ -121,7 +147,15 @@ namespace NetinaShop.Domain.Mappers result.ExpertCheck = p21.ExpertCheck; result.Tags = p21.Tags; result.Warranty = p21.Warranty; + result.BeDisplayed = p21.BeDisplayed; + result.HasExpressDelivery = p21.HasExpressDelivery; + result.MaxOrderCount = p21.MaxOrderCount; + result.Cost = p21.Cost; + result.PackingCost = p21.PackingCost; result.BrandId = p21.BrandId; + result.BrandName = p21.Brand == null ? null : p21.Brand.Name; + result.CategoryId = p21.CategoryId; + result.CategoryName = p21.Category == null ? null : p21.Category.Name; result.Specifications = funcMain10(p21.Specifications, result.Specifications); result.Reviews = funcMain11(p21.Reviews, result.Reviews); result.Files = funcMain12(p21.Files, result.Files); @@ -137,7 +171,15 @@ namespace NetinaShop.Domain.Mappers ExpertCheck = p29.ExpertCheck, Tags = p29.Tags, Warranty = p29.Warranty, + BeDisplayed = p29.BeDisplayed, + HasExpressDelivery = p29.HasExpressDelivery, + MaxOrderCount = p29.MaxOrderCount, + Cost = p29.Cost, + PackingCost = p29.PackingCost, BrandId = p29.BrandId, + BrandName = p29.Brand.Name, + CategoryId = p29.CategoryId, + CategoryName = p29.Category.Name, Specifications = p29.Specifications.Select(p30 => new SpecificationSDto() { Title = p30.Title, @@ -145,7 +187,7 @@ namespace NetinaShop.Domain.Mappers Value = p30.Value, IsFeature = p30.IsFeature, ProductId = p30.ProductId, - ParentId = p30.ParentId, + ParentId = p30.ParentId == null ? default(Guid) : (Guid)p30.ParentId, Id = p30.Id }).ToList(), Reviews = p29.Reviews.Select(p31 => new ReviewSDto() @@ -312,7 +354,7 @@ namespace NetinaShop.Domain.Mappers Value = item.Value, IsFeature = item.IsFeature, ProductId = item.ProductId, - ParentId = item.ParentId, + ParentId = (Guid?)item.ParentId, Id = item.Id }); i++; @@ -402,7 +444,7 @@ namespace NetinaShop.Domain.Mappers Value = item.Value, IsFeature = item.IsFeature, ProductId = item.ProductId, - ParentId = item.ParentId, + ParentId = (Guid?)item.ParentId, Id = item.Id }); i++; @@ -492,7 +534,7 @@ namespace NetinaShop.Domain.Mappers Value = item.Value, IsFeature = item.IsFeature, ProductId = item.ProductId, - ParentId = item.ParentId, + ParentId = item.ParentId == null ? default(Guid) : (Guid)item.ParentId, Id = item.Id }); i++; @@ -582,7 +624,7 @@ namespace NetinaShop.Domain.Mappers Value = item.Value, IsFeature = item.IsFeature, ProductId = item.ProductId, - ParentId = item.ParentId, + ParentId = item.ParentId == null ? default(Guid) : (Guid)item.ParentId, Id = item.Id }); i++; diff --git a/NetinaShop.Domain/Mappers/SpecificationMapper.g.cs b/NetinaShop.Domain/Mappers/SpecificationMapper.g.cs index bfdc311..e01bfda 100644 --- a/NetinaShop.Domain/Mappers/SpecificationMapper.g.cs +++ b/NetinaShop.Domain/Mappers/SpecificationMapper.g.cs @@ -16,7 +16,7 @@ namespace NetinaShop.Domain.Mappers Value = p1.Value, IsFeature = p1.IsFeature, ProductId = p1.ProductId, - ParentId = p1.ParentId, + ParentId = (Guid?)p1.ParentId, Id = p1.Id }; } @@ -33,7 +33,7 @@ namespace NetinaShop.Domain.Mappers result.Value = p2.Value; result.IsFeature = p2.IsFeature; result.ProductId = p2.ProductId; - result.ParentId = p2.ParentId; + result.ParentId = (Guid?)p2.ParentId; result.Id = p2.Id; return result; @@ -47,7 +47,7 @@ namespace NetinaShop.Domain.Mappers Value = p4.Value, IsFeature = p4.IsFeature, ProductId = p4.ProductId, - ParentId = p4.ParentId, + ParentId = p4.ParentId == null ? default(Guid) : (Guid)p4.ParentId, Id = p4.Id }; } @@ -64,7 +64,7 @@ namespace NetinaShop.Domain.Mappers result.Value = p5.Value; result.IsFeature = p5.IsFeature; result.ProductId = p5.ProductId; - result.ParentId = p5.ParentId; + result.ParentId = p5.ParentId == null ? default(Guid) : (Guid)p5.ParentId; result.Id = p5.Id; return result; @@ -76,7 +76,7 @@ namespace NetinaShop.Domain.Mappers Value = p7.Value, IsFeature = p7.IsFeature, ProductId = p7.ProductId, - ParentId = p7.ParentId, + ParentId = p7.ParentId == null ? default(Guid) : (Guid)p7.ParentId, Id = p7.Id }; } diff --git a/NetinaShop.Infrastructure/NetinaShop.Infrastructure.csproj b/NetinaShop.Infrastructure/NetinaShop.Infrastructure.csproj index 8993144..e517cc4 100644 --- a/NetinaShop.Infrastructure/NetinaShop.Infrastructure.csproj +++ b/NetinaShop.Infrastructure/NetinaShop.Infrastructure.csproj @@ -8,6 +8,7 @@ + @@ -19,14 +20,16 @@ - + + + diff --git a/NetinaShop.Infrastructure/Services/StorageService.cs b/NetinaShop.Infrastructure/Services/StorageService.cs new file mode 100644 index 0000000..65ee924 --- /dev/null +++ b/NetinaShop.Infrastructure/Services/StorageService.cs @@ -0,0 +1,114 @@ +using NetinaShop.Domain.Dtos.SmallDtos; +using NetinaShop.Domain.Enums; + +namespace NetinaShop.Infrastructure.Services; + +public class StorageService : IStorageService +{ + private IAmazonS3? _s3Client; + private string _bucketName = "vesmeh-content"; + private IAmazonS3 GetClientAsync() + { + var awsCredentials = new Amazon.Runtime.BasicAWSCredentials("979313b7-30fb-40ff-94d8-d0390d3fa876", "d37a1cc6acfea3a6f92c538ef0f6601f1edcdc9143942b6470e5d1032aa6bfe2"); + var config = new AmazonS3Config { ServiceURL = "https://s3.ir-thr-at1.arvanstorage.ir" }; + _s3Client = new AmazonS3Client(awsCredentials, config); + return _s3Client; + } + + public async Task UploadObjectFromFileAsync(string fileName, string filePath, string contentType, byte[] fileBytes) + { + using var memorySteam = new MemoryStream(fileBytes); + + var client = GetClientAsync(); + + var fileEx = fileName.Split('.').Last(); + fileName = fileName.Split('.').First(); + + fileName = fileName + "_" + StringExtensions.GetId(5) + "_" + DateTime.Today.ToString("yyyy-MM-dd-HH-mm-ss") + "." + fileEx; + + var putRequest = new PutObjectRequest + { + BucketName = _bucketName, + Key = Path.Combine(filePath, fileName), + ContentType = contentType, + InputStream = memorySteam, + CannedACL = S3CannedACL.PublicRead + }; + + putRequest.Metadata.Add("x-amz-meta-title", fileName); + + PutObjectResponse response = await client.PutObjectAsync(putRequest); + + return fileName; + } + + public async Task UploadObjectFromFileAsync(string fileName, string filePath, string contentType, Stream fileStream) + { + + var client = GetClientAsync(); + + var fileEx = fileName.Split('.').Last(); + fileName = fileName.Split('.').First(); + + fileName = fileName + "_" + StringExtensions.GetId(5) + "_" + DateTimeExtensions.DateTimeToUnixTimeStamp(DateTime.Now) + "." + fileEx; + + var putRequest = new PutObjectRequest + { + BucketName = _bucketName, + Key = Path.Combine(filePath, fileName), + ContentType = contentType, + InputStream = fileStream, + CannedACL = S3CannedACL.PublicRead + }; + + putRequest.Metadata.Add("x-amz-meta-title", fileName); + + PutObjectResponse response = await client.PutObjectAsync(putRequest); + return fileName; + } + + public async Task> GetStorageFiles(StorageFileType fileType) + { + var client = GetClientAsync(); + ListObjectsRequest request = new() { BucketName = _bucketName}; + switch (fileType) + { + case StorageFileType.Image: + request.Prefix = "Images/Med"; + break; + case StorageFileType.Video: + request.Prefix = "Videos"; + break; + default: + break; + } + var files = new List(); + do + { + ListObjectsResponse response = await client.ListObjectsAsync(request); + + // Process the response. + foreach (var s3Object in response.S3Objects.Where(s=>s.Size>0)) + { + files.Add(new StorageFileSDto + { + FileLocation = s3Object.Key, + FileName = s3Object.Key.Split('/').Last() + }); + } + + // If the response is truncated, set the marker to get the next + // set of keys. + if (response.IsTruncated) + { + request.Marker = response.NextMarker; + } + else + { + request = null; + } + } while (request != null); + + return files.OrderByDescending(o=>o.CreatedAt).ToList(); + } +} \ No newline at end of file diff --git a/NetinaShop.Repository/Handlers/Products/GetProductsQueryHandler.cs b/NetinaShop.Repository/Handlers/Products/GetProductsQueryHandler.cs index 6621055..896e630 100644 --- a/NetinaShop.Repository/Handlers/Products/GetProductsQueryHandler.cs +++ b/NetinaShop.Repository/Handlers/Products/GetProductsQueryHandler.cs @@ -32,6 +32,10 @@ public class GetProductsQueryHandler : IRequestHandler products }; } + else + { + products = products.OrderByDescending(p => p.CreatedAt); + } var productSDtos = await products.Skip(request.Page * 20) .Take(20) diff --git a/NetinaShop.Repository/Handlers/Products/UpdateProductCommandHandler.cs b/NetinaShop.Repository/Handlers/Products/UpdateProductCommandHandler.cs index 11ece2a..19259bd 100644 --- a/NetinaShop.Repository/Handlers/Products/UpdateProductCommandHandler.cs +++ b/NetinaShop.Repository/Handlers/Products/UpdateProductCommandHandler.cs @@ -31,7 +31,7 @@ public class UpdateProductCommandHandler : IRequestHandler s.ProductId == ent.Id).ToListAsync(cancellationToken); foreach (var dbSpecification in dbSpecifications) { - if (request.Specifications.Any(s => s.Id == dbSpecification.Id)) + if (request.Specifications.FirstOrDefault(s => s.Id != dbSpecification.Id) == null) { _repositoryWrapper.SetRepository().Delete(dbSpecification); await _repositoryWrapper.SaveChangesAsync(cancellationToken); @@ -49,7 +49,7 @@ public class UpdateProductCommandHandler : IRequestHandler s.ProductId == ent.Id).ToListAsync(cancellationToken); foreach (var dbFile in dbFiles) { - if (request.Files.Any(s => s.Id == dbFile.Id)) + if (request.Files.FirstOrDefault(s => s.Id == dbFile.Id)==null) { _repositoryWrapper.SetRepository().Delete(dbFile); await _repositoryWrapper.SaveChangesAsync(cancellationToken); diff --git a/NetinaShop.Repository/Migrations/20240129072040_editSpecificationNullParent.Designer.cs b/NetinaShop.Repository/Migrations/20240129072040_editSpecificationNullParent.Designer.cs new file mode 100644 index 0000000..a15d2a7 --- /dev/null +++ b/NetinaShop.Repository/Migrations/20240129072040_editSpecificationNullParent.Designer.cs @@ -0,0 +1,1583 @@ +// +using System; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using NetinaShop.Repository.Models; +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; + +#nullable disable + +namespace NetinaShop.Repository.Migrations +{ + [DbContext(typeof(ApplicationContext))] + [Migration("20240129072040_editSpecificationNullParent")] + partial class editSpecificationNullParent + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasDefaultSchema("public") + .HasAnnotation("ProductVersion", "8.0.0") + .HasAnnotation("Relational:MaxIdentifierLength", 63); + + NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("ClaimType") + .HasColumnType("text"); + + b.Property("ClaimValue") + .HasColumnType("text"); + + b.Property("RoleId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("RoleId"); + + b.ToTable("RoleClaims", "public"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("ClaimType") + .HasColumnType("text"); + + b.Property("ClaimValue") + .HasColumnType("text"); + + b.Property("UserId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("Claims", "public"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.Property("LoginProvider") + .HasColumnType("text"); + + b.Property("ProviderKey") + .HasColumnType("text"); + + b.Property("ProviderDisplayName") + .HasColumnType("text"); + + b.Property("UserId") + .HasColumnType("uuid"); + + b.HasKey("LoginProvider", "ProviderKey"); + + b.HasIndex("UserId"); + + b.ToTable("Logins", "public"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => + { + b.Property("UserId") + .HasColumnType("uuid"); + + b.Property("RoleId") + .HasColumnType("uuid"); + + b.HasKey("UserId", "RoleId"); + + b.HasIndex("RoleId"); + + b.ToTable("UserRoles", "public"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.Property("UserId") + .HasColumnType("uuid"); + + b.Property("LoginProvider") + .HasColumnType("text"); + + b.Property("Name") + .HasColumnType("text"); + + b.Property("Value") + .HasColumnType("text"); + + b.HasKey("UserId", "LoginProvider", "Name"); + + b.ToTable("Tokens", "public"); + }); + + modelBuilder.Entity("NetinaShop.Domain.Entities.Blogs.Blog", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CategoryId") + .HasColumnType("uuid"); + + b.Property("Content") + .IsRequired() + .HasColumnType("text"); + + b.Property("CreatedAt") + .HasColumnType("timestamp without time zone"); + + b.Property("CreatedBy") + .HasColumnType("text"); + + b.Property("IsRemoved") + .HasColumnType("boolean"); + + b.Property("IsSuggested") + .HasColumnType("boolean"); + + b.Property("ModifiedAt") + .HasColumnType("timestamp without time zone"); + + b.Property("ModifiedBy") + .HasColumnType("text"); + + b.Property("ReadingTime") + .HasColumnType("integer"); + + b.Property("RemovedAt") + .HasColumnType("timestamp without time zone"); + + b.Property("RemovedBy") + .HasColumnType("text"); + + b.Property("Summery") + .IsRequired() + .HasColumnType("text"); + + b.Property("Tags") + .IsRequired() + .HasColumnType("text"); + + b.Property("Title") + .IsRequired() + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("CategoryId"); + + b.ToTable("Blogs", "public"); + }); + + modelBuilder.Entity("NetinaShop.Domain.Entities.Blogs.BlogCategory", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedAt") + .HasColumnType("timestamp without time zone"); + + b.Property("CreatedBy") + .HasColumnType("text"); + + b.Property("Description") + .IsRequired() + .HasColumnType("text"); + + b.Property("IsRemoved") + .HasColumnType("boolean"); + + b.Property("ModifiedAt") + .HasColumnType("timestamp without time zone"); + + b.Property("ModifiedBy") + .HasColumnType("text"); + + b.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b.Property("RemovedAt") + .HasColumnType("timestamp without time zone"); + + b.Property("RemovedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.ToTable("BlogCategories", "public"); + }); + + modelBuilder.Entity("NetinaShop.Domain.Entities.Brands.Brand", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedAt") + .HasColumnType("timestamp without time zone"); + + b.Property("CreatedBy") + .HasColumnType("text"); + + b.Property("Description") + .IsRequired() + .HasColumnType("text"); + + b.Property("HasSpecialPage") + .HasColumnType("boolean"); + + b.Property("IsRemoved") + .HasColumnType("boolean"); + + b.Property("ModifiedAt") + .HasColumnType("timestamp without time zone"); + + b.Property("ModifiedBy") + .HasColumnType("text"); + + b.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b.Property("PageUrl") + .IsRequired() + .HasColumnType("text"); + + b.Property("RemovedAt") + .HasColumnType("timestamp without time zone"); + + b.Property("RemovedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.ToTable("Brands", "public"); + }); + + modelBuilder.Entity("NetinaShop.Domain.Entities.Discounts.Discount", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("AmountType") + .HasColumnType("integer"); + + b.Property("Code") + .IsRequired() + .HasColumnType("text"); + + b.Property("Count") + .HasColumnType("integer"); + + b.Property("CreatedAt") + .HasColumnType("timestamp without time zone"); + + b.Property("CreatedBy") + .HasColumnType("text"); + + b.Property("DiscountAmount") + .HasColumnType("bigint"); + + b.Property("DiscountPercent") + .HasColumnType("integer"); + + b.Property("Discriminator") + .IsRequired() + .HasMaxLength(21) + .HasColumnType("character varying(21)"); + + b.Property("ExpireDate") + .HasColumnType("timestamp without time zone"); + + b.Property("HasCode") + .HasColumnType("boolean"); + + b.Property("HasPriceCeiling") + .HasColumnType("boolean"); + + b.Property("HasPriceFloor") + .HasColumnType("boolean"); + + b.Property("IsForInvitation") + .HasColumnType("boolean"); + + b.Property("IsInfinity") + .HasColumnType("boolean"); + + b.Property("IsRemoved") + .HasColumnType("boolean"); + + b.Property("ModifiedAt") + .HasColumnType("timestamp without time zone"); + + b.Property("ModifiedBy") + .HasColumnType("text"); + + b.Property("PriceCeiling") + .HasColumnType("bigint"); + + b.Property("PriceFloor") + .HasColumnType("bigint"); + + b.Property("RemovedAt") + .HasColumnType("timestamp without time zone"); + + b.Property("RemovedBy") + .HasColumnType("text"); + + b.Property("StartDate") + .HasColumnType("timestamp without time zone"); + + b.Property("Type") + .HasColumnType("integer"); + + b.Property("UseCount") + .HasColumnType("bigint"); + + b.HasKey("Id"); + + b.ToTable("Discounts", "public"); + + b.HasDiscriminator("Discriminator").HasValue("Discount"); + + b.UseTphMappingStrategy(); + }); + + modelBuilder.Entity("NetinaShop.Domain.Entities.Orders.Order", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedAt") + .HasColumnType("timestamp without time zone"); + + b.Property("CreatedBy") + .HasColumnType("text"); + + b.Property("DeliveryPrice") + .HasColumnType("double precision"); + + b.Property("DiscountCode") + .IsRequired() + .HasColumnType("text"); + + b.Property("DiscountId") + .HasColumnType("uuid"); + + b.Property("DiscountPrice") + .HasColumnType("double precision"); + + b.Property("DoneAt") + .HasColumnType("timestamp without time zone"); + + b.Property("IsPayed") + .HasColumnType("boolean"); + + b.Property("IsRemoved") + .HasColumnType("boolean"); + + b.Property("ModifiedAt") + .HasColumnType("timestamp without time zone"); + + b.Property("ModifiedBy") + .HasColumnType("text"); + + b.Property("OrderAt") + .HasColumnType("timestamp without time zone"); + + b.Property("OrderStatus") + .HasColumnType("integer"); + + b.Property("PackingPrice") + .HasColumnType("double precision"); + + b.Property("PayedAt") + .HasColumnType("timestamp without time zone"); + + b.Property("PaymentMethod") + .HasColumnType("integer"); + + b.Property("PreparingMinute") + .HasColumnType("integer"); + + b.Property("RemovedAt") + .HasColumnType("timestamp without time zone"); + + b.Property("RemovedBy") + .HasColumnType("text"); + + b.Property("ServicePrice") + .HasColumnType("double precision"); + + b.Property("TaxesPrice") + .HasColumnType("double precision"); + + b.Property("TotalPrice") + .HasColumnType("double precision"); + + b.Property("TotalProductsPrice") + .HasColumnType("double precision"); + + b.Property("UserId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("DiscountId"); + + b.HasIndex("UserId"); + + b.ToTable("Orders", "public"); + }); + + modelBuilder.Entity("NetinaShop.Domain.Entities.Orders.OrderDelivery", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Address") + .IsRequired() + .HasColumnType("text"); + + b.Property("CreatedAt") + .HasColumnType("timestamp without time zone"); + + b.Property("CreatedBy") + .HasColumnType("text"); + + b.Property("DeliveryCost") + .HasColumnType("double precision"); + + b.Property("IsRemoved") + .HasColumnType("boolean"); + + b.Property("ModifiedAt") + .HasColumnType("timestamp without time zone"); + + b.Property("ModifiedBy") + .HasColumnType("text"); + + b.Property("OrderId") + .HasColumnType("uuid"); + + b.Property("PostalCode") + .IsRequired() + .HasColumnType("text"); + + b.Property("ReceiverFullName") + .IsRequired() + .HasColumnType("text"); + + b.Property("ReceiverPhoneNumber") + .IsRequired() + .HasColumnType("text"); + + b.Property("RemovedAt") + .HasColumnType("timestamp without time zone"); + + b.Property("RemovedBy") + .HasColumnType("text"); + + b.Property("ShippingId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("OrderId"); + + b.HasIndex("ShippingId"); + + b.ToTable("OrderDeliveries", "public"); + }); + + modelBuilder.Entity("NetinaShop.Domain.Entities.Orders.OrderProduct", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Count") + .HasColumnType("integer"); + + b.Property("CreatedAt") + .HasColumnType("timestamp without time zone"); + + b.Property("CreatedBy") + .HasColumnType("text"); + + b.Property("IsRemoved") + .HasColumnType("boolean"); + + b.Property("ModifiedAt") + .HasColumnType("timestamp without time zone"); + + b.Property("ModifiedBy") + .HasColumnType("text"); + + b.Property("OrderId") + .HasColumnType("uuid"); + + b.Property("OrderProductStatus") + .HasColumnType("integer"); + + b.Property("PackingCost") + .HasColumnType("double precision"); + + b.Property("PackingFee") + .HasColumnType("double precision"); + + b.Property("ProductCategoryId") + .HasColumnType("uuid"); + + b.Property("ProductCost") + .HasColumnType("double precision"); + + b.Property("ProductFee") + .HasColumnType("double precision"); + + b.Property("ProductId") + .HasColumnType("uuid"); + + b.Property("RemovedAt") + .HasColumnType("timestamp without time zone"); + + b.Property("RemovedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("OrderId"); + + b.HasIndex("ProductId"); + + b.ToTable("OrderProducts", "public"); + }); + + modelBuilder.Entity("NetinaShop.Domain.Entities.ProductCategories.ProductCategory", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedAt") + .HasColumnType("timestamp without time zone"); + + b.Property("CreatedBy") + .HasColumnType("text"); + + b.Property("Description") + .IsRequired() + .HasColumnType("text"); + + b.Property("IsMain") + .HasColumnType("boolean"); + + b.Property("IsRemoved") + .HasColumnType("boolean"); + + b.Property("ModifiedAt") + .HasColumnType("timestamp without time zone"); + + b.Property("ModifiedBy") + .HasColumnType("text"); + + b.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b.Property("ParentId") + .HasColumnType("uuid"); + + b.Property("RemovedAt") + .HasColumnType("timestamp without time zone"); + + b.Property("RemovedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("ParentId"); + + b.ToTable("ProductCategories", "public"); + }); + + modelBuilder.Entity("NetinaShop.Domain.Entities.Products.Product", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("BeDisplayed") + .HasColumnType("boolean"); + + b.Property("BrandId") + .HasColumnType("uuid"); + + b.Property("CategoryId") + .HasColumnType("uuid"); + + b.Property("Cost") + .HasColumnType("double precision"); + + b.Property("CreatedAt") + .HasColumnType("timestamp without time zone"); + + b.Property("CreatedBy") + .HasColumnType("text"); + + b.Property("EnglishName") + .IsRequired() + .HasColumnType("text"); + + b.Property("ExpertCheck") + .IsRequired() + .HasColumnType("text"); + + b.Property("HasExpressDelivery") + .HasColumnType("boolean"); + + b.Property("IsEnable") + .HasColumnType("boolean"); + + b.Property("IsRemoved") + .HasColumnType("boolean"); + + b.Property("MaxOrderCount") + .HasColumnType("integer"); + + b.Property("ModifiedAt") + .HasColumnType("timestamp without time zone"); + + b.Property("ModifiedBy") + .HasColumnType("text"); + + b.Property("PackingCost") + .HasColumnType("double precision"); + + b.Property("PersianName") + .IsRequired() + .HasColumnType("text"); + + b.Property("Rate") + .HasColumnType("real"); + + b.Property("RemovedAt") + .HasColumnType("timestamp without time zone"); + + b.Property("RemovedBy") + .HasColumnType("text"); + + b.Property("ReviewCount") + .HasColumnType("integer"); + + b.Property("Summery") + .IsRequired() + .HasColumnType("text"); + + b.Property("Tags") + .IsRequired() + .HasColumnType("text"); + + b.Property("Viewed") + .HasColumnType("integer"); + + b.Property("Warranty") + .IsRequired() + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("BrandId"); + + b.HasIndex("CategoryId"); + + b.ToTable("Products", "public"); + }); + + modelBuilder.Entity("NetinaShop.Domain.Entities.Products.Review", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Comment") + .IsRequired() + .HasColumnType("text"); + + b.Property("CreatedAt") + .HasColumnType("timestamp without time zone"); + + b.Property("CreatedBy") + .HasColumnType("text"); + + b.Property("IsBuyer") + .HasColumnType("boolean"); + + b.Property("IsConfirmed") + .HasColumnType("boolean"); + + b.Property("IsRemoved") + .HasColumnType("boolean"); + + b.Property("ModifiedAt") + .HasColumnType("timestamp without time zone"); + + b.Property("ModifiedBy") + .HasColumnType("text"); + + b.Property("ProductId") + .HasColumnType("uuid"); + + b.Property("Rate") + .HasColumnType("real"); + + b.Property("RemovedAt") + .HasColumnType("timestamp without time zone"); + + b.Property("RemovedBy") + .HasColumnType("text"); + + b.Property("Title") + .IsRequired() + .HasColumnType("text"); + + b.Property("UserId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("ProductId"); + + b.HasIndex("UserId"); + + b.ToTable("Reviews", "public"); + }); + + modelBuilder.Entity("NetinaShop.Domain.Entities.Products.Specification", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedAt") + .HasColumnType("timestamp without time zone"); + + b.Property("CreatedBy") + .HasColumnType("text"); + + b.Property("Detail") + .IsRequired() + .HasColumnType("text"); + + b.Property("IsFeature") + .HasColumnType("boolean"); + + b.Property("IsRemoved") + .HasColumnType("boolean"); + + b.Property("ModifiedAt") + .HasColumnType("timestamp without time zone"); + + b.Property("ModifiedBy") + .HasColumnType("text"); + + b.Property("ParentId") + .HasColumnType("uuid"); + + b.Property("ProductId") + .HasColumnType("uuid"); + + b.Property("RemovedAt") + .HasColumnType("timestamp without time zone"); + + b.Property("RemovedBy") + .HasColumnType("text"); + + b.Property("Title") + .IsRequired() + .HasColumnType("text"); + + b.Property("Value") + .IsRequired() + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("ParentId"); + + b.HasIndex("ProductId"); + + b.ToTable("Specifications", "public"); + }); + + modelBuilder.Entity("NetinaShop.Domain.Entities.StorageFiles.StorageFile", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedAt") + .HasColumnType("timestamp without time zone"); + + b.Property("CreatedBy") + .HasColumnType("text"); + + b.Property("Discriminator") + .IsRequired() + .HasMaxLength(34) + .HasColumnType("character varying(34)"); + + b.Property("FileLocation") + .IsRequired() + .HasColumnType("text"); + + b.Property("FileName") + .IsRequired() + .HasColumnType("text"); + + b.Property("FileType") + .HasColumnType("integer"); + + b.Property("IsHeader") + .HasColumnType("boolean"); + + b.Property("IsPrimary") + .HasColumnType("boolean"); + + b.Property("IsRemoved") + .HasColumnType("boolean"); + + b.Property("ModifiedAt") + .HasColumnType("timestamp without time zone"); + + b.Property("ModifiedBy") + .HasColumnType("text"); + + b.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b.Property("RemovedAt") + .HasColumnType("timestamp without time zone"); + + b.Property("RemovedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.ToTable("StorageFiles", "public"); + + b.HasDiscriminator("Discriminator").HasValue("StorageFile"); + + b.UseTphMappingStrategy(); + }); + + modelBuilder.Entity("NetinaShop.Domain.Entities.Users.ApplicationRole", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasColumnType("text"); + + b.Property("Description") + .IsRequired() + .HasColumnType("text"); + + b.Property("EnglishName") + .IsRequired() + .HasColumnType("text"); + + b.Property("Name") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("NormalizedName") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("PersianName") + .IsRequired() + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("NormalizedName") + .IsUnique() + .HasDatabaseName("RoleNameIndex"); + + b.ToTable("Roles", "public"); + }); + + modelBuilder.Entity("NetinaShop.Domain.Entities.Users.ApplicationUser", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("AccessFailedCount") + .HasColumnType("integer"); + + b.Property("BirthDate") + .HasColumnType("timestamp without time zone"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasColumnType("text"); + + b.Property("Email") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("EmailConfirmed") + .HasColumnType("boolean"); + + b.Property("FirstName") + .IsRequired() + .HasColumnType("text"); + + b.Property("Gender") + .HasColumnType("integer"); + + b.Property("LastName") + .IsRequired() + .HasColumnType("text"); + + b.Property("LockoutEnabled") + .HasColumnType("boolean"); + + b.Property("LockoutEnd") + .HasColumnType("timestamp with time zone"); + + b.Property("NationalId") + .IsRequired() + .HasColumnType("text"); + + b.Property("NormalizedEmail") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("NormalizedUserName") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("PasswordHash") + .HasColumnType("text"); + + b.Property("PhoneNumber") + .HasColumnType("text"); + + b.Property("PhoneNumberConfirmed") + .HasColumnType("boolean"); + + b.Property("SecurityStamp") + .HasColumnType("text"); + + b.Property("SignUpStatus") + .HasColumnType("integer"); + + b.Property("TwoFactorEnabled") + .HasColumnType("boolean"); + + b.Property("UserName") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.HasKey("Id"); + + b.HasIndex("NormalizedEmail") + .HasDatabaseName("EmailIndex"); + + b.HasIndex("NormalizedUserName") + .IsUnique() + .HasDatabaseName("UserNameIndex"); + + b.ToTable("Users", "public"); + }); + + modelBuilder.Entity("NetinaShop.Domain.Entities.Users.UserAddress", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Address") + .IsRequired() + .HasColumnType("text"); + + b.Property("CreatedAt") + .HasColumnType("timestamp without time zone"); + + b.Property("CreatedBy") + .HasColumnType("text"); + + b.Property("IsRemoved") + .HasColumnType("boolean"); + + b.Property("LocationLat") + .HasColumnType("real"); + + b.Property("LocationLong") + .HasColumnType("real"); + + b.Property("ModifiedAt") + .HasColumnType("timestamp without time zone"); + + b.Property("ModifiedBy") + .HasColumnType("text"); + + b.Property("PostalCode") + .IsRequired() + .HasColumnType("text"); + + b.Property("ReceiverFullName") + .IsRequired() + .HasColumnType("text"); + + b.Property("ReceiverPhoneNumber") + .IsRequired() + .HasColumnType("text"); + + b.Property("RemovedAt") + .HasColumnType("timestamp without time zone"); + + b.Property("RemovedBy") + .HasColumnType("text"); + + b.Property("UserId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("UserAddresses", "public"); + }); + + modelBuilder.Entity("NetinaShop.Domain.Entities.Users.UserFavoriteProduct", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedAt") + .HasColumnType("timestamp without time zone"); + + b.Property("CreatedBy") + .HasColumnType("text"); + + b.Property("IsRemoved") + .HasColumnType("boolean"); + + b.Property("ModifiedAt") + .HasColumnType("timestamp without time zone"); + + b.Property("ModifiedBy") + .HasColumnType("text"); + + b.Property("ProductId") + .HasColumnType("uuid"); + + b.Property("RemovedAt") + .HasColumnType("timestamp without time zone"); + + b.Property("RemovedBy") + .HasColumnType("text"); + + b.Property("UserId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("ProductId"); + + b.HasIndex("UserId"); + + b.ToTable("UserFavoriteProducts", "public"); + }); + + modelBuilder.Entity("NetinaShop.Domain.Entities.Warehouses.Shipping", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedAt") + .HasColumnType("timestamp without time zone"); + + b.Property("CreatedBy") + .HasColumnType("text"); + + b.Property("DeliveryCost") + .HasColumnType("double precision"); + + b.Property("IsExpressShipping") + .HasColumnType("boolean"); + + b.Property("IsOriginalWarehouse") + .HasColumnType("boolean"); + + b.Property("IsRemoved") + .HasColumnType("boolean"); + + b.Property("IsShipBySeller") + .HasColumnType("boolean"); + + b.Property("ModifiedAt") + .HasColumnType("timestamp without time zone"); + + b.Property("ModifiedBy") + .HasColumnType("text"); + + b.Property("RemovedAt") + .HasColumnType("timestamp without time zone"); + + b.Property("RemovedBy") + .HasColumnType("text"); + + b.Property("Title") + .IsRequired() + .HasColumnType("text"); + + b.Property("WarehouseName") + .IsRequired() + .HasColumnType("text"); + + b.HasKey("Id"); + + b.ToTable("Shippings", "public"); + }); + + modelBuilder.Entity("NetinaShop.Domain.Entities.Discounts.CategoryDiscount", b => + { + b.HasBaseType("NetinaShop.Domain.Entities.Discounts.Discount"); + + b.Property("CategoryId") + .HasColumnType("uuid"); + + b.HasIndex("CategoryId"); + + b.HasDiscriminator().HasValue("CategoryDiscount"); + }); + + modelBuilder.Entity("NetinaShop.Domain.Entities.Discounts.ProductDiscount", b => + { + b.HasBaseType("NetinaShop.Domain.Entities.Discounts.Discount"); + + b.Property("ProductId") + .HasColumnType("uuid"); + + b.HasIndex("ProductId"); + + b.HasDiscriminator().HasValue("ProductDiscount"); + }); + + modelBuilder.Entity("NetinaShop.Domain.Entities.Blogs.BlogStorageFile", b => + { + b.HasBaseType("NetinaShop.Domain.Entities.StorageFiles.StorageFile"); + + b.Property("BlogId") + .HasColumnType("uuid"); + + b.HasIndex("BlogId"); + + b.HasDiscriminator().HasValue("BlogStorageFile"); + }); + + modelBuilder.Entity("NetinaShop.Domain.Entities.Brands.BrandStorageFile", b => + { + b.HasBaseType("NetinaShop.Domain.Entities.StorageFiles.StorageFile"); + + b.Property("BrandId") + .HasColumnType("uuid"); + + b.HasIndex("BrandId"); + + b.HasDiscriminator().HasValue("BrandStorageFile"); + }); + + modelBuilder.Entity("NetinaShop.Domain.Entities.ProductCategories.ProductCategoryStorageFile", b => + { + b.HasBaseType("NetinaShop.Domain.Entities.StorageFiles.StorageFile"); + + b.Property("CategoryId") + .HasColumnType("uuid"); + + b.HasIndex("CategoryId"); + + b.HasDiscriminator().HasValue("ProductCategoryStorageFile"); + }); + + modelBuilder.Entity("NetinaShop.Domain.Entities.Products.ProductStorageFile", b => + { + b.HasBaseType("NetinaShop.Domain.Entities.StorageFiles.StorageFile"); + + b.Property("ProductId") + .HasColumnType("uuid"); + + b.HasIndex("ProductId"); + + b.HasDiscriminator().HasValue("ProductStorageFile"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => + { + b.HasOne("NetinaShop.Domain.Entities.Users.ApplicationRole", null) + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.HasOne("NetinaShop.Domain.Entities.Users.ApplicationUser", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.HasOne("NetinaShop.Domain.Entities.Users.ApplicationUser", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => + { + b.HasOne("NetinaShop.Domain.Entities.Users.ApplicationRole", null) + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("NetinaShop.Domain.Entities.Users.ApplicationUser", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.HasOne("NetinaShop.Domain.Entities.Users.ApplicationUser", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + }); + + modelBuilder.Entity("NetinaShop.Domain.Entities.Blogs.Blog", b => + { + b.HasOne("NetinaShop.Domain.Entities.Blogs.BlogCategory", "Category") + .WithMany("Blogs") + .HasForeignKey("CategoryId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Category"); + }); + + modelBuilder.Entity("NetinaShop.Domain.Entities.Orders.Order", b => + { + b.HasOne("NetinaShop.Domain.Entities.Discounts.Discount", null) + .WithMany("Orders") + .HasForeignKey("DiscountId"); + + b.HasOne("NetinaShop.Domain.Entities.Users.ApplicationUser", "User") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("User"); + }); + + modelBuilder.Entity("NetinaShop.Domain.Entities.Orders.OrderDelivery", b => + { + b.HasOne("NetinaShop.Domain.Entities.Orders.Order", "Order") + .WithMany("OrderDeliveries") + .HasForeignKey("OrderId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("NetinaShop.Domain.Entities.Warehouses.Shipping", "Shipping") + .WithMany() + .HasForeignKey("ShippingId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Order"); + + b.Navigation("Shipping"); + }); + + modelBuilder.Entity("NetinaShop.Domain.Entities.Orders.OrderProduct", b => + { + b.HasOne("NetinaShop.Domain.Entities.Orders.Order", "Order") + .WithMany("OrderProducts") + .HasForeignKey("OrderId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("NetinaShop.Domain.Entities.Products.Product", "Product") + .WithMany("OrderProducts") + .HasForeignKey("ProductId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Order"); + + b.Navigation("Product"); + }); + + modelBuilder.Entity("NetinaShop.Domain.Entities.ProductCategories.ProductCategory", b => + { + b.HasOne("NetinaShop.Domain.Entities.ProductCategories.ProductCategory", "Parent") + .WithMany() + .HasForeignKey("ParentId"); + + b.Navigation("Parent"); + }); + + modelBuilder.Entity("NetinaShop.Domain.Entities.Products.Product", b => + { + b.HasOne("NetinaShop.Domain.Entities.Brands.Brand", "Brand") + .WithMany("Products") + .HasForeignKey("BrandId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("NetinaShop.Domain.Entities.ProductCategories.ProductCategory", "Category") + .WithMany("Products") + .HasForeignKey("CategoryId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Brand"); + + b.Navigation("Category"); + }); + + modelBuilder.Entity("NetinaShop.Domain.Entities.Products.Review", b => + { + b.HasOne("NetinaShop.Domain.Entities.Products.Product", "Product") + .WithMany("Reviews") + .HasForeignKey("ProductId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("NetinaShop.Domain.Entities.Users.ApplicationUser", "User") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Product"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("NetinaShop.Domain.Entities.Products.Specification", b => + { + b.HasOne("NetinaShop.Domain.Entities.Products.Specification", "Parent") + .WithMany("Children") + .HasForeignKey("ParentId"); + + b.HasOne("NetinaShop.Domain.Entities.Products.Product", "Product") + .WithMany("Specifications") + .HasForeignKey("ProductId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Parent"); + + b.Navigation("Product"); + }); + + modelBuilder.Entity("NetinaShop.Domain.Entities.Users.UserAddress", b => + { + b.HasOne("NetinaShop.Domain.Entities.Users.ApplicationUser", "User") + .WithMany("Addresses") + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("User"); + }); + + modelBuilder.Entity("NetinaShop.Domain.Entities.Users.UserFavoriteProduct", b => + { + b.HasOne("NetinaShop.Domain.Entities.Products.Product", "Product") + .WithMany() + .HasForeignKey("ProductId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("NetinaShop.Domain.Entities.Users.ApplicationUser", "User") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Product"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("NetinaShop.Domain.Entities.Discounts.CategoryDiscount", b => + { + b.HasOne("NetinaShop.Domain.Entities.ProductCategories.ProductCategory", "Category") + .WithMany() + .HasForeignKey("CategoryId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Category"); + }); + + modelBuilder.Entity("NetinaShop.Domain.Entities.Discounts.ProductDiscount", b => + { + b.HasOne("NetinaShop.Domain.Entities.Products.Product", "Product") + .WithMany() + .HasForeignKey("ProductId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Product"); + }); + + modelBuilder.Entity("NetinaShop.Domain.Entities.Blogs.BlogStorageFile", b => + { + b.HasOne("NetinaShop.Domain.Entities.Blogs.Blog", "Blog") + .WithMany("Files") + .HasForeignKey("BlogId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Blog"); + }); + + modelBuilder.Entity("NetinaShop.Domain.Entities.Brands.BrandStorageFile", b => + { + b.HasOne("NetinaShop.Domain.Entities.Brands.Brand", "Brand") + .WithMany("Files") + .HasForeignKey("BrandId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Brand"); + }); + + modelBuilder.Entity("NetinaShop.Domain.Entities.ProductCategories.ProductCategoryStorageFile", b => + { + b.HasOne("NetinaShop.Domain.Entities.ProductCategories.ProductCategory", "Category") + .WithMany("Files") + .HasForeignKey("CategoryId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Category"); + }); + + modelBuilder.Entity("NetinaShop.Domain.Entities.Products.ProductStorageFile", b => + { + b.HasOne("NetinaShop.Domain.Entities.Products.Product", "Product") + .WithMany("Files") + .HasForeignKey("ProductId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Product"); + }); + + modelBuilder.Entity("NetinaShop.Domain.Entities.Blogs.Blog", b => + { + b.Navigation("Files"); + }); + + modelBuilder.Entity("NetinaShop.Domain.Entities.Blogs.BlogCategory", b => + { + b.Navigation("Blogs"); + }); + + modelBuilder.Entity("NetinaShop.Domain.Entities.Brands.Brand", b => + { + b.Navigation("Files"); + + b.Navigation("Products"); + }); + + modelBuilder.Entity("NetinaShop.Domain.Entities.Discounts.Discount", b => + { + b.Navigation("Orders"); + }); + + modelBuilder.Entity("NetinaShop.Domain.Entities.Orders.Order", b => + { + b.Navigation("OrderDeliveries"); + + b.Navigation("OrderProducts"); + }); + + modelBuilder.Entity("NetinaShop.Domain.Entities.ProductCategories.ProductCategory", b => + { + b.Navigation("Files"); + + b.Navigation("Products"); + }); + + modelBuilder.Entity("NetinaShop.Domain.Entities.Products.Product", b => + { + b.Navigation("Files"); + + b.Navigation("OrderProducts"); + + b.Navigation("Reviews"); + + b.Navigation("Specifications"); + }); + + modelBuilder.Entity("NetinaShop.Domain.Entities.Products.Specification", b => + { + b.Navigation("Children"); + }); + + modelBuilder.Entity("NetinaShop.Domain.Entities.Users.ApplicationUser", b => + { + b.Navigation("Addresses"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/NetinaShop.Repository/Migrations/20240129072040_editSpecificationNullParent.cs b/NetinaShop.Repository/Migrations/20240129072040_editSpecificationNullParent.cs new file mode 100644 index 0000000..f9aba60 --- /dev/null +++ b/NetinaShop.Repository/Migrations/20240129072040_editSpecificationNullParent.cs @@ -0,0 +1,68 @@ +using System; +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace NetinaShop.Repository.Migrations +{ + /// + public partial class editSpecificationNullParent : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropForeignKey( + name: "FK_Specifications_Specifications_ParentId", + schema: "public", + table: "Specifications"); + + migrationBuilder.AlterColumn( + name: "ParentId", + schema: "public", + table: "Specifications", + type: "uuid", + nullable: true, + oldClrType: typeof(Guid), + oldType: "uuid"); + + migrationBuilder.AddForeignKey( + name: "FK_Specifications_Specifications_ParentId", + schema: "public", + table: "Specifications", + column: "ParentId", + principalSchema: "public", + principalTable: "Specifications", + principalColumn: "Id"); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropForeignKey( + name: "FK_Specifications_Specifications_ParentId", + schema: "public", + table: "Specifications"); + + migrationBuilder.AlterColumn( + name: "ParentId", + schema: "public", + table: "Specifications", + type: "uuid", + nullable: false, + defaultValue: new Guid("00000000-0000-0000-0000-000000000000"), + oldClrType: typeof(Guid), + oldType: "uuid", + oldNullable: true); + + migrationBuilder.AddForeignKey( + name: "FK_Specifications_Specifications_ParentId", + schema: "public", + table: "Specifications", + column: "ParentId", + principalSchema: "public", + principalTable: "Specifications", + principalColumn: "Id", + onDelete: ReferentialAction.Restrict); + } + } +} diff --git a/NetinaShop.Repository/Migrations/ApplicationContextModelSnapshot.cs b/NetinaShop.Repository/Migrations/ApplicationContextModelSnapshot.cs index 411a713..af91875 100644 --- a/NetinaShop.Repository/Migrations/ApplicationContextModelSnapshot.cs +++ b/NetinaShop.Repository/Migrations/ApplicationContextModelSnapshot.cs @@ -802,7 +802,7 @@ namespace NetinaShop.Repository.Migrations b.Property("ModifiedBy") .HasColumnType("text"); - b.Property("ParentId") + b.Property("ParentId") .HasColumnType("uuid"); b.Property("ProductId") @@ -1409,9 +1409,7 @@ namespace NetinaShop.Repository.Migrations { b.HasOne("NetinaShop.Domain.Entities.Products.Specification", "Parent") .WithMany("Children") - .HasForeignKey("ParentId") - .OnDelete(DeleteBehavior.Restrict) - .IsRequired(); + .HasForeignKey("ParentId"); b.HasOne("NetinaShop.Domain.Entities.Products.Product", "Product") .WithMany("Specifications")