diff --git a/VizhTalk.BuildingBlocks.Domain/IRepositoryBase.cs b/VizhTalk.BuildingBlocks.Domain/IRepositoryBase.cs index 9da2e2e..f2dca7c 100644 --- a/VizhTalk.BuildingBlocks.Domain/IRepositoryBase.cs +++ b/VizhTalk.BuildingBlocks.Domain/IRepositoryBase.cs @@ -9,7 +9,7 @@ public interface IRepositoryBase Task UpdateAsync(T entity, CancellationToken cancellationToken = default); Task DeleteAsync(T entity, CancellationToken cancellationToken = default); Task DeleteById(TId id, CancellationToken cancellationToken = default); - Task GetById(TId id, CancellationToken cancellationToken = default); - Task Exists(Expression> predicate, CancellationToken cancellationToken = default); - Task Get(Expression> predicate, CancellationToken cancellationToken = default); + Task GetByIdAsync(TId id, CancellationToken cancellationToken = default); + Task ExistsAsync(Expression> predicate, CancellationToken cancellationToken = default); + Task GetAsync(Expression> predicate, CancellationToken cancellationToken = default); } diff --git a/VizhTalk.BuildingBlocks.Infrastructure/RepositoryBase.cs b/VizhTalk.BuildingBlocks.Infrastructure/RepositoryBase.cs index eeb434d..16b36e0 100644 --- a/VizhTalk.BuildingBlocks.Infrastructure/RepositoryBase.cs +++ b/VizhTalk.BuildingBlocks.Infrastructure/RepositoryBase.cs @@ -49,13 +49,13 @@ namespace VizhTalk.BuildingBlocks.Infrastructure entity.SetDeleted(true); } - public async Task Exists(Expression> predicate, CancellationToken cancellationToken = default) + public async Task ExistsAsync(Expression> predicate, CancellationToken cancellationToken = default) => await _dbContext.Set().Where(predicate).AnyAsync(cancellationToken); - public async Task Get(Expression> predicate, CancellationToken cancellationToken = default) - => await _dbContext.Set().Where(predicate).FirstOrDefaultAsync(predicate, cancellationToken); + public async Task GetAsync(Expression> predicate, CancellationToken cancellationToken = default) + => await _dbContext.Set().FirstOrDefaultAsync(predicate, cancellationToken); - public async Task GetById(TId id, CancellationToken cancellationToken = default) + public async Task GetByIdAsync(TId id, CancellationToken cancellationToken = default) { cancellationToken.ThrowIfCancellationRequested(); diff --git a/VizhTalk.Gateway.BFF/Mappers/UploadMapper.cs b/VizhTalk.Gateway.BFF/Mappers/UploadMapper.cs index c79db7f..118170e 100644 --- a/VizhTalk.Gateway.BFF/Mappers/UploadMapper.cs +++ b/VizhTalk.Gateway.BFF/Mappers/UploadMapper.cs @@ -7,7 +7,7 @@ namespace VizhTalk.Gateway.Web.BFF.Mappers { public static async Task AsFile(this IFile file) { - MemoryStream stream = new MemoryStream(); + using MemoryStream stream = new MemoryStream(); await file.CopyToAsync(stream); return new UploadFile() { diff --git a/VizhTalk.Gateway.BFF/Program.cs b/VizhTalk.Gateway.BFF/Program.cs index 4190ba2..3ba14ad 100644 --- a/VizhTalk.Gateway.BFF/Program.cs +++ b/VizhTalk.Gateway.BFF/Program.cs @@ -13,7 +13,8 @@ builder.Services.AddScoped(); builder.AddGraphQL() .AddQueryType() - .AddMutationType(); + .AddMutationType() + .AddType(); var app = builder.Build(); diff --git a/VizhTalk.Learning.Application/CommandHandlers/LanguageCommandHandler.cs b/VizhTalk.Learning.Application/CommandHandlers/LanguageCommandHandler.cs index 9472dcd..c03f405 100644 --- a/VizhTalk.Learning.Application/CommandHandlers/LanguageCommandHandler.cs +++ b/VizhTalk.Learning.Application/CommandHandlers/LanguageCommandHandler.cs @@ -23,7 +23,7 @@ public class CreateLanguageCommandHandler(IUnitOfWork unitOfWork, try { var title = request.Title.Trim(); - if (await unitOfWork.Language.Exists(x => x.Title == title)) + if (await unitOfWork.Language.ExistsAsync(x => x.Title == title)) throw new LanguageTitleAlreadyExistsException(); var fileGroup = await unitOfWork.FileGroup.GetByFileGroupKey(FileGroupKey.IconLanguage); @@ -71,7 +71,7 @@ public class UpdateLanguageCommandHandler(IUnitOfWork unitOfWork, CDNGrpc.CDNGrp { public async Task Handle(UpdateLanguageCommand request, CancellationToken cancellationToken) { - var language = await unitOfWork.Language.GetById(new LanguageId(request.Id), cancellationToken); + var language = await unitOfWork.Language.GetByIdAsync(new LanguageId(request.Id), cancellationToken); if (language == null) throw new LanguageNotFoundException(); diff --git a/VizhTalk.Learning.Presentation/Services/LanguageService.cs b/VizhTalk.Learning.Presentation/Services/LanguageService.cs index 657ef32..ae869e8 100644 --- a/VizhTalk.Learning.Presentation/Services/LanguageService.cs +++ b/VizhTalk.Learning.Presentation/Services/LanguageService.cs @@ -17,6 +17,7 @@ public class LanguageService(ILearningModule learningModule) : LanguageGrpc.Lang } public override async Task GetByTitle(GetLanguageByTitleQuery request, ServerCallContext context) { + await Task.Delay(10000); return await learningModule.Execute(request, context.CancellationToken); } public override async Task GetAll(GetAllLanguageQuery request, ServerCallContext context) diff --git a/VizhTalk.Services.Grpc.Learning/Pagination.cs b/VizhTalk.Services.Grpc.Learning/Pagination.cs index 2e3b9d1..d3475fe 100644 --- a/VizhTalk.Services.Grpc.Learning/Pagination.cs +++ b/VizhTalk.Services.Grpc.Learning/Pagination.cs @@ -9,8 +9,8 @@ namespace VizhTalk.Grpc.Learning public sealed partial class Pagination { public int Skip() - => PageNumber * (PageSize == 0 ? 10 : PageSize); + => (PageNumber - 1) * (PageSize == 0 ? 10 : PageSize); public int Take() - => PageNumber; + => PageSize; } } diff --git a/Vizhtalk.CDN.Presentation/Application/CommandHandlers/MediaCommandHandler.cs b/Vizhtalk.CDN.Presentation/Application/CommandHandlers/MediaCommandHandler.cs index 375d0e2..6186b28 100644 --- a/Vizhtalk.CDN.Presentation/Application/CommandHandlers/MediaCommandHandler.cs +++ b/Vizhtalk.CDN.Presentation/Application/CommandHandlers/MediaCommandHandler.cs @@ -1,13 +1,16 @@ -using System.Security.Cryptography; -using Grpc.Core; +using System.IO; +using System.Security.Cryptography; using Mapster; +using Microsoft.AspNetCore.Identity; +using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.StaticFiles; using Vizhtalk.CDN.Presentation.Application.Exceptions; using Vizhtalk.CDN.Presentation.Domain.Entities; +using Vizhtalk.CDN.Presentation.Domain.TypeIds; using Vizhtalk.CDN.Presentation.Domain.ValueObjects; +using Vizhtalk.CDN.Presentation.FileManagerServices; using VizhTalk.BuildingBlocks.Application.Configuration.Commnad; using VizhTalk.Grpc.CDN; -using static System.Runtime.InteropServices.JavaScript.JSType; namespace Vizhtalk.CDN.Presentation.Application.CommandHandlers; @@ -17,11 +20,11 @@ public class CreateMediaGroupCommandHandler(IUnitOfWork unitOfWork, CDNGrpc.CDNG { var title = request.Title.Trim(); - if (await unitOfWork.MediaGroup.Exists(a => a.Key == request.Key)) + if (await unitOfWork.MediaGroup.ExistsAsync(a => a.Key == request.Key)) throw new MediaGroupKeyAlreadyExistsException(); - var mediaGroup = MediaGroup.CreateNew(title,request.Key,request.IsSecure); + var mediaGroup = MediaGroup.CreateNew(title, request.Key, request.IsSecure); await unitOfWork.MediaGroup.AddAsync(mediaGroup, cancellationToken); await unitOfWork.SaveChangeAsync(cancellationToken); return mediaGroup.Adapt(); @@ -29,25 +32,44 @@ public class CreateMediaGroupCommandHandler(IUnitOfWork unitOfWork, CDNGrpc.CDNG } } -public class UploadFileCommandHandler(IUnitOfWork unitOfWork, CDNGrpc.CDNGrpcClient cdnClient) : ICommandHandler +public class UploadFileCommandHandler(IUnitOfWork unitOfWork, CDNGrpc.CDNGrpcClient cdnClient, IFileManagerService fileManager) : ICommandHandler { public async Task Handle(UploadFileCommand request, CancellationToken cancellationToken) { + cancellationToken.ThrowIfCancellationRequested(); - var fileBytes = request.File.ToByteArray(); + var mediaGroup = await unitOfWork.MediaGroup.GetByIdAsync(new MediaGroupId(request.MediaGroupId)); + if (mediaGroup == null) + throw new NotFoundMediaGroupException(); + if (mediaGroup.IsSecure && string.IsNullOrEmpty(request.ClientId)) + throw new MediaGroupIsSecureAndClientIsEmptyException(); + var fileBytes = request.File.File.ToArray(); var hash = ToSHA512Hash(fileBytes); - var media = await unitOfWork.Media.Get(a => a.Hash == hash); - + var media = await unitOfWork.Media.GetAsync(a => a.Hash == hash); if (media is null) { + var extension = ContentType.ContentTypeExtension.GetValueOrDefault(request.File.ContentType); + if (extension == null) + throw new ThisContentFileIsNotSupportedException(); - using MemoryStream stream = new MemoryStream(fileBytes); - + var path = await fileManager.SaveFile(fileBytes, extension, mediaGroup.Id); + media = Media.CreateNew(new ContentType(request.File.ContentType, fileBytes), fileBytes.Length, hash, path); } + var mediaGroupMedia = mediaGroup.AddMedia(new FileName(request.File.Name), media); + if (!string.IsNullOrEmpty(request.ClientId)) + mediaGroupMedia.AddMediaPermission(media, Client.CreateNew(new ClientId(request.ClientId))); - throw new NotImplementedException(); + await unitOfWork.MediaGroup.AddAsync(mediaGroup, cancellationToken); + await unitOfWork.SaveChangeAsync(cancellationToken); + + return new UploadFileDTO() + { + ContentType = request.File.ContentType, + MediaGroupMediaId = mediaGroupMedia.Id.Value, + Size = fileBytes.Length + }; } public string ToSHA512Hash(byte[] data) diff --git a/Vizhtalk.CDN.Presentation/Application/Exceptions/MediaGroupIsSecureAndClientIsEmptyException.cs b/Vizhtalk.CDN.Presentation/Application/Exceptions/MediaGroupIsSecureAndClientIsEmptyException.cs new file mode 100644 index 0000000..f7b17f2 --- /dev/null +++ b/Vizhtalk.CDN.Presentation/Application/Exceptions/MediaGroupIsSecureAndClientIsEmptyException.cs @@ -0,0 +1,11 @@ +using VizhTalk.BuildingBlocks.Application.Exceptions; + +namespace Vizhtalk.CDN.Presentation.Application.Exceptions +{ + public class MediaGroupIsSecureAndClientIsEmptyException : BaseException + { + public MediaGroupIsSecureAndClientIsEmptyException() : base("Media is a security group and the client is empty") + { + } + } +} diff --git a/Vizhtalk.CDN.Presentation/Application/Exceptions/NotFoundMediaGroupException.cs b/Vizhtalk.CDN.Presentation/Application/Exceptions/NotFoundMediaGroupException.cs new file mode 100644 index 0000000..c834b88 --- /dev/null +++ b/Vizhtalk.CDN.Presentation/Application/Exceptions/NotFoundMediaGroupException.cs @@ -0,0 +1,11 @@ +using VizhTalk.BuildingBlocks.Application.Exceptions; + +namespace Vizhtalk.CDN.Presentation.Application.Exceptions +{ + public class NotFoundMediaGroupException : BaseException + { + public NotFoundMediaGroupException() : base("not found media group") + { + } + } +} diff --git a/Vizhtalk.CDN.Presentation/Application/Exceptions/ThisContentFileIsNotSupportedException.cs b/Vizhtalk.CDN.Presentation/Application/Exceptions/ThisContentFileIsNotSupportedException.cs new file mode 100644 index 0000000..f138966 --- /dev/null +++ b/Vizhtalk.CDN.Presentation/Application/Exceptions/ThisContentFileIsNotSupportedException.cs @@ -0,0 +1,11 @@ +using VizhTalk.BuildingBlocks.Application.Exceptions; + +namespace Vizhtalk.CDN.Presentation.Application.Exceptions +{ + public class ThisContentFileIsNotSupportedException : BaseException + { + public ThisContentFileIsNotSupportedException() : base("This content file is not supported") + { + } + } +} diff --git a/Vizhtalk.CDN.Presentation/Application/Mappers/MapperConfig.cs b/Vizhtalk.CDN.Presentation/Application/Mappers/MapperConfig.cs index 04c95c7..444f32e 100644 --- a/Vizhtalk.CDN.Presentation/Application/Mappers/MapperConfig.cs +++ b/Vizhtalk.CDN.Presentation/Application/Mappers/MapperConfig.cs @@ -13,7 +13,7 @@ public class MapperConfig var config = TypeAdapterConfig.NewConfig() .MapWith(lan => new MediaGroupDTO() { - Id = lan.Id.Value + Id = lan.Id.Value, }); diff --git a/Vizhtalk.CDN.Presentation/Domain/Entities/Client.cs b/Vizhtalk.CDN.Presentation/Domain/Entities/Client.cs index 67fce33..3ecf518 100644 --- a/Vizhtalk.CDN.Presentation/Domain/Entities/Client.cs +++ b/Vizhtalk.CDN.Presentation/Domain/Entities/Client.cs @@ -5,6 +5,17 @@ namespace Vizhtalk.CDN.Presentation.Domain.Entities; public class Client : Entity { - private List _mediaClients = new(); - public IReadOnlyList MediaClients => _mediaClients; + private Client() + { + + } + private Client(ClientId id) + { + Id = id; + } + private List _mediaClients = new(); + public IReadOnlyList MediaClients => _mediaClients; + + public static Client CreateNew(ClientId clientId) + => new(clientId); } diff --git a/Vizhtalk.CDN.Presentation/Domain/Entities/Media.cs b/Vizhtalk.CDN.Presentation/Domain/Entities/Media.cs index 5037b24..6f7829b 100644 --- a/Vizhtalk.CDN.Presentation/Domain/Entities/Media.cs +++ b/Vizhtalk.CDN.Presentation/Domain/Entities/Media.cs @@ -26,6 +26,6 @@ public class Media : Entity public IReadOnlyList MediaGroupMedias => _mediaGroupMedia; - public static Media Create(ContentType contentType, int size, string hash, string path) + public static Media CreateNew(ContentType contentType, int size, string hash, string path) => new (contentType, size, hash, path); } \ No newline at end of file diff --git a/Vizhtalk.CDN.Presentation/Domain/Entities/MediaGroup.cs b/Vizhtalk.CDN.Presentation/Domain/Entities/MediaGroup.cs index 592e4f4..9d0815a 100644 --- a/Vizhtalk.CDN.Presentation/Domain/Entities/MediaGroup.cs +++ b/Vizhtalk.CDN.Presentation/Domain/Entities/MediaGroup.cs @@ -23,4 +23,10 @@ public class MediaGroup : Entity public static MediaGroup CreateNew(string title, string Key, bool isSecure) => new (new MediaGroupTitle(title), Key, isSecure); + public MediaGroupMedia AddMedia(FileName fileName, Media media) + { + var mediaGroupMedia = MediaGroupMedia.CreateNew(fileName, media, this); + _mediaGroupMedia.Add(mediaGroupMedia); + return mediaGroupMedia; + } } diff --git a/Vizhtalk.CDN.Presentation/Domain/Entities/MediaGroupMedia.cs b/Vizhtalk.CDN.Presentation/Domain/Entities/MediaGroupMedia.cs index 6cbd2cb..7eeb83a 100644 --- a/Vizhtalk.CDN.Presentation/Domain/Entities/MediaGroupMedia.cs +++ b/Vizhtalk.CDN.Presentation/Domain/Entities/MediaGroupMedia.cs @@ -1,14 +1,33 @@ using Vizhtalk.CDN.Presentation.Domain.TypeIds; +using Vizhtalk.CDN.Presentation.Domain.ValueObjects; using VizhTalk.BuildingBlocks.Domain; namespace Vizhtalk.CDN.Presentation.Domain.Entities; public class MediaGroupMedia : Entity { - private List _mediaClient = new(); - public MediaId MediaId { get; set; } - public MediaGroupId MediaGroupId { get; set; } - public Media Media { get; set; } - public MediaGroup MediaGroup { get; set; } - public IReadOnlyList MediaClients => _mediaClient; + private List _mediaPermissions = new(); + + private MediaGroupMedia(FileName fileName, + Media media, + MediaGroup mediaGroup) + { + Name = fileName; + Media = media; + MediaGroup = mediaGroup; + } + public FileName Name { get; private set; } + public MediaId MediaId { get; private set; } + public MediaGroupId MediaGroupId { get; private set; } + public Media Media { get; private set; } + public MediaGroup MediaGroup { get; private set; } + public IReadOnlyList MediaPermissions => _mediaPermissions; + + internal static MediaGroupMedia CreateNew(FileName fileName, + Media media, + MediaGroup mediaGroup) + => new(fileName, media, mediaGroup); + + public void AddMediaPermission(Media media, Client client) + => _mediaPermissions.Add(MediaPermission.CreateNew(media, client, this)); } diff --git a/Vizhtalk.CDN.Presentation/Domain/Entities/MediaClient.cs b/Vizhtalk.CDN.Presentation/Domain/Entities/MediaPermission.cs similarity index 50% rename from Vizhtalk.CDN.Presentation/Domain/Entities/MediaClient.cs rename to Vizhtalk.CDN.Presentation/Domain/Entities/MediaPermission.cs index 8b7cb77..58b4f33 100644 --- a/Vizhtalk.CDN.Presentation/Domain/Entities/MediaClient.cs +++ b/Vizhtalk.CDN.Presentation/Domain/Entities/MediaPermission.cs @@ -1,12 +1,20 @@ using Vizhtalk.CDN.Presentation.Domain.TypeIds; -using Vizhtalk.CDN.Presentation.Domain.ValueObjects; using VizhTalk.BuildingBlocks.Domain; namespace Vizhtalk.CDN.Presentation.Domain.Entities; -public class MediaClient : Entity +public class MediaPermission : Entity { - public MediaClientName Name { get; private set; } + private MediaPermission() + { + + } + private MediaPermission(Media media, Client client, MediaGroupMedia mediaGroupMedia) + { + Media = media; + Client = client; + MediaGroupMedia = mediaGroupMedia; + } public MediaId MediaId { get; private set; } public ClientId ClientId { get; private set; } public MediaGroupMediaId MediaGroupMediaId { get; set; } @@ -14,4 +22,7 @@ public class MediaClient : Entity public Client Client { get; private set; } public Media Media { get; private set; } public MediaGroupMedia MediaGroupMedia { get; private set; } + + public static MediaPermission CreateNew(Media media, Client client, MediaGroupMedia mediaGroupMedia) + => new(media, client, mediaGroupMedia); } diff --git a/Vizhtalk.CDN.Presentation/Domain/Repositories/IMediaClientRepository.cs b/Vizhtalk.CDN.Presentation/Domain/Repositories/IMediaClientRepository.cs index 2e30964..65494f3 100644 --- a/Vizhtalk.CDN.Presentation/Domain/Repositories/IMediaClientRepository.cs +++ b/Vizhtalk.CDN.Presentation/Domain/Repositories/IMediaClientRepository.cs @@ -4,4 +4,4 @@ using VizhTalk.BuildingBlocks.Domain; namespace Vizhtalk.CDN.Presentation.Domain.Repositories; -public interface IMediaClientRepository : IRepositoryBase; +public interface IMediaClientRepository : IRepositoryBase; diff --git a/Vizhtalk.CDN.Presentation/Domain/TypeIds/MediaGroupId.cs b/Vizhtalk.CDN.Presentation/Domain/TypeIds/MediaGroupId.cs index 6ffeab7..ac43390 100644 --- a/Vizhtalk.CDN.Presentation/Domain/TypeIds/MediaGroupId.cs +++ b/Vizhtalk.CDN.Presentation/Domain/TypeIds/MediaGroupId.cs @@ -2,9 +2,9 @@ namespace Vizhtalk.CDN.Presentation.Domain.TypeIds; -public class MediaGroupId : TypeIdValueBase +public class MediaGroupId : TypeIdValueBase { - public MediaGroupId(long value) : base(value) + public MediaGroupId(Guid value) : base(value) { } } diff --git a/Vizhtalk.CDN.Presentation/Domain/ValueObjects/ContentType.cs b/Vizhtalk.CDN.Presentation/Domain/ValueObjects/ContentType.cs index 556e6f1..6a40223 100644 --- a/Vizhtalk.CDN.Presentation/Domain/ValueObjects/ContentType.cs +++ b/Vizhtalk.CDN.Presentation/Domain/ValueObjects/ContentType.cs @@ -5,9 +5,42 @@ namespace Vizhtalk.CDN.Presentation.Domain.ValueObjects; public class ContentType : ValueObject { + public static readonly Dictionary ContentTypeExtension = new() + { + // Images + { "image/jpeg", ".jpg" }, + { "image/png", ".png" }, + { "image/gif", ".gif" }, + { "image/webp", ".webp" }, + { "image/bmp", ".bmp" }, + { "image/tiff", ".tiff" }, + { "image/svg+xml", ".svg" }, + + // Videos + { "video/mp4", ".mp4" }, + { "video/webm", ".webm" }, + { "video/ogg", ".ogv" }, + { "video/mpeg", ".mpeg" }, + { "video/3gpp", ".3gp" }, + { "video/3gpp2", ".3g2" }, + { "video/x-msvideo", ".avi" }, + { "video/x-matroska", ".mkv" }, + + // Audios + { "audio/mpeg", ".mp3" }, + { "audio/wav", ".wav" }, + { "audio/ogg", ".ogg" }, + { "audio/webm", ".weba" }, + { "audio/aac", ".aac" }, + { "audio/flac", ".flac" }, + { "audio/mp4", ".m4a" }, + { "audio/3gpp", ".3gp" }, + { "audio/3gpp2", ".3g2" } + }; + private ContentType() { - + } public ContentType(string contentType, byte[] fileBytes) { diff --git a/Vizhtalk.CDN.Presentation/Domain/ValueObjects/MediaClientName.cs b/Vizhtalk.CDN.Presentation/Domain/ValueObjects/FileName.cs similarity index 54% rename from Vizhtalk.CDN.Presentation/Domain/ValueObjects/MediaClientName.cs rename to Vizhtalk.CDN.Presentation/Domain/ValueObjects/FileName.cs index 85657f0..58c013c 100644 --- a/Vizhtalk.CDN.Presentation/Domain/ValueObjects/MediaClientName.cs +++ b/Vizhtalk.CDN.Presentation/Domain/ValueObjects/FileName.cs @@ -3,12 +3,12 @@ using VizhTalk.BuildingBlocks.Domain.Guards; namespace Vizhtalk.CDN.Presentation.Domain.ValueObjects { - public class MediaClientName : ValueObject + public class FileName : ValueObject { - public MediaClientName(string value) + public FileName(string value) { - value.NullOrEmpty(nameof(MediaClientName)); - value.MaxLength(nameof(MediaClientName), 150); + value.NullOrEmpty(nameof(FileName)); + value.MaxLength(nameof(FileName), 150); Value = value; } diff --git a/Vizhtalk.CDN.Presentation/Domain/ValueObjects/Rules/ValidContentTypeRule.cs b/Vizhtalk.CDN.Presentation/Domain/ValueObjects/Rules/ValidContentTypeRule.cs index 0beb6c7..a018c18 100644 --- a/Vizhtalk.CDN.Presentation/Domain/ValueObjects/Rules/ValidContentTypeRule.cs +++ b/Vizhtalk.CDN.Presentation/Domain/ValueObjects/Rules/ValidContentTypeRule.cs @@ -1,6 +1,7 @@ using System.Text.RegularExpressions; using System.Text; using VizhTalk.BuildingBlocks.Domain.Exceptions; +using System.Linq; namespace Vizhtalk.CDN.Presentation.Domain.ValueObjects.Rules; @@ -12,7 +13,7 @@ public class ValidContentTypeRule(string contentType, byte[] fileBytes) : IBusin public bool IsBroken() { // اول MimeType بررسی میشه - if (!_validContentTypes.Contains(contentType)) + if (!ContentType.ContentTypeExtension.ContainsKey(contentType)) return true; // بعد Magic Number بررسی میشه @@ -29,39 +30,6 @@ public class ValidContentTypeRule(string contentType, byte[] fileBytes) : IBusin return false; // یعنی مشکلی نیست } - private static readonly HashSet _validContentTypes = new HashSet(StringComparer.OrdinalIgnoreCase) - { - // Images - "image/jpeg", - "image/png", - "image/gif", - "image/webp", - "image/bmp", - "image/tiff", - "image/svg+xml", - - // Videos - "video/mp4", - "video/webm", - "video/ogg", - "video/mpeg", - "video/3gpp", - "video/3gpp2", - "video/x-msvideo", - "video/x-matroska", - - // Audios - "audio/mpeg", - "audio/wav", - "audio/ogg", - "audio/webm", - "audio/aac", - "audio/flac", - "audio/mp4", - "audio/3gpp", - "audio/3gpp2" - }; - private static bool HasValidMagicNumber(string contentType, byte[] fileBytes) { if (fileBytes == null || fileBytes.Length < 4) diff --git a/Vizhtalk.CDN.Presentation/FileManagerServices/FileManagerService.cs b/Vizhtalk.CDN.Presentation/FileManagerServices/FileManagerService.cs new file mode 100644 index 0000000..ab482e6 --- /dev/null +++ b/Vizhtalk.CDN.Presentation/FileManagerServices/FileManagerService.cs @@ -0,0 +1,21 @@ +using Vizhtalk.CDN.Presentation.Domain.Entities; +using Vizhtalk.CDN.Presentation.Domain.TypeIds; + +namespace Vizhtalk.CDN.Presentation.FileManagerServices; + +public class FileManagerService(IWebHostEnvironment env) : IFileManagerService +{ + public async Task SaveFile(byte[] fileBytes,string extension, MediaGroupId mediaGroupId) + { + var root = Path.Combine(env.ContentRootPath, "Files", mediaGroupId.Value.ToString()); + if (!Directory.Exists(root)) + Directory.CreateDirectory(root); + + + var fileName = Guid.NewGuid().ToString() + extension; + var filePath = Path.Combine(root, fileName); + using var streamFile = new FileStream(filePath, FileMode.Create); + await streamFile.WriteAsync(fileBytes, 0, fileBytes.Length); + return filePath; + } +} diff --git a/Vizhtalk.CDN.Presentation/FileManagerServices/IFileManagerService.cs b/Vizhtalk.CDN.Presentation/FileManagerServices/IFileManagerService.cs new file mode 100644 index 0000000..2e91ef5 --- /dev/null +++ b/Vizhtalk.CDN.Presentation/FileManagerServices/IFileManagerService.cs @@ -0,0 +1,8 @@ +using Vizhtalk.CDN.Presentation.Domain.TypeIds; + +namespace Vizhtalk.CDN.Presentation.FileManagerServices; + +public interface IFileManagerService +{ + Task SaveFile(byte[] fileBytes,string fileExtension, MediaGroupId mediaGroupId); +} diff --git a/Vizhtalk.CDN.Presentation/Infrastructure/EF/Configurations/MediaClientConfiguration.cs b/Vizhtalk.CDN.Presentation/Infrastructure/EF/Configurations/MediaClientConfiguration.cs index 74a8afc..5d09af3 100644 --- a/Vizhtalk.CDN.Presentation/Infrastructure/EF/Configurations/MediaClientConfiguration.cs +++ b/Vizhtalk.CDN.Presentation/Infrastructure/EF/Configurations/MediaClientConfiguration.cs @@ -4,18 +4,16 @@ using Vizhtalk.CDN.Presentation.Domain.Entities; namespace Vizhtalk.CDN.Presentation.Infrastructure.EF.Configurations; -public class MediaClientConfiguration : IEntityTypeConfiguration +public class MediaClientConfiguration : IEntityTypeConfiguration { - public void Configure(EntityTypeBuilder builder) + public void Configure(EntityTypeBuilder builder) { - builder.ToTable(nameof(MediaClient)); + builder.ToTable(nameof(MediaPermission)); builder.HasKey(x => x.Id); builder.Property(x => x.Id) .HasConversion(x => x.Value, x => new Domain.TypeIds.MediaClientId(x)); - builder.Property(x => x.Name) - .HasConversion(x => x.Value, x => new Domain.ValueObjects.MediaClientName(x)); builder.Property(x => x.ClientId) .HasConversion(x => x.Value, x => new Domain.TypeIds.ClientId(x)); @@ -26,11 +24,11 @@ public class MediaClientConfiguration : IEntityTypeConfiguration builder.Property(x => x.MediaId) .HasConversion(x => x.Value, x => new Domain.TypeIds.MediaId(x)); - builder.Metadata.FindNavigation(nameof(MediaClient.Media))? + builder.Metadata.FindNavigation(nameof(MediaPermission.Media))? .SetPropertyAccessMode(PropertyAccessMode.Property); - builder.Metadata.FindNavigation(nameof(MediaClient.Client))? + builder.Metadata.FindNavigation(nameof(MediaPermission.Client))? .SetPropertyAccessMode(PropertyAccessMode.Property); - builder.Metadata.FindNavigation(nameof(MediaClient.MediaGroupMedia))? + builder.Metadata.FindNavigation(nameof(MediaPermission.MediaGroupMedia))? .SetPropertyAccessMode(PropertyAccessMode.Property); } } diff --git a/Vizhtalk.CDN.Presentation/Infrastructure/EF/Configurations/MediaGroupMediaConfiguration.cs b/Vizhtalk.CDN.Presentation/Infrastructure/EF/Configurations/MediaGroupMediaConfiguration.cs index b3f9082..bfbb281 100644 --- a/Vizhtalk.CDN.Presentation/Infrastructure/EF/Configurations/MediaGroupMediaConfiguration.cs +++ b/Vizhtalk.CDN.Presentation/Infrastructure/EF/Configurations/MediaGroupMediaConfiguration.cs @@ -16,6 +16,9 @@ public class MediaGroupMediaConfiguration : IEntityTypeConfiguration x.MediaId) .HasConversion(x => x.Value, x => new Domain.TypeIds.MediaId(x)); + builder.Property(x => x.Name) + .HasConversion(x => x.Value, x => new Domain.ValueObjects.FileName(x)); + builder.Property(x => x.MediaGroupId) .HasConversion(x => x.Value, x => new Domain.TypeIds.MediaGroupId(x)); @@ -26,7 +29,7 @@ public class MediaGroupMediaConfiguration : IEntityTypeConfiguration(); builder.Services.Initialize(connectionString, databaseName); builder.Services.AddGrpc();