AplictionCDN

This commit is contained in:
pns-hassanzadeh
2025-09-06 21:51:16 +03:30
parent 32dcd49d45
commit 80920e69ab
29 changed files with 244 additions and 37 deletions
@@ -0,0 +1,25 @@
using Mapster;
using Vizhtalk.CDN.Presentation.Application.Exceptions;
using Vizhtalk.CDN.Presentation.Domain.Entities;
using VizhTalk.BuildingBlocks.Application.Configuration.Commnad;
using VizhTalk.Grpc.CDN;
namespace Vizhtalk.CDN.Presentation.Application.CommandHandlers;
public class CreateMediaGroupCommandHandler(IUnitOfWork unitOfWork, CDNGrpc.CDNGrpcClient cdnClient) : ICommandHandler<CreateMediaGroupCommand, MediaGroupDTO>
{
public async Task<MediaGroupDTO> Handle(CreateMediaGroupCommand request, CancellationToken cancellationToken)
{
var title = request.Title.Trim();
if (await unitOfWork.MediaGroup.Exists(a => a.Key == request.Key))
throw new MediaGroupKeyAlreadyExistsException();
var mediaGroup = MediaGroup.CreateNew(title,request.Key,request.IsSecure);
await unitOfWork.MediaGroup.AddAsync(mediaGroup, cancellationToken);
await unitOfWork.SaveChangeAsync(cancellationToken);
return mediaGroup.Adapt<MediaGroupDTO>();
}
}
@@ -0,0 +1,13 @@
using VizhTalk.BuildingBlocks.Application.Exceptions;
namespace Vizhtalk.CDN.Presentation.Application.Exceptions;
public class MediaGroupKeyAlreadyExistsException : BaseException
{
public MediaGroupKeyAlreadyExistsException() : base("Media groupKey already exists")
{
}
}
@@ -0,0 +1,13 @@
using VizhTalk.BuildingBlocks.Application.Exceptions;
namespace Vizhtalk.CDN.Presentation.Application.Exceptions;
public class MediaNotFoundException : BaseException
{
public MediaNotFoundException() : base("Media not found")
{
}
}
@@ -0,0 +1,13 @@
using VizhTalk.BuildingBlocks.Application.Exceptions;
namespace Vizhtalk.CDN.Presentation.Application.Exceptions;
public class UnauthorizedForMediaException : BaseException
{
public UnauthorizedForMediaException() : base("Unauthorized for media")
{
}
}
@@ -0,0 +1,5 @@
using VizhTalk.BuildingBlocks.Application;
namespace Vizhtalk.CDN.Presentation.Application;
public interface ICDNModule : IExecutor;
@@ -3,7 +3,7 @@ using VizhTalk.BuildingBlocks.Application;
namespace Vizhtalk.CDN.Presentation.Application
{
internal interface IUnitOfWork : IUnitOfWorkBase
public interface IUnitOfWork : IUnitOfWorkBase
{
IMediaRepository Media { get; }
IClientRepository Client { get; }
@@ -0,0 +1,5 @@
namespace Vizhtalk.CDN.Presentation.Application.QueryHandlers;
public class MediaQueryHandler
{
}
@@ -12,7 +12,7 @@ namespace Vizhtalk.CDN.Presentation.Controllers
public IActionResult Get(MediaGroupMediaId id)
{
return Unauthorized();
}
@@ -5,5 +5,6 @@ namespace Vizhtalk.CDN.Presentation.Domain.Entities;
public class Client : Entity<ClientId>
{
private List<MediaClient> _mediaClients = new();
public IReadOnlyList<MediaClient> MediaClients => _mediaClients;
}
@@ -6,9 +6,10 @@ namespace Vizhtalk.CDN.Presentation.Domain.Entities;
public class Media : Entity<MediaId>
{
private List<MediaGroupMedia> _mediaGroupMedia = new();
public ContentType ContentType { get; private set; }
public int Size { get; set; }
public string Path { get; private set; }
public string Hash { get; set; }
public List<MediaGroupMedia> MediaGroupMedias { get; set; }
public IReadOnlyList<MediaGroupMedia> MediaGroupMedias => _mediaGroupMedia;
}
@@ -6,8 +6,12 @@ namespace Vizhtalk.CDN.Presentation.Domain.Entities;
public class MediaClient : Entity<MediaClientId>
{
public MediaClientName Name { get; set; }
public MediaId MediaId { get; set; }
public ClientId ClientId { get; set; }
public MediaGroupMediaId MediaGroup { get; set; }
public MediaClientName Name { get; private set; }
public MediaId MediaId { get; private set; }
public ClientId ClientId { get; private set; }
public MediaGroupMediaId MediaGroupMediaId { get; set; }
public Client Client { get; private set; }
public Media Media { get; private set; }
public MediaGroupMedia MediaGroupMedia { get; private set; }
}
@@ -6,8 +6,21 @@ namespace Vizhtalk.CDN.Presentation.Domain.Entities;
public class MediaGroup : Entity<MediaGroupId>
{
private MediaGroup(MediaGroupTitle title, string key, bool isSecure)
{
Title = title;
Key = key;
IsSecure = isSecure;
}
private List<MediaGroupMedia> _mediaGroupMedia = new();
public bool IsSecure { get; set; }
public MediaGroupTitle Title { get; set; }
public string Key { get; set; }
public List<MediaGroupMedia> MediaGroupMedias { get; set; }
public IReadOnlyList<MediaGroupMedia> MediaGroupMedias => _mediaGroupMedia;
public static MediaGroup CreateNew(string title, string Key, bool isSecure)
=> new (new MediaGroupTitle(title), Key, isSecure);
}
@@ -5,7 +5,10 @@ namespace Vizhtalk.CDN.Presentation.Domain.Entities;
public class MediaGroupMedia : Entity<MediaGroupMediaId>
{
private List<MediaClient> _mediaClient = new();
public MediaId MediaId { get; set; }
public MediaGroupId MediaGroupId { get; set; }
public List<MediaClient> MediaClients { get; set; }
public Media Media { get; set; }
public MediaGroup MediaGroup { get; set; }
public IReadOnlyList<MediaClient> MediaClients => _mediaClient;
}
@@ -2,7 +2,7 @@
namespace Vizhtalk.CDN.Presentation.Domain.TypeIds;
public class ClientId : TypedIdValueBase<string>
public class ClientId : TypeIdValueBase<string>
{
public ClientId(string value) : base(value)
{
@@ -2,7 +2,7 @@
namespace Vizhtalk.CDN.Presentation.Domain.TypeIds;
public class MediaClientId : TypedIdValueBase<long>
public class MediaClientId : TypeIdValueBase<long>
{
public MediaClientId(long value) : base(value)
{
@@ -2,7 +2,7 @@
namespace Vizhtalk.CDN.Presentation.Domain.TypeIds;
public class MediaGroupId : TypedIdValueBase<long>
public class MediaGroupId : TypeIdValueBase<long>
{
public MediaGroupId(long value) : base(value)
{
@@ -2,7 +2,7 @@
namespace Vizhtalk.CDN.Presentation.Domain.TypeIds;
public class MediaGroupMediaId : TypedIdValueBase<Guid>
public class MediaGroupMediaId : TypeIdValueBase<Guid>
{
public MediaGroupMediaId(Guid value) : base(value)
{
@@ -2,7 +2,7 @@
namespace Vizhtalk.CDN.Presentation.Domain.TypeIds;
public class MediaId : TypedIdValueBase<long>
public class MediaId : TypeIdValueBase<long>
{
public MediaId(long value) : base(value)
{
@@ -5,11 +5,15 @@ namespace Vizhtalk.CDN.Presentation.Domain.ValueObjects;
public class ContentType : ValueObject
{
private ContentType()
{
}
public ContentType(string contentType, byte[] fileBytes)
{
CheckRule(new ValidContentTypeRule(contentType, fileBytes));
Value = contentType;
}
public string Value { get; set; }
public string Value { get; private set; }
}
@@ -16,23 +16,16 @@ namespace Vizhtalk.CDN.Presentation.Infrastructure;
public static class CDNStartup
{
public static void Initialize(this IServiceCollection services, string connectionString)
public static void Initialize(this IServiceCollection services, string connectionString, string databaseName)
{
var domainNotificationsMap = new BiDictionary<string, Type>();
services.AddDbContext<CDNDbContext>(op =>
services.AddDbContext<CDNDbContext>(options =>
{
op.UseMongoDB(connectionString);
options.UseMongoDB(connectionString, databaseName);
});
services.AddScoped<DbContext>(sp => sp.GetService<CDNDbContext>());
}
public static void UseCDNModule(this IApplicationBuilder app)
{
using var scope = app.ApplicationServices.CreateScope();
var db = scope.ServiceProvider.GetRequiredService<CDNDbContext>();
db.Database.Migrate();
}
}
@@ -0,0 +1,61 @@
using Vizhtalk.CDN.Presentation.Application;
using VizhTalk.BuildingBlocks.Application.Configuration.Commnad;
using VizhTalk.BuildingBlocks.Application.Configuration.Query;
using VizhTalk.BuildingBlocks.Application.Contract;
using VizhTalk.BuildingBlocks.Domain;
using VizhTalk.BuildingBlocks.Domain.Contract;
namespace Vizhtalk.CDN.Presentation.Infrastructure.Configuration;
public class CDNModule(IServiceProvider serviceProvider) : ICDNModule
{
public async Task Execute(ICommand command, CancellationToken cancellationToken = default)
{
var handlerType = typeof(ICommandHandler<>).MakeGenericType(command.GetType());
var handler = serviceProvider.GetRequiredService(handlerType);
dynamic dynamicHandler = handler;
dynamic dynamicCommand = command;
await dynamicHandler.Handle(dynamicCommand, cancellationToken);
}
public async Task<TResult> Execute<TResult>(ICommand<TResult> command, CancellationToken cancellationToken = default)
{
var commandType = command.GetType(); // مثلا CreateUserCommand
var resultType = commandType.GetInterface(typeof(ICommand<>).Name)!.GetGenericArguments()[0];
// حالا handlerType میشه:
var handlerType = typeof(ICommandHandler<,>).MakeGenericType(commandType, resultType);
var handler = serviceProvider.GetRequiredService(handlerType);
dynamic dynamicHandler = handler;
dynamic dynamicCommand = command;
return await dynamicHandler.Handle(dynamicCommand, cancellationToken);
}
public async Task<TResult> Execute<TResult>(IQuery<TResult> query, CancellationToken cancellationToken = default)
{
var queryType = query.GetType();
var resultType = queryType.GetInterface(typeof(IQuery<>).Name)!.GetGenericArguments()[0];
// حالا handlerType میشه:
var handlerType = typeof(IQueryHandler<,>).MakeGenericType(queryType, resultType);
var handler = serviceProvider.GetRequiredService(handlerType);
dynamic dynamicHandler = handler;
dynamic dynamicQuery = query;
return await dynamicHandler.Handle(dynamicQuery, cancellationToken);
}
public async Task Execute(IDomainEvent notification, CancellationToken cancellationToken = default)
{
var handlerType = typeof(INotificationHandler<>).MakeGenericType(notification.GetType());
var handler = serviceProvider.GetRequiredService(handlerType);
dynamic dynamicHandler = handler;
dynamic dynamicNotification = notification;
await dynamicHandler.Handle(dynamicNotification, cancellationToken);
}
}
@@ -13,5 +13,8 @@ public class ClientConfiguration : IEntityTypeConfiguration<Client>
builder.Property(x => x.Id)
.HasConversion(x => x.Value, x => new Domain.TypeIds.ClientId(x));
builder.Metadata.FindNavigation(nameof(Client.MediaClients))?
.SetPropertyAccessMode(PropertyAccessMode.Property);
}
}
@@ -13,5 +13,24 @@ public class MediaClientConfiguration : IEntityTypeConfiguration<MediaClient>
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));
builder.Property(x => x.MediaGroupMediaId)
.HasConversion(x => x.Value, x => new Domain.TypeIds.MediaGroupMediaId(x));
builder.Property(x => x.MediaId)
.HasConversion(x => x.Value, x => new Domain.TypeIds.MediaId(x));
builder.Metadata.FindNavigation(nameof(MediaClient.Media))?
.SetPropertyAccessMode(PropertyAccessMode.Property);
builder.Metadata.FindNavigation(nameof(MediaClient.Client))?
.SetPropertyAccessMode(PropertyAccessMode.Property);
builder.Metadata.FindNavigation(nameof(MediaClient.MediaGroupMedia))?
.SetPropertyAccessMode(PropertyAccessMode.Property);
}
}
@@ -11,9 +11,20 @@ public class MediaConfiguration : IEntityTypeConfiguration<Media>
builder.ToTable(nameof(Media));
builder.HasKey(x => x.Id);
builder.Property(x => x.Id)
.HasColumnName("MediaId")
.HasElementName("_id")
.HasConversion(x => x.Value, x => new Domain.TypeIds.MediaId(x));
builder.HasIndex(x => x.Hash);
builder.OwnsOne(x => x.ContentType, op =>
{
op.Property(x => x.Value)
.HasColumnName("ContentType")
.HasMaxLength(50);
});
builder.HasIndex(x => x.Hash);
builder.Metadata.FindNavigation(nameof(Media.MediaGroupMedias))?
.SetPropertyAccessMode(PropertyAccessMode.Property);
}
}
@@ -12,5 +12,9 @@ public class MediaGroupConfiguration : IEntityTypeConfiguration<MediaGroup>
builder.HasKey(x => x.Id);
builder.Property(x => x.Id)
.HasConversion(x => x.Value, x => new Domain.TypeIds.MediaGroupId(x));
builder.Metadata.FindNavigation(nameof(MediaGroup.MediaGroupMedias))?
.SetPropertyAccessMode(PropertyAccessMode.Property);
}
}
@@ -12,5 +12,21 @@ public class MediaGroupMediaConfiguration : IEntityTypeConfiguration<MediaGroupM
builder.HasKey(x => x.Id);
builder.Property(x => x.Id)
.HasConversion(x => x.Value, x => new Domain.TypeIds.MediaGroupMediaId(x));
builder.Property(x => x.MediaId)
.HasConversion(x => x.Value, x => new Domain.TypeIds.MediaId(x));
builder.Property(x => x.MediaGroupId)
.HasConversion(x => x.Value, x => new Domain.TypeIds.MediaGroupId(x));
builder.Metadata.FindNavigation(nameof(MediaGroupMedia.Media))?
.SetPropertyAccessMode(PropertyAccessMode.Property);
builder.Metadata.FindNavigation(nameof(MediaGroupMedia.MediaGroup))?
.SetPropertyAccessMode(PropertyAccessMode.Property);
builder.Metadata.FindNavigation(nameof(MediaGroupMedia.MediaClients))?
.SetPropertyAccessMode(PropertyAccessMode.Property);
}
}
+3 -3
View File
@@ -5,9 +5,9 @@ using Vizhtalk.CDN.Presentation.Services;
var builder = WebApplication.CreateBuilder(args);
var connectionString = builder.Configuration.GetConnectionString("VizhTalk.CDN")!;
var databaseName = builder.Configuration.GetConnectionString("VizhTalk.CDN.DatabaseName")!;
builder.Services.Initialize(connectionString);
builder.Services.Initialize(connectionString, databaseName);
builder.Services.AddGrpc();
var app = builder.Build();
@@ -18,6 +18,6 @@ app.MapGet("/", () => "Hello World!");
app.MapGrpcService<CDNService>();
app.UseCDNModule();
//app.UseCDNModule();
app.Run();
@@ -18,8 +18,7 @@
</ItemGroup>
<ItemGroup>
<Folder Include="Application\CommandHandlers\" />
<Folder Include="Application\QueryHandlers\" />
<Folder Include="Application\Mappers\" />
</ItemGroup>
</Project>
+4 -3
View File
@@ -5,8 +5,9 @@
"Microsoft.AspNetCore": "Warning"
}
},
"ConnectionStrings": {
"VizhTalk.CDN": "mongodb://localhost:27017/VizhTalk_CDN"
},
"ConnectionStrings": {
"VizhTalk.CDN": "mongodb://localhost:27017",
"VizhTalk.CDN.DatabaseName": "VizhTalk.CDN"
},
"AllowedHosts": "*"
}