feat : complete create and edit product , add file controller
add upload and get files in file controller , complete create and edit product with uploading imagesrelease
parent
1c780f9fac
commit
45a4791cd9
|
@ -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<IResult> GetFilesAsync([FromQuery]StorageFileType? fileType,[FromServices] IStorageService storageService, CancellationToken cancellationToken)
|
||||
=> TypedResults.Ok(await storageService.GetStorageFiles(fileType: fileType ?? StorageFileType.Image));
|
||||
public async Task<IResult> 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);
|
||||
}
|
||||
}
|
|
@ -52,7 +52,7 @@
|
|||
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.5.0" />
|
||||
<PackageReference Include="Swashbuckle.AspNetCore.Annotations" Version="6.5.0" />
|
||||
<PackageReference Include="Swashbuckle.AspNetCore.Filters" Version="7.0.12" />
|
||||
<PackageReference Include="System.Data.SqlClient" Version="4.8.5" />
|
||||
<PackageReference Include="System.Data.SqlClient" Version="4.8.6" />
|
||||
<PackageReference Include="System.Drawing.Common" Version="8.0.0" />
|
||||
</ItemGroup>
|
||||
|
||||
|
@ -74,8 +74,10 @@
|
|||
<Using Include="NetinaShop.Common.Models.Exception" />
|
||||
<Using Include="NetinaShop.Common.Models.Mapper" />
|
||||
<Using Include="NetinaShop.Core" />
|
||||
<Using Include="NetinaShop.Core.Abstracts" />
|
||||
<Using Include="NetinaShop.Core.CoreServices.Abstracts" />
|
||||
<Using Include="NetinaShop.Core.Models.Api" />
|
||||
<Using Include="NetinaShop.Core.Utilities" />
|
||||
<Using Include="NetinaShop.Domain" />
|
||||
<Using Include="NetinaShop.Domain.CommandQueries.Commands" />
|
||||
<Using Include="NetinaShop.Domain.CommandQueries.Queries" />
|
||||
|
|
|
@ -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; }
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
|
@ -24,7 +24,7 @@
|
|||
<PackageReference Include="MD.PersianDateTime.Standard" Version="2.5.0" />
|
||||
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
|
||||
<PackageReference Include="System.ComponentModel.Annotations" Version="5.0.0" />
|
||||
<PackageReference Include="System.IdentityModel.Tokens.Jwt" Version="7.0.3" />
|
||||
<PackageReference Include="System.IdentityModel.Tokens.Jwt" Version="7.2.0" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
|
|
@ -0,0 +1,9 @@
|
|||
namespace NetinaShop.Core.Abstracts;
|
||||
|
||||
public interface IStorageService : IScopedDependency
|
||||
{
|
||||
Task<string> UploadObjectFromFileAsync(string fileName, string filePath, string contentType, byte[] fileBytes);
|
||||
Task<string> UploadObjectFromFileAsync(string fileName, string filePath, string contentType, Stream fileStream);
|
||||
|
||||
Task<List<StorageFileSDto>> GetStorageFiles(StorageFileType fileType);
|
||||
}
|
|
@ -12,6 +12,7 @@
|
|||
<PackageReference Include="AspNetCoreRateLimit.Redis" Version="2.0.0" />
|
||||
<PackageReference Include="Autofac.Extras.Quartz" Version="9.0.0" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Mvc.Core" Version="2.2.5" />
|
||||
<PackageReference Include="SixLabors.ImageSharp" Version="3.1.0" />
|
||||
<PackageReference Include="Quartz" Version="3.8.0" />
|
||||
</ItemGroup>
|
||||
|
||||
|
@ -22,10 +23,10 @@
|
|||
|
||||
|
||||
<ItemGroup>
|
||||
<Folder Include="Abstracts\" />
|
||||
<Folder Include="BaseServices\Abstracts\" />
|
||||
<Folder Include="EntityServices\ReviewHandlers\" />
|
||||
<Folder Include="Models\Api\" />
|
||||
<Folder Include="Utilities\" />
|
||||
</ItemGroup>
|
||||
|
||||
|
||||
|
|
|
@ -0,0 +1,18 @@
|
|||
using SixLabors.ImageSharp;
|
||||
using SixLabors.ImageSharp.Processing;
|
||||
|
||||
namespace NetinaShop.Core.Utilities;
|
||||
|
||||
public static class ImageConvertor
|
||||
{
|
||||
public static async Task<Stream> 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;
|
||||
}
|
||||
}
|
|
@ -8,8 +8,15 @@ public class ProductLDto : BaseDto<ProductLDto,Product>
|
|||
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<SpecificationSDto> Specifications { get; set; } = new();
|
||||
public List<ReviewSDto> Reviews { get; set; } = new();
|
||||
|
|
|
@ -1,11 +1,26 @@
|
|||
namespace NetinaShop.Domain.Dtos.SmallDtos;
|
||||
using Microsoft.IdentityModel.Tokens;
|
||||
|
||||
namespace NetinaShop.Domain.Dtos.SmallDtos;
|
||||
|
||||
public class StorageFileSDto : BaseDto<StorageFileSDto , StorageFile>
|
||||
{
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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; }
|
||||
|
|
|
@ -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<Specification> Children { get; internal set; } = new();
|
||||
|
|
|
@ -2,6 +2,8 @@
|
|||
|
||||
public enum StorageFileType
|
||||
{
|
||||
[Display(Name = "Images")]
|
||||
Image,
|
||||
[Display(Name = "Videos")]
|
||||
Video
|
||||
}
|
|
@ -1,6 +0,0 @@
|
|||
namespace NetinaShop.Domain.Mappers
|
||||
{
|
||||
public static partial class CategoryStorageFileMapper
|
||||
{
|
||||
}
|
||||
}
|
|
@ -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<SpecificationSDto, Specification>(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<Specification>(),
|
||||
Reviews = p13.Reviews.Select<ReviewSDto, Review>(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<Specification, SpecificationSDto>(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<SpecificationSDto>(),
|
||||
Reviews = p29.Reviews.Select<Review, ReviewSDto>(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++;
|
||||
|
|
|
@ -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
|
||||
};
|
||||
}
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="AWSSDK.S3" Version="3.7.305.17" />
|
||||
<PackageReference Include="Refit" Version="7.0.0" />
|
||||
</ItemGroup>
|
||||
|
||||
|
@ -19,14 +20,16 @@
|
|||
|
||||
<ItemGroup>
|
||||
<Folder Include="Models\" />
|
||||
<Folder Include="Services\" />
|
||||
</ItemGroup>
|
||||
|
||||
|
||||
<ItemGroup>
|
||||
<Using Include="Amazon.S3" />
|
||||
<Using Include="Amazon.S3.Model" />
|
||||
<Using Include="Microsoft.Extensions.Hosting" />
|
||||
<Using Include="Microsoft.Extensions.Logging" />
|
||||
<Using Include="Microsoft.Extensions.Options" />
|
||||
<Using Include="NetinaShop.Common.Extensions" />
|
||||
<Using Include="NetinaShop.Common.Models" />
|
||||
<Using Include="NetinaShop.Common.Models.Api" />
|
||||
<Using Include="NetinaShop.Common.Models.Exception" />
|
||||
|
|
|
@ -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<string> 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<string> 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<List<StorageFileSDto>> 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<StorageFileSDto>();
|
||||
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();
|
||||
}
|
||||
}
|
|
@ -32,6 +32,10 @@ public class GetProductsQueryHandler : IRequestHandler<GetProductsQuery, List<Pr
|
|||
_ => products
|
||||
};
|
||||
}
|
||||
else
|
||||
{
|
||||
products = products.OrderByDescending(p => p.CreatedAt);
|
||||
}
|
||||
|
||||
var productSDtos = await products.Skip(request.Page * 20)
|
||||
.Take(20)
|
||||
|
|
|
@ -31,7 +31,7 @@ public class UpdateProductCommandHandler : IRequestHandler<UpdateProductCommand,
|
|||
.Where(s => 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<Specification>().Delete(dbSpecification);
|
||||
await _repositoryWrapper.SaveChangesAsync(cancellationToken);
|
||||
|
@ -49,7 +49,7 @@ public class UpdateProductCommandHandler : IRequestHandler<UpdateProductCommand,
|
|||
.Where(s => 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<ProductStorageFile>().Delete(dbFile);
|
||||
await _repositoryWrapper.SaveChangesAsync(cancellationToken);
|
||||
|
|
1583
NetinaShop.Repository/Migrations/20240129072040_editSpecificationNullParent.Designer.cs
generated
100644
1583
NetinaShop.Repository/Migrations/20240129072040_editSpecificationNullParent.Designer.cs
generated
100644
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,68 @@
|
|||
using System;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace NetinaShop.Repository.Migrations
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public partial class editSpecificationNullParent : Migration
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropForeignKey(
|
||||
name: "FK_Specifications_Specifications_ParentId",
|
||||
schema: "public",
|
||||
table: "Specifications");
|
||||
|
||||
migrationBuilder.AlterColumn<Guid>(
|
||||
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");
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropForeignKey(
|
||||
name: "FK_Specifications_Specifications_ParentId",
|
||||
schema: "public",
|
||||
table: "Specifications");
|
||||
|
||||
migrationBuilder.AlterColumn<Guid>(
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -802,7 +802,7 @@ namespace NetinaShop.Repository.Migrations
|
|||
b.Property<string>("ModifiedBy")
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<Guid>("ParentId")
|
||||
b.Property<Guid?>("ParentId")
|
||||
.HasColumnType("uuid");
|
||||
|
||||
b.Property<Guid>("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")
|
||||
|
|
Loading…
Reference in New Issue